<commit_before>package processd

import (
	"flag"
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/minotar/imgd/pkg/mcclient/mcuser"
	"github.com/minotar/imgd/pkg/processd/mcskin"
	"github.com/minotar/imgd/pkg/skind"
	"github.com/minotar/imgd/pkg/util/log"
	"github.com/minotar/imgd/pkg/util/route_helpers"

	"github.com/weaveworks/common/server"
)

var (
	DefaultProcessRoutes = map[string]skind.SkinProcessor{
		"Avatar":                 mcskin.HandlerHead,
		"Helm":                   mcskin.HandlerHelm,
		"Cube":                   mcskin.HandlerCube,
		"CubeHelm":               mcskin.HandlerCubeHelm,
		"Bust":                   mcskin.HandlerBust,
		"Body":                   mcskin.HandlerBody,
		"Armor/Bust|Armour/Bust": mcskin.HandlerArmorBust,
		"Armor/Body|Armour/Body": mcskin.HandlerArmorBody,
	}
)

type Config struct {
	Server          server.Config `yaml:"server,omitempty"`
	UpstreamTimeout time.Duration `yaml:"upstream_timeout"`
	SkindURL        string        `yaml:"skind_url,omitempty"`
	Logger          log.Logger
	CorsAllowAll    bool
	UseETags        bool
	CacheControlTTL time.Duration
}

// RegisterFlags registers flag.
func (c *Config) RegisterFlags(f *flag.FlagSet) {
	//c.Server.ExcludeRequestInLog = true

	f.DurationVar(&c.UpstreamTimeout, "processd.upstream-timeout", 15*time.Second, "Timeout for Skin lookup")
	f.StringVar(&c.SkindURL, "processd.skind-url", "http://localhost:4643/skin/", "API for skin lookups")
	f.BoolVar(&c.CorsAllowAll, "processd.cors-allow-all", true, "Permissive CORS policy")
	f.BoolVar(&c.UseETags, "processd.use-etags", true, "Use etags to skip re-processing")
	f.DurationVar(&c.CacheControlTTL, "processd.cache-control-ttl", time.Duration(6)*time.Hour, "Cache TTL returned to clients")

	c.Server.RegisterFlags(f)
}

type Processd struct {
	Cfg Config

	Server        *server.Server
	Client        *http.Client
	UserAgent     string
	SkindURL      string
	ProcessRoutes map[string]skind.SkinProcessor
}

func New(cfg Config) (*Processd, error) {
	// Set namespace for all metrics
	cfg.Server.MetricsNamespace = "processd"
	// Set the GRPC to localhost only
	cfg.Server.GRPCListenAddress = "127.0.0.3"

	processd := &Processd{
		Cfg: cfg,
		Client: &http.Client{
			Timeout: cfg.UpstreamTimeout,
		},
		UserAgent:     "minotar/imgd/processd (https://github.com/minotar/imgd) - default",
		SkindURL:      cfg.SkindURL,
		ProcessRoutes: DefaultProcessRoutes,
	}

	return processd, nil
}

// need some skin lookup wrapper

func handleSkinLookupError(w http.ResponseWriter, r *http.Request, logger log.Logger, processFunc skind.SkinProcessor) {
	skinIO := mcuser.GetSteveTextureIO()

	handler := processFunc(logger, skinIO)
	handler.ServeHTTP(w, r)
}

func (p *Processd) SkinLookupWrapper(processFunc skind.SkinProcessor) http.HandlerFunc {
	logger := p.Cfg.Logger

	return func(w http.ResponseWriter, r *http.Request) {

		userReq := route_helpers.MuxToUserReq(r)
		var userLookup string

		if userReq.UUID != "" {
			userLookup = userReq.UUID
		} else if userReq.Username != "" {
			userLookup = userReq.Username
		} else {
			logger.Errorf("Request came through without Username/UUID: %v", mux.Vars(r))
			handleSkinLookupError(w, r, logger, processFunc)
			return
		}

		skinReq, err := http.NewRequestWithContext(r.Context(), "GET", fmt.Sprint(p.SkindURL, userLookup), nil)
		if err != nil {
			//return nil, fmt.Errorf("unable to create request: %v", err)
			//Use Steve and call original process logic?
			logger.Errorf("It broken: %v", err)
			handleSkinLookupError(w, r, logger, processFunc)
			return
		}

		reqETag := r.Header.Get("If-None-Match")

		skinReq.Header.Set("User-Agent", p.UserAgent)
		if p.Cfg.UseETags {
			skinReq.Header.Set("If-None-Match", reqETag)
		}
		//req.Header.Set("X-Request-ID", "magic-to-use-existing-or-add-new")

		resp, err := p.Client.Do(skinReq)
		if err != nil {
			//return nil, fmt.Errorf("unable to GET URL: %v", err)
			//Use Steve and call original process logic?
			logger.Errorf("It broken: %v", err)
			handleSkinLookupError(w, r, logger, processFunc)
			return
		}
		// The processFunc *MUST* close the resp.Body via the TextureIO object
		//defer resp.Body.Close()

		w.Header().Add("Cache-Control", fmt.Sprintf("public, max-age=%d", int(p.Cfg.CacheControlTTL.Seconds())))

		if p.Cfg.UseETags {
			respETag := resp.Header.Get("ETag")
			// ETag is always included (even for 304 responses)
			w.Header().Set("ETag", respETag)

			// If the response was a StatusNotModified (it should be as we already sent the If-None-Match!)
			// If the ETag matches from request to response, then no need to process
			if resp.StatusCode == http.StatusNotModified || reqETag == respETag {
				w.WriteHeader(http.StatusNotModified)
				return
			}
		}

		skinIO := mcuser.TextureIO{
			ReadCloser: resp.Body,
		}

		// Up to this point, the processing could be metric'd "generically" and the type of processing was irrelevant
		handler := processFunc(logger, skinIO)
		handler.ServeHTTP(w, r)
	}
}

func (p *Processd) Run() error {
	//t.Server.HTTP.Handle("/services", http.HandlerFunc(t.servicesHandler))
	if err := p.initServer(); err != nil {
		return err
	}
	// init other bits

	return p.Server.Run()

	//return nil
}

func (p *Processd) initServer() error {
	serv, err := server.New(p.Cfg.Server)
	if err != nil {
		return err
	}

	//serv.HTTP.Use(route_helpers.LoggingMiddleware(p.Cfg.Logger))

	if p.Cfg.CorsAllowAll {
		serv.HTTP.Use(route_helpers.CorsHandler)
	}

	p.Server = serv
	p.routes()
	return nil
}
<commit_msg>Processd: handle empty ETag<commit_after>package processd

import (
	"flag"
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/minotar/imgd/pkg/mcclient/mcuser"
	"github.com/minotar/imgd/pkg/processd/mcskin"
	"github.com/minotar/imgd/pkg/skind"
	"github.com/minotar/imgd/pkg/util/log"
	"github.com/minotar/imgd/pkg/util/route_helpers"

	"github.com/weaveworks/common/server"
)

var (
	DefaultProcessRoutes = map[string]skind.SkinProcessor{
		"Avatar":                 mcskin.HandlerHead,
		"Helm":                   mcskin.HandlerHelm,
		"Cube":                   mcskin.HandlerCube,
		"CubeHelm":               mcskin.HandlerCubeHelm,
		"Bust":                   mcskin.HandlerBust,
		"Body":                   mcskin.HandlerBody,
		"Armor/Bust|Armour/Bust": mcskin.HandlerArmorBust,
		"Armor/Body|Armour/Body": mcskin.HandlerArmorBody,
	}
)

type Config struct {
	Server          server.Config `yaml:"server,omitempty"`
	UpstreamTimeout time.Duration `yaml:"upstream_timeout"`
	SkindURL        string        `yaml:"skind_url,omitempty"`
	Logger          log.Logger
	CorsAllowAll    bool
	UseETags        bool
	CacheControlTTL time.Duration
}

// RegisterFlags registers flag.
func (c *Config) RegisterFlags(f *flag.FlagSet) {
	//c.Server.ExcludeRequestInLog = true

	f.DurationVar(&c.UpstreamTimeout, "processd.upstream-timeout", 15*time.Second, "Timeout for Skin lookup")
	f.StringVar(&c.SkindURL, "processd.skind-url", "http://localhost:4643/skin/", "API for skin lookups")
	f.BoolVar(&c.CorsAllowAll, "processd.cors-allow-all", true, "Permissive CORS policy")
	f.BoolVar(&c.UseETags, "processd.use-etags", true, "Use etags to skip re-processing")
	f.DurationVar(&c.CacheControlTTL, "processd.cache-control-ttl", time.Duration(6)*time.Hour, "Cache TTL returned to clients")

	c.Server.RegisterFlags(f)
}

type Processd struct {
	Cfg Config

	Server        *server.Server
	Client        *http.Client
	UserAgent     string
	SkindURL      string
	ProcessRoutes map[string]skind.SkinProcessor
}

func New(cfg Config) (*Processd, error) {
	// Set namespace for all metrics
	cfg.Server.MetricsNamespace = "processd"
	// Set the GRPC to localhost only
	cfg.Server.GRPCListenAddress = "127.0.0.3"

	processd := &Processd{
		Cfg: cfg,
		Client: &http.Client{
			Timeout: cfg.UpstreamTimeout,
		},
		UserAgent:     "minotar/imgd/processd (https://github.com/minotar/imgd) - default",
		SkindURL:      cfg.SkindURL,
		ProcessRoutes: DefaultProcessRoutes,
	}

	return processd, nil
}

// need some skin lookup wrapper

func handleSkinLookupError(w http.ResponseWriter, r *http.Request, logger log.Logger, processFunc skind.SkinProcessor) {
	skinIO := mcuser.GetSteveTextureIO()

	handler := processFunc(logger, skinIO)
	handler.ServeHTTP(w, r)
}

func (p *Processd) SkinLookupWrapper(processFunc skind.SkinProcessor) http.HandlerFunc {
	logger := p.Cfg.Logger

	return func(w http.ResponseWriter, r *http.Request) {

		userReq := route_helpers.MuxToUserReq(r)
		var userLookup string

		if userReq.UUID != "" {
			userLookup = userReq.UUID
		} else if userReq.Username != "" {
			userLookup = userReq.Username
		} else {
			logger.Errorf("Request came through without Username/UUID: %v", mux.Vars(r))
			handleSkinLookupError(w, r, logger, processFunc)
			return
		}

		skinReq, err := http.NewRequestWithContext(r.Context(), "GET", fmt.Sprint(p.SkindURL, userLookup), nil)
		if err != nil {
			//return nil, fmt.Errorf("unable to create request: %v", err)
			//Use Steve and call original process logic?
			logger.Errorf("It broken: %v", err)
			handleSkinLookupError(w, r, logger, processFunc)
			return
		}

		reqETag := r.Header.Get("If-None-Match")

		skinReq.Header.Set("User-Agent", p.UserAgent)
		if p.Cfg.UseETags && reqETag != "" {
			skinReq.Header.Set("If-None-Match", reqETag)
		}
		//req.Header.Set("X-Request-ID", "magic-to-use-existing-or-add-new")

		resp, err := p.Client.Do(skinReq)
		if err != nil {
			//return nil, fmt.Errorf("unable to GET URL: %v", err)
			//Use Steve and call original process logic?
			logger.Errorf("It broken: %v", err)
			handleSkinLookupError(w, r, logger, processFunc)
			return
		}
		// The processFunc *MUST* close the resp.Body via the TextureIO object
		//defer resp.Body.Close()

		// Todo: Add handling for non-200/304 response codes, add handing for skind errors (API error vs. non-username)

		w.Header().Add("Cache-Control", fmt.Sprintf("public, max-age=%d", int(p.Cfg.CacheControlTTL.Seconds())))

		if p.Cfg.UseETags {
			respETag := resp.Header.Get("ETag")
			if respETag != "" {
				// ETag is always included (even for 304 responses)
				w.Header().Set("ETag", respETag)
			}

			// If the response was a StatusNotModified (it should be as we already sent the If-None-Match!)
			// If the ETag matches from request to response, then no need to process
			if resp.StatusCode == http.StatusNotModified || (respETag != "" && reqETag == respETag) {
				w.WriteHeader(http.StatusNotModified)
				return
			}
		}

		skinIO := mcuser.TextureIO{
			ReadCloser: resp.Body,
		}

		// Up to this point, the processing could be metric'd "generically" and the type of processing was irrelevant
		handler := processFunc(logger, skinIO)
		handler.ServeHTTP(w, r)
	}
}

func (p *Processd) Run() error {
	//t.Server.HTTP.Handle("/services", http.HandlerFunc(t.servicesHandler))
	if err := p.initServer(); err != nil {
		return err
	}
	// init other bits

	return p.Server.Run()

	//return nil
}

func (p *Processd) initServer() error {
	serv, err := server.New(p.Cfg.Server)
	if err != nil {
		return err
	}

	//serv.HTTP.Use(route_helpers.LoggingMiddleware(p.Cfg.Logger))

	if p.Cfg.CorsAllowAll {
		serv.HTTP.Use(route_helpers.CorsHandler)
	}

	p.Server = serv
	p.routes()
	return nil
}
<|endoftext|>


<commit_before>package progress

import (
	"io"
	"sync/atomic"
	"time"

	"github.intel.com/hpdd/logging/alert"
	"github.intel.com/hpdd/logging/debug"
)

// The default buffer size in io.copyBuffer() is 32KB -- this is the
// read size seen when the checksummer is running.
const ckSumSig = 32 * 1024

type (
	progressFunc func(uint64, uint64) error

	// ReaderAtSeeker groups the io.ReaderAt and io.Seeker interfaces
	ReaderAtSeeker interface {
		io.ReaderAt
		io.Reader
		io.Seeker
	}

	progressUpdater struct {
		done        chan struct{}
		bytesCopied uint64
	}

	// Reader wraps an io.ReaderAt and periodically invokes the
	// supplied callback to provide progress updates.
	Reader struct {
		progressUpdater

		src ReaderAtSeeker
	}

	// Writer wraps an io.WriterAt and periodically invokes the
	// supplied callback to provide progress updates.
	Writer struct {
		progressUpdater

		dst io.WriterAt
	}
)

// startUpdates creates a goroutine to periodically call the supplied
// callback with updated progress information. The callback must accept
// an int64 representing the last update value, and an int64 representing
// the delta between the last update value and the current bytes-copied count.
func (p *progressUpdater) startUpdates(updateEvery time.Duration, f progressFunc) {
	p.done = make(chan struct{})

	if updateEvery > 0 && f != nil {
		var lastTotal uint64
		go func() {
			for {
				select {
				case <-time.After(updateEvery):
					copied := atomic.LoadUint64(&p.bytesCopied)
					if err := f(lastTotal, copied-lastTotal); err != nil {
						alert.Warnf("Error received from updater callback: %s", err)
						// Should we return here?
					}
					lastTotal = copied
				case <-p.done:
					debug.Print("Shutting down updater goroutine")
					return
				}
			}
		}()
	}
}

// StopUpdates kills the updater goroutine
func (p *progressUpdater) StopUpdates() {
	p.done <- struct{}{}
}

// Seek calls the wrapped Seeker's Seek
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
	return r.src.Seek(offset, whence)
}

// Read calls the wrapped ReaderAt's ReadAt with offset 0
func (r *Reader) Read(p []byte) (int, error) {
	// Should we count these too?
	return r.src.Read(p)
}

// ReadAt reads len(p) bytes into p starting at offset off in the underlying
// input source. It returns the number of bytes read (0 <= n <= len(p)) and
// any error encountered.
func (r *Reader) ReadAt(p []byte, off int64) (int, error) {
	n, err := r.src.ReadAt(p, off)

	// Stupid hack to work around double-counting for progress updates.
	// Each file is read twice -- once for checksumming, then again
	// to actually transfer the data.
	if n != ckSumSig {
		atomic.AddUint64(&r.bytesCopied, uint64(n))
	}

	return n, err
}

// NewReader returns a new Reader
func NewReader(src ReaderAtSeeker, updateEvery time.Duration, f progressFunc) *Reader {
	r := &Reader{
		src: src,
	}

	r.startUpdates(updateEvery, f)

	return r
}

// WriteAt writes len(p) bytes from p to the underlying data stream at
// offset off. It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early. WriteAt
// must return a non-nil error if it returns n < len(p).
func (w *Writer) WriteAt(p []byte, off int64) (int, error) {
	n, err := w.dst.WriteAt(p, off)

	atomic.AddUint64(&w.bytesCopied, uint64(n))

	return n, err
}

// NewWriter returns a new Writer
func NewWriter(dst io.WriterAt, updateEvery time.Duration, f progressFunc) *Writer {
	w := &Writer{
		dst: dst,
	}
	w.startUpdates(updateEvery, f)

	return w
}
<commit_msg>Avoid 4k reads when using the aws sdk<commit_after>package progress

import (
	"io"
	"sync/atomic"
	"time"

	"github.intel.com/hpdd/logging/alert"
	"github.intel.com/hpdd/logging/debug"
)

// The default buffer size in io.copyBuffer() is 32KB -- this is the
// read size seen when the checksummer is running.
const ckSumSig = 32 * 1024

type (
	progressFunc func(uint64, uint64) error

	// ReaderAtSeeker groups the io.ReaderAt and io.Seeker interfaces
	ReaderAtSeeker interface {
		io.ReaderAt
		io.Reader
		io.Seeker
	}

	progressUpdater struct {
		done        chan struct{}
		bytesCopied uint64
	}

	// Reader wraps an io.ReaderAt and periodically invokes the
	// supplied callback to provide progress updates.
	Reader struct {
		progressUpdater

		src ReaderAtSeeker
	}

	// Writer wraps an io.WriterAt and periodically invokes the
	// supplied callback to provide progress updates.
	Writer struct {
		progressUpdater

		dst io.WriterAt
	}
)

// startUpdates creates a goroutine to periodically call the supplied
// callback with updated progress information. The callback must accept
// an int64 representing the last update value, and an int64 representing
// the delta between the last update value and the current bytes-copied count.
func (p *progressUpdater) startUpdates(updateEvery time.Duration, f progressFunc) {
	p.done = make(chan struct{})

	if updateEvery > 0 && f != nil {
		var lastTotal uint64
		go func() {
			for {
				select {
				case <-time.After(updateEvery):
					copied := atomic.LoadUint64(&p.bytesCopied)
					if err := f(lastTotal, copied-lastTotal); err != nil {
						alert.Warnf("Error received from updater callback: %s", err)
						// Should we return here?
					}
					lastTotal = copied
				case <-p.done:
					debug.Print("Shutting down updater goroutine")
					return
				}
			}
		}()
	}
}

// StopUpdates kills the updater goroutine
func (p *progressUpdater) StopUpdates() {
	p.done <- struct{}{}
}

// Seek calls the wrapped Seeker's Seek
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
	return r.src.Seek(offset, whence)
}

// Read calls internal Read and tracks how many bytes were read.
func (r *Reader) Read(p []byte) (n int, err error) {
	n, err = r.src.Read(p)
	atomic.AddUint64(&r.bytesCopied, uint64(n))
	return
}

// DISABLED
//
// The go http client package wraps the socket with a bufio.NewWriter() with
// the default 4k buffer. When aws sdk sends our file data, it ends up using
// io.Copy(w, src) to copy the data. This uses bufio Writer.ReadFrom() method
// and this reads from from our file into a 4k buf. One way to fix this could
// have been to implment a WriteTo method so we could read any size buffer we
// wanted to, but this doesn't work because the aws sdk has wrapped our file
// object with several others.
//
// One way to trick the sdk to read larger buffer sizes is to disable ReadAt and
// force the sdk to fall back to Read each chunk with one call. This is much
// better for lustre, but now read IO is single threaded, so this isn't so good
// either.  On the positive side, now the file is only read once as the sdk is
// able to sign each chunk from buffer in memeory, and also now we could
// calculate the sha1 for the whole file like we do in the posix mover.
//
// It is a shame that go-aws-sdk doesn't provide a callback for updating status
// like boto does.

// ReadAt reads len(p) bytes into p starting at offset off in the underlying
// input source. It returns the number of bytes read (0 <= n <= len(p)) and
// any error encountered.
/*
func (r *Reader) ReadAt(p []byte, off int64) (int, error) {
	n, err := r.src.ReadAt(p, off)

	// Stupid hack to work around double-counting for progress updates.
	// Each file is read twice -- once for checksumming, then again
	// to actually transfer the data.
	if n != ckSumSig {
		atomic.AddUint64(&r.bytesCopied, uint64(n))
	}

	return n, err
}
*/

// NewReader returns a new Reader
func NewReader(src ReaderAtSeeker, updateEvery time.Duration, f progressFunc) *Reader {
	r := &Reader{
		src: src,
	}

	r.startUpdates(updateEvery, f)

	return r
}

// WriteAt writes len(p) bytes from p to the underlying data stream at
// offset off. It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early. WriteAt
// must return a non-nil error if it returns n < len(p).
func (w *Writer) WriteAt(p []byte, off int64) (int, error) {
	n, err := w.dst.WriteAt(p, off)

	atomic.AddUint64(&w.bytesCopied, uint64(n))

	return n, err
}

// NewWriter returns a new Writer
func NewWriter(dst io.WriterAt, updateEvery time.Duration, f progressFunc) *Writer {
	w := &Writer{
		dst: dst,
	}
	w.startUpdates(updateEvery, f)

	return w
}
<|endoftext|>


<commit_before>package utils_test

import (
	"testing"

	"github.com/simonswine/kube-lego/pkg/utils"

	"github.com/stretchr/testify/assert"
)

func TestStringSliceLowerCase(t *testing.T) {
	assert.Equal(
		t,
		[]string{"abc", "def"},
		utils.StringSliceLowerCase([]string{"AbC", "def"}),
	)
	assert.Equal(
		t,
		[]string{"abc", "def", "def"},
		utils.StringSliceLowerCase([]string{"abc", "def", "def"}),
	)
}

func TestStringSliceDistinct(t *testing.T) {
	assert.Equal(
		t,
		[]string{"abc", "def"},
		utils.StringSliceDistinct([]string{"abc", "def"}),
	)
	assert.Equal(
		t,
		[]string{"abc", "def"},
		utils.StringSliceDistinct([]string{"abc", "def", "def"}),
	)
	assert.Equal(
		t,
		[]string{},
		utils.StringSliceDistinct([]string{}),
	)
}

func TestHashStringSlice(t *testing.T) {
	assert.Equal(
		t,
		"d41d8cd98f00b204e9800998ecf8427e",
		utils.HashStringSlice([]string{"abc", "def"}),
	)
	assert.Equal(
		t,
		utils.HashStringSlice([]string{"def", "abc"}),
		utils.HashStringSlice([]string{"abc", "def"}),
	)
}
<commit_msg>Fix failing test<commit_after>package utils_test

import (
	"sort"
	"testing"

	"github.com/simonswine/kube-lego/pkg/utils"

	"github.com/stretchr/testify/assert"
)

func TestStringSliceLowerCase(t *testing.T) {
	assert.Equal(
		t,
		[]string{"abc", "def"},
		utils.StringSliceLowerCase([]string{"AbC", "def"}),
	)
	assert.Equal(
		t,
		[]string{"abc", "def", "def"},
		utils.StringSliceLowerCase([]string{"abc", "def", "def"}),
	)
}

func TestStringSliceDistinct(t *testing.T) {
	assert.Equal(
		t,
		[]string{"abc", "def"},
		utils.StringSliceDistinct([]string{"abc", "def"}),
	)
	output := utils.StringSliceDistinct([]string{"abc", "def", "def"})
	sort.Strings(output)
	assert.Equal(
		t,
		[]string{"abc", "def"},
		output,
	)
	assert.Equal(
		t,
		[]string{},
		utils.StringSliceDistinct([]string{}),
	)
}

func TestHashStringSlice(t *testing.T) {
	assert.Equal(
		t,
		"d41d8cd98f00b204e9800998ecf8427e",
		utils.HashStringSlice([]string{"abc", "def"}),
	)
	assert.Equal(
		t,
		utils.HashStringSlice([]string{"def", "abc"}),
		utils.HashStringSlice([]string{"abc", "def"}),
	)
}
<|endoftext|>


<commit_before>package build

const client = `
"use strict";

exports.createClient = function(args) {
    var events = require('events');
    var channel = new events.EventEmitter();
	var http = require('http');
    var server = http.createServer(function (request, response) {
	  if (request.method!='POST') {
	    response.writeHead(404);
        response.end('');
		return;
	  }

	  var body = '';
	  request.on('data', function(chunk) { body += chunk.toString(); });
	  request.on('end', function() {
		switch (request.url) {
		case '/redirect':
		  channel.emit('redirect', body);
		  response.writeHead(204);
		  response.end('');
		  break;
		case '/error':
		  channel.emit('error', body);
		  response.writeHead(204);
		  response.end('');
		  break;
		default:
		  response.writeHead(404);
		  response.end('');
		 };
	  });
    });

    server.listen(0, '127.0.0.1', 1, function() {
        var nodeWebkitAddr = 'http://127.0.0.1:'+server.address().port;
        console.log('Listening for golang-nw on '+nodeWebkitAddr);
        startClient(channel, nodeWebkitAddr, args);
    });

	return channel;
};

function startClient(channel, nodeWebkitAddr, args) {
    var path = require('path');
    var exe = '.'+path.sep+'{{ .Bin }}';
    console.log('Using client: ' + exe);

    // Now start the client process
    var childProcess = require('child_process');

	var env = process.env;
	env['{{ .EnvVar }}'] = nodeWebkitAddr;
    var p = childProcess.spawn(exe, args, {env: env});

    p.stdout.on('data', function(data) {
        console.log(data.toString());
    });

    p.stderr.on('data', function(data) {
        console.error(data.toString());
    });

    p.on('error', function(err) {
        console.error('child error: ' + err);
        channel.emit('error', err);
    });

    p.on('close', function(code) {
        console.log('child process closed with code ' + code);
        channel.emit('close', code);
    });

    p.on('exit', function(code) {
        console.log('child process exited with code ' + code);
        channel.emit('exit', code);
    });

    channel.kill = function() {
        p.kill();
    }
};
`
<commit_msg>Split printfs on newlines when logging to console<commit_after>package build

const client = `
"use strict";

exports.createClient = function(args) {
    var events = require('events');
    var channel = new events.EventEmitter();
	var http = require('http');
    var server = http.createServer(function (request, response) {
	  if (request.method!='POST') {
	    response.writeHead(404);
        response.end('');
		return;
	  }

	  var body = '';
	  request.on('data', function(chunk) { body += chunk.toString(); });
	  request.on('end', function() {
		switch (request.url) {
		case '/redirect':
		  channel.emit('redirect', body);
		  response.writeHead(204);
		  response.end('');
		  break;
		case '/error':
		  channel.emit('error', body);
		  response.writeHead(204);
		  response.end('');
		  break;
		default:
		  response.writeHead(404);
		  response.end('');
		 };
	  });
    });

    server.listen(0, '127.0.0.1', 1, function() {
        var nodeWebkitAddr = 'http://127.0.0.1:'+server.address().port;
        console.log('Listening for golang-nw on '+nodeWebkitAddr);
        startClient(channel, nodeWebkitAddr, args);
    });

	return channel;
};

func logMessage(data, logger) {
    var lines = data.toString().split('\n');
    for (var i = 0; i < lines.length; i++) {
        if (lines[i]) {
            logger(lines[i]);
        }
    }
}

function startClient(channel, nodeWebkitAddr, args) {
    var path = require('path');
    var exe = '.'+path.sep+'{{ .Bin }}';
    console.log('Using client: ' + exe);

    // Now start the client process
    var childProcess = require('child_process');

	var env = process.env;
	env['{{ .EnvVar }}'] = nodeWebkitAddr;
    var p = childProcess.spawn(exe, args, {env: env});

    p.stdout.on('data', function(data) {
        logMessage(data, console.log);
    });

    p.stderr.on('data', function(data) {
        logMessage(data, console.error);
    });

    p.on('error', function(err) {
        console.error('child error: ' + err);
        channel.emit('error', err);
    });

    p.on('close', function(code) {
        console.log('child process closed with code ' + code);
        channel.emit('close', code);
    });

    p.on('exit', function(code) {
        console.log('child process exited with code ' + code);
        channel.emit('exit', code);
    });

    channel.kill = function() {
        p.kill();
    }
};
`
<|endoftext|>


<commit_before>package itemDef

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strconv"
)

type Field struct {
	Name    string      `json:name`
	Type    string      `json:type`
	Label   string      `json:label`
	Input   string      `json:input`
	Require string      `json:require`
	Unique  string      `json:unique`
	Model   string      `json:model`
	Enum    []string    `json:enum`
	Default interface{} `json:default`
}

type ItemDef struct {
	Name      string  `json:name`
	Fields    []Field `json:fields`
	fieldMaps map[string]Field
}

func (this *ItemDef) initAccDate() map[string]Field {
	fieldMap := make(map[string]Field, len(this.Fields))
	for _, field := range this.Fields {
		fieldMap[field.Name] = field
	}
	return fieldMap
}

func (this *ItemDef) IsValidField(fieldName string) bool {
	_, ok := this.fieldMaps[fieldName]
	return ok
}

func (this *ItemDef) GetFieldMap() map[string]Field {
	fieldMap := make(map[string]Field, len(this.Fields))
	for _, field := range this.Fields {
		fieldMap[field.Name] = field
	}
	return fieldMap
}

func (field *Field) GetValue(valueString string) (interface{}, bool) {
	switch field.Type {
	case "string":
		return valueString, true
	case "int64":
		value, err := strconv.ParseInt(valueString, 10, 64)
		if err != nil {
			return value, true
		} else {
			return 0, false
		}
	default:
		return 0, false
	}
}

var EntityDefMap = make(map[string]ItemDef)

func init() {
	//fmt.Println("initItemDefMap")
	bytes, err := ioutil.ReadFile("conf/item.json")
	if err != nil {
		fmt.Println("ReadFile: ", err.Error())
	}
	//    fmt.Println("readFile", bytes)
	if err := json.Unmarshal(bytes, &EntityDefMap); err != nil {
		fmt.Println("Unmarshal: ", err.Error())
	}
	//fmt.Println("itemde", EntityDefMap)
	for _, oDef := range EntityDefMap {
		oDef.initAccDate()
		//		odefd, ok := EntityDefMap["user"]
		//		if ok {
		//			for idx, v := range odefd.Fields {
		//				fmt.Println(v.Name, idx, v.Model)
		//			}
		//		}
	}
	//	fmt.Println(EntityDefMap)
}
<commit_msg>no message<commit_after><|endoftext|>


<commit_before>package markdown

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"

	"github.com/BurntSushi/toml"
	"gopkg.in/yaml.v2"
)

var (
	parsers = []MetadataParser{
		&JSONMetadataParser{metadata: Metadata{Variables: make(map[string]interface{})}},
		&TOMLMetadataParser{metadata: Metadata{Variables: make(map[string]interface{})}},
		&YAMLMetadataParser{metadata: Metadata{Variables: make(map[string]interface{})}},
	}
)

// Metadata stores a page's metadata
type Metadata struct {
	// Page title
	Title string

	// Page template
	Template string

	// Variables to be used with Template
	Variables map[string]interface{}
}

// load loads parsed values in parsedMap into Metadata
func (m *Metadata) load(parsedMap map[string]interface{}) {
	if template, ok := parsedMap["title"]; ok {
		m.Title, _ = template.(string)
	}
	if template, ok := parsedMap["template"]; ok {
		m.Template, _ = template.(string)
	}
	if variables, ok := parsedMap["variables"]; ok {
		m.Variables, _ = variables.(map[string]interface{})
	}
}

// MetadataParser is a an interface that must be satisfied by each parser
type MetadataParser interface {
	// Opening identifier
	Opening() []byte

	// Closing identifier
	Closing() []byte

	// Parse the metadata.
	// Returns the remaining page contents (Markdown)
	// after extracting metadata
	Parse([]byte) ([]byte, error)

	// Parsed metadata.
	// Should be called after a call to Parse returns no error
	Metadata() Metadata
}

// JSONMetadataParser is the MetdataParser for JSON
type JSONMetadataParser struct {
	metadata Metadata
}

// Parse the metadata
func (j *JSONMetadataParser) Parse(b []byte) ([]byte, error) {
	m := make(map[string]interface{})

	// Read the preceding JSON object
	decoder := json.NewDecoder(bytes.NewReader(b))
	if err := decoder.Decode(&m); err != nil {
		return b, err
	}

	j.metadata.load(m)

	// Retrieve remaining bytes after decoding
	buf := make([]byte, len(b))
	n, err := decoder.Buffered().Read(buf)
	if err != nil {
		return b, err
	}

	return buf[:n], nil
}

// Metadata returns parsed metadata.  It should be called
// only after a call to Parse returns without error.
func (j *JSONMetadataParser) Metadata() Metadata {
	return j.metadata
}

// Opening returns the opening identifier JSON metadata
func (j *JSONMetadataParser) Opening() []byte {
	return []byte("{")
}

// Closing returns the closing identifier JSON metadata
func (j *JSONMetadataParser) Closing() []byte {
	return []byte("}")
}

// TOMLMetadataParser is the MetadataParser for TOML
type TOMLMetadataParser struct {
	metadata Metadata
}

// Parse the metadata
func (t *TOMLMetadataParser) Parse(b []byte) ([]byte, error) {
	b, markdown, err := extractMetadata(t, b)
	if err != nil {
		return markdown, err
	}
	m := make(map[string]interface{})
	if err := toml.Unmarshal(b, &m); err != nil {
		return markdown, err
	}
	t.metadata.load(m)
	return markdown, nil
}

// Metadata returns parsed metadata.  It should be called
// only after a call to Parse returns without error.
func (t *TOMLMetadataParser) Metadata() Metadata {
	return t.metadata
}

// Opening returns the opening identifier TOML metadata
func (t *TOMLMetadataParser) Opening() []byte {
	return []byte("+++")
}

// Closing returns the closing identifier TOML metadata
func (t *TOMLMetadataParser) Closing() []byte {
	return []byte("+++")
}

// YAMLMetadataParser is the MetadataParser for YAML
type YAMLMetadataParser struct {
	metadata Metadata
}

// Parse the metadata
func (y *YAMLMetadataParser) Parse(b []byte) ([]byte, error) {
	b, markdown, err := extractMetadata(y, b)
	if err != nil {
		return markdown, err
	}

	m := make(map[string]interface{})
	if err := yaml.Unmarshal(b, &m); err != nil {
		return markdown, err
	}

	// convert variables (if present) to map[string]interface{}
	// to match expected type
	if vars, ok := m["variables"].(map[interface{}]interface{}); ok {
		vars1 := make(map[string]interface{})
		for k, v := range vars {
			if key, ok := k.(string); ok {
				vars1[key] = v
			}
		}
		m["variables"] = vars1
	}

	y.metadata.load(m)
	return markdown, nil
}

// Metadata returns parsed metadata.  It should be called
// only after a call to Parse returns without error.
func (y *YAMLMetadataParser) Metadata() Metadata {
	return y.metadata
}

// Opening returns the opening identifier YAML metadata
func (y *YAMLMetadataParser) Opening() []byte {
	return []byte("---")
}

// Closing returns the closing identifier YAML metadata
func (y *YAMLMetadataParser) Closing() []byte {
	return []byte("---")
}

// extractMetadata separates metadata content from from markdown content in b.
// It returns the metadata, the remaining bytes (markdown), and an error, if any.
func extractMetadata(parser MetadataParser, b []byte) (metadata []byte, markdown []byte, err error) {
	b = bytes.TrimSpace(b)
	reader := bufio.NewReader(bytes.NewBuffer(b))

	// Read first line, which should indicate metadata or not
	line, err := reader.ReadBytes('\n')
	if err != nil || !bytes.Equal(bytes.TrimSpace(line), parser.Opening()) {
		return nil, b, fmt.Errorf("first line missing expected metadata identifier")
	}

	// buffer for metadata contents
	metaBuf := bytes.Buffer{}

	// Read remaining lines until closing identifier is found
	for {
		line, err := reader.ReadBytes('\n')
		if err != nil {
			if err == io.EOF {
				// no closing metadata identifier found
				return nil, nil, fmt.Errorf("metadata not closed ('%s' not found)", parser.Closing())
			}
			return nil, nil, err
		}

		// if closing identifier found, the remaining bytes must be markdown content
		if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) {
			break
		}

		metaBuf.Write(line)
		metaBuf.WriteString("\r\n")
	}

	// By now, the rest of the buffer contains markdown content
	contentBuf := new(bytes.Buffer)
	io.Copy(contentBuf, reader)

	return metaBuf.Bytes(), contentBuf.Bytes(), nil
}

// findParser finds the parser using line that contains opening identifier
func findParser(b []byte) MetadataParser {
	var line []byte
	// Read first line
	if _, err := fmt.Fscanln(bytes.NewReader(b), &line); err != nil {
		return nil
	}
	line = bytes.TrimSpace(line)
	for _, parser := range parsers {
		if bytes.Equal(parser.Opening(), line) {
			return parser
		}
	}
	return nil
}
<commit_msg>markdown: Fix when md file has front matter but empty body<commit_after>package markdown

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"

	"github.com/BurntSushi/toml"
	"gopkg.in/yaml.v2"
)

var (
	parsers = []MetadataParser{
		&JSONMetadataParser{metadata: Metadata{Variables: make(map[string]interface{})}},
		&TOMLMetadataParser{metadata: Metadata{Variables: make(map[string]interface{})}},
		&YAMLMetadataParser{metadata: Metadata{Variables: make(map[string]interface{})}},
	}
)

// Metadata stores a page's metadata
type Metadata struct {
	// Page title
	Title string

	// Page template
	Template string

	// Variables to be used with Template
	Variables map[string]interface{}
}

// load loads parsed values in parsedMap into Metadata
func (m *Metadata) load(parsedMap map[string]interface{}) {
	if template, ok := parsedMap["title"]; ok {
		m.Title, _ = template.(string)
	}
	if template, ok := parsedMap["template"]; ok {
		m.Template, _ = template.(string)
	}
	if variables, ok := parsedMap["variables"]; ok {
		m.Variables, _ = variables.(map[string]interface{})
	}
}

// MetadataParser is a an interface that must be satisfied by each parser
type MetadataParser interface {
	// Opening identifier
	Opening() []byte

	// Closing identifier
	Closing() []byte

	// Parse the metadata.
	// Returns the remaining page contents (Markdown)
	// after extracting metadata
	Parse([]byte) ([]byte, error)

	// Parsed metadata.
	// Should be called after a call to Parse returns no error
	Metadata() Metadata
}

// JSONMetadataParser is the MetdataParser for JSON
type JSONMetadataParser struct {
	metadata Metadata
}

// Parse the metadata
func (j *JSONMetadataParser) Parse(b []byte) ([]byte, error) {
	m := make(map[string]interface{})

	// Read the preceding JSON object
	decoder := json.NewDecoder(bytes.NewReader(b))
	if err := decoder.Decode(&m); err != nil {
		return b, err
	}

	j.metadata.load(m)

	// Retrieve remaining bytes after decoding
	buf := make([]byte, len(b))
	n, err := decoder.Buffered().Read(buf)
	if err != nil {
		return b, err
	}

	return buf[:n], nil
}

// Metadata returns parsed metadata.  It should be called
// only after a call to Parse returns without error.
func (j *JSONMetadataParser) Metadata() Metadata {
	return j.metadata
}

// Opening returns the opening identifier JSON metadata
func (j *JSONMetadataParser) Opening() []byte {
	return []byte("{")
}

// Closing returns the closing identifier JSON metadata
func (j *JSONMetadataParser) Closing() []byte {
	return []byte("}")
}

// TOMLMetadataParser is the MetadataParser for TOML
type TOMLMetadataParser struct {
	metadata Metadata
}

// Parse the metadata
func (t *TOMLMetadataParser) Parse(b []byte) ([]byte, error) {
	b, markdown, err := extractMetadata(t, b)
	if err != nil {
		return markdown, err
	}
	m := make(map[string]interface{})
	if err := toml.Unmarshal(b, &m); err != nil {
		return markdown, err
	}
	t.metadata.load(m)
	return markdown, nil
}

// Metadata returns parsed metadata.  It should be called
// only after a call to Parse returns without error.
func (t *TOMLMetadataParser) Metadata() Metadata {
	return t.metadata
}

// Opening returns the opening identifier TOML metadata
func (t *TOMLMetadataParser) Opening() []byte {
	return []byte("+++")
}

// Closing returns the closing identifier TOML metadata
func (t *TOMLMetadataParser) Closing() []byte {
	return []byte("+++")
}

// YAMLMetadataParser is the MetadataParser for YAML
type YAMLMetadataParser struct {
	metadata Metadata
}

// Parse the metadata
func (y *YAMLMetadataParser) Parse(b []byte) ([]byte, error) {
	b, markdown, err := extractMetadata(y, b)
	if err != nil {
		return markdown, err
	}

	m := make(map[string]interface{})
	if err := yaml.Unmarshal(b, &m); err != nil {
		return markdown, err
	}

	// convert variables (if present) to map[string]interface{}
	// to match expected type
	if vars, ok := m["variables"].(map[interface{}]interface{}); ok {
		vars1 := make(map[string]interface{})
		for k, v := range vars {
			if key, ok := k.(string); ok {
				vars1[key] = v
			}
		}
		m["variables"] = vars1
	}

	y.metadata.load(m)
	return markdown, nil
}

// Metadata returns parsed metadata.  It should be called
// only after a call to Parse returns without error.
func (y *YAMLMetadataParser) Metadata() Metadata {
	return y.metadata
}

// Opening returns the opening identifier YAML metadata
func (y *YAMLMetadataParser) Opening() []byte {
	return []byte("---")
}

// Closing returns the closing identifier YAML metadata
func (y *YAMLMetadataParser) Closing() []byte {
	return []byte("---")
}

// extractMetadata separates metadata content from from markdown content in b.
// It returns the metadata, the remaining bytes (markdown), and an error, if any.
func extractMetadata(parser MetadataParser, b []byte) (metadata []byte, markdown []byte, err error) {
	b = bytes.TrimSpace(b)
	reader := bufio.NewReader(bytes.NewBuffer(b))

	// Read first line, which should indicate metadata or not
	line, err := reader.ReadBytes('\n')
	if err != nil || !bytes.Equal(bytes.TrimSpace(line), parser.Opening()) {
		return nil, b, fmt.Errorf("first line missing expected metadata identifier")
	}

	// buffer for metadata contents
	metaBuf := bytes.Buffer{}

	// Read remaining lines until closing identifier is found
	for {
		line, err := reader.ReadBytes('\n')
		if err != nil && err != io.EOF {
			return nil, nil, err
		}

		// if closing identifier found, the remaining bytes must be markdown content
		if bytes.Equal(bytes.TrimSpace(line), parser.Closing()) {
			break
		}

		// if file ended, by this point no closing identifier was found
		if err == io.EOF {
			return nil, nil, fmt.Errorf("metadata not closed ('%s' not found)", parser.Closing())
		}

		metaBuf.Write(line)
		metaBuf.WriteString("\r\n")
	}

	// By now, the rest of the buffer contains markdown content
	contentBuf := new(bytes.Buffer)
	io.Copy(contentBuf, reader)

	return metaBuf.Bytes(), contentBuf.Bytes(), nil
}

// findParser finds the parser using line that contains opening identifier
func findParser(b []byte) MetadataParser {
	var line []byte
	// Read first line
	if _, err := fmt.Fscanln(bytes.NewReader(b), &line); err != nil {
		return nil
	}
	line = bytes.TrimSpace(line)
	for _, parser := range parsers {
		if bytes.Equal(parser.Opening(), line) {
			return parser
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build !windows

package container_test

import (
	"github.com/juju/testing"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/container"
	containertesting "github.com/juju/juju/container/testing"
	"github.com/juju/juju/instance"
	coretesting "github.com/juju/juju/testing"
)

type imageURLSuite struct {
	coretesting.BaseSuite
}

var _ = gc.Suite(&imageURLSuite{})

func (s *imageURLSuite) SetUpTest(c *gc.C) {
	testing.PatchExecutable(c, s, "ubuntu-cloudimg-query", containertesting.FakeLxcURLScript)
}

func (s *imageURLSuite) TestImageURL(c *gc.C) {
	imageURLGetter := container.NewImageURLGetter(
		container.ImageURLGetterConfig{
			ServerRoot:        "host:port",
			ModelUUID:         "12345",
			CACert:            []byte("cert"),
			CloudimgBaseUrl:   "",
			Stream:            "released",
			ImageDownloadFunc: container.ImageDownloadURL,
		})
	imageURL, err := imageURLGetter.ImageURL(instance.LXC, "trusty", "amd64")
	c.Assert(err, gc.IsNil)
	c.Assert(imageURL, gc.Equals, "https://host:port/model/12345/images/lxc/trusty/amd64/trusty-released-amd64-root.tar.gz")
	c.Assert(imageURLGetter.CACert(), gc.DeepEquals, []byte("cert"))
}

func (s *imageURLSuite) TestImageURLOtherBase(c *gc.C) {
	var calledBaseURL string
	baseURL := "other://cloud-images"
	mockFunc := func(kind instance.ContainerType, series, arch, stream, cloudimgBaseUrl string) (string, error) {
		calledBaseURL = cloudimgBaseUrl
		return "omg://wat/trusty-released-amd64-root.tar.gz", nil
	}
	imageURLGetter := container.NewImageURLGetter(
		container.ImageURLGetterConfig{
			ServerRoot:        "host:port",
			ModelUUID:         "12345",
			CACert:            []byte("cert"),
			CloudimgBaseUrl:   baseURL,
			Stream:            "released",
			ImageDownloadFunc: mockFunc,
		})
	imageURL, err := imageURLGetter.ImageURL(instance.LXC, "trusty", "amd64")
	c.Assert(err, gc.IsNil)
	c.Assert(imageURL, gc.Equals, "https://host:port/model/12345/images/lxc/trusty/amd64/trusty-released-amd64-root.tar.gz")
	c.Assert(imageURLGetter.CACert(), gc.DeepEquals, []byte("cert"))
	c.Assert(calledBaseURL, gc.Equals, baseURL)
}

func (s *imageURLSuite) TestImageDownloadURL(c *gc.C) {
	imageDownloadURL, err := container.ImageDownloadURL(instance.LXC, "trusty", "amd64", "released", "")
	c.Assert(err, gc.IsNil)
	c.Assert(imageDownloadURL, gc.Equals, "test://cloud-images/trusty-released-amd64-root.tar.gz")
}

func (s *imageURLSuite) TestImageDownloadURLOtherBase(c *gc.C) {
	imageDownloadURL, err := container.ImageDownloadURL(instance.LXC, "trusty", "amd64", "released", "other://cloud-images")
	c.Assert(err, gc.IsNil)
	c.Assert(imageDownloadURL, gc.Equals, "other://cloud-images/trusty-released-amd64-root.tar.gz")
}

func (s *imageURLSuite) TestImageDownloadURLUnsupportedContainer(c *gc.C) {
	_, err := container.ImageDownloadURL(instance.KVM, "trusty", "amd64", "released", "")
	c.Assert(err, gc.ErrorMatches, "unsupported container .*")
}
<commit_msg>Added test to cover streams other than "released".<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build !windows

package container_test

import (
	"fmt"

	"github.com/juju/testing"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/container"
	containertesting "github.com/juju/juju/container/testing"
	"github.com/juju/juju/instance"
	coretesting "github.com/juju/juju/testing"
)

type imageURLSuite struct {
	coretesting.BaseSuite
}

var _ = gc.Suite(&imageURLSuite{})

func (s *imageURLSuite) SetUpTest(c *gc.C) {
	testing.PatchExecutable(c, s, "ubuntu-cloudimg-query", containertesting.FakeLxcURLScript)
}

func (s *imageURLSuite) TestImageURL(c *gc.C) {
	s.assertImageURLForStream(c, "released")
	s.assertImageURLForStream(c, "daily")
}

func (s *imageURLSuite) assertImageURLForStream(c *gc.C, stream string) {
	imageURLGetter := container.NewImageURLGetter(
		container.ImageURLGetterConfig{
			ServerRoot:        "host:port",
			ModelUUID:         "12345",
			CACert:            []byte("cert"),
			CloudimgBaseUrl:   "",
			Stream:            stream,
			ImageDownloadFunc: container.ImageDownloadURL,
		})
	imageURL, err := imageURLGetter.ImageURL(instance.LXC, "trusty", "amd64")
	c.Assert(err, gc.IsNil)
	c.Assert(imageURL, gc.Equals, fmt.Sprintf("https://host:port/model/12345/images/lxc/trusty/amd64/trusty-%s-amd64-root.tar.gz", stream))
	c.Assert(imageURLGetter.CACert(), gc.DeepEquals, []byte("cert"))
}

func (s *imageURLSuite) TestImageURLOtherBase(c *gc.C) {
	var calledBaseURL string
	baseURL := "other://cloud-images"
	mockFunc := func(kind instance.ContainerType, series, arch, stream, cloudimgBaseUrl string) (string, error) {
		calledBaseURL = cloudimgBaseUrl
		return "omg://wat/trusty-released-amd64-root.tar.gz", nil
	}
	imageURLGetter := container.NewImageURLGetter(
		container.ImageURLGetterConfig{
			ServerRoot:        "host:port",
			ModelUUID:         "12345",
			CACert:            []byte("cert"),
			CloudimgBaseUrl:   baseURL,
			Stream:            "released",
			ImageDownloadFunc: mockFunc,
		})
	imageURL, err := imageURLGetter.ImageURL(instance.LXC, "trusty", "amd64")
	c.Assert(err, gc.IsNil)
	c.Assert(imageURL, gc.Equals, "https://host:port/model/12345/images/lxc/trusty/amd64/trusty-released-amd64-root.tar.gz")
	c.Assert(imageURLGetter.CACert(), gc.DeepEquals, []byte("cert"))
	c.Assert(calledBaseURL, gc.Equals, baseURL)
}

func (s *imageURLSuite) TestImageDownloadURL(c *gc.C) {
	imageDownloadURL, err := container.ImageDownloadURL(instance.LXC, "trusty", "amd64", "released", "")
	c.Assert(err, gc.IsNil)
	c.Assert(imageDownloadURL, gc.Equals, "test://cloud-images/trusty-released-amd64-root.tar.gz")
}

func (s *imageURLSuite) TestImageDownloadURLOtherBase(c *gc.C) {
	imageDownloadURL, err := container.ImageDownloadURL(instance.LXC, "trusty", "amd64", "released", "other://cloud-images")
	c.Assert(err, gc.IsNil)
	c.Assert(imageDownloadURL, gc.Equals, "other://cloud-images/trusty-released-amd64-root.tar.gz")
}

func (s *imageURLSuite) TestImageDownloadURLUnsupportedContainer(c *gc.C) {
	_, err := container.ImageDownloadURL(instance.KVM, "trusty", "amd64", "released", "")
	c.Assert(err, gc.ErrorMatches, "unsupported container .*")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/docker/containerd/osutils"
	"github.com/docker/docker/pkg/term"
)

func writeMessage(f *os.File, level string, err error) {
	fmt.Fprintf(f, `{"level": "%s","msg": "%s"}`, level, err)
}

type controlMessage struct {
	Type   int
	Width  int
	Height int
}

// containerd-shim is a small shim that sits in front of a runtime implementation
// that allows it to be repartented to init and handle reattach from the caller.
//
// the cwd of the shim should be the bundle for the container.  Arg1 should be the path
// to the state directory where the shim can locate fifos and other information.
func main() {
	flag.Parse()
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	f, err := os.OpenFile(filepath.Join(cwd, "shim-log.json"), os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666)
	if err != nil {
		panic(err)
	}
	if err := start(f); err != nil {
		// this means that the runtime failed starting the container and will have the
		// proper error messages in the runtime log so we should to treat this as a
		// shim failure because the sim executed properly
		if err == errRuntime {
			f.Close()
			return
		}
		// log the error instead of writing to stderr because the shim will have
		// /dev/null as it's stdio because it is supposed to be reparented to system
		// init and will not have anyone to read from it
		writeMessage(f, "error", err)
		f.Close()
		os.Exit(1)
	}
}

func start(log *os.File) error {
	// start handling signals as soon as possible so that things are properly reaped
	// or if runtime exits before we hit the handler
	signals := make(chan os.Signal, 2048)
	signal.Notify(signals)
	// set the shim as the subreaper for all orphaned processes created by the container
	if err := osutils.SetSubreaper(1); err != nil {
		return err
	}
	// open the exit pipe
	f, err := os.OpenFile("exit", syscall.O_WRONLY, 0)
	if err != nil {
		return err
	}
	defer f.Close()
	control, err := os.OpenFile("control", syscall.O_RDWR, 0)
	if err != nil {
		return err
	}
	defer control.Close()
	p, err := newProcess(flag.Arg(0), flag.Arg(1), flag.Arg(2))
	if err != nil {
		return err
	}
	defer func() {
		if err := p.Close(); err != nil {
			writeMessage(log, "warn", err)
		}
	}()
	if err := p.create(); err != nil {
		p.delete()
		return err
	}
	msgC := make(chan controlMessage, 32)
	go func() {
		for {
			var m controlMessage
			if _, err := fmt.Fscanf(control, "%d %d %d\n", &m.Type, &m.Width, &m.Height); err != nil {
				continue
			}
			msgC <- m
		}
	}()
	var exitShim bool
	for {
		select {
		case s := <-signals:
			switch s {
			case syscall.SIGCHLD:
				exits, _ := osutils.Reap()
				for _, e := range exits {
					// check to see if runtime is one of the processes that has exited
					if e.Pid == p.pid() {
						exitShim = true
						writeInt("exitStatus", e.Status)
					}
				}
			}
			// runtime has exited so the shim can also exit
			if exitShim {
				// Let containerd take care of calling the runtime delete
				f.Close()
				p.Wait()
				return nil
			}
		case msg := <-msgC:
			switch msg.Type {
			case 0:
				// close stdin
				if p.stdinCloser != nil {
					p.stdinCloser.Close()
				}
			case 1:
				if p.console == nil {
					continue
				}
				ws := term.Winsize{
					Width:  uint16(msg.Width),
					Height: uint16(msg.Height),
				}
				term.SetWinsize(p.console.Fd(), &ws)
			}
		}
	}
	return nil
}

func writeInt(path string, i int) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = fmt.Fprintf(f, "%d", i)
	return err
}
<commit_msg>shim: Fix description of cwd and arguments given to the shim process (#307)<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/docker/containerd/osutils"
	"github.com/docker/docker/pkg/term"
)

func writeMessage(f *os.File, level string, err error) {
	fmt.Fprintf(f, `{"level": "%s","msg": "%s"}`, level, err)
}

type controlMessage struct {
	Type   int
	Width  int
	Height int
}

// containerd-shim is a small shim that sits in front of a runtime implementation
// that allows it to be repartented to init and handle reattach from the caller.
//
// the cwd of the shim should be the path to the state directory where the shim
// can locate fifos and other information.
// Arg0: id of the container
// Arg1: bundle path
// Arg2: runtime binary
func main() {
	flag.Parse()
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	f, err := os.OpenFile(filepath.Join(cwd, "shim-log.json"), os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666)
	if err != nil {
		panic(err)
	}
	if err := start(f); err != nil {
		// this means that the runtime failed starting the container and will have the
		// proper error messages in the runtime log so we should to treat this as a
		// shim failure because the sim executed properly
		if err == errRuntime {
			f.Close()
			return
		}
		// log the error instead of writing to stderr because the shim will have
		// /dev/null as it's stdio because it is supposed to be reparented to system
		// init and will not have anyone to read from it
		writeMessage(f, "error", err)
		f.Close()
		os.Exit(1)
	}
}

func start(log *os.File) error {
	// start handling signals as soon as possible so that things are properly reaped
	// or if runtime exits before we hit the handler
	signals := make(chan os.Signal, 2048)
	signal.Notify(signals)
	// set the shim as the subreaper for all orphaned processes created by the container
	if err := osutils.SetSubreaper(1); err != nil {
		return err
	}
	// open the exit pipe
	f, err := os.OpenFile("exit", syscall.O_WRONLY, 0)
	if err != nil {
		return err
	}
	defer f.Close()
	control, err := os.OpenFile("control", syscall.O_RDWR, 0)
	if err != nil {
		return err
	}
	defer control.Close()
	p, err := newProcess(flag.Arg(0), flag.Arg(1), flag.Arg(2))
	if err != nil {
		return err
	}
	defer func() {
		if err := p.Close(); err != nil {
			writeMessage(log, "warn", err)
		}
	}()
	if err := p.create(); err != nil {
		p.delete()
		return err
	}
	msgC := make(chan controlMessage, 32)
	go func() {
		for {
			var m controlMessage
			if _, err := fmt.Fscanf(control, "%d %d %d\n", &m.Type, &m.Width, &m.Height); err != nil {
				continue
			}
			msgC <- m
		}
	}()
	var exitShim bool
	for {
		select {
		case s := <-signals:
			switch s {
			case syscall.SIGCHLD:
				exits, _ := osutils.Reap()
				for _, e := range exits {
					// check to see if runtime is one of the processes that has exited
					if e.Pid == p.pid() {
						exitShim = true
						writeInt("exitStatus", e.Status)
					}
				}
			}
			// runtime has exited so the shim can also exit
			if exitShim {
				// Let containerd take care of calling the runtime delete
				f.Close()
				p.Wait()
				return nil
			}
		case msg := <-msgC:
			switch msg.Type {
			case 0:
				// close stdin
				if p.stdinCloser != nil {
					p.stdinCloser.Close()
				}
			case 1:
				if p.console == nil {
					continue
				}
				ws := term.Winsize{
					Width:  uint16(msg.Width),
					Height: uint16(msg.Height),
				}
				term.SetWinsize(p.console.Fd(), &ws)
			}
		}
	}
	return nil
}

func writeInt(path string, i int) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = fmt.Fprintf(f, "%d", i)
	return err
}
<|endoftext|>


<commit_before>package asset

import (
	"context"
	"database/sql"
	"encoding/json"
	"time"

	"chain/core/signers"
	"chain/crypto/ed25519"
	"chain/crypto/ed25519/hd25519"
	"chain/database/pg"
	"chain/errors"
	"chain/metrics"
	"chain/net/http/httpjson"
	"chain/protocol/bc"
	"chain/protocol/vm"
	"chain/protocol/vmutil"
)

var (
	ErrArchived = errors.New("asset archived")
)

type Asset struct {
	AssetID          bc.AssetID             `json:"id"`
	Alias            *string                `json:"alias"`
	Definition       map[string]interface{} `json:"definition"`
	IssuanceProgram  []byte                 `json:"issuance_program"`
	InitialBlockHash bc.Hash                `json:"initial_block_hash"`
	Signer           *signers.Signer        `json:"signer"`
	Tags             map[string]interface{} `json:"tags"`
	sortID           string
}

// Define defines a new Asset.
func Define(ctx context.Context, xpubs []string, quorum int, definition map[string]interface{}, initialBlockHash bc.Hash, alias *string, tags map[string]interface{}, clientToken *string) (*Asset, error) {
	dbtx, ctx, err := pg.Begin(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "define asset")
	}
	defer dbtx.Rollback(ctx)
	assetSigner, err := signers.Create(ctx, "asset", xpubs, quorum, clientToken)
	if err != nil {
		return nil, err
	}

	serializedDef, err := serializeAssetDef(definition)
	if err != nil {
		return nil, errors.Wrap(err, "serializing asset definition")
	}

	path := signers.Path(assetSigner, signers.AssetKeySpace, nil)

	derivedXPubs := hd25519.DeriveXPubs(assetSigner.XPubs, path)
	derivedPKs := hd25519.XPubKeys(derivedXPubs)
	issuanceProgram, err := programWithDefinition(derivedPKs, assetSigner.Quorum, serializedDef)
	if err != nil {
		return nil, err
	}

	asset := &Asset{
		Alias:            alias,
		Definition:       definition,
		IssuanceProgram:  issuanceProgram,
		InitialBlockHash: initialBlockHash,
		AssetID:          bc.ComputeAssetID(issuanceProgram, initialBlockHash, 1),
		Signer:           assetSigner,
		Tags:             tags,
	}

	asset, err = insertAsset(ctx, asset, clientToken)
	if err != nil {
		return nil, errors.Wrap(err, "inserting asset")
	}

	err = insertAssetTags(ctx, asset.AssetID, tags)
	if err != nil {
		return nil, errors.Wrap(err, "inserting asset tags")
	}

	err = dbtx.Commit(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "committing define asset dbtx")
	}

	// Note, this should be okay to do outside of the SQL txn
	// because each step should be idempotent. Also, we have no
	// guarantee that the query engine uses the same db handle.
	err = indexAnnotatedAsset(ctx, asset)
	if err != nil {
		return nil, errors.Wrap(err, "indexing annotated asset")
	}

	return asset, nil
}

// SetTags sets tags on the given asset and its associated signer.
func SetTags(ctx context.Context, id bc.AssetID, alias string, newTags map[string]interface{}) (*Asset, error) {
	dbtx, ctx, err := pg.Begin(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "set asset tags")
	}
	defer dbtx.Rollback(ctx)

	a, err := lookupAsset(ctx, id, alias)
	if err != nil {
		return nil, errors.Wrap(err)
	}

	err = insertAssetTags(ctx, a.AssetID, newTags)
	if err != nil {
		return nil, errors.Wrap(err)
	}
	a.Tags = newTags

	err = dbtx.Commit(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "committing set asset tags dbtx")
	}

	// Note, this should be okay to do outside of the SQL txn
	// because each step should be idempotent. Also, we have no
	// guarantee that the query engine uses the same db handle.
	err = indexAnnotatedAsset(ctx, a)
	if err != nil {
		return nil, errors.Wrap(err, "indexing annotated asset")
	}

	return a, nil
}

// FindByID retrieves an Asset record along with its signer, given an assetID.
func FindByID(ctx context.Context, id bc.AssetID) (*Asset, error) {
	return lookupAsset(ctx, id, "")
}

// FindByAlias retrieves an Asset record along with its signer,
// given an asset alias.
func FindByAlias(ctx context.Context, alias string) (*Asset, error) {
	return lookupAsset(ctx, bc.AssetID{}, alias)
}

// Archive marks an Asset record as archived, effectively "deleting" it.
func Archive(ctx context.Context, id bc.AssetID, alias string) error {
	asset, err := lookupAsset(ctx, id, alias)
	if err != nil {
		return errors.Wrap(err, "asset is missing")
	}

	dbtx, ctx, err := pg.Begin(ctx)
	if err != nil {
		return errors.Wrap(err, "archive asset")
	}
	defer dbtx.Rollback(ctx)

	const q = `UPDATE assets SET archived = true WHERE id = $1`
	_, err = pg.Exec(ctx, q, asset.AssetID.String())
	if err != nil {
		return errors.Wrap(err, "archive asset query")
	}

	if asset.Signer != nil {
		err = signers.Archive(ctx, "asset", asset.Signer.ID)
		if err != nil {
			return errors.Wrap(err, "archive asset signer")
		}
	}

	err = dbtx.Commit(ctx)
	if err != nil {
		return errors.Wrap(err, "committing archive asset dbtx")
	}

	return nil
}

// insertAsset adds the asset to the database. If the asset has a client token,
// and there already exists an asset for the same issuer node with that client
// token, insertAsset will lookup and return the existing asset instead.
func insertAsset(ctx context.Context, asset *Asset, clientToken *string) (*Asset, error) {
	defer metrics.RecordElapsed(time.Now())
	const q = `
    INSERT INTO assets
	 	(id, alias, signer_id, initial_block_hash, issuance_program, definition, client_token)
    VALUES($1, $2, $3, $4, $5, $6, $7)
    ON CONFLICT (client_token) DO NOTHING
	RETURNING sort_id
  `
	defParams, err := mapToNullString(asset.Definition)
	if err != nil {
		return nil, err
	}

	var signerID sql.NullString
	if asset.Signer != nil {
		signerID = sql.NullString{Valid: true, String: asset.Signer.ID}
	}

	err = pg.QueryRow(
		ctx, q,
		asset.AssetID, asset.Alias, signerID,
		asset.InitialBlockHash, asset.IssuanceProgram,
		defParams, clientToken,
	).Scan(&asset.sortID)

	if pg.IsUniqueViolation(err) {
		return nil, errors.WithDetail(httpjson.ErrBadRequest, "non-unique alias")
	} else if err == sql.ErrNoRows && clientToken != nil {
		// There is already an asset with the provided client
		// token. We should return the existing asset.
		asset, err = assetByClientToken(ctx, *clientToken)
		if err != nil {
			return nil, errors.Wrap(err, "retrieving existing asset")
		}
	} else if err != nil {
		return nil, err
	}
	return asset, nil
}

// insertAssetTags inserts a set of tags for the given assetID.
// It must take place inside a database transaction.
func insertAssetTags(ctx context.Context, assetID bc.AssetID, tags map[string]interface{}) error {
	_ = pg.FromContext(ctx).(pg.Tx) // panics if not in a db transaction
	tagsParam, err := mapToNullString(tags)
	if err != nil {
		return errors.Wrap(err)
	}

	const q = `
		INSERT INTO asset_tags (asset_id, tags) VALUES ($1, $2)
		ON CONFLICT (asset_id) DO UPDATE SET tags = $2
	`
	_, err = pg.Exec(ctx, q, assetID.String(), tagsParam)
	if err != nil {
		return errors.Wrap(err)
	}

	return nil
}

func lookupAsset(ctx context.Context, idQ bc.AssetID, aliasQ string) (*Asset, error) {
	if idQ != (bc.AssetID{}) && aliasQ != "" {
		return nil, errors.New("cannot refer to asset by both ID and alias")
	}

	const q = `
		SELECT id, alias, issuance_program, definition, initial_block_hash, signer_id, archived, sort_id
		FROM assets
		WHERE id=$1 OR ($2!='' AND alias=$2)
	`

	var (
		a          Asset
		alias      sql.NullString
		archived   bool
		signerID   sql.NullString
		definition []byte
	)
	err := pg.QueryRow(ctx, q, idQ.String(), aliasQ).Scan(
		&a.AssetID,
		&alias,
		&a.IssuanceProgram,
		&definition,
		&a.InitialBlockHash,
		&signerID,
		&archived,
		&a.sortID,
	)
	if err == sql.ErrNoRows {
		return nil, pg.ErrUserInputNotFound
	} else if err != nil {
		return nil, err
	}
	if archived {
		return nil, ErrArchived
	}

	if signerID.Valid {
		// Only try to fetch the signer if this is a
		// local asset.
		sig, err := signers.Find(ctx, "asset", signerID.String)
		if err != nil {
			return nil, errors.Wrap(err, "couldn't find signer")
		}
		a.Signer = sig
	}

	if len(definition) > 0 {
		err := json.Unmarshal(definition, &a.Definition)
		if err != nil {
			return nil, errors.Wrap(err)
		}
	}

	if alias.Valid {
		a.Alias = &alias.String
	}

	const tagQ = `SELECT tags FROM asset_tags WHERE asset_id=$1`
	var tags []byte
	err = pg.QueryRow(ctx, tagQ, a.AssetID.String()).Scan(&tags)
	if err != nil && err != sql.ErrNoRows {
		return nil, errors.Wrap(err)
	}

	if len(tags) > 0 {
		err := json.Unmarshal(tags, &a.Tags)
		if err != nil {
			return nil, errors.Wrap(err)
		}
	}

	return &a, nil
}

// assetByClientToken loads an asset from the database using its client token.
func assetByClientToken(ctx context.Context, clientToken string) (*Asset, error) {
	const q = `
		SELECT id, alias, issuance_program, definition,
			initial_block_hash, signer_id, archived, sort_id
		FROM assets
		WHERE client_token=$1
	`
	var (
		a          Asset
		archived   bool
		signerID   sql.NullString
		alias      sql.NullString
		definition []byte
	)
	err := pg.QueryRow(ctx, q, clientToken).Scan(
		&a.AssetID,
		&alias,
		&a.IssuanceProgram,
		&definition,
		&a.InitialBlockHash,
		&signerID,
		&archived,
		&a.sortID,
	)
	if err == sql.ErrNoRows {
		return nil, pg.ErrUserInputNotFound
	} else if err != nil {
		return nil, errors.Wrap(err)
	}
	if archived {
		return nil, ErrArchived
	}

	if alias.Valid {
		a.Alias = &alias.String
	}

	if len(definition) > 0 {
		err := json.Unmarshal(definition, &a.Definition)
		if err != nil {
			return nil, errors.Wrap(err)
		}
	}

	const tagQ = `SELECT tags FROM asset_tags WHERE asset_id=$1`
	var tags []byte
	err = pg.QueryRow(ctx, tagQ, a.AssetID.String()).Scan(&tags)
	if err != nil && err != sql.ErrNoRows {
		return nil, errors.Wrap(err)
	}

	if len(tags) > 0 {
		err := json.Unmarshal(tags, &a.Tags)
		if err != nil {
			return nil, errors.Wrap(err)
		}
	}

	if signerID.Valid {
		sig, err := signers.Find(ctx, "asset", signerID.String)
		if err != nil {
			return nil, errors.Wrap(err, "couldn't find signer")
		}
		a.Signer = sig
	}

	return &a, nil
}

// serializeAssetDef produces a canonical byte representation of an asset
// definition. Currently, this is implemented using pretty-printed JSON.
// As is the standard for Go's map[string] serialization, object keys will
// appear in lexicographic order. Although this is mostly meant for machine
// consumption, the JSON is pretty-printed for easy reading.
// The empty asset def is an empty byte slice.
func serializeAssetDef(def map[string]interface{}) ([]byte, error) {
	return json.MarshalIndent(def, "", "  ")
}

func programWithDefinition(pubkeys []ed25519.PublicKey, nrequired int, definition []byte) ([]byte, error) {
	issuanceProg := vmutil.P2DPMultiSigProgram(pubkeys, nrequired)
	builder := vmutil.NewBuilder()
	builder.AddData(definition).AddOp(vm.OP_DROP)
	builder.AddRawBytes(issuanceProg)
	return builder.Program, nil
}

func definitionFromProgram(program []byte) ([]byte, error) {
	pops, err := vm.ParseProgram(program)
	if err != nil {
		return nil, err
	}
	if len(pops) < 2 {
		return nil, errors.New("bad issuance program")
	}
	if pops[1].Op != vm.OP_DROP {
		return nil, errors.New("bad issuance program")
	}
	return pops[0].Data, nil
}

func mapToNullString(in map[string]interface{}) (*sql.NullString, error) {
	var mapJSON []byte
	if len(in) != 0 {
		var err error
		mapJSON, err = json.Marshal(in)
		if err != nil {
			return nil, errors.Wrap(err)
		}
	}
	return &sql.NullString{String: string(mapJSON), Valid: len(mapJSON) > 0}, nil
}
<commit_msg>core/asset: extract asset query function<commit_after>package asset

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"time"

	"chain/core/signers"
	"chain/crypto/ed25519"
	"chain/crypto/ed25519/hd25519"
	"chain/database/pg"
	"chain/errors"
	"chain/metrics"
	"chain/net/http/httpjson"
	"chain/protocol/bc"
	"chain/protocol/vm"
	"chain/protocol/vmutil"
)

var (
	ErrArchived = errors.New("asset archived")
)

type Asset struct {
	AssetID          bc.AssetID             `json:"id"`
	Alias            *string                `json:"alias"`
	Definition       map[string]interface{} `json:"definition"`
	IssuanceProgram  []byte                 `json:"issuance_program"`
	InitialBlockHash bc.Hash                `json:"initial_block_hash"`
	Signer           *signers.Signer        `json:"signer"`
	Tags             map[string]interface{} `json:"tags"`
	sortID           string
}

// Define defines a new Asset.
func Define(ctx context.Context, xpubs []string, quorum int, definition map[string]interface{}, initialBlockHash bc.Hash, alias *string, tags map[string]interface{}, clientToken *string) (*Asset, error) {
	dbtx, ctx, err := pg.Begin(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "define asset")
	}
	defer dbtx.Rollback(ctx)
	assetSigner, err := signers.Create(ctx, "asset", xpubs, quorum, clientToken)
	if err != nil {
		return nil, err
	}

	serializedDef, err := serializeAssetDef(definition)
	if err != nil {
		return nil, errors.Wrap(err, "serializing asset definition")
	}

	path := signers.Path(assetSigner, signers.AssetKeySpace, nil)

	derivedXPubs := hd25519.DeriveXPubs(assetSigner.XPubs, path)
	derivedPKs := hd25519.XPubKeys(derivedXPubs)
	issuanceProgram, err := programWithDefinition(derivedPKs, assetSigner.Quorum, serializedDef)
	if err != nil {
		return nil, err
	}

	asset := &Asset{
		Alias:            alias,
		Definition:       definition,
		IssuanceProgram:  issuanceProgram,
		InitialBlockHash: initialBlockHash,
		AssetID:          bc.ComputeAssetID(issuanceProgram, initialBlockHash, 1),
		Signer:           assetSigner,
		Tags:             tags,
	}

	asset, err = insertAsset(ctx, asset, clientToken)
	if err != nil {
		return nil, errors.Wrap(err, "inserting asset")
	}

	err = insertAssetTags(ctx, asset.AssetID, tags)
	if err != nil {
		return nil, errors.Wrap(err, "inserting asset tags")
	}

	err = dbtx.Commit(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "committing define asset dbtx")
	}

	// Note, this should be okay to do outside of the SQL txn
	// because each step should be idempotent. Also, we have no
	// guarantee that the query engine uses the same db handle.
	err = indexAnnotatedAsset(ctx, asset)
	if err != nil {
		return nil, errors.Wrap(err, "indexing annotated asset")
	}

	return asset, nil
}

// SetTags sets tags on the given asset and its associated signer.
func SetTags(ctx context.Context, id bc.AssetID, alias string, newTags map[string]interface{}) (*Asset, error) {
	dbtx, ctx, err := pg.Begin(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "set asset tags")
	}
	defer dbtx.Rollback(ctx)

	a, err := lookupAsset(ctx, id, alias)
	if err != nil {
		return nil, errors.Wrap(err)
	}

	err = insertAssetTags(ctx, a.AssetID, newTags)
	if err != nil {
		return nil, errors.Wrap(err)
	}
	a.Tags = newTags

	err = dbtx.Commit(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "committing set asset tags dbtx")
	}

	// Note, this should be okay to do outside of the SQL txn
	// because each step should be idempotent. Also, we have no
	// guarantee that the query engine uses the same db handle.
	err = indexAnnotatedAsset(ctx, a)
	if err != nil {
		return nil, errors.Wrap(err, "indexing annotated asset")
	}

	return a, nil
}

// FindByID retrieves an Asset record along with its signer, given an assetID.
func FindByID(ctx context.Context, id bc.AssetID) (*Asset, error) {
	return lookupAsset(ctx, id, "")
}

// FindByAlias retrieves an Asset record along with its signer,
// given an asset alias.
func FindByAlias(ctx context.Context, alias string) (*Asset, error) {
	return lookupAsset(ctx, bc.AssetID{}, alias)
}

// Archive marks an Asset record as archived, effectively "deleting" it.
func Archive(ctx context.Context, id bc.AssetID, alias string) error {
	asset, err := lookupAsset(ctx, id, alias)
	if err != nil {
		return errors.Wrap(err, "asset is missing")
	}

	dbtx, ctx, err := pg.Begin(ctx)
	if err != nil {
		return errors.Wrap(err, "archive asset")
	}
	defer dbtx.Rollback(ctx)

	const q = `UPDATE assets SET archived = true WHERE id = $1`
	_, err = pg.Exec(ctx, q, asset.AssetID.String())
	if err != nil {
		return errors.Wrap(err, "archive asset query")
	}

	if asset.Signer != nil {
		err = signers.Archive(ctx, "asset", asset.Signer.ID)
		if err != nil {
			return errors.Wrap(err, "archive asset signer")
		}
	}

	err = dbtx.Commit(ctx)
	if err != nil {
		return errors.Wrap(err, "committing archive asset dbtx")
	}

	return nil
}

// insertAsset adds the asset to the database. If the asset has a client token,
// and there already exists an asset for the same issuer node with that client
// token, insertAsset will lookup and return the existing asset instead.
func insertAsset(ctx context.Context, asset *Asset, clientToken *string) (*Asset, error) {
	defer metrics.RecordElapsed(time.Now())
	const q = `
    INSERT INTO assets
	 	(id, alias, signer_id, initial_block_hash, issuance_program, definition, client_token)
    VALUES($1, $2, $3, $4, $5, $6, $7)
    ON CONFLICT (client_token) DO NOTHING
	RETURNING sort_id
  `
	defParams, err := mapToNullString(asset.Definition)
	if err != nil {
		return nil, err
	}

	var signerID sql.NullString
	if asset.Signer != nil {
		signerID = sql.NullString{Valid: true, String: asset.Signer.ID}
	}

	err = pg.QueryRow(
		ctx, q,
		asset.AssetID, asset.Alias, signerID,
		asset.InitialBlockHash, asset.IssuanceProgram,
		defParams, clientToken,
	).Scan(&asset.sortID)

	if pg.IsUniqueViolation(err) {
		return nil, errors.WithDetail(httpjson.ErrBadRequest, "non-unique alias")
	} else if err == sql.ErrNoRows && clientToken != nil {
		// There is already an asset with the provided client
		// token. We should return the existing asset.
		asset, err = assetByClientToken(ctx, *clientToken)
		if err != nil {
			return nil, errors.Wrap(err, "retrieving existing asset")
		}
	} else if err != nil {
		return nil, err
	}
	return asset, nil
}

// insertAssetTags inserts a set of tags for the given assetID.
// It must take place inside a database transaction.
func insertAssetTags(ctx context.Context, assetID bc.AssetID, tags map[string]interface{}) error {
	_ = pg.FromContext(ctx).(pg.Tx) // panics if not in a db transaction
	tagsParam, err := mapToNullString(tags)
	if err != nil {
		return errors.Wrap(err)
	}

	const q = `
		INSERT INTO asset_tags (asset_id, tags) VALUES ($1, $2)
		ON CONFLICT (asset_id) DO UPDATE SET tags = $2
	`
	_, err = pg.Exec(ctx, q, assetID.String(), tagsParam)
	if err != nil {
		return errors.Wrap(err)
	}

	return nil
}

func lookupAsset(ctx context.Context, idQ bc.AssetID, aliasQ string) (*Asset, error) {
	if idQ != (bc.AssetID{}) && aliasQ != "" {
		return nil, errors.New("cannot refer to asset by both ID and alias")
	}

	return assetQuery(ctx, "id=$1 OR ($2!='' AND alias=$2)", idQ, aliasQ)
}

// assetByClientToken loads an asset from the database using its client token.
func assetByClientToken(ctx context.Context, clientToken string) (*Asset, error) {
	return assetQuery(ctx, "client_token=$1", clientToken)
}

func assetQuery(ctx context.Context, pred string, args ...interface{}) (*Asset, error) {
	const baseQ = `
		SELECT id, alias, issuance_program, definition,
			initial_block_hash, signer_id, archived, sort_id
		FROM assets
		WHERE %s
		LIMIT 1
	`
	var (
		a          Asset
		alias      sql.NullString
		archived   bool
		signerID   sql.NullString
		definition []byte
	)
	err := pg.QueryRow(ctx, fmt.Sprintf(baseQ, pred), args...).Scan(
		&a.AssetID,
		&alias,
		&a.IssuanceProgram,
		&definition,
		&a.InitialBlockHash,
		&signerID,
		&archived,
		&a.sortID,
	)
	if err == sql.ErrNoRows {
		return nil, pg.ErrUserInputNotFound
	} else if err != nil {
		return nil, err
	}
	if archived {
		return nil, ErrArchived
	}

	if signerID.Valid {
		// Only try to fetch the signer if this is a
		// local asset.
		sig, err := signers.Find(ctx, "asset", signerID.String)
		if err != nil {
			return nil, errors.Wrap(err, "couldn't find signer")
		}
		a.Signer = sig
	}

	if len(definition) > 0 {
		err := json.Unmarshal(definition, &a.Definition)
		if err != nil {
			return nil, errors.Wrap(err)
		}
	}

	if alias.Valid {
		a.Alias = &alias.String
	}

	const tagQ = `SELECT tags FROM asset_tags WHERE asset_id=$1`
	var tags []byte
	err = pg.QueryRow(ctx, tagQ, a.AssetID.String()).Scan(&tags)
	if err != nil && err != sql.ErrNoRows {
		return nil, errors.Wrap(err)
	}

	if len(tags) > 0 {
		err := json.Unmarshal(tags, &a.Tags)
		if err != nil {
			return nil, errors.Wrap(err)
		}
	}

	return &a, nil
}

// serializeAssetDef produces a canonical byte representation of an asset
// definition. Currently, this is implemented using pretty-printed JSON.
// As is the standard for Go's map[string] serialization, object keys will
// appear in lexicographic order. Although this is mostly meant for machine
// consumption, the JSON is pretty-printed for easy reading.
// The empty asset def is an empty byte slice.
func serializeAssetDef(def map[string]interface{}) ([]byte, error) {
	return json.MarshalIndent(def, "", "  ")
}

func programWithDefinition(pubkeys []ed25519.PublicKey, nrequired int, definition []byte) ([]byte, error) {
	issuanceProg := vmutil.P2DPMultiSigProgram(pubkeys, nrequired)
	builder := vmutil.NewBuilder()
	builder.AddData(definition).AddOp(vm.OP_DROP)
	builder.AddRawBytes(issuanceProg)
	return builder.Program, nil
}

func definitionFromProgram(program []byte) ([]byte, error) {
	pops, err := vm.ParseProgram(program)
	if err != nil {
		return nil, err
	}
	if len(pops) < 2 {
		return nil, errors.New("bad issuance program")
	}
	if pops[1].Op != vm.OP_DROP {
		return nil, errors.New("bad issuance program")
	}
	return pops[0].Data, nil
}

func mapToNullString(in map[string]interface{}) (*sql.NullString, error) {
	var mapJSON []byte
	if len(in) != 0 {
		var err error
		mapJSON, err = json.Marshal(in)
		if err != nil {
			return nil, errors.Wrap(err)
		}
	}
	return &sql.NullString{String: string(mapJSON), Valid: len(mapJSON) > 0}, nil
}
<|endoftext|>


<commit_before>package context

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"github.com/silenceper/wechat/util"
)

const (
	//AccessTokenURL 获取access_token的接口
	AccessTokenURL = "https://api.weixin.qq.com/cgi-bin/token"
)

//ResAccessToken struct
type ResAccessToken struct {
	util.CommonError

	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

//SetAccessTokenLock 设置读写锁（一个appID一个读写锁）
func (ctx *Context) SetAccessTokenLock(l *sync.RWMutex) {
	ctx.accessTokenLock = l
}

//GetAccessToken 获取access_token
func (ctx *Context) GetAccessToken() (accessToken string, err error) {
	ctx.accessTokenLock.Lock()
	defer ctx.accessTokenLock.Unlock()

	accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
	val := ctx.Cache.Get(accessTokenCacheKey)
	if val != nil {
		accessToken = val.(string)
		return
	}

	//从微信服务器获取
	var resAccessToken ResAccessToken
	resAccessToken, err = ctx.GetAccessTokenFromServer()
	if err != nil {
		return
	}

	accessToken = resAccessToken.AccessToken
	return
}

//GetAccessTokenFromServer 强制从微信服务器获取token
func (ctx *Context) GetAccessTokenFromServer() (resAccessToken ResAccessToken, err error) {
	url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s", AccessTokenURL, ctx.AppID, ctx.AppSecret)
	var body []byte
	body, err = util.HTTPGet(url)
	err = json.Unmarshal(body, &resAccessToken)
	if err != nil {
		return
	}
	if resAccessToken.ErrMsg != "" {
		err = fmt.Errorf("get access_token error : errcode=%v , errormsg=%v", resAccessToken.ErrCode, resAccessToken.ErrMsg)
		return
	}

	accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
	expires := resAccessToken.ExpiresIn - 1500
	err = ctx.Cache.Set(accessTokenCacheKey, resAccessToken.AccessToken, time.Duration(expires)*time.Second)
	return
}
<commit_msg>handle err<commit_after>package context

import (
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"github.com/silenceper/wechat/util"
)

const (
	//AccessTokenURL 获取access_token的接口
	AccessTokenURL = "https://api.weixin.qq.com/cgi-bin/token"
)

//ResAccessToken struct
type ResAccessToken struct {
	util.CommonError

	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

//SetAccessTokenLock 设置读写锁（一个appID一个读写锁）
func (ctx *Context) SetAccessTokenLock(l *sync.RWMutex) {
	ctx.accessTokenLock = l
}

//GetAccessToken 获取access_token
func (ctx *Context) GetAccessToken() (accessToken string, err error) {
	ctx.accessTokenLock.Lock()
	defer ctx.accessTokenLock.Unlock()

	accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
	val := ctx.Cache.Get(accessTokenCacheKey)
	if val != nil {
		accessToken = val.(string)
		return
	}

	//从微信服务器获取
	var resAccessToken ResAccessToken
	resAccessToken, err = ctx.GetAccessTokenFromServer()
	if err != nil {
		return
	}

	accessToken = resAccessToken.AccessToken
	return
}

//GetAccessTokenFromServer 强制从微信服务器获取token
func (ctx *Context) GetAccessTokenFromServer() (resAccessToken ResAccessToken, err error) {
	url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s", AccessTokenURL, ctx.AppID, ctx.AppSecret)
	var body []byte
	body, err = util.HTTPGet(url)
	if err != nil {
		return
	}
	err = json.Unmarshal(body, &resAccessToken)
	if err != nil {
		return
	}
	if resAccessToken.ErrMsg != "" {
		err = fmt.Errorf("get access_token error : errcode=%v , errormsg=%v", resAccessToken.ErrCode, resAccessToken.ErrMsg)
		return
	}

	accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
	expires := resAccessToken.ExpiresIn - 1500
	err = ctx.Cache.Set(accessTokenCacheKey, resAccessToken.AccessToken, time.Duration(expires)*time.Second)
	return
}
<|endoftext|>


<commit_before>package utils

import (
	"io/ioutil"
	"log"
	"strings"
)

func ReplaceTokenInFile(file string, find string, replaceWith string) {
	input, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatalln(err)
	}

	lines := strings.Split(string(input), "\n")

	for i, line := range lines {
		if strings.Contains(line, find) {
			lines[i] = replaceWith
		}
	}
	output := strings.Join(lines, "\n")
	err = ioutil.WriteFile(file, []byte(output), 0644)
	if err != nil {
		log.Fatalln(err)
	}
}

func InArray(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}
<commit_msg>Added Array<commit_after>package utils

import (
	"io/ioutil"
	"log"
	"strings"
)

func ReplaceTokenInFile(file string, find string, replaceWith string) {
	input, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatalln(err)
	}

	lines := strings.Split(string(input), "\n")

	for i, line := range lines {
		if strings.Contains(line, find) {
			lines[i] = replaceWith
		}
	}
	output := strings.Join(lines, "\n")
	err = ioutil.WriteFile(file, []byte(output), 0644)
	if err != nil {
		log.Fatalln(err)
	}
}

func InArray(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func Array(values ...string) []string {
	var out []string
	for _, value := range values {
		out = append(out, value)
	}
	return out
}
<|endoftext|>


<commit_before>package dist

import (
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"erlang/term"
	"errors"
	"flag"
	"io"
	"log"
	"math/rand"
	"net"
	"strconv"
	"strings"
	"time"
)

var dTrace bool

func init() {
	flag.BoolVar(&dTrace, "erlang.dist.trace", false, "trace erlang distribution protocol")
}

func dLog(f string, a ...interface{}) {
	if dTrace {
		log.Printf(f, a...)
	}
}

type flagId uint32

const (
	PUBLISHED           = flagId(0x1)
	ATOM_CACHE          = 0x2
	EXTENDED_REFERENCES = 0x4
	DIST_MONITOR        = 0x8
	FUN_TAGS            = 0x10
	DIST_MONITOR_NAME   = 0x20
	HIDDEN_ATOM_CACHE   = 0x40
	NEW_FUN_TAGS        = 0x80
	EXTENDED_PIDS_PORTS = 0x100
	EXPORT_PTR_TAG      = 0x200
	BIT_BINARIES        = 0x400
	NEW_FLOATS          = 0x800
	UNICODE_IO          = 0x1000
	DIST_HDR_ATOM_CACHE = 0x2000
	SMALL_ATOM_TAGS     = 0x4000
)

type nodeFlag flagId

func (nf nodeFlag) toUint32() (flag uint32) {
	flag = uint32(nf)
	return
}

func (nf nodeFlag) isSet(f flagId) (is bool) {
	is = (uint32(nf) & uint32(f)) != 0
	return
}

func toNodeFlag(f ...flagId) (nf nodeFlag) {
	var flags uint32
	for _, v := range f {
		flags |= uint32(v)
	}
	nf = nodeFlag(flags)
	return
}

type nodeState uint8

const (
	HANDSHAKE nodeState = iota
	CONNECTED
)

type NodeDesc struct {
	Name      string
	Cookie    string
	Hidden    bool
	remote    *NodeDesc
	state     nodeState
	challenge uint32
	flag      nodeFlag
	version   uint16
}

func NewNodeDesc(name, cookie string, isHidden bool) (nd *NodeDesc) {
	nd = &NodeDesc{
		Name:    name,
		Cookie:  cookie,
		Hidden:  isHidden,
		remote:  nil,
		state:   HANDSHAKE,
		flag:    toNodeFlag(PUBLISHED, UNICODE_IO, EXTENDED_PIDS_PORTS, EXTENDED_REFERENCES),
		version: 5,
	}
	return nd
}

func (currNd *NodeDesc) ReadMessage(c net.Conn) (ts []term.Term, err error) {
	rcbuf := new(bytes.Buffer)

	var buf []byte

	for {
		var n int
		rbuf := make([]byte, 1024)
		n, err = c.Read(rbuf)

		if (err != nil) && (n == 0) {
			dLog("Stop enode loop (%d): %v", n, err)
			return
		}
		rcbuf.Write(rbuf[:n])
		if n < len(rbuf) {
			break
		}
	}

	buf = rcbuf.Bytes()

	switch currNd.state {
	case HANDSHAKE:
		length := binary.BigEndian.Uint16(buf[0:2])
		msg := buf[2:]
		dLog("Read from enode %d: %v", length, msg)

		sendData := func(data []byte) (int, error) {
			reply := make([]byte, len(data)+2)
			binary.BigEndian.PutUint16(reply[0:2], uint16(len(data)))
			copy(reply[2:], data)
			dLog("Write to enode: %v", reply)
			return c.Write(reply)
		}

		switch msg[0] {
		case 'n':
			sn := currNd.read_SEND_NAME(msg)
			// Statuses: ok, nok, ok_simultaneous, alive, not_allowed
			sok := currNd.compose_SEND_STATUS(sn, true)
			_, err = sendData(sok)
			if err != nil {
				return
			}

			rand.Seed(time.Now().UTC().UnixNano())
			currNd.challenge = rand.Uint32()

			// Now send challenge
			challenge := currNd.compose_SEND_CHALLENGE(sn)
			sendData(challenge)
			if err != nil {
				return
			}

		case 'r':
			sn := currNd.remote
			ok := currNd.read_SEND_CHALLENGE_REPLY(sn, msg)
			if ok {
				challengeAck := currNd.compose_SEND_CHALLENGE_ACK(sn)
				sendData(challengeAck)
				if err != nil {
					return
				}
				dLog("Remote: %#v", sn)
				ts = []term.Term{term.Term(term.Tuple{term.Atom("$go_set_node"), term.Atom(sn.Name)})}
			} else {
				err = errors.New("bad handshake")
				return
			}
		}
	case CONNECTED:
		length := binary.BigEndian.Uint32(buf[0:4])
		msg := buf[4:]
		dLog("Read from enode %d: %v", length, msg)

		if length == 0 {
			dLog("Keepalive")
			return
		}

		switch msg[0] {
		case 'p':
			pos := 1
			dLog("BIN TERM: %v", msg[pos:])
			ts = make([]term.Term, 0)
			for {
				res, nr := currNd.read_TERM(msg[pos:])
				if nr == 0 {
					break
				}
				ts = append(ts, res)
				pos += nr
				dLog("READ TERM (%d): %#v", nr, res)
			}
		}
	}
	return
}

func (currNd *NodeDesc) WriteMessage(c net.Conn, ts []term.Term) (err error) {
	sendData := func(data []byte) (int, error) {
		reply := make([]byte, len(data)+4)
		binary.BigEndian.PutUint32(reply[0:4], uint32(len(data)))
		copy(reply[4:], data)
		dLog("Write to enode: %v", reply)
		return c.Write(reply)
	}

	buf := new(bytes.Buffer)
	buf.Write([]byte{'p'})
	for _, v := range ts {
		term.WriteTerm(v, buf)
	}
	dLog("WRITE: %#v: %#v", ts, buf.Bytes())
	n, err := sendData(buf.Bytes())
	dLog("W: %d, ERR: %#v", n, err)
	return nil
}

func (nd *NodeDesc) compose_SEND_NAME() (msg []byte) {
	msg = make([]byte, 7+len(nd.Name))
	msg[0] = byte('n')
	binary.BigEndian.PutUint16(msg[1:3], nd.version)
	binary.BigEndian.PutUint32(msg[3:7], nd.flag.toUint32())
	copy(msg[7:], nd.Name)
	return
}

func (currNd *NodeDesc) read_SEND_NAME(msg []byte) (nd *NodeDesc) {
	version := binary.BigEndian.Uint16(msg[1:3])
	flag := nodeFlag(binary.BigEndian.Uint32(msg[3:7]))
	name := string(msg[7:])
	nd = &NodeDesc{
		Name:    name,
		version: version,
		flag:    flag,
	}
	currNd.remote = nd
	return
}

func (currNd *NodeDesc) compose_SEND_STATUS(nd *NodeDesc, isOk bool) (msg []byte) {
	msg = make([]byte, 3)
	msg[0] = byte('s')
	copy(msg[1:], "ok")
	return
}

func (currNd *NodeDesc) compose_SEND_CHALLENGE(nd *NodeDesc) (msg []byte) {
	msg = make([]byte, 11+len(currNd.Name))
	msg[0] = byte('n')
	binary.BigEndian.PutUint16(msg[1:3], currNd.version)
	binary.BigEndian.PutUint32(msg[3:7], currNd.flag.toUint32())
	binary.BigEndian.PutUint32(msg[7:11], currNd.challenge)
	copy(msg[11:], currNd.Name)
	return
}

func (currNd *NodeDesc) read_SEND_CHALLENGE_REPLY(nd *NodeDesc, msg []byte) (isOk bool) {
	nd.challenge = binary.BigEndian.Uint32(msg[1:5])
	digestB := msg[5:]

	digestA := genDigest(currNd.challenge, currNd.Cookie)
	if bytes.Compare(digestA, digestB) == 0 {
		isOk = true
		currNd.state = CONNECTED
	} else {
		dLog("BAD HANDSHAKE: digestA: %+v, digestB: %+v", digestA, digestB)
		isOk = false
	}
	return
}

func (currNd *NodeDesc) compose_SEND_CHALLENGE_ACK(nd *NodeDesc) (msg []byte) {
	msg = make([]byte, 17)
	msg[0] = byte('a')

	digestB := genDigest(nd.challenge, currNd.Cookie) // FIXME: use his cookie, not mine

	copy(msg[1:], digestB)
	return
}

func genDigest(challenge uint32, cookie string) (sum []byte) {
	h := md5.New()
	s := strings.Join([]string{cookie, strconv.FormatUint(uint64(challenge), 10)}, "")
	io.WriteString(h, s)
	sum = h.Sum(nil)
	return
}

func (nd NodeDesc) Flags() (flags []string) {
	fs := map[flagId]string{
		PUBLISHED:           "PUBLISHED",
		ATOM_CACHE:          "ATOM_CACHE",
		EXTENDED_REFERENCES: "EXTENDED_REFERENCES",
		DIST_MONITOR:        "DIST_MONITOR",
		FUN_TAGS:            "FUN_TAGS",
		DIST_MONITOR_NAME:   "DIST_MONITOR_NAME",
		HIDDEN_ATOM_CACHE:   "HIDDEN_ATOM_CACHE",
		NEW_FUN_TAGS:        "NEW_FUN_TAGS",
		EXTENDED_PIDS_PORTS: "EXTENDED_PIDS_PORTS",
		EXPORT_PTR_TAG:      "EXPORT_PTR_TAG",
		BIT_BINARIES:        "BIT_BINARIES",
		NEW_FLOATS:          "NEW_FLOATS",
		UNICODE_IO:          "UNICODE_IO",
		DIST_HDR_ATOM_CACHE: "DIST_HDR_ATOM_CACHE",
		SMALL_ATOM_TAGS:     "SMALL_ATOM_TAGS",
	}

	for k, v := range fs {
		if nd.flag.isSet(k) {
			flags = append(flags, v)
		}
	}
	return
}

func (currNd *NodeDesc) read_TERM(msg []byte) (t term.Term, n int) {
	t, n = term.Read(msg)
	return
}
<commit_msg>Remove log<commit_after>package dist

import (
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"erlang/term"
	"errors"
	"flag"
	"io"
	"log"
	"math/rand"
	"net"
	"strconv"
	"strings"
	"time"
)

var dTrace bool

func init() {
	flag.BoolVar(&dTrace, "erlang.dist.trace", false, "trace erlang distribution protocol")
}

func dLog(f string, a ...interface{}) {
	if dTrace {
		log.Printf(f, a...)
	}
}

type flagId uint32

const (
	PUBLISHED           = flagId(0x1)
	ATOM_CACHE          = 0x2
	EXTENDED_REFERENCES = 0x4
	DIST_MONITOR        = 0x8
	FUN_TAGS            = 0x10
	DIST_MONITOR_NAME   = 0x20
	HIDDEN_ATOM_CACHE   = 0x40
	NEW_FUN_TAGS        = 0x80
	EXTENDED_PIDS_PORTS = 0x100
	EXPORT_PTR_TAG      = 0x200
	BIT_BINARIES        = 0x400
	NEW_FLOATS          = 0x800
	UNICODE_IO          = 0x1000
	DIST_HDR_ATOM_CACHE = 0x2000
	SMALL_ATOM_TAGS     = 0x4000
)

type nodeFlag flagId

func (nf nodeFlag) toUint32() (flag uint32) {
	flag = uint32(nf)
	return
}

func (nf nodeFlag) isSet(f flagId) (is bool) {
	is = (uint32(nf) & uint32(f)) != 0
	return
}

func toNodeFlag(f ...flagId) (nf nodeFlag) {
	var flags uint32
	for _, v := range f {
		flags |= uint32(v)
	}
	nf = nodeFlag(flags)
	return
}

type nodeState uint8

const (
	HANDSHAKE nodeState = iota
	CONNECTED
)

type NodeDesc struct {
	Name      string
	Cookie    string
	Hidden    bool
	remote    *NodeDesc
	state     nodeState
	challenge uint32
	flag      nodeFlag
	version   uint16
}

func NewNodeDesc(name, cookie string, isHidden bool) (nd *NodeDesc) {
	nd = &NodeDesc{
		Name:    name,
		Cookie:  cookie,
		Hidden:  isHidden,
		remote:  nil,
		state:   HANDSHAKE,
		flag:    toNodeFlag(PUBLISHED, UNICODE_IO, EXTENDED_PIDS_PORTS, EXTENDED_REFERENCES),
		version: 5,
	}
	return nd
}

func (currNd *NodeDesc) ReadMessage(c net.Conn) (ts []term.Term, err error) {
	rcbuf := new(bytes.Buffer)

	var buf []byte

	for {
		var n int
		rbuf := make([]byte, 1024)
		n, err = c.Read(rbuf)

		if (err != nil) && (n == 0) {
			dLog("Stop enode loop (%d): %v", n, err)
			return
		}
		rcbuf.Write(rbuf[:n])
		if n < len(rbuf) {
			break
		}
	}

	buf = rcbuf.Bytes()

	switch currNd.state {
	case HANDSHAKE:
		length := binary.BigEndian.Uint16(buf[0:2])
		msg := buf[2:]
		dLog("Read from enode %d: %v", length, msg)

		sendData := func(data []byte) (int, error) {
			reply := make([]byte, len(data)+2)
			binary.BigEndian.PutUint16(reply[0:2], uint16(len(data)))
			copy(reply[2:], data)
			dLog("Write to enode: %v", reply)
			return c.Write(reply)
		}

		switch msg[0] {
		case 'n':
			sn := currNd.read_SEND_NAME(msg)
			// Statuses: ok, nok, ok_simultaneous, alive, not_allowed
			sok := currNd.compose_SEND_STATUS(sn, true)
			_, err = sendData(sok)
			if err != nil {
				return
			}

			rand.Seed(time.Now().UTC().UnixNano())
			currNd.challenge = rand.Uint32()

			// Now send challenge
			challenge := currNd.compose_SEND_CHALLENGE(sn)
			sendData(challenge)
			if err != nil {
				return
			}

		case 'r':
			sn := currNd.remote
			ok := currNd.read_SEND_CHALLENGE_REPLY(sn, msg)
			if ok {
				challengeAck := currNd.compose_SEND_CHALLENGE_ACK(sn)
				sendData(challengeAck)
				if err != nil {
					return
				}
				dLog("Remote: %#v", sn)
				ts = []term.Term{term.Term(term.Tuple{term.Atom("$go_set_node"), term.Atom(sn.Name)})}
			} else {
				err = errors.New("bad handshake")
				return
			}
		}
	case CONNECTED:
		length := binary.BigEndian.Uint32(buf[0:4])
		msg := buf[4:]
		dLog("Read from enode %d: %v", length, msg)

		if length == 0 {
			dLog("Keepalive")
			return
		}

		switch msg[0] {
		case 'p':
			pos := 1
			dLog("BIN TERM: %v", msg[pos:])
			ts = make([]term.Term, 0)
			for {
				res, nr := currNd.read_TERM(msg[pos:])
				if nr == 0 {
					break
				}
				ts = append(ts, res)
				pos += nr
				dLog("READ TERM (%d): %#v", nr, res)
			}
		}
	}
	return
}

func (currNd *NodeDesc) WriteMessage(c net.Conn, ts []term.Term) (err error) {
	sendData := func(data []byte) (int, error) {
		reply := make([]byte, len(data)+4)
		binary.BigEndian.PutUint32(reply[0:4], uint32(len(data)))
		copy(reply[4:], data)
		dLog("Write to enode: %v", reply)
		return c.Write(reply)
	}

	buf := new(bytes.Buffer)
	buf.Write([]byte{'p'})
	for _, v := range ts {
		term.WriteTerm(v, buf)
	}
	dLog("WRITE: %#v: %#v", ts, buf.Bytes())
	sendData(buf.Bytes())
	return nil
}

func (nd *NodeDesc) compose_SEND_NAME() (msg []byte) {
	msg = make([]byte, 7+len(nd.Name))
	msg[0] = byte('n')
	binary.BigEndian.PutUint16(msg[1:3], nd.version)
	binary.BigEndian.PutUint32(msg[3:7], nd.flag.toUint32())
	copy(msg[7:], nd.Name)
	return
}

func (currNd *NodeDesc) read_SEND_NAME(msg []byte) (nd *NodeDesc) {
	version := binary.BigEndian.Uint16(msg[1:3])
	flag := nodeFlag(binary.BigEndian.Uint32(msg[3:7]))
	name := string(msg[7:])
	nd = &NodeDesc{
		Name:    name,
		version: version,
		flag:    flag,
	}
	currNd.remote = nd
	return
}

func (currNd *NodeDesc) compose_SEND_STATUS(nd *NodeDesc, isOk bool) (msg []byte) {
	msg = make([]byte, 3)
	msg[0] = byte('s')
	copy(msg[1:], "ok")
	return
}

func (currNd *NodeDesc) compose_SEND_CHALLENGE(nd *NodeDesc) (msg []byte) {
	msg = make([]byte, 11+len(currNd.Name))
	msg[0] = byte('n')
	binary.BigEndian.PutUint16(msg[1:3], currNd.version)
	binary.BigEndian.PutUint32(msg[3:7], currNd.flag.toUint32())
	binary.BigEndian.PutUint32(msg[7:11], currNd.challenge)
	copy(msg[11:], currNd.Name)
	return
}

func (currNd *NodeDesc) read_SEND_CHALLENGE_REPLY(nd *NodeDesc, msg []byte) (isOk bool) {
	nd.challenge = binary.BigEndian.Uint32(msg[1:5])
	digestB := msg[5:]

	digestA := genDigest(currNd.challenge, currNd.Cookie)
	if bytes.Compare(digestA, digestB) == 0 {
		isOk = true
		currNd.state = CONNECTED
	} else {
		dLog("BAD HANDSHAKE: digestA: %+v, digestB: %+v", digestA, digestB)
		isOk = false
	}
	return
}

func (currNd *NodeDesc) compose_SEND_CHALLENGE_ACK(nd *NodeDesc) (msg []byte) {
	msg = make([]byte, 17)
	msg[0] = byte('a')

	digestB := genDigest(nd.challenge, currNd.Cookie) // FIXME: use his cookie, not mine

	copy(msg[1:], digestB)
	return
}

func genDigest(challenge uint32, cookie string) (sum []byte) {
	h := md5.New()
	s := strings.Join([]string{cookie, strconv.FormatUint(uint64(challenge), 10)}, "")
	io.WriteString(h, s)
	sum = h.Sum(nil)
	return
}

func (nd NodeDesc) Flags() (flags []string) {
	fs := map[flagId]string{
		PUBLISHED:           "PUBLISHED",
		ATOM_CACHE:          "ATOM_CACHE",
		EXTENDED_REFERENCES: "EXTENDED_REFERENCES",
		DIST_MONITOR:        "DIST_MONITOR",
		FUN_TAGS:            "FUN_TAGS",
		DIST_MONITOR_NAME:   "DIST_MONITOR_NAME",
		HIDDEN_ATOM_CACHE:   "HIDDEN_ATOM_CACHE",
		NEW_FUN_TAGS:        "NEW_FUN_TAGS",
		EXTENDED_PIDS_PORTS: "EXTENDED_PIDS_PORTS",
		EXPORT_PTR_TAG:      "EXPORT_PTR_TAG",
		BIT_BINARIES:        "BIT_BINARIES",
		NEW_FLOATS:          "NEW_FLOATS",
		UNICODE_IO:          "UNICODE_IO",
		DIST_HDR_ATOM_CACHE: "DIST_HDR_ATOM_CACHE",
		SMALL_ATOM_TAGS:     "SMALL_ATOM_TAGS",
	}

	for k, v := range fs {
		if nd.flag.isSet(k) {
			flags = append(flags, v)
		}
	}
	return
}

func (currNd *NodeDesc) read_TERM(msg []byte) (t term.Term, n int) {
	t, n = term.Read(msg)
	return
}
<|endoftext|>