<commit_before>/*
 *    Copyright (C) 2015 Christian Muehlhaeuser
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 */

// beehive's RESTful api for introspection and configuration
package api

import (
	_ "log"
	"net/http"
	_ "strconv"

	"github.com/muesli/beehive/bees"

	"github.com/emicklei/go-restful"
	_ "github.com/emicklei/go-restful/swagger"
)

// POST http://localhost:8080/v1/hives
//
// GET http://localhost:8080/v1/hives/1
//
// PUT http://localhost:8080/v1/hives/1
//
// DELETE http://localhost:8080/v1/hives/1
//

type Hive struct {
	Id          string
	Description string
	Image       string
	Options     []bees.BeeOptionDescriptor
	Events      []bees.EventDescriptor
	Actions     []bees.ActionDescriptor
}

type HiveResource struct {
}

func (r HiveResource) Register(container *restful.Container) {
	ws := new(restful.WebService)
	ws.Path("/v1/hives").
		Doc("Manage Hives").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON)

	ws.Route(ws.GET("").To(r.findHives).
		// docs
		Doc("get all hives").
		Operation("findHives").
		Writes(Hive{}))

	container.Add(ws)
}

// GET http://localhost:8080/v1/hives
//
func (r HiveResource) findHives(request *restful.Request, response *restful.Response) {
	hives := bees.GetBeeFactories()
	if len(hives) == 0 {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "404: No hives could be found.")
		return
	}

	res := []Hive{}
	for _, hive := range hives {
		h := Hive{
			Id:          (*hive).Name(),
			Description: (*hive).Description(),
			Image:       (*hive).Image(),
			Options:     (*hive).Options(),
			Events:      (*hive).Events(),
			Actions:     (*hive).Actions(),
		}

		res = append(res, h)
	}
	response.WriteEntity(res)
}
<commit_msg>Made hives API ember-data compatible.<commit_after>/*
 *    Copyright (C) 2015 Christian Muehlhaeuser
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors:
 *      Christian Muehlhaeuser <muesli@gmail.com>
 */

// beehive's RESTful api for introspection and configuration
package api

import (
	_ "log"
	"net/http"
	_ "strconv"

	"github.com/muesli/beehive/bees"

	"github.com/emicklei/go-restful"
	_ "github.com/emicklei/go-restful/swagger"
)

// POST http://localhost:8080/v1/hives
//
// GET http://localhost:8080/v1/hives/1
//
// PUT http://localhost:8080/v1/hives/1
//
// DELETE http://localhost:8080/v1/hives/1
//

type Hive struct {
	Id          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	Image       string `json:"image"`
	Options     []bees.BeeOptionDescriptor
	Events      []bees.EventDescriptor
	Actions     []bees.ActionDescriptor
}

type Hives struct {
	Hives 		[]Hive
}

type HiveResource struct {
}

func (r HiveResource) Register(container *restful.Container) {
	ws := new(restful.WebService)
	ws.Path("/v1/hives").
		Doc("Manage Hives").
		Consumes(restful.MIME_JSON).
		Produces(restful.MIME_JSON)

	ws.Route(ws.GET("").To(r.findHives).
		// docs
		Doc("get all hives").
		Operation("findHives").
		Writes(Hive{}))

	container.Add(ws)
}

// GET http://localhost:8080/v1/hives
//
func (r HiveResource) findHives(request *restful.Request, response *restful.Response) {
	hives := bees.GetBeeFactories()
	if len(hives) == 0 {
		response.AddHeader("Content-Type", "text/plain")
		response.WriteErrorString(http.StatusNotFound, "404: No hives could be found.")
		return
	}

	res := Hives{}
	for _, hive := range hives {
		h := Hive{
			Id:          (*hive).Name(),
			Name:        (*hive).Name(),
			Description: (*hive).Description(),
			Image:       "/images/" + (*hive).Image(),
			Options:     (*hive).Options(),
			Events:      (*hive).Events(),
			Actions:     (*hive).Actions(),
		}

		res.Hives = append(res.Hives, h)
	}
	response.WriteEntity(res)
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"io"
	"net/http"
)

func init() {
	Router.PathPrefix("/xhr/stats/").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			rsp, err := http.Get(fmt.Sprintf("http://127.0.0.1:8874%s", r.URL.Path[10:]))
			if rsp != nil {
				defer rsp.Body.Close()
			}
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			io.Copy(w, rsp.Body)
		},
	))
}
<commit_msg>work around the inflexible fetch api that the ui uses in the backend because: fn javascript hype...<commit_after>package api

import (
	"fmt"
	"io"
	"net/http"
)

func statsPassthrough(uri string, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	rsp, err := http.Get(fmt.Sprintf("http://127.0.0.1:8874%s", uri))
	if rsp != nil {
		defer rsp.Body.Close()
	}
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	io.Copy(w, rsp.Body)
}

func init() {
	Router.PathPrefix("/api/v0/xhr/stats/").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			statsPassthrough(r.URL.Path[17:], w, r)
		},
	))
	Router.PathPrefix("/xhr/stats/").Methods("GET").Handler(jwtHandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			statsPassthrough(r.URL.Path[10:], w, r)
		},
	))
}
<|endoftext|>


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

// Package compress 提供一个支持内容压缩的中间件。
package compress

import (
	"bufio"
	"compress/flate"
	"compress/gzip"
	"io"
	"net"
	"net/http"
	"strings"
)

// 同时实现了 http.ResponseWriter 和 http.Hijacker 接口。
type compressWriter struct {
	gzw io.Writer
	rw  http.ResponseWriter
	hj  http.Hijacker
}

func (cw *compressWriter) Write(bs []byte) (int, error) {
	h := cw.rw.Header()
	if h.Get("Content-Type") == "" {
		h.Set("Content-Type", http.DetectContentType(bs))
	}

	return cw.gzw.Write(bs)
}

func (cw *compressWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return cw.hj.Hijack()
}

func (cw *compressWriter) Header() http.Header {
	return cw.rw.Header()
}

func (cw *compressWriter) WriteHeader(code int) {
	cw.rw.WriteHeader(code)
}

type compress struct {
	h http.Handler
}

// New 构建一个支持压缩的中间件。
// 支持 gzip 或是 deflate 功能的 handler。
// 根据客户端请求内容自动匹配相应的压缩算法，优先匹配 gzip。
//
// NOTE: 经过压缩的内容，可能需要重新指定 Content-Type，系统检测的类型未必正确。
func New(next http.Handler) http.Handler {
	return &compress{h: next}
}

func (c *compress) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		c.h.ServeHTTP(w, r)
		return
	}

	var gzw io.WriteCloser
	var encoding string
	encodings := strings.Split(r.Header.Get("Accept-Encoding"), ",")
	for _, encoding = range encodings {
		encoding = strings.ToLower(strings.TrimSpace(encoding))

		if encoding == "gzip" {
			gzw = gzip.NewWriter(w)
			break
		}

		if encoding == "deflate" {
			var err error
			gzw, err = flate.NewWriter(w, flate.DefaultCompression)
			if err != nil { // 若出错，不压缩，直接返回
				c.h.ServeHTTP(w, r)
				return
			}
			break
		}
	} // end for
	if gzw == nil { // 不支持的压缩格式
		return
	}

	w.Header().Set("Content-Encoding", encoding)
	w.Header().Add("Vary", "Accept-Encoding")
	cw := &compressWriter{
		gzw: gzw,
		rw:  w,
		hj:  hj,
	}

	// 只要 gzw!=nil 的，必须会执行到此处。
	defer gzw.Close()

	// 此处可能 panic，所以得保证在 panic 之前，gzw 变量已经 Close
	c.h.ServeHTTP(cw, r)
}
<commit_msg>为 compress 添加错误日志输出功能<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package compress 提供一个支持内容压缩的中间件。
package compress

import (
	"bufio"
	"compress/flate"
	"compress/gzip"
	"io"
	"log"
	"net"
	"net/http"
	"strings"
)

// 同时实现了 http.ResponseWriter 和 http.Hijacker 接口。
type compressWriter struct {
	gzw io.Writer
	rw  http.ResponseWriter
	hj  http.Hijacker
}

func (cw *compressWriter) Write(bs []byte) (int, error) {
	h := cw.rw.Header()
	if h.Get("Content-Type") == "" {
		h.Set("Content-Type", http.DetectContentType(bs))
	}

	return cw.gzw.Write(bs)
}

func (cw *compressWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return cw.hj.Hijack()
}

func (cw *compressWriter) Header() http.Header {
	return cw.rw.Header()
}

func (cw *compressWriter) WriteHeader(code int) {
	cw.rw.WriteHeader(code)
}

type compress struct {
	h      http.Handler
	errlog *log.Logger
}

// New 构建一个支持压缩的中间件。
// 支持 gzip 或是 deflate 功能的 handler。
// 根据客户端请求内容自动匹配相应的压缩算法，优先匹配 gzip。
//
// NOTE: 经过压缩的内容，可能需要重新指定 Content-Type，系统检测的类型未必正确。
func New(next http.Handler, errlog *log.Logger) http.Handler {
	return &compress{
		h:      next,
		errlog: errlog,
	}
}

func (c *compress) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		c.h.ServeHTTP(w, r)
		return
	}

	var gzw io.WriteCloser
	var encoding string
	encodings := strings.Split(r.Header.Get("Accept-Encoding"), ",")
	for _, encoding = range encodings {
		encoding = strings.ToLower(strings.TrimSpace(encoding))

		if encoding == "gzip" {
			gzw = gzip.NewWriter(w)
			break
		}

		if encoding == "deflate" {
			var err error
			gzw, err = flate.NewWriter(w, flate.DefaultCompression)
			if err != nil { // 若出错，不压缩，直接返回
				c.errlog.Println(err)
				c.h.ServeHTTP(w, r)
				return
			}
			break
		}
	} // end for
	if gzw == nil { // 不支持的压缩格式
		return
	}

	w.Header().Set("Content-Encoding", encoding)
	w.Header().Add("Vary", "Accept-Encoding")
	cw := &compressWriter{
		gzw: gzw,
		rw:  w,
		hj:  hj,
	}

	// 只要 gzw!=nil 的，必须会执行到此处。
	defer gzw.Close()

	// 此处可能 panic，所以得保证在 panic 之前，gzw 变量已经 Close
	c.h.ServeHTTP(cw, r)
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"net/http"
)

type APIResponse struct {
	Results []Result `json:"list"`
	Tags    []string `json:"tags"`
	Type    string   `json:"result_type"`
}

type Result struct {
	Id         int32  `json:"defid"`
	Author     string `json:"author"`
	Definition string `json:"definition"`
	Link       string `json:"permalink"`
	ThumbsDown int32  `json:"thumbs_down"`
	ThumbsUp   int32  `json:"thumbs_up"`
	Word       string `json:"word"`
}

func DefineWord(word string) (response *APIResponse, err error) {

	endpoint := fmt.Sprintf("http://api.urbandictionary.com/v0/define?page=%d&term=%s", 1, word)
	resp, err := http.Get(endpoint)

	if err != nil {
		return
	}

	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&response)
	if err != nil {
		return nil, err
	}

	return
}
<commit_msg>fine . deal w/ lots of words<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

type APIResponse struct {
	Results []Result `json:"list"`
	Tags    []string `json:"tags"`
	Type    string   `json:"result_type"`
}

type Result struct {
	Id         int32  `json:"defid"`
	Author     string `json:"author"`
	Definition string `json:"definition"`
	Link       string `json:"permalink"`
	ThumbsDown int32  `json:"thumbs_down"`
	ThumbsUp   int32  `json:"thumbs_up"`
	Word       string `json:"word"`
}

func DefineWord(word string) (response *APIResponse, err error) {

	endpoint := fmt.Sprintf("http://api.urbandictionary.com/v0/define?page=%d&term=%s", 1, url.QueryEscape(word))
	resp, err := http.Get(endpoint)

	if err != nil {
		return
	}

	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&response)
	if err != nil {
		return nil, err
	}

	return
}
<|endoftext|>


<commit_before>package namesys

import (
	"bytes"
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	pb "github.com/ipfs/go-ipfs/namesys/pb"
	path "github.com/ipfs/go-ipfs/path"
	pin "github.com/ipfs/go-ipfs/pin"
	ft "github.com/ipfs/go-ipfs/unixfs"

	u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
	routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing"
	proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
	peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer"
	ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto"
	ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore"
	dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query"
	base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32"
)

const ipnsPrefix = "/ipns/"

const PublishPutValTimeout = time.Minute
const DefaultRecordTTL = 24 * time.Hour

// IpnsPublisher is capable of publishing and resolving names to the IPFS
// routing system.
type IpnsPublisher struct {
	routing routing.ValueStore
	ds      ds.Datastore

	// Used to ensure we assign IPNS records *sequential* sequence numbers.
	mu sync.Mutex
}

// NewIpnsPublisher constructs a publisher for the IPFS Routing name system.
func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher {
	if ds == nil {
		panic("nil datastore")
	}
	return &IpnsPublisher{routing: route, ds: ds}
}

// Publish implements Publisher. Accepts a keypair and a value,
// and publishes it out to the routing system
func (p *IpnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
	log.Debugf("Publish %s", value)
	return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL))
}

func IpnsDsKey(id peer.ID) ds.Key {
	return ds.NewKey("/ipns/" + base32.RawStdEncoding.EncodeToString([]byte(id)))
}

// PublishedNames returns the latest IPNS records published by this node and
// their expiration times.
//
// This method will not search the routing system for records published by other
// nodes.
func (p *IpnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) {
	query, err := p.ds.Query(dsquery.Query{
		Prefix: ipnsPrefix,
	})
	if err != nil {
		return nil, err
	}
	defer query.Close()

	records := make(map[peer.ID]*pb.IpnsEntry)
	for {
		select {
		case result, ok := <-query.Next():
			if !ok {
				return records, nil
			}
			if result.Error != nil {
				return nil, result.Error
			}
			value, ok := result.Value.([]byte)
			if !ok {
				log.Error("found ipns record that we couldn't convert to a value")
				continue
			}
			e := new(pb.IpnsEntry)
			if err := proto.Unmarshal(value, e); err != nil {
				// Might as well return what we can.
				log.Error("found an invalid IPNS entry:", err)
				continue
			}
			if !strings.HasPrefix(result.Key, ipnsPrefix) {
				log.Errorf("datastore query for keys with prefix %s returned a key: %s", ipnsPrefix, result.Key)
				continue
			}
			k := result.Key[len(ipnsPrefix):]
			pid, err := base32.RawStdEncoding.DecodeString(k)
			if err != nil {
				log.Errorf("ipns ds key invalid: %s", result.Key)
				continue
			}
			records[peer.ID(pid)] = e
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	}
}

// GetPublished returns the record this node has published corresponding to the
// given peer ID.
//
// If `checkRouting` is true and we have no existing record, this method will
// check the routing system for any existing records.
func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*30)
	defer cancel()

	dsVal, err := p.ds.Get(IpnsDsKey(id))
	var value []byte
	switch err {
	case nil:
		var ok bool
		value, ok = dsVal.([]byte)
		if !ok {
			return nil, fmt.Errorf("found ipns record that we couldn't convert to a value")
		}
	case ds.ErrNotFound:
		if !checkRouting {
			return nil, nil
		}
		_, ipnskey := IpnsKeysForID(id)
		value, err = p.routing.GetValue(ctx, ipnskey)
		if err != nil {
			// Not found or other network issue. Can't really do
			// anything about this case.
			return nil, nil
		}
	default:
		return nil, err
	}
	e := new(pb.IpnsEntry)
	if err := proto.Unmarshal(value, e); err != nil {
		return nil, err
	}
	return e, nil
}

func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) {
	id, err := peer.IDFromPrivateKey(k)
	if err != nil {
		return nil, err
	}

	p.mu.Lock()
	defer p.mu.Unlock()

	// get previous records sequence number
	rec, err := p.GetPublished(ctx, id, true)
	if err != nil {
		return nil, err
	}

	seqno := rec.GetSequence() // returns 0 if rec is nil
	if rec != nil && value != path.Path(rec.GetValue()) {
		// Don't bother incrementing the sequence number unless the
		// value changes.
		seqno++
	}

	// Create record
	entry, err := CreateRoutingEntryData(k, value, seqno, eol)
	if err != nil {
		return nil, err
	}

	// Set the TTL
	// TODO: Make this less hacky.
	ttl, ok := checkCtxTTL(ctx)
	if ok {
		entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
	}

	data, err := proto.Marshal(entry)
	if err != nil {
		return nil, err
	}

	// Put the new record.
	if err := p.ds.Put(IpnsDsKey(id), data); err != nil {
		return nil, err
	}
	return entry, nil
}

// PublishWithEOL is a temporary stand in for the ipns records implementation
// see here for more details: https://github.com/ipfs/specs/tree/master/records
func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {
	record, err := p.updateRecord(ctx, k, value, eol)
	if err != nil {
		return err
	}

	return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record)
}

// setting the TTL on published records is an experimental feature.
// as such, i'm using the context to wire it through to avoid changing too
// much code along the way.
func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
	v := ctx.Value("ipns-publish-ttl")
	if v == nil {
		return 0, false
	}

	d, ok := v.(time.Duration)
	return d, ok
}

func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, entry *pb.IpnsEntry) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	errs := make(chan error, 2) // At most two errors (IPNS, and public key)

	id, err := peer.IDFromPublicKey(k)
	if err != nil {
		return err
	}

	// Attempt to extract the public key from the ID
	extractedPublicKey, err := id.ExtractPublicKey()
	if err != nil {
		return err
	}

	namekey, ipnskey := IpnsKeysForID(id)

	go func() {
		errs <- PublishEntry(ctx, r, ipnskey, entry)
	}()

	// Publish the public key if a public key cannot be extracted from the ID
	if extractedPublicKey == nil {
		go func() {
			errs <- PublishPublicKey(ctx, r, namekey, k)
		}()

		if err := waitOnErrChan(ctx, errs); err != nil {
			return err
		}
	}

	return waitOnErrChan(ctx, errs)
}

func waitOnErrChan(ctx context.Context, errs chan error) error {
	select {
	case err := <-errs:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}

func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error {
	log.Debugf("Storing pubkey at: %s", k)
	pkbytes, err := pubk.Bytes()
	if err != nil {
		return err
	}

	// Store associated public key
	timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
	defer cancel()
	return r.PutValue(timectx, k, pkbytes)
}

func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error {
	timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
	defer cancel()

	data, err := proto.Marshal(rec)
	if err != nil {
		return err
	}

	log.Debugf("Storing ipns entry at: %s", ipnskey)
	// Store ipns entry at "/ipns/"+h(pubkey)
	return r.PutValue(timectx, ipnskey, data)
}

func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) {
	entry := new(pb.IpnsEntry)

	entry.Value = []byte(val)
	typ := pb.IpnsEntry_EOL
	entry.ValidityType = &typ
	entry.Sequence = proto.Uint64(seq)
	entry.Validity = []byte(u.FormatRFC3339(eol))

	sig, err := pk.Sign(ipnsEntryDataForSig(entry))
	if err != nil {
		return nil, err
	}
	entry.Signature = sig
	return entry, nil
}

func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte {
	return bytes.Join([][]byte{
		e.Value,
		e.Validity,
		[]byte(fmt.Sprint(e.GetValidityType())),
	},
		[]byte{})
}

// InitializeKeyspace sets the ipns record for the given key to
// point to an empty directory.
// TODO: this doesnt feel like it belongs here
func InitializeKeyspace(ctx context.Context, pub Publisher, pins pin.Pinner, key ci.PrivKey) error {
	emptyDir := ft.EmptyDirNode()

	// pin recursively because this might already be pinned
	// and doing a direct pin would throw an error in that case
	err := pins.Pin(ctx, emptyDir, true)
	if err != nil {
		return err
	}

	err = pins.Flush()
	if err != nil {
		return err
	}

	return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid()))
}

func IpnsKeysForID(id peer.ID) (name, ipns string) {
	namekey := "/pk/" + string(id)
	ipnskey := "/ipns/" + string(id)

	return namekey, ipnskey
}
<commit_msg>log on network error when resolving the last published IPNS record<commit_after>package namesys

import (
	"bytes"
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	pb "github.com/ipfs/go-ipfs/namesys/pb"
	path "github.com/ipfs/go-ipfs/path"
	pin "github.com/ipfs/go-ipfs/pin"
	ft "github.com/ipfs/go-ipfs/unixfs"

	u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
	routing "gx/ipfs/QmUHRKTeaoASDvDj7cTAXsmjAY7KQ13ErtzkQHZQq6uFUz/go-libp2p-routing"
	proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
	peer "gx/ipfs/QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74/go-libp2p-peer"
	ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto"
	ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore"
	dsquery "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/query"
	base32 "gx/ipfs/QmfVj3x4D6Jkq9SEoi5n2NmoUomLwoeiwnYz2KQa15wRw6/base32"
)

const ipnsPrefix = "/ipns/"

const PublishPutValTimeout = time.Minute
const DefaultRecordTTL = 24 * time.Hour

// IpnsPublisher is capable of publishing and resolving names to the IPFS
// routing system.
type IpnsPublisher struct {
	routing routing.ValueStore
	ds      ds.Datastore

	// Used to ensure we assign IPNS records *sequential* sequence numbers.
	mu sync.Mutex
}

// NewIpnsPublisher constructs a publisher for the IPFS Routing name system.
func NewIpnsPublisher(route routing.ValueStore, ds ds.Datastore) *IpnsPublisher {
	if ds == nil {
		panic("nil datastore")
	}
	return &IpnsPublisher{routing: route, ds: ds}
}

// Publish implements Publisher. Accepts a keypair and a value,
// and publishes it out to the routing system
func (p *IpnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
	log.Debugf("Publish %s", value)
	return p.PublishWithEOL(ctx, k, value, time.Now().Add(DefaultRecordTTL))
}

func IpnsDsKey(id peer.ID) ds.Key {
	return ds.NewKey("/ipns/" + base32.RawStdEncoding.EncodeToString([]byte(id)))
}

// PublishedNames returns the latest IPNS records published by this node and
// their expiration times.
//
// This method will not search the routing system for records published by other
// nodes.
func (p *IpnsPublisher) ListPublished(ctx context.Context) (map[peer.ID]*pb.IpnsEntry, error) {
	query, err := p.ds.Query(dsquery.Query{
		Prefix: ipnsPrefix,
	})
	if err != nil {
		return nil, err
	}
	defer query.Close()

	records := make(map[peer.ID]*pb.IpnsEntry)
	for {
		select {
		case result, ok := <-query.Next():
			if !ok {
				return records, nil
			}
			if result.Error != nil {
				return nil, result.Error
			}
			value, ok := result.Value.([]byte)
			if !ok {
				log.Error("found ipns record that we couldn't convert to a value")
				continue
			}
			e := new(pb.IpnsEntry)
			if err := proto.Unmarshal(value, e); err != nil {
				// Might as well return what we can.
				log.Error("found an invalid IPNS entry:", err)
				continue
			}
			if !strings.HasPrefix(result.Key, ipnsPrefix) {
				log.Errorf("datastore query for keys with prefix %s returned a key: %s", ipnsPrefix, result.Key)
				continue
			}
			k := result.Key[len(ipnsPrefix):]
			pid, err := base32.RawStdEncoding.DecodeString(k)
			if err != nil {
				log.Errorf("ipns ds key invalid: %s", result.Key)
				continue
			}
			records[peer.ID(pid)] = e
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	}
}

// GetPublished returns the record this node has published corresponding to the
// given peer ID.
//
// If `checkRouting` is true and we have no existing record, this method will
// check the routing system for any existing records.
func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouting bool) (*pb.IpnsEntry, error) {
	ctx, cancel := context.WithTimeout(ctx, time.Second*30)
	defer cancel()

	dsVal, err := p.ds.Get(IpnsDsKey(id))
	var value []byte
	switch err {
	case nil:
		var ok bool
		value, ok = dsVal.([]byte)
		if !ok {
			return nil, fmt.Errorf("found ipns record that we couldn't convert to a value")
		}
	case ds.ErrNotFound:
		if !checkRouting {
			return nil, nil
		}
		_, ipnskey := IpnsKeysForID(id)
		value, err = p.routing.GetValue(ctx, ipnskey)
		if err != nil {
			// Not found or other network issue. Can't really do
			// anything about this case.
			if err != routing.ErrNotFound {
				log.Debugf("error when determining the last published IPNS record for %s: %s", id, err)
			}

			return nil, nil
		}
	default:
		return nil, err
	}
	e := new(pb.IpnsEntry)
	if err := proto.Unmarshal(value, e); err != nil {
		return nil, err
	}
	return e, nil
}

func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) {
	id, err := peer.IDFromPrivateKey(k)
	if err != nil {
		return nil, err
	}

	p.mu.Lock()
	defer p.mu.Unlock()

	// get previous records sequence number
	rec, err := p.GetPublished(ctx, id, true)
	if err != nil {
		return nil, err
	}

	seqno := rec.GetSequence() // returns 0 if rec is nil
	if rec != nil && value != path.Path(rec.GetValue()) {
		// Don't bother incrementing the sequence number unless the
		// value changes.
		seqno++
	}

	// Create record
	entry, err := CreateRoutingEntryData(k, value, seqno, eol)
	if err != nil {
		return nil, err
	}

	// Set the TTL
	// TODO: Make this less hacky.
	ttl, ok := checkCtxTTL(ctx)
	if ok {
		entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
	}

	data, err := proto.Marshal(entry)
	if err != nil {
		return nil, err
	}

	// Put the new record.
	if err := p.ds.Put(IpnsDsKey(id), data); err != nil {
		return nil, err
	}
	return entry, nil
}

// PublishWithEOL is a temporary stand in for the ipns records implementation
// see here for more details: https://github.com/ipfs/specs/tree/master/records
func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {
	record, err := p.updateRecord(ctx, k, value, eol)
	if err != nil {
		return err
	}

	return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record)
}

// setting the TTL on published records is an experimental feature.
// as such, i'm using the context to wire it through to avoid changing too
// much code along the way.
func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
	v := ctx.Value("ipns-publish-ttl")
	if v == nil {
		return 0, false
	}

	d, ok := v.(time.Duration)
	return d, ok
}

func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey, entry *pb.IpnsEntry) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	errs := make(chan error, 2) // At most two errors (IPNS, and public key)

	id, err := peer.IDFromPublicKey(k)
	if err != nil {
		return err
	}

	// Attempt to extract the public key from the ID
	extractedPublicKey, err := id.ExtractPublicKey()
	if err != nil {
		return err
	}

	namekey, ipnskey := IpnsKeysForID(id)

	go func() {
		errs <- PublishEntry(ctx, r, ipnskey, entry)
	}()

	// Publish the public key if a public key cannot be extracted from the ID
	if extractedPublicKey == nil {
		go func() {
			errs <- PublishPublicKey(ctx, r, namekey, k)
		}()

		if err := waitOnErrChan(ctx, errs); err != nil {
			return err
		}
	}

	return waitOnErrChan(ctx, errs)
}

func waitOnErrChan(ctx context.Context, errs chan error) error {
	select {
	case err := <-errs:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}

func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error {
	log.Debugf("Storing pubkey at: %s", k)
	pkbytes, err := pubk.Bytes()
	if err != nil {
		return err
	}

	// Store associated public key
	timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
	defer cancel()
	return r.PutValue(timectx, k, pkbytes)
}

func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error {
	timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
	defer cancel()

	data, err := proto.Marshal(rec)
	if err != nil {
		return err
	}

	log.Debugf("Storing ipns entry at: %s", ipnskey)
	// Store ipns entry at "/ipns/"+h(pubkey)
	return r.PutValue(timectx, ipnskey, data)
}

func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) {
	entry := new(pb.IpnsEntry)

	entry.Value = []byte(val)
	typ := pb.IpnsEntry_EOL
	entry.ValidityType = &typ
	entry.Sequence = proto.Uint64(seq)
	entry.Validity = []byte(u.FormatRFC3339(eol))

	sig, err := pk.Sign(ipnsEntryDataForSig(entry))
	if err != nil {
		return nil, err
	}
	entry.Signature = sig
	return entry, nil
}

func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte {
	return bytes.Join([][]byte{
		e.Value,
		e.Validity,
		[]byte(fmt.Sprint(e.GetValidityType())),
	},
		[]byte{})
}

// InitializeKeyspace sets the ipns record for the given key to
// point to an empty directory.
// TODO: this doesnt feel like it belongs here
func InitializeKeyspace(ctx context.Context, pub Publisher, pins pin.Pinner, key ci.PrivKey) error {
	emptyDir := ft.EmptyDirNode()

	// pin recursively because this might already be pinned
	// and doing a direct pin would throw an error in that case
	err := pins.Pin(ctx, emptyDir, true)
	if err != nil {
		return err
	}

	err = pins.Flush()
	if err != nil {
		return err
	}

	return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid()))
}

func IpnsKeysForID(id peer.ID) (name, ipns string) {
	namekey := "/pk/" + string(id)
	ipnskey := "/ipns/" + string(id)

	return namekey, ipnskey
}
<|endoftext|>


<commit_before>package client

import (
	"runtime"
	"strings"
	"time"

	"github.com/fluffle/goirc/logging"
)

var tagsReplacer = strings.NewReplacer("\\:", ";", "\\s", " ", "\\r", "\r", "\\n", "\n")

// We parse an incoming line into this struct. Line.Cmd is used as the trigger
// name for incoming event handlers and is the IRC verb, the first sequence
// of non-whitespace characters after ":nick!user@host", e.g. PRIVMSG.
//   Raw =~ ":nick!user@host cmd args[] :text"
//   Src == "nick!user@host"
//   Cmd == e.g. PRIVMSG, 332
type Line struct {
	Tags                   map[string]string
	Nick, Ident, Host, Src string
	Cmd, Raw               string
	Args                   []string
	Time                   time.Time
}

// Copy returns a deep copy of the Line.
func (l *Line) Copy() *Line {
	nl := *l
	nl.Args = make([]string, len(l.Args))
	copy(nl.Args, l.Args)
	if l.Tags != nil {
		nl.Tags = make(map[string]string)
		for k, v := range l.Tags {
			nl.Tags[k] = v
		}
	}
	return &nl
}

// Text returns the contents of the text portion of a line. This only really
// makes sense for lines with a :text part, but there are a lot of them.
func (line *Line) Text() string {
	if len(line.Args) > 0 {
		return line.Args[len(line.Args)-1]
	}
	return ""
}

// Target returns the contextual target of the line, usually the first Arg
// for the IRC verb. If the line was broadcast from a channel, the target
// will be that channel. If the line was sent directly by a user, the target
// will be that user.
func (line *Line) Target() string {
	// TODO(fluffle): Add 005 CHANTYPES parsing for this?
	switch line.Cmd {
	case PRIVMSG, NOTICE, ACTION:
		if !line.Public() {
			return line.Nick
		}
	case CTCP, CTCPREPLY:
		if !line.Public() {
			return line.Nick
		}
		return line.Args[1]
	}
	if len(line.Args) > 0 {
		return line.Args[0]
	}
	return ""
}

// Public returns true if the line is the result of an IRC user sending
// a message to a channel the client has joined instead of directly
// to the client.
//
// NOTE: This is very permissive, allowing all 4 RFC channel types even if
// your server doesn't technically support them.
func (line *Line) Public() bool {
	switch line.Cmd {
	case PRIVMSG, NOTICE, ACTION:
		switch line.Args[0][0] {
		case '#', '&', '+', '!':
			return true
		}
	case CTCP, CTCPREPLY:
		// CTCP prepends the CTCP verb to line.Args, thus for the message
		//   :nick!user@host PRIVMSG #foo :\001BAR baz\001
		// line.Args contains: []string{"BAR", "#foo", "baz"}
		// TODO(fluffle): Arguably this is broken, and we should have
		// line.Args containing: []string{"#foo", "BAR", "baz"}
		// ... OR change conn.Ctcp()'s argument order to be consistent.
		switch line.Args[1][0] {
		case '#', '&', '+', '!':
			return true
		}
	}
	return false
}

// ParseLine creates a Line from an incoming message from the IRC server.
//
// It contains special casing for CTCP messages, most notably CTCP ACTION.
// All CTCP messages have the \001 bytes stripped from the message and the
// CTCP command separated from any subsequent text. Then, CTCP ACTIONs are
// rewritten such that Line.Cmd == ACTION. Other CTCP messages have Cmd
// set to CTCP or CTCPREPLY, and the CTCP command prepended to line.Args.
//
// ParseLine also parses IRCv3 tags, if received. If a line does not have
// the tags section, Line.Tags will be nil. Tags are optional, and will
// only be included after the correct CAP command.
//
// http://ircv3.net/specs/core/capability-negotiation-3.1.html
// http://ircv3.net/specs/core/message-tags-3.2.html
func ParseLine(s string) *Line {
	line := &Line{Raw: s}

	if s[0] == '@' {
		var rawTags string
		line.Tags = make(map[string]string)
		if idx := strings.Index(s, " "); idx != -1 {
			rawTags, s = s[1:idx], s[idx+1:]
		} else {
			return nil
		}

		// ; is represented as \: in a tag, so it's safe to split on ;
		for _, tag := range strings.Split(rawTags, ";") {
			if tag == "" {
				continue
			}

			pair := strings.SplitN(tagsReplacer.Replace(tag), "=", 2)
			if len(pair) < 2 {
				line.Tags[tag] = ""
			} else {
				line.Tags[pair[0]] = pair[1]
			}
		}
	}

	if s[0] == ':' {
		// remove a source and parse it
		if idx := strings.Index(s, " "); idx != -1 {
			line.Src, s = s[1:idx], s[idx+1:]
		} else {
			// pretty sure we shouldn't get here ...
			return nil
		}

		// src can be the hostname of the irc server or a nick!user@host
		line.Host = line.Src
		nidx, uidx := strings.Index(line.Src, "!"), strings.Index(line.Src, "@")
		if uidx != -1 && nidx != -1 {
			line.Nick = line.Src[:nidx]
			line.Ident = line.Src[nidx+1 : uidx]
			line.Host = line.Src[uidx+1:]
		}
	}

	// now we're here, we've parsed a :nick!user@host or :server off
	// s should contain "cmd args[] :text"
	args := strings.SplitN(s, " :", 2)
	if len(args) > 1 {
		args = append(strings.Fields(args[0]), args[1])
	} else {
		args = strings.Fields(args[0])
	}
	line.Cmd = strings.ToUpper(args[0])
	if len(args) > 1 {
		line.Args = args[1:]
	}

	// So, I think CTCP and (in particular) CTCP ACTION are better handled as
	// separate events as opposed to forcing people to have gargantuan
	// handlers to cope with the possibilities.
	if (line.Cmd == PRIVMSG || line.Cmd == NOTICE) &&
		len(line.Args[1]) > 2 &&
		strings.HasPrefix(line.Args[1], "\001") &&
		strings.HasSuffix(line.Args[1], "\001") {
		// WOO, it's a CTCP message
		t := strings.SplitN(strings.Trim(line.Args[1], "\001"), " ", 2)
		if len(t) > 1 {
			// Replace the line with the unwrapped CTCP
			line.Args[1] = t[1]
		}
		if c := strings.ToUpper(t[0]); c == ACTION && line.Cmd == PRIVMSG {
			// make a CTCP ACTION it's own event a-la PRIVMSG
			line.Cmd = c
		} else {
			// otherwise, dispatch a generic CTCP/CTCPREPLY event that
			// contains the type of CTCP in line.Args[0]
			if line.Cmd == PRIVMSG {
				line.Cmd = CTCP
			} else {
				line.Cmd = CTCPREPLY
			}
			line.Args = append([]string{c}, line.Args...)
		}
	}
	return line
}

func (line *Line) argslen(minlen int) bool {
	pc, _, _, _ := runtime.Caller(1)
	fn := runtime.FuncForPC(pc)
	if len(line.Args) <= minlen {
		logging.Warn("%s: too few arguments: %s", fn.Name(), strings.Join(line.Args, " "))
		return false
	}
	return true
}
<commit_msg>ParseLine: ignore empty lines (instead of crashing)<commit_after>package client

import (
	"runtime"
	"strings"
	"time"

	"github.com/fluffle/goirc/logging"
)

var tagsReplacer = strings.NewReplacer("\\:", ";", "\\s", " ", "\\r", "\r", "\\n", "\n")

// We parse an incoming line into this struct. Line.Cmd is used as the trigger
// name for incoming event handlers and is the IRC verb, the first sequence
// of non-whitespace characters after ":nick!user@host", e.g. PRIVMSG.
//   Raw =~ ":nick!user@host cmd args[] :text"
//   Src == "nick!user@host"
//   Cmd == e.g. PRIVMSG, 332
type Line struct {
	Tags                   map[string]string
	Nick, Ident, Host, Src string
	Cmd, Raw               string
	Args                   []string
	Time                   time.Time
}

// Copy returns a deep copy of the Line.
func (l *Line) Copy() *Line {
	nl := *l
	nl.Args = make([]string, len(l.Args))
	copy(nl.Args, l.Args)
	if l.Tags != nil {
		nl.Tags = make(map[string]string)
		for k, v := range l.Tags {
			nl.Tags[k] = v
		}
	}
	return &nl
}

// Text returns the contents of the text portion of a line. This only really
// makes sense for lines with a :text part, but there are a lot of them.
func (line *Line) Text() string {
	if len(line.Args) > 0 {
		return line.Args[len(line.Args)-1]
	}
	return ""
}

// Target returns the contextual target of the line, usually the first Arg
// for the IRC verb. If the line was broadcast from a channel, the target
// will be that channel. If the line was sent directly by a user, the target
// will be that user.
func (line *Line) Target() string {
	// TODO(fluffle): Add 005 CHANTYPES parsing for this?
	switch line.Cmd {
	case PRIVMSG, NOTICE, ACTION:
		if !line.Public() {
			return line.Nick
		}
	case CTCP, CTCPREPLY:
		if !line.Public() {
			return line.Nick
		}
		return line.Args[1]
	}
	if len(line.Args) > 0 {
		return line.Args[0]
	}
	return ""
}

// Public returns true if the line is the result of an IRC user sending
// a message to a channel the client has joined instead of directly
// to the client.
//
// NOTE: This is very permissive, allowing all 4 RFC channel types even if
// your server doesn't technically support them.
func (line *Line) Public() bool {
	switch line.Cmd {
	case PRIVMSG, NOTICE, ACTION:
		switch line.Args[0][0] {
		case '#', '&', '+', '!':
			return true
		}
	case CTCP, CTCPREPLY:
		// CTCP prepends the CTCP verb to line.Args, thus for the message
		//   :nick!user@host PRIVMSG #foo :\001BAR baz\001
		// line.Args contains: []string{"BAR", "#foo", "baz"}
		// TODO(fluffle): Arguably this is broken, and we should have
		// line.Args containing: []string{"#foo", "BAR", "baz"}
		// ... OR change conn.Ctcp()'s argument order to be consistent.
		switch line.Args[1][0] {
		case '#', '&', '+', '!':
			return true
		}
	}
	return false
}

// ParseLine creates a Line from an incoming message from the IRC server.
//
// It contains special casing for CTCP messages, most notably CTCP ACTION.
// All CTCP messages have the \001 bytes stripped from the message and the
// CTCP command separated from any subsequent text. Then, CTCP ACTIONs are
// rewritten such that Line.Cmd == ACTION. Other CTCP messages have Cmd
// set to CTCP or CTCPREPLY, and the CTCP command prepended to line.Args.
//
// ParseLine also parses IRCv3 tags, if received. If a line does not have
// the tags section, Line.Tags will be nil. Tags are optional, and will
// only be included after the correct CAP command.
//
// http://ircv3.net/specs/core/capability-negotiation-3.1.html
// http://ircv3.net/specs/core/message-tags-3.2.html
func ParseLine(s string) *Line {
	line := &Line{Raw: s}

	if s == "" {
		return nil
	}

	if s[0] == '@' {
		var rawTags string
		line.Tags = make(map[string]string)
		if idx := strings.Index(s, " "); idx != -1 {
			rawTags, s = s[1:idx], s[idx+1:]
		} else {
			return nil
		}

		// ; is represented as \: in a tag, so it's safe to split on ;
		for _, tag := range strings.Split(rawTags, ";") {
			if tag == "" {
				continue
			}

			pair := strings.SplitN(tagsReplacer.Replace(tag), "=", 2)
			if len(pair) < 2 {
				line.Tags[tag] = ""
			} else {
				line.Tags[pair[0]] = pair[1]
			}
		}
	}

	if s[0] == ':' {
		// remove a source and parse it
		if idx := strings.Index(s, " "); idx != -1 {
			line.Src, s = s[1:idx], s[idx+1:]
		} else {
			// pretty sure we shouldn't get here ...
			return nil
		}

		// src can be the hostname of the irc server or a nick!user@host
		line.Host = line.Src
		nidx, uidx := strings.Index(line.Src, "!"), strings.Index(line.Src, "@")
		if uidx != -1 && nidx != -1 {
			line.Nick = line.Src[:nidx]
			line.Ident = line.Src[nidx+1 : uidx]
			line.Host = line.Src[uidx+1:]
		}
	}

	// now we're here, we've parsed a :nick!user@host or :server off
	// s should contain "cmd args[] :text"
	args := strings.SplitN(s, " :", 2)
	if len(args) > 1 {
		args = append(strings.Fields(args[0]), args[1])
	} else {
		args = strings.Fields(args[0])
	}
	line.Cmd = strings.ToUpper(args[0])
	if len(args) > 1 {
		line.Args = args[1:]
	}

	// So, I think CTCP and (in particular) CTCP ACTION are better handled as
	// separate events as opposed to forcing people to have gargantuan
	// handlers to cope with the possibilities.
	if (line.Cmd == PRIVMSG || line.Cmd == NOTICE) &&
		len(line.Args[1]) > 2 &&
		strings.HasPrefix(line.Args[1], "\001") &&
		strings.HasSuffix(line.Args[1], "\001") {
		// WOO, it's a CTCP message
		t := strings.SplitN(strings.Trim(line.Args[1], "\001"), " ", 2)
		if len(t) > 1 {
			// Replace the line with the unwrapped CTCP
			line.Args[1] = t[1]
		}
		if c := strings.ToUpper(t[0]); c == ACTION && line.Cmd == PRIVMSG {
			// make a CTCP ACTION it's own event a-la PRIVMSG
			line.Cmd = c
		} else {
			// otherwise, dispatch a generic CTCP/CTCPREPLY event that
			// contains the type of CTCP in line.Args[0]
			if line.Cmd == PRIVMSG {
				line.Cmd = CTCP
			} else {
				line.Cmd = CTCPREPLY
			}
			line.Args = append([]string{c}, line.Args...)
		}
	}
	return line
}

func (line *Line) argslen(minlen int) bool {
	pc, _, _, _ := runtime.Caller(1)
	fn := runtime.FuncForPC(pc)
	if len(line.Args) <= minlen {
		logging.Warn("%s: too few arguments: %s", fn.Name(), strings.Join(line.Args, " "))
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package compose

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// Container models the `docker inspect` command output.
type Container struct {
	ID              string           `json:"Id"`
	Name            string           `json:"Name,omitempty"`
	Created         time.Time        `json:"Created,omitempty"`
	Config          *Config          `json:"Config,omitempty"`
	State           State            `json:"State,omitempty"`
	Image           string           `json:"Image,omitempty"`
	NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty"`
}

// Config models the config section of the `docker inspect` command output.
type Config struct {
	Hostname     string              `json:"Hostname,omitempty"`
	ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"`
	Env          []string            `json:"Env,omitempty"`
	Cmd          []string            `json:"Cmd"`
	Image        string              `json:"Image,omitempty"`
	Labels       map[string]string   `json:"Labels,omitempty"`
}

// State models the state section of the `docker inspect` command.
type State struct {
	Running    bool      `json:"Running,omitempty"`
	Paused     bool      `json:"Paused,omitempty"`
	Restarting bool      `json:"Restarting,omitempty"`
	OOMKilled  bool      `json:"OOMKilled,omitempty"`
	Pid        int       `json:"Pid,omitempty"`
	ExitCode   int       `json:"ExitCode,omitempty"`
	Error      string    `json:"Error,omitempty"`
	StartedAt  time.Time `json:"StartedAt,omitempty"`
	FinishedAt time.Time `json:"FinishedAt,omitempty"`
}

// NetworkSettings models the network settings section of the `docker inspect` command.
type NetworkSettings struct {
	Ports map[string][]PortBinding `json:"Ports,omitempty"`
}

// PortBinding models a port binding in the network settings section of the `docker inspect command.
type PortBinding struct {
	HostIP   string `json:"HostIP,omitempty"`
	HostPort string `json:"HostPort,omitempty"`
}

// Inspect inspects a container using the `docker inspect` command and returns a parsed version of its output.
func Inspect(id string) (*Container, error) {
	out, err := runCmd("docker", "inspect", id)
	if err != nil {
		return nil, fmt.Errorf("compose: error inspecting container: %v", err)
	}

	var inspect []*Container
	if err := json.Unmarshal([]byte(out), &inspect); err != nil {
		return nil, fmt.Errorf("compose: error parsing inspect output: %v", err)
	}
	if len(inspect) != 1 {
		return nil, fmt.Errorf("compose: inspect returned %v results, 1 expected", len(inspect))
	}

	return inspect[0], nil
}

// MustInspect is like Inspect, but panics on error.
func MustInspect(id string) *Container {
	container, err := Inspect(id)
	if err != nil {
		panic(err)
	}
	return container
}

// GetFirstPublicPort returns the first public public port mapped to the given exposedPort, for the given proto ("tcp", "udp", etc.), if found.
func (c *Container) GetFirstPublicPort(exposedPort uint32, proto string) (uint32, error) {
	if c.NetworkSettings == nil {
		return 0, fmt.Errorf("compose: no network settings for container '%v'", c.Name)
	}

	portSpec := fmt.Sprintf("%v/%v", exposedPort, strings.ToLower(proto))
	mapping, ok := c.NetworkSettings.Ports[portSpec]
	if !ok || len(mapping) == 0 {
		return 0, fmt.Errorf("compose: no public port for %v", portSpec)
	}

	port, err := strconv.ParseUint(mapping[0].HostPort, 10, 32)
	if err != nil {
		return 0, fmt.Errorf("compose: error parsing port '%v'", mapping[0].HostPort)
	}

	return uint32(port), nil
}

// MustGetFirstPublicPort is like GetFirstPublicPort, but panics on error.
func (c *Container) MustGetFirstPublicPort(exposedPort uint32, proto string) uint32 {
	port, err := c.GetFirstPublicPort(exposedPort, proto)
	if err != nil {
		panic(err)
	}
	return port
}
<commit_msg>Fix CircleCI config.<commit_after>package compose

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// Container models the `docker inspect` command output.
type Container struct {
	ID              string           `json:"Id"`
	Name            string           `json:"Name,omitempty"`
	Created         time.Time        `json:"Created,omitempty"`
	Config          *Config          `json:"Config,omitempty"`
	State           State            `json:"State,omitempty"`
	Image           string           `json:"Image,omitempty"`
	NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty"`
}

// Config models the config section of the `docker inspect` command output.
type Config struct {
	Hostname     string              `json:"Hostname,omitempty"`
	ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"`
	Env          []string            `json:"Env,omitempty"`
	Cmd          []string            `json:"Cmd"`
	Image        string              `json:"Image,omitempty"`
	Labels       map[string]string   `json:"Labels,omitempty"`
}

// State models the state section of the `docker inspect` command.
type State struct {
	Running    bool      `json:"Running,omitempty"`
	Paused     bool      `json:"Paused,omitempty"`
	Restarting bool      `json:"Restarting,omitempty"`
	OOMKilled  bool      `json:"OOMKilled,omitempty"`
	Pid        int       `json:"Pid,omitempty"`
	ExitCode   int       `json:"ExitCode,omitempty"`
	Error      string    `json:"Error,omitempty"`
	StartedAt  time.Time `json:"StartedAt,omitempty"`
	FinishedAt time.Time `json:"FinishedAt,omitempty"`
}

// NetworkSettings models the network settings section of the `docker inspect` command.
type NetworkSettings struct {
	Ports map[string][]PortBinding `json:"Ports,omitempty"`
}

// PortBinding models a port binding in the network settings section of the `docker inspect command.
type PortBinding struct {
	HostIP   string `json:"HostIP,omitempty"`
	HostPort string `json:"HostPort,omitempty"`
}

// Inspect inspects a container using the `docker inspect` command and returns a parsed version of its output.
func Inspect(id string) (*Container, error) {
	out, err := runCmd("docker", "inspect", id)
	if err != nil {
		return nil, fmt.Errorf("compose: error inspecting container: %v", err)
	}

	fmt.Printf("------XXX\n%v\n------\n", out)

	var inspect []*Container
	if err := json.Unmarshal([]byte(out), &inspect); err != nil {
		return nil, fmt.Errorf("compose: error parsing inspect output: %v", err)
	}
	if len(inspect) != 1 {
		return nil, fmt.Errorf("compose: inspect returned %v results, 1 expected", len(inspect))
	}

	return inspect[0], nil
}

// MustInspect is like Inspect, but panics on error.
func MustInspect(id string) *Container {
	container, err := Inspect(id)
	if err != nil {
		panic(err)
	}
	return container
}

// GetFirstPublicPort returns the first public public port mapped to the given exposedPort, for the given proto ("tcp", "udp", etc.), if found.
func (c *Container) GetFirstPublicPort(exposedPort uint32, proto string) (uint32, error) {
	if c.NetworkSettings == nil {
		return 0, fmt.Errorf("compose: no network settings for container '%v'", c.Name)
	}

	portSpec := fmt.Sprintf("%v/%v", exposedPort, strings.ToLower(proto))
	mapping, ok := c.NetworkSettings.Ports[portSpec]
	if !ok || len(mapping) == 0 {
		return 0, fmt.Errorf("compose: no public port for %v", portSpec)
	}

	port, err := strconv.ParseUint(mapping[0].HostPort, 10, 32)
	if err != nil {
		return 0, fmt.Errorf("compose: error parsing port '%v'", mapping[0].HostPort)
	}

	return uint32(port), nil
}

// MustGetFirstPublicPort is like GetFirstPublicPort, but panics on error.
func (c *Container) MustGetFirstPublicPort(exposedPort uint32, proto string) uint32 {
	port, err := c.GetFirstPublicPort(exposedPort, proto)
	if err != nil {
		panic(err)
	}
	return port
}
<|endoftext|>


<commit_before>package concourse

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"strings"

	"github.com/concourse/atc"
	"github.com/concourse/go-concourse/concourse/internal"
	"github.com/tedsuo/rata"
	"gopkg.in/yaml.v2"
)

func (team *team) PipelineConfig(pipelineName string) (atc.Config, atc.RawConfig, string, bool, error) {
	params := rata.Params{
		"pipeline_name": pipelineName,
		"team_name":     team.name,
	}

	var configResponse atc.ConfigResponse

	responseHeaders := http.Header{}
	response := internal.Response{
		Headers: &responseHeaders,
		Result:  &configResponse,
	}
	err := team.connection.Send(internal.Request{
		RequestName: atc.GetConfig,
		Params:      params,
	}, &response)

	switch err.(type) {
	case nil:
		version := responseHeaders.Get(atc.ConfigVersionHeader)

		if len(configResponse.Errors) > 0 {
			return atc.Config{}, configResponse.RawConfig, version, false, PipelineConfigError{configResponse.Errors}
		}

		return *configResponse.Config, configResponse.RawConfig, version, true, nil
	case internal.ResourceNotFoundError:
		return atc.Config{}, atc.RawConfig(""), "", false, nil
	default:
		return atc.Config{}, atc.RawConfig(""), "", false, err
	}
}

type pipelineConfigResponse struct {
	Config *atc.Config
	Errors []string `json:"errors"`
}

type configValidationError struct {
	ErrorMessages []string `json:"errors"`
}

func (c configValidationError) Error() string {
	return fmt.Sprintf("invalid configuration:\n%s", strings.Join(c.ErrorMessages, "\n"))
}

type ConfigWarning struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

type setConfigResponse struct {
	Errors   []string        `json:"errors"`
	Warnings []ConfigWarning `json:"warnings"`
}

func (team *team) CreateOrUpdatePipelineConfig(pipelineName string, configVersion string, passedConfig atc.Config) (bool, bool, []ConfigWarning, error) {
	params := rata.Params{
		"pipeline_name": pipelineName,
		"team_name":     team.name,
	}

	response := internal.Response{}
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	yamlWriter, err := writer.CreatePart(
		textproto.MIMEHeader{
			"Content-type": {"application/x-yaml"},
		},
	)
	if err != nil {
		return false, false, []ConfigWarning{}, err
	}

	rawConfig, err := yaml.Marshal(passedConfig)
	if err != nil {
		return false, false, []ConfigWarning{}, err
	}

	_, err = yamlWriter.Write(rawConfig)
	if err != nil {
		return false, false, []ConfigWarning{}, err
	}

	writer.Close()

	err = team.connection.Send(internal.Request{
		ReturnResponseBody: true,
		RequestName:        atc.SaveConfig,
		Params:             params,
		Body:               body,
		Header: http.Header{
			"Content-Type":          {writer.FormDataContentType()},
			atc.ConfigVersionHeader: {configVersion},
		},
	},
		&response,
	)
	if err != nil {
		if unexpectedResponseError, ok := err.(internal.UnexpectedResponseError); ok {
			if unexpectedResponseError.StatusCode == http.StatusBadRequest {
				var validationErr configValidationError

				err = json.Unmarshal([]byte(unexpectedResponseError.Body), &validationErr)
				if err != nil {
					return false, false, []ConfigWarning{}, err
				}

				return false, false, []ConfigWarning{}, validationErr
			}
		}

		return false, false, []ConfigWarning{}, err
	}

	configResponse := setConfigResponse{}
	readCloser, ok := response.Result.(io.ReadCloser)
	if !ok {
		return false, false, []ConfigWarning{}, errors.New("Failed to assert type of response result")
	}
	defer readCloser.Close()

	contents, err := ioutil.ReadAll(readCloser)
	if err != nil {
		return false, false, []ConfigWarning{}, err
	}

	err = json.Unmarshal(contents, &configResponse)
	if err != nil {
		return false, false, []ConfigWarning{}, err
	}

	return response.Created, !response.Created, configResponse.Warnings, nil
}
<commit_msg>don't do no stinkin multipart<commit_after>package concourse

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/concourse/atc"
	"github.com/concourse/go-concourse/concourse/internal"
	"github.com/tedsuo/rata"
	"gopkg.in/yaml.v2"
)

func (team *team) PipelineConfig(pipelineName string) (atc.Config, atc.RawConfig, string, bool, error) {
	params := rata.Params{
		"pipeline_name": pipelineName,
		"team_name":     team.name,
	}

	var configResponse atc.ConfigResponse

	responseHeaders := http.Header{}
	response := internal.Response{
		Headers: &responseHeaders,
		Result:  &configResponse,
	}
	err := team.connection.Send(internal.Request{
		RequestName: atc.GetConfig,
		Params:      params,
	}, &response)

	switch err.(type) {
	case nil:
		version := responseHeaders.Get(atc.ConfigVersionHeader)

		if len(configResponse.Errors) > 0 {
			return atc.Config{}, configResponse.RawConfig, version, false, PipelineConfigError{configResponse.Errors}
		}

		return *configResponse.Config, configResponse.RawConfig, version, true, nil
	case internal.ResourceNotFoundError:
		return atc.Config{}, atc.RawConfig(""), "", false, nil
	default:
		return atc.Config{}, atc.RawConfig(""), "", false, err
	}
}

type pipelineConfigResponse struct {
	Config *atc.Config
	Errors []string `json:"errors"`
}

type configValidationError struct {
	ErrorMessages []string `json:"errors"`
}

func (c configValidationError) Error() string {
	return fmt.Sprintf("invalid configuration:\n%s", strings.Join(c.ErrorMessages, "\n"))
}

type ConfigWarning struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

type setConfigResponse struct {
	Errors   []string        `json:"errors"`
	Warnings []ConfigWarning `json:"warnings"`
}

func (team *team) CreateOrUpdatePipelineConfig(pipelineName string, configVersion string, passedConfig atc.Config) (bool, bool, []ConfigWarning, error) {
	params := rata.Params{
		"pipeline_name": pipelineName,
		"team_name":     team.name,
	}

	response := internal.Response{}

	yamlConfig, err := yaml.Marshal(passedConfig)
	if err != nil {
		return false, false, []ConfigWarning{}, err
	}

	err = team.connection.Send(internal.Request{
		ReturnResponseBody: true,
		RequestName:        atc.SaveConfig,
		Params:             params,
		Body:               bytes.NewBuffer(yamlConfig),
		Header: http.Header{
			"Content-Type":          {"application/x-yaml"},
			atc.ConfigVersionHeader: {configVersion},
		},
	},
		&response,
	)
	if err != nil {
		if unexpectedResponseError, ok := err.(internal.UnexpectedResponseError); ok {
			if unexpectedResponseError.StatusCode == http.StatusBadRequest {
				var validationErr configValidationError

				err = json.Unmarshal([]byte(unexpectedResponseError.Body), &validationErr)
				if err != nil {
					return false, false, []ConfigWarning{}, err
				}

				return false, false, []ConfigWarning{}, validationErr
			}
		}

		return false, false, []ConfigWarning{}, err
	}

	configResponse := setConfigResponse{}
	readCloser, ok := response.Result.(io.ReadCloser)
	if !ok {
		return false, false, []ConfigWarning{}, errors.New("Failed to assert type of response result")
	}
	defer readCloser.Close()

	contents, err := ioutil.ReadAll(readCloser)
	if err != nil {
		return false, false, []ConfigWarning{}, err
	}

	err = json.Unmarshal(contents, &configResponse)
	if err != nil {
		return false, false, []ConfigWarning{}, err
	}

	return response.Created, !response.Created, configResponse.Warnings, nil
}
<|endoftext|>


<commit_before>// Package appConfig ~/.{appName}/configの書き込み,作成,読み込みを行うパッケージ
package appConfig

import (
	"io/ioutil"
	"os"
	"os/user"
	"strings"
)

type AppConfig struct {
	ConfigFileName string
	ConfigDirPath  string
	AppName        string
}

// NewAppConfig create AppConfig.
func NewAppConfig(appName string) *AppConfig {
	dirPath, err := createAppConfigDirPath(appName)
	if err != nil {
		dirPath = "./"
	}
	return &AppConfig{
		ConfigFileName: "config",
		ConfigDirPath:  dirPath,
		AppName:        appName,
	}
}

// configファイルを作成する中身は空.
func (a AppConfig) WriteAppConfig(data []byte) error {
	if err := createAppConfigDir(a.AppName); err != nil {
		return err
	}
	return ioutil.WriteFile(a.AppConfigFilePath(), data, os.FileMode(0644))
}

func (a AppConfig) AppConfigFilePath() string {
	return strings.Join([]string{a.ConfigDirPath, a.ConfigFileName}, "/")
}

// configファイルを読み込む[]byte.
func (a AppConfig) ReadAppConfig() ([]byte, error) {
	return ioutil.ReadFile(a.AppConfigFilePath())
}

func (a AppConfig) RemoveAppConfig() error {
	return os.RemoveAll(a.ConfigDirPath)
}

// ~/.{appName}ディレクトリを作成
// すでに存在する場合スルー
func createAppConfigDir(appName string) error {
	dirPath, err := createAppConfigDirPath(appName)
	if err != nil {
		return err
	}

	// check
	if _, err := ioutil.ReadDir(dirPath); err == nil {
		return nil
	}

	// create dir
	return os.Mkdir(dirPath, os.FileMode(0755))
}

func createAppConfigDirPath(appName string) (string, error) {
	// home
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	dirName := "." + appName
	dirPath := strings.Join([]string{usr.HomeDir, dirName}, "/")
	return dirPath, nil
}

<commit_msg>feat: NewDefaultAppConfig Method<commit_after>// Package appConfig ~/.{appName}/configの書き込み,作成,読み込みを行うパッケージ
package appConfig

import (
	"io/ioutil"
	"os"
	"os/user"
	"strings"
)

type AppConfig struct {
	ConfigFileName string
	ConfigDirPath  string
	AppName        string
}

// NewAppConfig create AppConfig.
func NewDefaultAppConfig(appName string) *AppConfig {
	return NewAppConfig(appName, "config")
}

func NewAppConfig(appName, configFileName string) *AppConfig {
	dirPath, err := createAppConfigDirPath(appName)
	if err != nil {
		dirPath = "./"
	}
	return &AppConfig{
		ConfigFileName: configFileName,
		ConfigDirPath:  dirPath,
		AppName:        appName,
	}
}

// configファイルを作成する中身は空.
func (a AppConfig) WriteAppConfig(data []byte) error {
	if err := createAppConfigDir(a.AppName); err != nil {
		return err
	}
	return ioutil.WriteFile(a.AppConfigFilePath(), data, os.FileMode(0644))
}

func (a AppConfig) AppConfigFilePath() string {
	return strings.Join([]string{a.ConfigDirPath, a.ConfigFileName}, "/")
}

// configファイルを読み込む[]byte.
func (a AppConfig) ReadAppConfig() ([]byte, error) {
	return ioutil.ReadFile(a.AppConfigFilePath())
}

func (a AppConfig) RemoveAppConfig() error {
	return os.RemoveAll(a.ConfigDirPath)
}

// ~/.{appName}ディレクトリを作成
// すでに存在する場合スルー
func createAppConfigDir(appName string) error {
	dirPath, err := createAppConfigDirPath(appName)
	if err != nil {
		return err
	}

	// check
	if _, err := ioutil.ReadDir(dirPath); err == nil {
		return nil
	}

	// create dir
	return os.Mkdir(dirPath, os.FileMode(0755))
}

func createAppConfigDirPath(appName string) (string, error) {
	// home
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	dirName := "." + appName
	dirPath := strings.Join([]string{usr.HomeDir, dirName}, "/")
	return dirPath, nil
}
<|endoftext|>


<commit_before>package cfclient

import (
	"encoding/json"
	"io/ioutil"
	"time"

	"github.com/pkg/errors"
)

const (
	//AppCrash app.crash event const
	AppCrash = "app.crash"
	//AppStart audit.app.start event const
	AppStart = "audit.app.start"
	//AppStop audit.app.stop event const
	AppStop = "audit.app.stop"
	//AppUpdate audit.app.update event const
	AppUpdate = "audit.app.update"
	//AppCreate audit.app.create event const
	AppCreate = "audit.app.create"
	//AppDelete audit.app.delete-request event const
	AppDelete = "audit.app.delete-request"
	//AppSSHAuth audit.app.ssh-authorized event const
	AppSSHAuth = "audit.app.ssh-authorized"
	//AppSSHUnauth audit.app.ssh-unauthorized event const
	AppSSHUnauth = "audit.app.ssh-unauthorized"
	//AppRestage audit.app.restage event const
	AppRestage = "audit.app.restage"
	//AppMapRoute audit.app.map-route event const
	AppMapRoute = "audit.app.map-route"
	//AppUnmapRoute audit.app.unmap-route event const
	AppUnmapRoute = "audit.app.unmap-route"
	//FilterTimestamp const for query filter timestamp
	FilterTimestamp = "timestamp"
	//FilterActee const for query filter actee
	FilterActee = "actee"
)

//ValidOperators const for all valid operators in a query
var ValidOperators = []string{":", ">=", "<=", "<", ">", "IN"}

// AppEventResponse the entire response
type AppEventResponse struct {
	Results   int                `json:"total_results"`
	Pages     int                `json:"total_pages"`
	PrevURL   string             `json:"prev_url"`
	NextURL   string             `json:"next_url"`
	Resources []AppEventResource `json:"resources"`
}

// AppEventResource the event resources
type AppEventResource struct {
	Meta   Meta           `json:"metadata"`
	Entity AppEventEntity `json:"entity"`
}

//AppEventQuery a struct for defining queries like 'q=filter>value' or 'q=filter IN a,b,c'
type AppEventQuery struct {
	Filter   string
	Operator string
	Value    string
}

// The AppEventEntity the actual app event body
type AppEventEntity struct {
	//EventTypes are app.crash, audit.app.start, audit.app.stop, audit.app.update, audit.app.create, audit.app.delete-request
	EventType string `json:"type"`
	//The GUID of the actor.
	Actor string `json:"actor"`
	//The actor type, user or app
	ActorType string `json:"actor_type"`
	//The name of the actor.
	ActorName string `json:"actor_name"`
	//The GUID of the actee.
	Actee string `json:"actee"`
	//The actee type, space, app or v3-app
	ActeeType string `json:"actee_type"`
	//The name of the actee.
	ActeeName string `json:"actee_name"`
	//Timestamp format "2016-02-26T13:29:44Z". The event creation time.
	Timestamp time.Time `json:"timestamp"`
	MetaData  struct {
		//app.crash event fields
		ExitDescription string `json:"exit_description,omitempty"`
		ExitReason      string `json:"reason,omitempty"`
		ExitStatus      string `json:"exit_status,omitempty"`

		Request struct {
			Name              string  `json:"name,omitempty"`
			Instances         float64 `json:"instances,omitempty"`
			State             string  `json:"state,omitempty"`
			Memory            float64 `json:"memory,omitempty"`
			EnvironmentVars   string  `json:"environment_json,omitempty"`
			DockerCredentials string  `json:"docker_credentials_json,omitempty"`
			//audit.app.create event fields
			Console            bool    `json:"console,omitempty"`
			Buildpack          string  `json:"buildpack,omitempty"`
			Space              string  `json:"space_guid,omitempty"`
			HealthcheckType    string  `json:"health_check_type,omitempty"`
			HealthcheckTimeout float64 `json:"health_check_timeout,omitempty"`
			Production         bool    `json:"production,omitempty"`
			//app.crash event fields
			Index float64 `json:"index,omitempty"`
		} `json:"request"`
	} `json:"metadata"`
}

// ListAppEvents returns all app events based on eventType
func (c *Client) ListAppEvents(eventType string) ([]AppEventEntity, error) {
	return c.ListAppEventsByQuery(eventType, nil)
}

// ListAppEventsByQuery returns all app events based on eventType and queries
func (c *Client) ListAppEventsByQuery(eventType string, queries []AppEventQuery) ([]AppEventEntity, error) {
	var events []AppEventEntity

	if eventType != AppCrash && eventType != AppStart && eventType != AppStop && eventType != AppUpdate && eventType != AppCreate &&
		eventType != AppDelete && eventType != AppSSHAuth && eventType != AppSSHUnauth && eventType != AppRestage &&
		eventType != AppMapRoute && eventType != AppUnmapRoute {
		return nil, errors.New("Unsupported app event type " + eventType)
	}

	var query = "/v2/events?q=type:" + eventType
	//adding the additional queries
	if len(queries) > 0 {
		for _, eventQuery := range queries {
			if eventQuery.Filter != FilterTimestamp && eventQuery.Filter != FilterActee {
				return nil, errors.New("Unsupported query filter type " + eventQuery.Filter)
			}
			if !stringInSlice(eventQuery.Operator, ValidOperators) {
				return nil, errors.New("Unsupported query operator type " + eventQuery.Operator)
			}
			query += "&q=" + eventQuery.Filter + eventQuery.Operator + eventQuery.Value
		}
	}

	for {
		eventResponse, err := c.getAppEventsResponse(query)
		if err != nil {
			return []AppEventEntity{}, err
		}
		for _, event := range eventResponse.Resources {
			events = append(events, event.Entity)
		}
		query = eventResponse.NextURL
		if query == "" {
			break
		}
	}

	return events, nil
}

func (c *Client) getAppEventsResponse(query string) (AppEventResponse, error) {
	var eventResponse AppEventResponse
	r := c.NewRequest("GET", query)
	resp, err := c.DoRequest(r)
	if err != nil {
		return AppEventResponse{}, errors.Wrap(err, "Error requesting appevents")
	}
	defer resp.Body.Close()
	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return AppEventResponse{}, errors.Wrap(err, "Error reading appevents response body")
	}

	err = json.Unmarshal(resBody, &eventResponse)
	if err != nil {
		return AppEventResponse{}, errors.Wrap(err, "Error unmarshalling appevent")
	}
	return eventResponse, nil
}

func stringInSlice(str string, list []string) bool {
	for _, v := range list {
		if v == str {
			return true
		}
	}
	return false
}
<commit_msg>Remove superfluous comments on exported consts<commit_after>package cfclient

import (
	"encoding/json"
	"io/ioutil"
	"time"

	"github.com/pkg/errors"
)

// Exported event constants
const (
	AppCrash      = "app.crash"
	AppStart      = "audit.app.start"
	AppStop       = "audit.app.stop"
	AppUpdate     = "audit.app.update"
	AppCreate     = "audit.app.create"
	AppDelete     = "audit.app.delete-request"
	AppSSHAuth    = "audit.app.ssh-authorized"
	AppSSHUnauth  = "audit.app.ssh-unauthorized"
	AppRestage    = "audit.app.restage"
	AppMapRoute   = "audit.app.map-route"
	AppUnmapRoute = "audit.app.unmap-route"
)

// Exported filter constants
const (
	FilterTimestamp = "timestamp"
	FilterActee     = "actee"
)

//ValidOperators const for all valid operators in a query
var ValidOperators = []string{":", ">=", "<=", "<", ">", "IN"}

// AppEventResponse the entire response
type AppEventResponse struct {
	Results   int                `json:"total_results"`
	Pages     int                `json:"total_pages"`
	PrevURL   string             `json:"prev_url"`
	NextURL   string             `json:"next_url"`
	Resources []AppEventResource `json:"resources"`
}

// AppEventResource the event resources
type AppEventResource struct {
	Meta   Meta           `json:"metadata"`
	Entity AppEventEntity `json:"entity"`
}

//AppEventQuery a struct for defining queries like 'q=filter>value' or 'q=filter IN a,b,c'
type AppEventQuery struct {
	Filter   string
	Operator string
	Value    string
}

// The AppEventEntity the actual app event body
type AppEventEntity struct {
	//EventTypes are app.crash, audit.app.start, audit.app.stop, audit.app.update, audit.app.create, audit.app.delete-request
	EventType string `json:"type"`
	//The GUID of the actor.
	Actor string `json:"actor"`
	//The actor type, user or app
	ActorType string `json:"actor_type"`
	//The name of the actor.
	ActorName string `json:"actor_name"`
	//The GUID of the actee.
	Actee string `json:"actee"`
	//The actee type, space, app or v3-app
	ActeeType string `json:"actee_type"`
	//The name of the actee.
	ActeeName string `json:"actee_name"`
	//Timestamp format "2016-02-26T13:29:44Z". The event creation time.
	Timestamp time.Time `json:"timestamp"`
	MetaData  struct {
		//app.crash event fields
		ExitDescription string `json:"exit_description,omitempty"`
		ExitReason      string `json:"reason,omitempty"`
		ExitStatus      string `json:"exit_status,omitempty"`

		Request struct {
			Name              string  `json:"name,omitempty"`
			Instances         float64 `json:"instances,omitempty"`
			State             string  `json:"state,omitempty"`
			Memory            float64 `json:"memory,omitempty"`
			EnvironmentVars   string  `json:"environment_json,omitempty"`
			DockerCredentials string  `json:"docker_credentials_json,omitempty"`
			//audit.app.create event fields
			Console            bool    `json:"console,omitempty"`
			Buildpack          string  `json:"buildpack,omitempty"`
			Space              string  `json:"space_guid,omitempty"`
			HealthcheckType    string  `json:"health_check_type,omitempty"`
			HealthcheckTimeout float64 `json:"health_check_timeout,omitempty"`
			Production         bool    `json:"production,omitempty"`
			//app.crash event fields
			Index float64 `json:"index,omitempty"`
		} `json:"request"`
	} `json:"metadata"`
}

// ListAppEvents returns all app events based on eventType
func (c *Client) ListAppEvents(eventType string) ([]AppEventEntity, error) {
	return c.ListAppEventsByQuery(eventType, nil)
}

// ListAppEventsByQuery returns all app events based on eventType and queries
func (c *Client) ListAppEventsByQuery(eventType string, queries []AppEventQuery) ([]AppEventEntity, error) {
	var events []AppEventEntity

	if eventType != AppCrash && eventType != AppStart && eventType != AppStop && eventType != AppUpdate && eventType != AppCreate &&
		eventType != AppDelete && eventType != AppSSHAuth && eventType != AppSSHUnauth && eventType != AppRestage &&
		eventType != AppMapRoute && eventType != AppUnmapRoute {
		return nil, errors.New("Unsupported app event type " + eventType)
	}

	var query = "/v2/events?q=type:" + eventType
	//adding the additional queries
	if len(queries) > 0 {
		for _, eventQuery := range queries {
			if eventQuery.Filter != FilterTimestamp && eventQuery.Filter != FilterActee {
				return nil, errors.New("Unsupported query filter type " + eventQuery.Filter)
			}
			if !stringInSlice(eventQuery.Operator, ValidOperators) {
				return nil, errors.New("Unsupported query operator type " + eventQuery.Operator)
			}
			query += "&q=" + eventQuery.Filter + eventQuery.Operator + eventQuery.Value
		}
	}

	for {
		eventResponse, err := c.getAppEventsResponse(query)
		if err != nil {
			return []AppEventEntity{}, err
		}
		for _, event := range eventResponse.Resources {
			events = append(events, event.Entity)
		}
		query = eventResponse.NextURL
		if query == "" {
			break
		}
	}

	return events, nil
}

func (c *Client) getAppEventsResponse(query string) (AppEventResponse, error) {
	var eventResponse AppEventResponse
	r := c.NewRequest("GET", query)
	resp, err := c.DoRequest(r)
	if err != nil {
		return AppEventResponse{}, errors.Wrap(err, "Error requesting appevents")
	}
	defer resp.Body.Close()
	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return AppEventResponse{}, errors.Wrap(err, "Error reading appevents response body")
	}

	err = json.Unmarshal(resBody, &eventResponse)
	if err != nil {
		return AppEventResponse{}, errors.Wrap(err, "Error unmarshalling appevent")
	}
	return eventResponse, nil
}

func stringInSlice(str string, list []string) bool {
	for _, v := range list {
		if v == str {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package config

import (
	log "github.com/Sirupsen/logrus"
	yaml "github.com/cloudfoundry-incubator/candiedyaml"

	"strings"

	"github.com/rancher/os/util"
)

type CfgFunc func(*CloudConfig) (*CloudConfig, error)

func ChainCfgFuncs(cfg *CloudConfig, cfgFuncs ...CfgFunc) (*CloudConfig, error) {
	for i, cfgFunc := range cfgFuncs {
		log.Debugf("[%d/%d] Starting", i+1, len(cfgFuncs))
		var err error
		if cfg, err = cfgFunc(cfg); err != nil {
			log.Errorf("Failed [%d/%d] %d%%", i+1, len(cfgFuncs), ((i + 1) * 100 / len(cfgFuncs)))
			return cfg, err
		}
		log.Debugf("[%d/%d] Done %d%%", i+1, len(cfgFuncs), ((i + 1) * 100 / len(cfgFuncs)))
	}
	return cfg, nil
}

func filterKey(data map[interface{}]interface{}, key []string) (filtered, rest map[interface{}]interface{}) {
	if len(key) == 0 {
		return data, map[interface{}]interface{}{}
	}

	filtered = map[interface{}]interface{}{}
	rest = util.MapCopy(data)

	k := key[0]
	if d, ok := data[k]; ok {
		switch d := d.(type) {

		case map[interface{}]interface{}:
			f, r := filterKey(d, key[1:])

			if len(f) != 0 {
				filtered[k] = f
			}

			if len(r) != 0 {
				rest[k] = r
			} else {
				delete(rest, k)
			}

		default:
			filtered[k] = d
			delete(rest, k)
		}

	}

	return
}

func filterPrivateKeys(data map[interface{}]interface{}) map[interface{}]interface{} {
	for _, privateKey := range PrivateKeys {
		_, data = filterKey(data, strings.Split(privateKey, "."))
	}

	return data
}

func getOrSetVal(args string, data map[interface{}]interface{}, value interface{}) (interface{}, map[interface{}]interface{}) {
	parts := strings.Split(args, ".")

	tData := data
	if value != nil {
		tData = util.MapCopy(data)
	}
	t := tData
	for i, part := range parts {
		val, ok := t[part]
		last := i+1 == len(parts)

		// Reached end, set the value
		if last && value != nil {
			if s, ok := value.(string); ok {
				value = unmarshalOrReturnString(s)
			}

			t[part] = value
			return value, tData
		}

		// Missing intermediate key, create key
		if !last && value != nil && !ok {
			newData := map[interface{}]interface{}{}
			t[part] = newData
			t = newData
			continue
		}

		if !ok {
			break
		}

		if last {
			return val, tData
		}

		newData, ok := val.(map[interface{}]interface{})
		if !ok {
			break
		}

		t = newData
	}

	return "", tData
}

// Replace newlines and colons with random strings
// This is done to avoid YAML treating these as special characters
var (
	newlineMagicString = "9XsJcx6dR5EERYCC"
	colonMagicString   = "V0Rc21pIVknMm2rr"
)

func reverseReplacement(result interface{}) interface{} {
	switch val := result.(type) {
	case map[interface{}]interface{}:
		for k, v := range val {
			val[k] = reverseReplacement(v)
		}
		return val
	case []interface{}:
		for i, item := range val {
			val[i] = reverseReplacement(item)
		}
		return val
	case string:
		val = strings.Replace(val, newlineMagicString, "\n", -1)
		val = strings.Replace(val, colonMagicString, ":", -1)
		return val
	}

	return result
}

func unmarshalOrReturnString(value string) (result interface{}) {
	value = strings.Replace(value, "\n", newlineMagicString, -1)
	value = strings.Replace(value, ":", colonMagicString, -1)
	if err := yaml.Unmarshal([]byte(value), &result); err != nil {
		result = value
	}
	result = reverseReplacement(result)
	return
}

func parseCmdline(cmdLine string) map[interface{}]interface{} {
	result := make(map[interface{}]interface{})

outer:
	for _, part := range strings.Split(cmdLine, " ") {
		if !strings.HasPrefix(part, "rancher.") {
			continue
		}

		var value string
		kv := strings.SplitN(part, "=", 2)

		if len(kv) == 1 {
			value = "true"
		} else {
			value = kv[1]
		}

		current := result
		keys := strings.Split(kv[0], ".")
		for i, key := range keys {
			if i == len(keys)-1 {
				current[key] = unmarshalOrReturnString(value)
			} else {
				if obj, ok := current[key]; ok {
					if newCurrent, ok := obj.(map[interface{}]interface{}); ok {
						current = newCurrent
					} else {
						continue outer
					}
				} else {
					newCurrent := make(map[interface{}]interface{})
					current[key] = newCurrent
					current = newCurrent
				}
			}
		}
	}

	log.Debugf("Input obj %v", result)
	return result
}
<commit_msg>ALL top level cloud-config keys as kernel args work now if prefixed with "cc.", while prefixed just with "rancher." works as well for legacy backward compat<commit_after>package config

import (
	log "github.com/Sirupsen/logrus"
	yaml "github.com/cloudfoundry-incubator/candiedyaml"

	"strings"

	"github.com/rancher/os/util"
)

type CfgFunc func(*CloudConfig) (*CloudConfig, error)

func ChainCfgFuncs(cfg *CloudConfig, cfgFuncs ...CfgFunc) (*CloudConfig, error) {
	for i, cfgFunc := range cfgFuncs {
		log.Debugf("[%d/%d] Starting", i+1, len(cfgFuncs))
		var err error
		if cfg, err = cfgFunc(cfg); err != nil {
			log.Errorf("Failed [%d/%d] %d%%", i+1, len(cfgFuncs), ((i + 1) * 100 / len(cfgFuncs)))
			return cfg, err
		}
		log.Debugf("[%d/%d] Done %d%%", i+1, len(cfgFuncs), ((i + 1) * 100 / len(cfgFuncs)))
	}
	return cfg, nil
}

func filterKey(data map[interface{}]interface{}, key []string) (filtered, rest map[interface{}]interface{}) {
	if len(key) == 0 {
		return data, map[interface{}]interface{}{}
	}

	filtered = map[interface{}]interface{}{}
	rest = util.MapCopy(data)

	k := key[0]
	if d, ok := data[k]; ok {
		switch d := d.(type) {

		case map[interface{}]interface{}:
			f, r := filterKey(d, key[1:])

			if len(f) != 0 {
				filtered[k] = f
			}

			if len(r) != 0 {
				rest[k] = r
			} else {
				delete(rest, k)
			}

		default:
			filtered[k] = d
			delete(rest, k)
		}

	}

	return
}

func filterPrivateKeys(data map[interface{}]interface{}) map[interface{}]interface{} {
	for _, privateKey := range PrivateKeys {
		_, data = filterKey(data, strings.Split(privateKey, "."))
	}

	return data
}

func getOrSetVal(args string, data map[interface{}]interface{}, value interface{}) (interface{}, map[interface{}]interface{}) {
	parts := strings.Split(args, ".")

	tData := data
	if value != nil {
		tData = util.MapCopy(data)
	}
	t := tData
	for i, part := range parts {
		val, ok := t[part]
		last := i+1 == len(parts)

		// Reached end, set the value
		if last && value != nil {
			if s, ok := value.(string); ok {
				value = unmarshalOrReturnString(s)
			}

			t[part] = value
			return value, tData
		}

		// Missing intermediate key, create key
		if !last && value != nil && !ok {
			newData := map[interface{}]interface{}{}
			t[part] = newData
			t = newData
			continue
		}

		if !ok {
			break
		}

		if last {
			return val, tData
		}

		newData, ok := val.(map[interface{}]interface{})
		if !ok {
			break
		}

		t = newData
	}

	return "", tData
}

// Replace newlines and colons with random strings
// This is done to avoid YAML treating these as special characters
var (
	newlineMagicString = "9XsJcx6dR5EERYCC"
	colonMagicString   = "V0Rc21pIVknMm2rr"
)

func reverseReplacement(result interface{}) interface{} {
	switch val := result.(type) {
	case map[interface{}]interface{}:
		for k, v := range val {
			val[k] = reverseReplacement(v)
		}
		return val
	case []interface{}:
		for i, item := range val {
			val[i] = reverseReplacement(item)
		}
		return val
	case string:
		val = strings.Replace(val, newlineMagicString, "\n", -1)
		val = strings.Replace(val, colonMagicString, ":", -1)
		return val
	}

	return result
}

func unmarshalOrReturnString(value string) (result interface{}) {
	value = strings.Replace(value, "\n", newlineMagicString, -1)
	value = strings.Replace(value, ":", colonMagicString, -1)
	if err := yaml.Unmarshal([]byte(value), &result); err != nil {
		result = value
	}
	result = reverseReplacement(result)
	return
}

func parseCmdline(cmdLine string) map[interface{}]interface{} {
	result := make(map[interface{}]interface{})

outer:
	for _, part := range strings.Split(cmdLine, " ") {
		if strings.HasPrefix(part, "cc.") {
			part = part[3:]
		} else if !strings.HasPrefix(part, "rancher.") {
			continue
		}

		var value string
		kv := strings.SplitN(part, "=", 2)

		if len(kv) == 1 {
			value = "true"
		} else {
			value = kv[1]
		}

		current := result
		keys := strings.Split(kv[0], ".")
		for i, key := range keys {
			if i == len(keys)-1 {
				current[key] = unmarshalOrReturnString(value)
			} else {
				if obj, ok := current[key]; ok {
					if newCurrent, ok := obj.(map[interface{}]interface{}); ok {
						current = newCurrent
					} else {
						continue outer
					}
				} else {
					newCurrent := make(map[interface{}]interface{})
					current[key] = newCurrent
					current = newCurrent
				}
			}
		}
	}

	log.Debugf("Input obj %v", result)
	return result
}
<|endoftext|>


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

package jaeger

import (
	"fmt"

	"github.com/jaegertracing/jaeger/model"
	"github.com/jaegertracing/jaeger/thrift-gen/jaeger"
)

// FromDomain takes an arrya of model.Span and returns
// an array of jaeger.Span.  If errors are found during
// conversion of tags, then error tags are appended.
func FromDomain(spans []*model.Span) []*jaeger.Span {
	jSpans := make([]*jaeger.Span, len(spans))
	dToJ := domainToJaegerTransformer{}
	for idx, span := range spans {
		jSpans[idx] = dToJ.transformSpan(span)
	}
	return jSpans
}

// FromDomainSpan takes a single model.Span and
// converts it into a jaeger.Span.  If errors are found
// during conversion of tags, then error tags are appended.
func FromDomainSpan(span *model.Span) *jaeger.Span {
	dToJ := &domainToJaegerTransformer{}
	return dToJ.transformSpan(span)
}

type domainToJaegerTransformer struct{}

func (d domainToJaegerTransformer) keyValueToTag(kv *model.KeyValue) *jaeger.Tag {
	if kv.VType == model.StringType {
		stringValue := string(kv.VStr)
		return &jaeger.Tag{
			Key:   kv.Key,
			VType: jaeger.TagType_STRING,
			VStr:  &stringValue,
		}
	}

	if kv.VType == model.Int64Type {
		intValue := kv.Int64()
		return &jaeger.Tag{
			Key:   kv.Key,
			VType: jaeger.TagType_LONG,
			VLong: &intValue,
		}
	}

	if kv.VType == model.BinaryType {
		binaryValue := kv.Binary()
		return &jaeger.Tag{
			Key:     kv.Key,
			VType:   jaeger.TagType_BINARY,
			VBinary: binaryValue,
		}
	}

	if kv.VType == model.BoolType {
		boolValue := kv.Bool()
		return &jaeger.Tag{
			Key:   kv.Key,
			VType: jaeger.TagType_BOOL,
			VBool: &boolValue,
		}
	}

	if kv.VType == model.Float64Type {
		floatValue := kv.Float64()
		return &jaeger.Tag{
			Key:     kv.Key,
			VType:   jaeger.TagType_DOUBLE,
			VDouble: &floatValue,
		}
	}

	errString := fmt.Sprintf("No suitable tag type found for: %#v", kv.VType)
	errTag := &jaeger.Tag{
		Key:   "Error",
		VType: jaeger.TagType_STRING,
		VStr:  &errString,
	}

	return errTag
}

func (d domainToJaegerTransformer) convertKeyValuesToTags(kvs model.KeyValues) []*jaeger.Tag {
	jaegerTags := make([]*jaeger.Tag, len(kvs))
	for idx, kv := range kvs {
		jaegerTags[idx] = d.keyValueToTag(&kv)
	}
	return jaegerTags
}

func (d domainToJaegerTransformer) convertLogs(logs []model.Log) []*jaeger.Log {
	jaegerLogs := make([]*jaeger.Log, len(logs))
	for idx, log := range logs {
		jaegerLogs[idx] = &jaeger.Log{
			Timestamp: int64(model.TimeAsEpochMicroseconds(log.Timestamp)),
			Fields:    d.convertKeyValuesToTags(log.Fields),
		}
	}
	return jaegerLogs
}

func (d domainToJaegerTransformer) convertSpanRefs(refs []model.SpanRef) []*jaeger.SpanRef {
	jaegerSpanRefs := make([]*jaeger.SpanRef, len(refs))
	for idx, ref := range refs {
		jaegerSpanRefs[idx] = &jaeger.SpanRef{
			RefType:     jaeger.SpanRefType(ref.RefType),
			TraceIdLow:  int64(ref.TraceID.Low),
			TraceIdHigh: int64(ref.TraceID.High),
			SpanId:      int64(ref.SpanID),
		}
	}
	return jaegerSpanRefs
}

func (d domainToJaegerTransformer) transformSpan(span *model.Span) *jaeger.Span {
	tags := d.convertKeyValuesToTags(span.Tags)
	logs := d.convertLogs(span.Logs)
	refs := d.convertSpanRefs(span.References)

	jaegerSpan := &jaeger.Span{
		TraceIdLow:    int64(span.TraceID.Low),
		TraceIdHigh:   int64(span.TraceID.High),
		SpanId:        int64(span.SpanID),
		ParentSpanId:  int64(span.ParentSpanID()),
		OperationName: span.OperationName,
		References:    refs,
		Flags:         int32(span.Flags),
		StartTime:     int64(model.TimeAsEpochMicroseconds(span.StartTime)),
		Duration:      int64(model.DurationAsMicroseconds(span.Duration)),
		Tags:          tags,
		Logs:          logs,
	}
	return jaegerSpan
}
<commit_msg>fix type comversion (#2921)<commit_after>// Copyright (c) 2019 The Jaeger Authors.
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jaeger

import (
	"fmt"

	"github.com/jaegertracing/jaeger/model"
	"github.com/jaegertracing/jaeger/thrift-gen/jaeger"
)

// FromDomain takes an arrya of model.Span and returns
// an array of jaeger.Span.  If errors are found during
// conversion of tags, then error tags are appended.
func FromDomain(spans []*model.Span) []*jaeger.Span {
	jSpans := make([]*jaeger.Span, len(spans))
	dToJ := domainToJaegerTransformer{}
	for idx, span := range spans {
		jSpans[idx] = dToJ.transformSpan(span)
	}
	return jSpans
}

// FromDomainSpan takes a single model.Span and
// converts it into a jaeger.Span.  If errors are found
// during conversion of tags, then error tags are appended.
func FromDomainSpan(span *model.Span) *jaeger.Span {
	dToJ := &domainToJaegerTransformer{}
	return dToJ.transformSpan(span)
}

type domainToJaegerTransformer struct{}

func (d domainToJaegerTransformer) keyValueToTag(kv *model.KeyValue) *jaeger.Tag {
	if kv.VType == model.StringType {
		stringValue := kv.VStr
		return &jaeger.Tag{
			Key:   kv.Key,
			VType: jaeger.TagType_STRING,
			VStr:  &stringValue,
		}
	}

	if kv.VType == model.Int64Type {
		intValue := kv.Int64()
		return &jaeger.Tag{
			Key:   kv.Key,
			VType: jaeger.TagType_LONG,
			VLong: &intValue,
		}
	}

	if kv.VType == model.BinaryType {
		binaryValue := kv.Binary()
		return &jaeger.Tag{
			Key:     kv.Key,
			VType:   jaeger.TagType_BINARY,
			VBinary: binaryValue,
		}
	}

	if kv.VType == model.BoolType {
		boolValue := kv.Bool()
		return &jaeger.Tag{
			Key:   kv.Key,
			VType: jaeger.TagType_BOOL,
			VBool: &boolValue,
		}
	}

	if kv.VType == model.Float64Type {
		floatValue := kv.Float64()
		return &jaeger.Tag{
			Key:     kv.Key,
			VType:   jaeger.TagType_DOUBLE,
			VDouble: &floatValue,
		}
	}

	errString := fmt.Sprintf("No suitable tag type found for: %#v", kv.VType)
	errTag := &jaeger.Tag{
		Key:   "Error",
		VType: jaeger.TagType_STRING,
		VStr:  &errString,
	}

	return errTag
}

func (d domainToJaegerTransformer) convertKeyValuesToTags(kvs model.KeyValues) []*jaeger.Tag {
	jaegerTags := make([]*jaeger.Tag, len(kvs))
	for idx, kv := range kvs {
		jaegerTags[idx] = d.keyValueToTag(&kv)
	}
	return jaegerTags
}

func (d domainToJaegerTransformer) convertLogs(logs []model.Log) []*jaeger.Log {
	jaegerLogs := make([]*jaeger.Log, len(logs))
	for idx, log := range logs {
		jaegerLogs[idx] = &jaeger.Log{
			Timestamp: int64(model.TimeAsEpochMicroseconds(log.Timestamp)),
			Fields:    d.convertKeyValuesToTags(log.Fields),
		}
	}
	return jaegerLogs
}

func (d domainToJaegerTransformer) convertSpanRefs(refs []model.SpanRef) []*jaeger.SpanRef {
	jaegerSpanRefs := make([]*jaeger.SpanRef, len(refs))
	for idx, ref := range refs {
		jaegerSpanRefs[idx] = &jaeger.SpanRef{
			RefType:     jaeger.SpanRefType(ref.RefType),
			TraceIdLow:  int64(ref.TraceID.Low),
			TraceIdHigh: int64(ref.TraceID.High),
			SpanId:      int64(ref.SpanID),
		}
	}
	return jaegerSpanRefs
}

func (d domainToJaegerTransformer) transformSpan(span *model.Span) *jaeger.Span {
	tags := d.convertKeyValuesToTags(span.Tags)
	logs := d.convertLogs(span.Logs)
	refs := d.convertSpanRefs(span.References)

	jaegerSpan := &jaeger.Span{
		TraceIdLow:    int64(span.TraceID.Low),
		TraceIdHigh:   int64(span.TraceID.High),
		SpanId:        int64(span.SpanID),
		ParentSpanId:  int64(span.ParentSpanID()),
		OperationName: span.OperationName,
		References:    refs,
		Flags:         int32(span.Flags),
		StartTime:     int64(model.TimeAsEpochMicroseconds(span.StartTime)),
		Duration:      int64(model.DurationAsMicroseconds(span.Duration)),
		Tags:          tags,
		Logs:          logs,
	}
	return jaegerSpan
}
<|endoftext|>