<commit_before>package main

import (
	"flag"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"github.com/chronos-tachyon/mulberry/mulberrygrove/daemon"
)

var (
	config   = flag.String("config", "", "path to the YAML configuration file to share")
	keyring  = flag.String("keyring", "", "path to the GPG secret keyring to sign with")
	keyid    = flag.String("keyid", "", "hex ID of the GPG identity to sign with")
	bindNet  = flag.String("net", "tcp", "protocol to bind to")
	bindAddr = flag.String("bind", ":8080", "address to bind to")
)

func main() {
	flag.Parse()
	if *config == "" {
		log.Fatalf("fatal: missing required flag: -config")
	}
	if *keyring == "" {
		log.Fatalf("fatal: missing required flag: -keyring")
	}
	if *keyid == "" {
		log.Fatalf("fatal: missing required flag: -keyid")
	}
	kid, err := strconv.ParseUint(*keyid, 16, 64)
	if err != nil {
		log.Fatalf("fatal: failed to parse -keyid %q: %v", *keyid, err)
	}
	d := daemon.New(*config, *keyring, kid)

	l, err := net.Listen(*bindNet, *bindAddr)
	if err != nil {
		log.Fatalf("fatal: failed to listen: %v", err)
	}

	// Ignore SIGHUP
	hupch := make(chan os.Signal)
	signal.Notify(hupch, syscall.SIGHUP)
	defer signal.Stop(hupch)
	go (func() {
		for {
			sig := <-hupch
			if sig == nil {
				break
			}
		}
	})()

	// Shut down gracefully on SIGINT or SIGTERM
	sigch := make(chan os.Signal)
	signal.Notify(sigch, syscall.SIGINT, syscall.SIGTERM)
	defer signal.Stop(sigch)
	go (func() {
		sig := <-sigch
		log.Printf("info: got signal %v", sig)
		l.Close()
	})()

	log.Printf("info: looping")
	d.Start()
	http.Handle("/config", d)
	err = http.Serve(l, nil)
	operr, ok := err.(*net.OpError)
	if !ok || operr.Op != "accept" && operr.Err.Error() != "use of closed network connection" {
		log.Fatalf("fatal: %v", err)
	}
	d.Stop()
	d.Await()
	log.Printf("info: graceful shutdown")
}
<commit_msg>Oh hey, there's a signal.Ignore() function.<commit_after>package main

import (
	"flag"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"github.com/chronos-tachyon/mulberry/mulberrygrove/daemon"
)

var (
	config   = flag.String("config", "", "path to the YAML configuration file to share")
	keyring  = flag.String("keyring", "", "path to the GPG secret keyring to sign with")
	keyid    = flag.String("keyid", "", "hex ID of the GPG identity to sign with")
	bindNet  = flag.String("net", "tcp", "protocol to bind to")
	bindAddr = flag.String("bind", ":8080", "address to bind to")
)

func main() {
	flag.Parse()
	if *config == "" {
		log.Fatalf("fatal: missing required flag: -config")
	}
	if *keyring == "" {
		log.Fatalf("fatal: missing required flag: -keyring")
	}
	if *keyid == "" {
		log.Fatalf("fatal: missing required flag: -keyid")
	}
	kid, err := strconv.ParseUint(*keyid, 16, 64)
	if err != nil {
		log.Fatalf("fatal: failed to parse -keyid %q: %v", *keyid, err)
	}
	d := daemon.New(*config, *keyring, kid)

	l, err := net.Listen(*bindNet, *bindAddr)
	if err != nil {
		log.Fatalf("fatal: failed to listen: %v", err)
	}

	// Ignore SIGHUP
	signal.Ignore(syscall.SIGHUP)

	// Shut down gracefully on SIGINT or SIGTERM
	sigch := make(chan os.Signal)
	signal.Notify(sigch, syscall.SIGINT, syscall.SIGTERM)
	defer signal.Stop(sigch)
	go (func() {
		sig := <-sigch
		log.Printf("info: got signal %v", sig)
		l.Close()
	})()

	log.Printf("info: looping")
	d.Start()
	http.Handle("/config", d)
	err = http.Serve(l, nil)
	operr, ok := err.(*net.OpError)
	if !ok || operr.Op != "accept" && operr.Err.Error() != "use of closed network connection" {
		log.Fatalf("fatal: %v", err)
	}
	d.Stop()
	d.Await()
	log.Printf("info: graceful shutdown")
}
<|endoftext|>


<commit_before>package update

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
)

func copyFile(dst, src string, mode os.FileMode) error {
	// only use absolute paths to prevent epic fails
	if !path.IsAbs(dst) || !path.IsAbs(src) {
		return fmt.Errorf("copyFile must use absolute paths")
	}

	// does source exist and is it a regular file?
	srcFileStat, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !srcFileStat.Mode().IsRegular() {
		return fmt.Errorf("%s is not a regular file", src)
	}

	// open source
	srcFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	// open destination
	dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mode)
	if err != nil {
		return err
	}
	defer dstFile.Close()
	defer dstFile.Sync()

	// copy
	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return err
	}

	return nil
}

func setupUtilityScripts(rootDir, configureDir string) error {
	binDir := path.Join(rootDir, "opt", "bin")
	scriptsDir := path.Join(rootDir, "etc", "systemd", "system", "scripts")
	ignoreScripts := []string{
		"protonet_zpool.sh",
		"platconf",
	}

	binDirContents, err := ioutil.ReadDir(binDir)
	if err != nil {
		return err
	}

	// remove contents of /opt/bin/
	// TODO don't remove and then copy
	// instead copy to another dir and then replace the directories
	// so the operation is more atomic
removeBindirContents:
	for _, f := range binDirContents {
		fullpath := path.Join(binDir, f.Name())
		basename := f.Name()

		// should we leave this one behind?
		for _, toSkip := range ignoreScripts {
			if basename == toSkip {
				log.Println("setupUtilityScripts: skipping", toSkip)
				continue removeBindirContents
			}
		}

		if !f.IsDir() {
			log.Println("Removing old", basename)
			err := os.Remove(fullpath)
			if err != nil {
				return fmt.Errorf("Failed to remove '%s': %s", fullpath, err.Error())
			}
		}
	}

	// remove contents of /etc/systemd/system/scripts/
	scriptsDirContents, err := ioutil.ReadDir(scriptsDir)
	if err != nil {
		return err
	}

	for _, f := range scriptsDirContents {
		fullpath := path.Join(scriptsDir, f.Name())
		basename := f.Name()
		if f.Mode().IsRegular() {
			log.Println("Removing old", basename)
			err := os.Remove(fullpath)
			if err != nil {
				return err
			}
		}
	}

	// install new scripts
	log.Println("Installing new scripts")
	newScriptsContents, err := ioutil.ReadDir(path.Join(configureDir, "scripts"))
	if err != nil {
		return err
	}
	for _, f := range newScriptsContents {
		fullpath := path.Join(configureDir, "scripts", f.Name())
		basename := f.Name()
		dst := path.Join(scriptsDir, basename)
		linkLocation := strings.TrimSuffix(path.Join(binDir, basename), ".sh")
		log.Println("\t", "*", basename)
		err = copyFile(dst, fullpath, 0755)
		if err != nil {
			return fmt.Errorf("setupUtilityScripts: failed to copy file: %s", err.Error())
		}
		err = os.Symlink(dst, linkLocation)
		if err != nil {
			return fmt.Errorf("setupUtilityScripts: failed to symlink: %s", err.Error())
		}
	}

	log.Println("Done.")
	return nil
}
<commit_msg>added pullAllImages<commit_after>package update

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"

	"github.com/experimental-platform/platconf/platconf"
)

func copyFile(dst, src string, mode os.FileMode) error {
	// only use absolute paths to prevent epic fails
	if !path.IsAbs(dst) || !path.IsAbs(src) {
		return fmt.Errorf("copyFile must use absolute paths")
	}

	// does source exist and is it a regular file?
	srcFileStat, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !srcFileStat.Mode().IsRegular() {
		return fmt.Errorf("%s is not a regular file", src)
	}

	// open source
	srcFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	// open destination
	dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mode)
	if err != nil {
		return err
	}
	defer dstFile.Close()
	defer dstFile.Sync()

	// copy
	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return err
	}

	return nil
}

func setupUtilityScripts(rootDir, configureDir string) error {
	binDir := path.Join(rootDir, "opt", "bin")
	scriptsDir := path.Join(rootDir, "etc", "systemd", "system", "scripts")
	ignoreScripts := []string{
		"protonet_zpool.sh",
		"platconf",
	}

	binDirContents, err := ioutil.ReadDir(binDir)
	if err != nil {
		return err
	}

	// remove contents of /opt/bin/
	// TODO don't remove and then copy
	// instead copy to another dir and then replace the directories
	// so the operation is more atomic
removeBindirContents:
	for _, f := range binDirContents {
		fullpath := path.Join(binDir, f.Name())
		basename := f.Name()

		// should we leave this one behind?
		for _, toSkip := range ignoreScripts {
			if basename == toSkip {
				log.Println("setupUtilityScripts: skipping", toSkip)
				continue removeBindirContents
			}
		}

		if !f.IsDir() {
			log.Println("Removing old", basename)
			err := os.Remove(fullpath)
			if err != nil {
				return fmt.Errorf("Failed to remove '%s': %s", fullpath, err.Error())
			}
		}
	}

	// remove contents of /etc/systemd/system/scripts/
	scriptsDirContents, err := ioutil.ReadDir(scriptsDir)
	if err != nil {
		return err
	}

	for _, f := range scriptsDirContents {
		fullpath := path.Join(scriptsDir, f.Name())
		basename := f.Name()
		if f.Mode().IsRegular() {
			log.Println("Removing old", basename)
			err := os.Remove(fullpath)
			if err != nil {
				return err
			}
		}
	}

	// install new scripts
	log.Println("Installing new scripts")
	newScriptsContents, err := ioutil.ReadDir(path.Join(configureDir, "scripts"))
	if err != nil {
		return err
	}
	for _, f := range newScriptsContents {
		fullpath := path.Join(configureDir, "scripts", f.Name())
		basename := f.Name()
		dst := path.Join(scriptsDir, basename)
		linkLocation := strings.TrimSuffix(path.Join(binDir, basename), ".sh")
		log.Println("\t", "*", basename)
		err = copyFile(dst, fullpath, 0755)
		if err != nil {
			return fmt.Errorf("setupUtilityScripts: failed to copy file: %s", err.Error())
		}
		err = os.Symlink(dst, linkLocation)
		if err != nil {
			return fmt.Errorf("setupUtilityScripts: failed to symlink: %s", err.Error())
		}
	}

	log.Println("Done.")
	return nil
}

func pullAllImages(manifest *platconf.ReleaseManifestV2) error {
	// TODO add retry

	type pullerMsg struct {
		ImgName string
		Error   error
	}

	imagesTotal := len(manifest.Images)
	imagesChan := make(chan platconf.ReleaseManifestV2Image)
	pullersTotal := 4
	pullerChan := make(chan pullerMsg)

	for i := 0; i < pullersTotal; i++ {
		go func() {
			for {
				img, ok := <-imagesChan
				if !ok {
					return
				}

				err := pullImage(img.Name, img.Tag)
				pullerChan <- pullerMsg{
					ImgName: img.Name,
					Error:   err,
				}
			}
		}()
	}

	go func() {
		for _, img := range manifest.Images {
			imagesChan <- img
		}
	}()

	for i := 0; i < imagesTotal; i++ {
		msg := <-pullerChan
		if msg.Error != nil {
			log.Printf("Downloading '%s': FAILED", msg.ImgName)
			log.Printf("Downloading '%s': %s", msg.ImgName, msg.Error.Error())
			return msg.Error
		}

		log.Printf("Downloading '%s': OK", msg.ImgName)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

package logger

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"runtime"
	"testing"
	"time"
)

// Time returned in calling now(), setup and test in init.
var t1 = time.Date(2015, 9, 1, 14, 22, 36, 0, time.UTC)

func init() {
	now = func() time.Time {
		return t1
	}
}

// EventWriter that collects the events and errors.
type eventWriter struct {
	events []Event
	errors []error
	closed bool
}

func (ew *eventWriter) Write(event Event) error {
	ew.events = append(ew.events, event)
	return nil
}

func (ew *eventWriter) HandleError(err error) {
	ew.errors = append(ew.errors, err)
}

func (ew *eventWriter) Close() error {
	ew.closed = true
	return nil
}

// A data type to be used in calling Log.
type user struct {
	ID   int
	Name string
}

func TestLog(t *testing.T) {
	defer reset()
	var ew eventWriter
	Start(&ew)

	tags := Tags{"my", "tags"}
	eventType := NewEventType("my-event-type")
	data := user{1, "Thomas"}
	event := Event{
		Type:      eventType,
		Timestamp: now(),
		Tags:      tags,
		Message:   "My event",
		Data:      data,
	}

	Debug(tags, "Debug message")
	Debugf(tags, "Debug %s message", "formatted")
	Info(tags, "Info message")
	Infof(tags, "Info %s message", "formatted")
	Warn(tags, "Warn message")
	Warnf(tags, "Warn %s message", "formatted")
	Error(tags, errors.New("Error message"))
	Errorf(tags, "Error %s message", "formatted")

	defer func() {
		recv := recover()
		Fatal(tags, recv)
		testThumstone(tags)
		Log(event)

		if err := Close(); err != nil {
			t.Fatal("Unexpected error closing: " + err.Error())
		}

		if len(ew.errors) != 0 {
			t.Fatalf("Unexpected error(s): %v", ew.errors)
		}

		pc, file, _, _ := runtime.Caller(0)
		fn := runtime.FuncForPC(pc)

		expected := []Event{
			{Type: DebugEvent, Timestamp: now(), Tags: tags, Message: "Debug message"},
			{Type: DebugEvent, Timestamp: now(), Tags: tags, Message: "Debug formatted message"},
			{Type: InfoEvent, Timestamp: now(), Tags: tags, Message: "Info message"},
			{Type: InfoEvent, Timestamp: now(), Tags: tags, Message: "Info formatted message"},
			{Type: WarnEvent, Timestamp: now(), Tags: tags, Message: "Warn message"},
			{Type: WarnEvent, Timestamp: now(), Tags: tags, Message: "Warn formatted message"},
			{Type: ErrorEvent, Timestamp: now(), Tags: tags, Message: "Error message"},
			{Type: ErrorEvent, Timestamp: now(), Tags: tags, Message: "Error formatted message"},
			{Type: FatalEvent, Timestamp: now(), Tags: tags, Message: "Fatal message"},
			{Type: ThumbEvent, Timestamp: now(), Tags: tags, Message: "Function testThumstone called by " +
				fn.Name() + ", from file " + file + " on line 81"},
			event,
		}

		if len(ew.events) != len(expected) {
			t.Fatalf("Expected to have %d events, but got %d",
				len(expected), len(ew.events))
		}

		for i, event := range ew.events {
			expectedEvent := expected[i]

			if expectedEvent.Type == FatalEvent {
				// sortof test the stack trace, best we can do.
				stackTrace := event.Data.([]byte)
				if !bytes.HasPrefix(stackTrace, []byte("goroutine")) {
					t.Errorf("Expected a stack trace as data for a Fatal event, but got %s ",
						string(stackTrace))
				} else if bytes.Index(stackTrace, []byte("logger.getStackTrace")) != -1 ||
					bytes.Index(stackTrace, []byte("logger.Fatal")) != -1 {
					t.Errorf("Expected the stack trace to not contain the logger.Fatal and logger.getStackTrace, but got %s ",
						string(stackTrace))
				}

				event.Data = nil
			}

			if expected, got := expectedEvent, event; !reflect.DeepEqual(expected, got) {
				t.Errorf("Expected event #%d to be %v, but got %v", i, expected, got)
			}
		}
	}()
	panic("Fatal message")
}

func TestStartTwice(t *testing.T) {
	defer reset()
	var ew eventWriter
	Start(&ew)
	if err := Close(); err != nil {
		t.Fatal("Unexpected error closing initial log: " + err.Error())
	}

	defer expectPanic(t, "logger: can only Start once")
	Start(&ew)
}

func TestStartNoEventWriter(t *testing.T) {
	defer reset()
	defer expectPanic(t, "logger: need atleast a single EventWriter to write to")
	Start()
}

// EventWriter that always returns a write error with the event message in it.
type errorEventWriter struct {
	closeError error
	errors     []error
}

func (eew *errorEventWriter) Write(event Event) error {
	return errors.New("Write error: " + event.Message)
}

func (eew *errorEventWriter) HandleError(err error) {
	eew.errors = append(eew.errors, err)
}

func (eew *errorEventWriter) Close() error {
	return eew.closeError
}

func TestErrorEventWriter(t *testing.T) {
	closeError := errors.New("Close error")

	defer reset()
	eew := errorEventWriter{
		closeError: closeError,
	}
	Start(&eew)

	tags := Tags{"my", "tags"}
	Info(tags, "Info message1")
	Info(tags, "Info message2")
	Info(tags, "Info message3")
	Info(tags, "Info message4")
	Info(tags, "Info message5")
	Info(tags, "Won't be written to the writer")

	if err := Close(); err == nil {
		t.Fatal("Expected a closing error, but didn't get one")
	} else if err != closeError {
		t.Fatalf("Expceted the closing error to be %q, but got %q",
			closeError.Error(), err.Error())
	}

	// 6 = 5 bad write errors + 1 bad EventWriter error.
	if expected, got := 6, len(eew.errors); got != expected {
		t.Fatalf("Expected %d errors, but only got %d", expected, got)
	}

	// Expected errors:
	// 0 - 4: write event 1.
	// 5:     EventWriter is bad.

	for i, got := range eew.errors {
		var expected error
		if i == 5 {
			expected = ErrBadEventWriter
		} else {
			d := 1
			expected = fmt.Errorf("Write error: Info message%d", d)
		}

		if got.Error() != expected.Error() {
			t.Errorf("Expected error #%d to be %q, but got %q",
				i, expected.Error(), got.Error())
		}
	}
}

func testThumstone(tags Tags) {
	Thumbstone(tags, "testThumstone")
}

func reset() {
	eventChannel = make(chan Event, defaultEventChannelSize)
	eventChannelClosed = make(chan struct{}, 1)
	eventWriters = []EventWriter{}
	started = false
}

func expectPanic(t *testing.T, expected string) {
	recv := recover()
	if recv == nil {
		t.Fatal(`Expected a panic, but didn't get one`)
	}

	got := recv.(string)
	if got != expected {
		t.Fatalf("Expected panic value to be %s, but got %s", expected, got)
	}
}

func TestGetStackTrace(t *testing.T) {
	t.Parallel()

	// Fake the Fatal call.
	var stackTrace []byte
	func() {
		stackTrace = getStackTrace()
	}()

	if !bytes.HasPrefix(stackTrace, []byte("goroutine")) {
		t.Errorf("Expected the stack trace to start with goroutine, but got %s ",
			string(stackTrace))
	} else if bytes.Index(stackTrace, []byte("logger.getStackTrace")) != -1 ||
		bytes.Index(stackTrace, []byte("logger.TestGetStackTrace.func1")) != -1 {
		t.Errorf("Expected the stack trace to not contain the logger.TestGetStackTrace.func1 and logger.getStackTrace, but got %s ",
			string(stackTrace))
	}
}
<commit_msg>Cleanup TestLog<commit_after>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

package logger

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"runtime"
	"testing"
	"time"
)

// Time returned in calling now(), setup and test in init.
var t1 = time.Date(2015, 9, 1, 14, 22, 36, 0, time.UTC)

func init() {
	now = func() time.Time {
		return t1
	}
}

// EventWriter that collects the events and errors.
type eventWriter struct {
	events []Event
	errors []error
	closed bool
}

func (ew *eventWriter) Write(event Event) error {
	ew.events = append(ew.events, event)
	return nil
}

func (ew *eventWriter) HandleError(err error) {
	ew.errors = append(ew.errors, err)
}

func (ew *eventWriter) Close() error {
	ew.closed = true
	return nil
}

// A data type to be used in calling Log.
type user struct {
	ID   int
	Name string
}

func TestLog(t *testing.T) {
	defer reset()
	var ew eventWriter
	Start(&ew)

	tags := Tags{"my", "tags"}
	eventType := NewEventType("my-event-type")
	data := user{1, "Thomas"}
	event := Event{
		Type:      eventType,
		Timestamp: now(),
		Tags:      tags,
		Message:   "My event",
		Data:      data,
	}
	recv := getPanicRecoveredValue("Fatal message")

	Debug(tags, "Debug message")
	Debugf(tags, "Debug %s message", "formatted")
	Info(tags, "Info message")
	Infof(tags, "Info %s message", "formatted")
	Warn(tags, "Warn message")
	Warnf(tags, "Warn %s message", "formatted")
	Error(tags, errors.New("Error message"))
	Errorf(tags, "Error %s message", "formatted")
	Fatal(tags, recv)
	testThumstone(tags)
	Log(event)

	if err := Close(); err != nil {
		t.Fatal("Unexpected error closing: " + err.Error())
	}

	if len(ew.errors) != 0 {
		t.Fatalf("Unexpected error(s): %v", ew.errors)
	}

	pc, file, _, _ := runtime.Caller(0)
	fn := runtime.FuncForPC(pc)

	expected := []Event{
		{Type: DebugEvent, Timestamp: now(), Tags: tags, Message: "Debug message"},
		{Type: DebugEvent, Timestamp: now(), Tags: tags, Message: "Debug formatted message"},
		{Type: InfoEvent, Timestamp: now(), Tags: tags, Message: "Info message"},
		{Type: InfoEvent, Timestamp: now(), Tags: tags, Message: "Info formatted message"},
		{Type: WarnEvent, Timestamp: now(), Tags: tags, Message: "Warn message"},
		{Type: WarnEvent, Timestamp: now(), Tags: tags, Message: "Warn formatted message"},
		{Type: ErrorEvent, Timestamp: now(), Tags: tags, Message: "Error message"},
		{Type: ErrorEvent, Timestamp: now(), Tags: tags, Message: "Error formatted message"},
		{Type: FatalEvent, Timestamp: now(), Tags: tags, Message: "Fatal message"},
		{Type: ThumbEvent, Timestamp: now(), Tags: tags, Message: "Function testThumstone called by " +
			fn.Name() + ", from file " + file + " on line 79"},
		event,
	}

	if len(ew.events) != len(expected) {
		t.Fatalf("Expected to have %d events, but got %d",
			len(expected), len(ew.events))
	}

	for i, event := range ew.events {
		expectedEvent := expected[i]

		if expectedEvent.Type == FatalEvent {
			// sortof test the stack trace, best we can do.
			stackTrace := event.Data.([]byte)
			if !bytes.HasPrefix(stackTrace, []byte("goroutine")) {
				t.Errorf("Expected a stack trace as data for a Fatal event, but got %s ",
					string(stackTrace))
			} else if bytes.Index(stackTrace, []byte("logger.getStackTrace")) != -1 ||
				bytes.Index(stackTrace, []byte("logger.Fatal")) != -1 {
				t.Errorf("Expected the stack trace to not contain the logger.Fatal and logger.getStackTrace, but got %s ",
					string(stackTrace))
			}

			event.Data = nil
		}

		if expected, got := expectedEvent, event; !reflect.DeepEqual(expected, got) {
			t.Errorf("Expected event #%d to be %v, but got %v", i, expected, got)
		}
	}
}

func getPanicRecoveredValue(msg string) (recv interface{}) {
	defer func() {
		recv = recover()
	}()
	panic(msg)
}

func TestStartTwice(t *testing.T) {
	defer reset()
	var ew eventWriter
	Start(&ew)
	if err := Close(); err != nil {
		t.Fatal("Unexpected error closing initial log: " + err.Error())
	}

	defer expectPanic(t, "logger: can only Start once")
	Start(&ew)
}

func TestStartNoEventWriter(t *testing.T) {
	defer reset()
	defer expectPanic(t, "logger: need atleast a single EventWriter to write to")
	Start()
}

// EventWriter that always returns a write error with the event message in it.
type errorEventWriter struct {
	closeError error
	errors     []error
}

func (eew *errorEventWriter) Write(event Event) error {
	return errors.New("Write error: " + event.Message)
}

func (eew *errorEventWriter) HandleError(err error) {
	eew.errors = append(eew.errors, err)
}

func (eew *errorEventWriter) Close() error {
	return eew.closeError
}

func TestErrorEventWriter(t *testing.T) {
	closeError := errors.New("Close error")

	defer reset()
	eew := errorEventWriter{
		closeError: closeError,
	}
	Start(&eew)

	tags := Tags{"my", "tags"}
	Info(tags, "Info message1")
	Info(tags, "Info message2")
	Info(tags, "Info message3")
	Info(tags, "Info message4")
	Info(tags, "Info message5")
	Info(tags, "Won't be written to the writer")

	if err := Close(); err == nil {
		t.Fatal("Expected a closing error, but didn't get one")
	} else if err != closeError {
		t.Fatalf("Expceted the closing error to be %q, but got %q",
			closeError.Error(), err.Error())
	}

	// 6 = 5 bad write errors + 1 bad EventWriter error.
	if expected, got := 6, len(eew.errors); got != expected {
		t.Fatalf("Expected %d errors, but only got %d", expected, got)
	}

	// Expected errors:
	// 0 - 4: write event 1.
	// 5:     EventWriter is bad.

	for i, got := range eew.errors {
		var expected error
		if i == 5 {
			expected = ErrBadEventWriter
		} else {
			d := 1
			expected = fmt.Errorf("Write error: Info message%d", d)
		}

		if got.Error() != expected.Error() {
			t.Errorf("Expected error #%d to be %q, but got %q",
				i, expected.Error(), got.Error())
		}
	}
}

func testThumstone(tags Tags) {
	Thumbstone(tags, "testThumstone")
}

func reset() {
	eventChannel = make(chan Event, defaultEventChannelSize)
	eventChannelClosed = make(chan struct{}, 1)
	eventWriters = []EventWriter{}
	started = false
}

func expectPanic(t *testing.T, expected string) {
	recv := recover()
	if recv == nil {
		t.Fatal(`Expected a panic, but didn't get one`)
	}

	got := recv.(string)
	if got != expected {
		t.Fatalf("Expected panic value to be %s, but got %s", expected, got)
	}
}

func TestGetStackTrace(t *testing.T) {
	t.Parallel()

	// Fake the Fatal call.
	var stackTrace []byte
	func() {
		stackTrace = getStackTrace()
	}()

	if !bytes.HasPrefix(stackTrace, []byte("goroutine")) {
		t.Errorf("Expected the stack trace to start with goroutine, but got %s ",
			string(stackTrace))
	} else if bytes.Index(stackTrace, []byte("logger.getStackTrace")) != -1 ||
		bytes.Index(stackTrace, []byte("logger.TestGetStackTrace.func1")) != -1 {
		t.Errorf("Expected the stack trace to not contain the logger.TestGetStackTrace.func1 and logger.getStackTrace, but got %s ",
			string(stackTrace))
	}
}
<|endoftext|>


<commit_before>// Package admin desrcibes the admin view containing references to
// various managers and editors
package admin

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"

	"github.com/bosssauce/ponzu/content"
	"github.com/bosssauce/ponzu/system/admin/user"
	"github.com/bosssauce/ponzu/system/db"
)

var startAdminHTML = `<!doctype html>

        <title>{{ .Logo }}</title>

        <link rel="stylesheet" href="/admin/static/dashboard/css/material-icons.css" />
        <link rel="stylesheet" href="/admin/static/dashboard/css/materialize.min.css" />
        <link rel="stylesheet" href="/admin/static/editor/css/materialNote.css" />
        <link rel="stylesheet" href="/admin/static/dashboard/css/admin.css" />

        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">

                {{ .Logo }}

Logout

`

var mainAdminHTML = `

Content

                    {{ range $t, $f := .Types }}

playlist_add{{ $t }}

                    {{ end }}

System

settingsConfiguration

supervisor_accountUsers

            {{ if .Subview}}

                {{ .Subview }}

            {{ end }}`

var endAdminHTML = `

Powered by © Ponzu  |  open-sourced by Boss Sauce Creative

`

type admin struct {
	Logo    string
	Types   map[string]func() interface{}
	Subview template.HTML
}

// Admin ...
func Admin(view []byte) ([]byte, error) {
	cfg, err := db.Config("name")
	if err != nil {
		return nil, err
	}

	if cfg == nil {
		cfg = []byte("")
	}

	a := admin{
		Logo:    string(cfg),
		Types:   content.Types,
		Subview: template.HTML(view),
	}

	buf := &bytes.Buffer{}
	html := startAdminHTML + mainAdminHTML + endAdminHTML
	tmpl := template.Must(template.New("admin").Parse(html))
	err = tmpl.Execute(buf, a)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

var initAdminHTML = `

Welcome!

You need to initialize your system by filling out the form below. All of
    this information can be updated later on, but you will not be able to start
    without first completing this step.

    <form method="post" action="/admin/init" class="row">

Configuration

            <input placeholder="Enter the name of your site (interal use only)" class="validate required" type="text" id="name" name="name"/>
            Site Name

            <input placeholder="Used for acquiring SSL certificate (e.g. www.example.com or  example.com)" class="validate" type="text" id="domain" name="domain"/>
            Domain

Admin Details

            <input placeholder="Your email address e.g. you@example.com" class="validate required" type="email" id="email" name="email"/>
            Email

            <input placeholder="Enter a strong password" class="validate required" type="password" id="password" name="password"/>
            Password

        <button class="btn waves-effect waves-light right">Start</button>
    </form>

`

// Init ...
func Init() ([]byte, error) {
	html := startAdminHTML + initAdminHTML + endAdminHTML

	cfg, err := db.Config("name")
	if err != nil {
		return nil, err
	}

	if cfg == nil {
		cfg = []byte("")
	}

	a := admin{
		Logo: string(cfg),
	}

	buf := &bytes.Buffer{}
	tmpl := template.Must(template.New("init").Parse(html))
	err = tmpl.Execute(buf, a)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

var loginAdminHTML = `

Welcome!

Please log in to the system using your email address and password.

    <form method="post" action="/admin/login" class="row">

            <input placeholder="Enter your email address e.g. you@example.com" class="validate required" type="email" id="email" name="email"/>
            Email

            <input placeholder="Enter your password" class="validate required" type="password" id="password" name="password"/>
            Password

        <button class="btn waves-effect waves-light right">Log in</button>
    </form>

`

// Login ...
func Login() ([]byte, error) {
	html := startAdminHTML + loginAdminHTML + endAdminHTML

	cfg, err := db.Config("name")
	if err != nil {
		return nil, err
	}

	if cfg == nil {
		cfg = []byte("")
	}

	a := admin{
		Logo: string(cfg),
	}

	buf := &bytes.Buffer{}
	tmpl := template.Must(template.New("login").Parse(html))
	err = tmpl.Execute(buf, a)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// UsersList ...
func UsersList(req *http.Request) ([]byte, error) {
	html := `

Edit your account:

        <form class="row" enctype="multipart/form-data" action="/admin/configure/users/edit" method="post">

                Email Address
                <input type="email" name="email" value="{{ .User.Email }}"/>

To approve changes, enter your password:

                Current Password
                <input type="password" name="password"/>

                New Password: (leave blank if no password change needed)
                <input name="new_password" type="password"/>

                <button class="btn waves-effect waves-light green right" type="submit">Save</button>

        </form>

Add a new user:

        <form class="row" enctype="multipart/form-data" action="/admin/configure/users" method="post">

                Email Address
                <input type="email" name="email" value=""/>

                Password
                <input type="password" name="password"/>

                <button class="btn waves-effect waves-light green right" type="submit">Add User</button>

        </form>

Remove Admin Users

            {{ range .Users }}

                {{ .Email }}
                <form enctype="multipart/form-data" class="delete-user __ponzu right" action="/admin/configure/users/delete" method="post">
                    Delete
                    <input type="hidden" name="email" value="{{ .Email }}"/>
                    <input type="hidden" name="id" value="{{ .ID }}"/>
                </form>

            {{ end }}

    `
	script := `

    `
	// get current user out to pass as data to execute template
	j, err := db.CurrentUser(req)
	if err != nil {
		fmt.Println("CurrentUser")
		return nil, err
	}

	var usr user.User
	err = json.Unmarshal(j, &usr)
	if err != nil {
		fmt.Println("Unmarshal usr")
		return nil, err
	}

	// get all users to list
	jj, err := db.UserAll()
	if err != nil {
		fmt.Println("UserAll")
		return nil, err
	}

	var usrs []user.User
	for i := range jj {
		var u user.User
		err = json.Unmarshal(jj[i], &u)
		if err != nil {
			fmt.Println("Unmarshal in jj", jj[i])
			return nil, err
		}
		if u.Email != usr.Email {
			usrs = append(usrs, u)
		}
	}

	// make buffer to execute html into then pass buffer's bytes to Admin
	buf := &bytes.Buffer{}
	tmpl := template.Must(template.New("users").Parse(html + script))
	data := map[string]interface{}{
		"User":  usr,
		"Users": usrs,
	}

	err = tmpl.Execute(buf, data)
	if err != nil {
		fmt.Println("Execute")
		return nil, err
	}

	view, err := Admin(buf.Bytes())
	if err != nil {
		return nil, err
	}

	return view, nil
}

var err400HTML = `

400 Error: Bad Request

Sorry, the request was unable to be completed.

`

// Error400 creates a subview for a 400 error page
func Error400() ([]byte, error) {
	view, err := Admin([]byte(err400HTML))
	if err != nil {
		return nil, err
	}

	return view, nil
}

var err404HTML = `

404 Error: Not Found

Sorry, the page you requested could not be found.

`

// Error404 creates a subview for a 404 error page
func Error404() ([]byte, error) {
	view, err := Admin([]byte(err404HTML))
	if err != nil {
		return nil, err
	}

	return view, nil
}

var err405HTML = `

405 Error: Method Not Allowed

Sorry, the page you requested could not be found.

`

// Error405 creates a subview for a 405 error page
func Error405() ([]byte, error) {
	view, err := Admin([]byte(err405HTML))
	if err != nil {
		return nil, err
	}

	return view, nil
}

var err500HTML = `

500 Error: Internal Service Error

Sorry, something unexpectedly went wrong.

`

// Error500 creates a subview for a 500 error page
func Error500() ([]byte, error) {
	view, err := Admin([]byte(err500HTML))
	if err != nil {
		return nil, err
	}

	return view, nil
}
<commit_msg>debugging json output<commit_after>// Package admin desrcibes the admin view containing references to
// various managers and editors
package admin

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"

	"github.com/bosssauce/ponzu/content"
	"github.com/bosssauce/ponzu/system/admin/user"
	"github.com/bosssauce/ponzu/system/db"
)

var startAdminHTML = `<!doctype html>

        <title>{{ .Logo }}</title>

        <link rel="stylesheet" href="/admin/static/dashboard/css/material-icons.css" />
        <link rel="stylesheet" href="/admin/static/dashboard/css/materialize.min.css" />
        <link rel="stylesheet" href="/admin/static/editor/css/materialNote.css" />
        <link rel="stylesheet" href="/admin/static/dashboard/css/admin.css" />

        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">

                {{ .Logo }}

Logout

`

var mainAdminHTML = `

Content

                    {{ range $t, $f := .Types }}

playlist_add{{ $t }}

                    {{ end }}

System

settingsConfiguration

supervisor_accountUsers

            {{ if .Subview}}

                {{ .Subview }}

            {{ end }}`

var endAdminHTML = `

Powered by © Ponzu  |  open-sourced by Boss Sauce Creative

`

type admin struct {
	Logo    string
	Types   map[string]func() interface{}
	Subview template.HTML
}

// Admin ...
func Admin(view []byte) ([]byte, error) {
	cfg, err := db.Config("name")
	if err != nil {
		return nil, err
	}

	if cfg == nil {
		cfg = []byte("")
	}

	a := admin{
		Logo:    string(cfg),
		Types:   content.Types,
		Subview: template.HTML(view),
	}

	buf := &bytes.Buffer{}
	html := startAdminHTML + mainAdminHTML + endAdminHTML
	tmpl := template.Must(template.New("admin").Parse(html))
	err = tmpl.Execute(buf, a)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

var initAdminHTML = `

Welcome!

You need to initialize your system by filling out the form below. All of
    this information can be updated later on, but you will not be able to start
    without first completing this step.

    <form method="post" action="/admin/init" class="row">

Configuration

            <input placeholder="Enter the name of your site (interal use only)" class="validate required" type="text" id="name" name="name"/>
            Site Name

            <input placeholder="Used for acquiring SSL certificate (e.g. www.example.com or  example.com)" class="validate" type="text" id="domain" name="domain"/>
            Domain

Admin Details

            <input placeholder="Your email address e.g. you@example.com" class="validate required" type="email" id="email" name="email"/>
            Email

            <input placeholder="Enter a strong password" class="validate required" type="password" id="password" name="password"/>
            Password

        <button class="btn waves-effect waves-light right">Start</button>
    </form>

`

// Init ...
func Init() ([]byte, error) {
	html := startAdminHTML + initAdminHTML + endAdminHTML

	cfg, err := db.Config("name")
	if err != nil {
		return nil, err
	}

	if cfg == nil {
		cfg = []byte("")
	}

	a := admin{
		Logo: string(cfg),
	}

	buf := &bytes.Buffer{}
	tmpl := template.Must(template.New("init").Parse(html))
	err = tmpl.Execute(buf, a)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

var loginAdminHTML = `

Welcome!

Please log in to the system using your email address and password.

    <form method="post" action="/admin/login" class="row">

            <input placeholder="Enter your email address e.g. you@example.com" class="validate required" type="email" id="email" name="email"/>
            Email

            <input placeholder="Enter your password" class="validate required" type="password" id="password" name="password"/>
            Password

        <button class="btn waves-effect waves-light right">Log in</button>
    </form>

`

// Login ...
func Login() ([]byte, error) {
	html := startAdminHTML + loginAdminHTML + endAdminHTML

	cfg, err := db.Config("name")
	if err != nil {
		return nil, err
	}

	if cfg == nil {
		cfg = []byte("")
	}

	a := admin{
		Logo: string(cfg),
	}

	buf := &bytes.Buffer{}
	tmpl := template.Must(template.New("login").Parse(html))
	err = tmpl.Execute(buf, a)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// UsersList ...
func UsersList(req *http.Request) ([]byte, error) {
	html := `

Edit your account:

        <form class="row" enctype="multipart/form-data" action="/admin/configure/users/edit" method="post">

                Email Address
                <input type="email" name="email" value="{{ .User.Email }}"/>

To approve changes, enter your password:

                Current Password
                <input type="password" name="password"/>

                New Password: (leave blank if no password change needed)
                <input name="new_password" type="password"/>

                <button class="btn waves-effect waves-light green right" type="submit">Save</button>

        </form>

Add a new user:

        <form class="row" enctype="multipart/form-data" action="/admin/configure/users" method="post">

                Email Address
                <input type="email" name="email" value=""/>

                Password
                <input type="password" name="password"/>

                <button class="btn waves-effect waves-light green right" type="submit">Add User</button>

        </form>

Remove Admin Users

            {{ range .Users }}

                {{ .Email }}
                <form enctype="multipart/form-data" class="delete-user __ponzu right" action="/admin/configure/users/delete" method="post">
                    Delete
                    <input type="hidden" name="email" value="{{ .Email }}"/>
                    <input type="hidden" name="id" value="{{ .ID }}"/>
                </form>

            {{ end }}

    `
	script := `

    `
	// get current user out to pass as data to execute template
	j, err := db.CurrentUser(req)
	if err != nil {
		fmt.Println("CurrentUser")
		return nil, err
	}

	fmt.Println(string(j))

	var usr user.User
	err = json.Unmarshal(j, &usr)
	if err != nil {
		fmt.Println("Unmarshal usr")
		return nil, err
	}

	// get all users to list
	jj, err := db.UserAll()
	if err != nil {
		fmt.Println("UserAll")
		return nil, err
	}

	var usrs []user.User
	for i := range jj {
		var u user.User
		err = json.Unmarshal(jj[i], &u)
		if err != nil {
			fmt.Println("Unmarshal in jj", jj[i])
			return nil, err
		}
		if u.Email != usr.Email {
			usrs = append(usrs, u)
		}
	}

	// make buffer to execute html into then pass buffer's bytes to Admin
	buf := &bytes.Buffer{}
	tmpl := template.Must(template.New("users").Parse(html + script))
	data := map[string]interface{}{
		"User":  usr,
		"Users": usrs,
	}

	err = tmpl.Execute(buf, data)
	if err != nil {
		fmt.Println("Execute")
		return nil, err
	}

	view, err := Admin(buf.Bytes())
	if err != nil {
		return nil, err
	}

	return view, nil
}

var err400HTML = `

400 Error: Bad Request

Sorry, the request was unable to be completed.

`

// Error400 creates a subview for a 400 error page
func Error400() ([]byte, error) {
	view, err := Admin([]byte(err400HTML))
	if err != nil {
		return nil, err
	}

	return view, nil
}

var err404HTML = `

404 Error: Not Found

Sorry, the page you requested could not be found.

`

// Error404 creates a subview for a 404 error page
func Error404() ([]byte, error) {
	view, err := Admin([]byte(err404HTML))
	if err != nil {
		return nil, err
	}

	return view, nil
}

var err405HTML = `

405 Error: Method Not Allowed

Sorry, the page you requested could not be found.

`

// Error405 creates a subview for a 405 error page
func Error405() ([]byte, error) {
	view, err := Admin([]byte(err405HTML))
	if err != nil {
		return nil, err
	}

	return view, nil
}

var err500HTML = `

500 Error: Internal Service Error

Sorry, something unexpectedly went wrong.

`

// Error500 creates a subview for a 500 error page
func Error500() ([]byte, error) {
	view, err := Admin([]byte(err500HTML))
	if err != nil {
		return nil, err
	}

	return view, nil
}
<|endoftext|>


<commit_before>package dag

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"reflect"
	"sync"

	"github.com/golang/protobuf/jsonpb"
	"github.com/n0stack/n0stack/n0proto/deployment/v0"
	"github.com/n0stack/n0stack/n0proto/pool/v0"
	"github.com/n0stack/n0stack/n0proto/provisioning/v0"

	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc"
)

var Marshaler = &jsonpb.Marshaler{
	EnumsAsInts:  true,
	EmitDefaults: false,
	OrigName:     true,
}

type Task struct {
	ResourceType string                 `yaml:"resource_type"`
	Action       string                 `yaml:"action"`
	Args         map[string]interface{} `yaml:"args"`
	DependOn     []string               `yaml:"depend_on"`
	// Rollback []*Task `yaml:"rollback"`

	child   []string
	depends int
}

// return response JSON bytes
func (a Task) Do(conn *grpc.ClientConn) (proto.Message, error) {
	var grpcCliType reflect.Type
	var grpcCliValue reflect.Value

	// TODO :生成自動化
	switch a.ResourceType {
	case "node", "Node":
		grpcCliType = reflect.TypeOf(ppool.NewNodeServiceClient(conn))
		grpcCliValue = reflect.ValueOf(ppool.NewNodeServiceClient(conn))
	case "network", "Network":
		grpcCliType = reflect.TypeOf(ppool.NewNetworkServiceClient(conn))
		grpcCliValue = reflect.ValueOf(ppool.NewNetworkServiceClient(conn))
	case "block_storage", "BlockStorage":
		grpcCliType = reflect.TypeOf(pprovisioning.NewBlockStorageServiceClient(conn))
		grpcCliValue = reflect.ValueOf(pprovisioning.NewBlockStorageServiceClient(conn))
	case "virtual_machine", "VirtualMachine":
		grpcCliType = reflect.TypeOf(pprovisioning.NewBlockStorageServiceClient(conn))
		grpcCliValue = reflect.ValueOf(pprovisioning.NewBlockStorageServiceClient(conn))
	case "image", "Image":
		grpcCliType = reflect.TypeOf(pdeployment.NewImageServiceClient(conn))
		grpcCliValue = reflect.ValueOf(pdeployment.NewImageServiceClient(conn))
	case "flavor", "Flavor":
		grpcCliType = reflect.TypeOf(pdeployment.NewFlavorServiceClient(conn))
		grpcCliValue = reflect.ValueOf(pdeployment.NewFlavorServiceClient(conn))
	default:
		return nil, fmt.Errorf("Resource type '%s' do not exist", a.ResourceType)
	}

	fnt, ok := grpcCliType.MethodByName(a.Action)
	if !ok {
		return nil, fmt.Errorf("Resource type '%s' do not have action '%s'", a.ResourceType, a.Action)
	}

	// 1st arg is instance, 2nd is context.Background()
	// TODO: 何かがおかしい、 argsElem is "**SomeMessage", so use argsElem.Elem() in Call
	argsType := fnt.Type.In(2)
	argsElem := reflect.New(argsType)
	if a.Args == nil {
		a.Args = make(map[string]interface{})
	}
	buf, err := json.Marshal(a.Args)
	if err != nil {
		return nil, fmt.Errorf("Args is invalid, set fields of message '%s' err=%s", argsType.String(), err.Error())
	}
	if err := json.Unmarshal(buf, argsElem.Interface()); err != nil {
		return nil, fmt.Errorf("Args is invalid, set fields of message '%s' err=%s", argsType.String(), err.Error())
	}

	out := fnt.Func.Call([]reflect.Value{grpcCliValue, reflect.ValueOf(context.Background()), argsElem.Elem()})
	if err, _ := out[1].Interface().(error); err != nil {
		return nil, fmt.Errorf("got error response: %s", err.Error())
	}

	return out[0].Interface().(proto.Message), nil
}

// topological sort
// 実際遅いけどもういいや O(E^2 + V)
func CheckDAG(tasks map[string]*Task) error {
	result := 0

	for k, _ := range tasks {
		tasks[k].child = make([]string, 0)
		tasks[k].depends = len(tasks[k].DependOn)
	}

	for k, v := range tasks {
		for _, d := range v.DependOn {
			if _, ok := tasks[d]; !ok {
				return fmt.Errorf("Depended task '%s' do not exist", d)
			}

			tasks[d].child = append(tasks[d].child, k)
		}
	}

	s := make([]string, 0, len(tasks))
	for k, v := range tasks {
		if v.depends == 0 {
			s = append(s, k)
			result++
		}
	}

	for len(s) != 0 {
		n := s[len(s)-1]
		s = s[:len(s)-1]

		for _, c := range tasks[n].child {
			tasks[c].depends--
			if tasks[c].depends == 0 {
				s = append(s, c)
				result++
			}
		}
	}

	if result != len(tasks) {
		return fmt.Errorf("This request is not DAG")
	}

	return nil
}

type ActionResult struct {
	Name string
	Res  proto.Message
	Err  error
}

// 出力で時間を出したほうがよさそう
func DoDAG(tasks map[string]*Task, out io.Writer, conn *grpc.ClientConn) bool {
	for k, _ := range tasks {
		tasks[k].child = make([]string, 0)
		tasks[k].depends = len(tasks[k].DependOn)
	}

	for k, v := range tasks {
		for _, d := range v.DependOn {
			tasks[d].child = append(tasks[d].child, k)
		}
	}

	resultChan := make(chan ActionResult, 100)
	wg := new(sync.WaitGroup)
	total := len(tasks)
	done := 0

	doTask := func(taskName string) {
		defer wg.Done()

		result, err := tasks[taskName].Do(conn)
		resultChan <- ActionResult{
			Name: taskName,
			Res:  result,
			Err:  err,
		}
	}

	for k, v := range tasks {
		if v.depends == 0 {
			wg.Add(1)
			fmt.Fprintf(out, "---> Task '%s' is started\n", k)
			log.Printf("[DEBUG] Task '%s' is started: %+v", k, v)
			go doTask(k)
		}
	}

	failed := false
	for r := range resultChan {
		done++

		if r.Err != nil {
			fmt.Fprintf(out, "---> [ %d/%d ] Task '%s' is failed: %s\n", done, total, r.Name, r.Err.Error())

			if !failed {
				failed = true

				// すでにリクエストしたタスクの終了を待つ
				fmt.Fprintf(out, "---> Wait to finish requested tasks\n")
				go func() {
					wg.Wait()
					close(resultChan)
				}()
			}
		} else {
			res, _ := Marshaler.MarshalToString(r.Res)

			if failed {
				fmt.Fprintf(out, "---> [ %d/%d ] Task '%s', which was requested until failed, is finished\n--- Response ---\n%s\n", done, total, r.Name, res)
			} else {
				fmt.Fprintf(out, "---> [ %d/%d ] Task '%s' is finished\n--- Response ---\n%s\n", done, total, r.Name, res)

				// queueing
				for _, d := range tasks[r.Name].child {
					tasks[d].depends--
					if tasks[d].depends == 0 {
						wg.Add(1)
						fmt.Fprintf(out, "---> Task '%s' is started\n", d)
						log.Printf("[DEBUG] Task '%s' is started: %+v", d, tasks[d])
						go doTask(d)
					}
				}
			}
		}

		if !failed && done == total {
			close(resultChan)
		}
	}

	if failed {
		// TODO: rollback

		return false
	}

	return true
}
<commit_msg>typo in DAG<commit_after>package dag

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"reflect"
	"sync"

	"github.com/golang/protobuf/jsonpb"
	"github.com/n0stack/n0stack/n0proto/deployment/v0"
	"github.com/n0stack/n0stack/n0proto/pool/v0"
	"github.com/n0stack/n0stack/n0proto/provisioning/v0"

	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc"
)

var Marshaler = &jsonpb.Marshaler{
	EnumsAsInts:  true,
	EmitDefaults: false,
	OrigName:     true,
}

type Task struct {
	ResourceType string                 `yaml:"resource_type"`
	Action       string                 `yaml:"action"`
	Args         map[string]interface{} `yaml:"args"`
	DependOn     []string               `yaml:"depend_on"`
	// Rollback []*Task `yaml:"rollback"`

	child   []string
	depends int
}

// return response JSON bytes
func (a Task) Do(conn *grpc.ClientConn) (proto.Message, error) {
	var grpcCliType reflect.Type
	var grpcCliValue reflect.Value

	// TODO :生成自動化
	switch a.ResourceType {
	case "node", "Node":
		grpcCliType = reflect.TypeOf(ppool.NewNodeServiceClient(conn))
		grpcCliValue = reflect.ValueOf(ppool.NewNodeServiceClient(conn))
	case "network", "Network":
		grpcCliType = reflect.TypeOf(ppool.NewNetworkServiceClient(conn))
		grpcCliValue = reflect.ValueOf(ppool.NewNetworkServiceClient(conn))
	case "block_storage", "BlockStorage":
		grpcCliType = reflect.TypeOf(pprovisioning.NewBlockStorageServiceClient(conn))
		grpcCliValue = reflect.ValueOf(pprovisioning.NewBlockStorageServiceClient(conn))
	case "virtual_machine", "VirtualMachine":
		grpcCliType = reflect.TypeOf(pprovisioning.NewVirtualMachineServiceClient(conn))
		grpcCliValue = reflect.ValueOf(pprovisioning.NewVirtualMachineServiceClient(conn))
	case "image", "Image":
		grpcCliType = reflect.TypeOf(pdeployment.NewImageServiceClient(conn))
		grpcCliValue = reflect.ValueOf(pdeployment.NewImageServiceClient(conn))
	case "flavor", "Flavor":
		grpcCliType = reflect.TypeOf(pdeployment.NewFlavorServiceClient(conn))
		grpcCliValue = reflect.ValueOf(pdeployment.NewFlavorServiceClient(conn))
	default:
		return nil, fmt.Errorf("Resource type '%s' do not exist", a.ResourceType)
	}

	fnt, ok := grpcCliType.MethodByName(a.Action)
	if !ok {
		return nil, fmt.Errorf("Resource type '%s' do not have action '%s'", a.ResourceType, a.Action)
	}

	// 1st arg is instance, 2nd is context.Background()
	// TODO: 何かがおかしい、 argsElem is "**SomeMessage", so use argsElem.Elem() in Call
	argsType := fnt.Type.In(2)
	argsElem := reflect.New(argsType)
	if a.Args == nil {
		a.Args = make(map[string]interface{})
	}
	buf, err := json.Marshal(a.Args)
	if err != nil {
		return nil, fmt.Errorf("Args is invalid, set fields of message '%s' err=%s", argsType.String(), err.Error())
	}
	if err := json.Unmarshal(buf, argsElem.Interface()); err != nil {
		return nil, fmt.Errorf("Args is invalid, set fields of message '%s' err=%s", argsType.String(), err.Error())
	}

	out := fnt.Func.Call([]reflect.Value{grpcCliValue, reflect.ValueOf(context.Background()), argsElem.Elem()})
	if err, _ := out[1].Interface().(error); err != nil {
		return nil, fmt.Errorf("got error response: %s", err.Error())
	}

	return out[0].Interface().(proto.Message), nil
}

// topological sort
// 実際遅いけどもういいや O(E^2 + V)
func CheckDAG(tasks map[string]*Task) error {
	result := 0

	for k, _ := range tasks {
		tasks[k].child = make([]string, 0)
		tasks[k].depends = len(tasks[k].DependOn)
	}

	for k, v := range tasks {
		for _, d := range v.DependOn {
			if _, ok := tasks[d]; !ok {
				return fmt.Errorf("Depended task '%s' do not exist", d)
			}

			tasks[d].child = append(tasks[d].child, k)
		}
	}

	s := make([]string, 0, len(tasks))
	for k, v := range tasks {
		if v.depends == 0 {
			s = append(s, k)
			result++
		}
	}

	for len(s) != 0 {
		n := s[len(s)-1]
		s = s[:len(s)-1]

		for _, c := range tasks[n].child {
			tasks[c].depends--
			if tasks[c].depends == 0 {
				s = append(s, c)
				result++
			}
		}
	}

	if result != len(tasks) {
		return fmt.Errorf("This request is not DAG")
	}

	return nil
}

type ActionResult struct {
	Name string
	Res  proto.Message
	Err  error
}

// 出力で時間を出したほうがよさそう
func DoDAG(tasks map[string]*Task, out io.Writer, conn *grpc.ClientConn) bool {
	for k, _ := range tasks {
		tasks[k].child = make([]string, 0)
		tasks[k].depends = len(tasks[k].DependOn)
	}

	for k, v := range tasks {
		for _, d := range v.DependOn {
			tasks[d].child = append(tasks[d].child, k)
		}
	}

	resultChan := make(chan ActionResult, 100)
	wg := new(sync.WaitGroup)
	total := len(tasks)
	done := 0

	doTask := func(taskName string) {
		defer wg.Done()

		result, err := tasks[taskName].Do(conn)
		resultChan <- ActionResult{
			Name: taskName,
			Res:  result,
			Err:  err,
		}
	}

	for k, v := range tasks {
		if v.depends == 0 {
			wg.Add(1)
			fmt.Fprintf(out, "---> Task '%s' is started\n", k)
			log.Printf("[DEBUG] Task '%s' is started: %+v", k, v)
			go doTask(k)
		}
	}

	failed := false
	for r := range resultChan {
		done++

		if r.Err != nil {
			fmt.Fprintf(out, "---> [ %d/%d ] Task '%s' is failed: %s\n", done, total, r.Name, r.Err.Error())

			if !failed {
				failed = true

				// すでにリクエストしたタスクの終了を待つ
				fmt.Fprintf(out, "---> Wait to finish requested tasks\n")
				go func() {
					wg.Wait()
					close(resultChan)
				}()
			}
		} else {
			res, _ := Marshaler.MarshalToString(r.Res)

			if failed {
				fmt.Fprintf(out, "---> [ %d/%d ] Task '%s', which was requested until failed, is finished\n--- Response ---\n%s\n", done, total, r.Name, res)
			} else {
				fmt.Fprintf(out, "---> [ %d/%d ] Task '%s' is finished\n--- Response ---\n%s\n", done, total, r.Name, res)

				// queueing
				for _, d := range tasks[r.Name].child {
					tasks[d].depends--
					if tasks[d].depends == 0 {
						wg.Add(1)
						fmt.Fprintf(out, "---> Task '%s' is started\n", d)
						log.Printf("[DEBUG] Task '%s' is started: %+v", d, tasks[d])
						go doTask(d)
					}
				}
			}
		}

		if !failed && done == total {
			close(resultChan)
		}
	}

	if failed {
		// TODO: rollback

		return false
	}

	return true
}
<|endoftext|>


<commit_before>package test

import (
  "api"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"encoding/json"
)

func execShowSpaList() *api.Spas {
  //ShowSpaList実行
	req, _ := http.NewRequest("GET", "v1/spas", nil)
	res := httptest.NewRecorder()
	api.ShowSpaList(res, req)

  //レスポンスを構造体に変換
	data, _ := ioutil.ReadAll(res.Body)
	obj := new(api.Spas)
	json.Unmarshal(([]byte)(string(data)), obj)
  return obj
}

func TestShowSpaList(t *testing.T) {
  db.Query("INSERT INTO spa (name, address) VALUES(?, ?)", "木下温泉", "北海道")
  db.Query("INSERT INTO spa (name, address) VALUES(?, ?)", "木下温泉2", "北海道2")

  result := execShowSpaList()

  assetEqual(t, result.Spas[0].Name,    "木下温泉")
  assetEqual(t, result.Spas[0].Address, "北海道")
  assetEqual(t, result.Spas[1].Name,    "木下温泉2")
  assetEqual(t, result.Spas[1].Address, "北海道2")
}
<commit_msg>show spa list リファクタリング<commit_after>package test

import (
  "api"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
	"encoding/json"
)

func execShowSpaList() *api.Spas {
  //ShowSpaList実行
	req, _ := http.NewRequest("GET", "v1/spas", nil)
	res := httptest.NewRecorder()
	api.ShowSpaList(res, req)

  //レスポンスを構造体に変換
	data, _ := ioutil.ReadAll(res.Body)
	spas := new(api.Spas)
	json.Unmarshal(([]byte)(string(data)), spas)
  return spas
}

func TestShowSpaList(t *testing.T) {
  db.Query("INSERT INTO spa (name, address) VALUES(?, ?)", "木下温泉", "北海道")
  db.Query("INSERT INTO spa (name, address) VALUES(?, ?)", "木下温泉2", "北海道2")

  result := execShowSpaList()

  assetEqual(t, result.Spas[0].Name,    "木下温泉")
  assetEqual(t, result.Spas[0].Address, "北海道")
  assetEqual(t, result.Spas[1].Name,    "木下温泉2")
  assetEqual(t, result.Spas[1].Address, "北海道2")
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package codec

import (
	"bytes"
	"runtime"
	"unsafe"

	"github.com/juju/errors"
)

// EncodeBytes encodes the slice value using an escape based encoding,
// with the following rule:
//  \x00 -> \x00\xFF
//  \xFF -> \xFF\x00 if the first byte is \xFF
// EncodeBytes will append \x00\x01 at the end of the encoded value to
// indicate the termination.
// EncodeBytes guarantees the encoded value is in ascending order for comparison,
// The encoded value is >= SmallestNoneNilValue and < InfiniteValue.
func EncodeBytes(b []byte, data []byte) []byte {
	// Allocate more space to avoid unnecessary slice growing
	bs := reallocBytes(b, len(data)+20)
	if len(data) > 0 && data[0] == 0xFF {
		// we must escape 0xFF here to guarantee encoded value < InfiniteValue \xFF\xFF.
		bs = append(bs, 0xFF, 0x00)
		data = data[1:]
	}

	for {
		// find 0x00 and escape it.
		i := bytes.IndexByte(data, 0x00)
		if i == -1 {
			break
		}
		bs = append(bs, data[:i]...)
		bs = append(bs, 0x00, 0xFF)
		data = data[i+1:]
	}
	bs = append(bs, data...)
	return append(bs, 0x00, 0x01)
}

// DecodeBytes decodes bytes which is encoded by EncodeBytes before,
// returns the leftover bytes and decoded value if no error.
func DecodeBytes(b []byte) ([]byte, []byte, error) {
	return decodeBytes(b, 0xFF, 0x00, 0x01)
}

func decodeBytes(b []byte, escapeFirst byte, escape byte, term byte) ([]byte, []byte, error) {
	if len(b) < 2 {
		return nil, nil, errors.Errorf("insufficient bytes to decode value")
	}

	var r []byte

	if b[0] == escapeFirst {
		if b[1] != ^escapeFirst {
			return nil, nil, errors.Errorf("invalid escape byte, must 0x%x, but 0x%x", ^escapeFirst, b[1])
		}
		r = append(r, escapeFirst)
		b = b[2:]
	}

	for {
		i := bytes.IndexByte(b, escape)
		if i == -1 {
			return nil, nil, errors.Errorf("invalid termination in bytes")
		}

		if i+1 >= len(b) {
			return nil, nil, errors.Errorf("malformed escaped bytes")
		}

		if b[i+1] == term {
			if r == nil {
				r = b[:i]
			} else {
				r = append(r, b[:i]...)
			}

			return b[i+2:], r, nil
		}

		if b[i+1] != ^escape {
			return nil, nil, errors.Errorf("invalid escape byte, must 0x%x, but got 0x%0x", ^escape, b[i+1])
		}

		// here mean we may have \x00 in origin slice, so realloc a large buffer
		// to avoid relloaction again, the final decoded slice length is < len(b) certainly.
		// TODO: we can record the escape offset and then do the alloc + copy in the end.
		r = reallocBytes(r, len(b))
		r = append(r, b[:i]...)
		r = append(r, escape)
		b = b[i+2:]
	}
}

// EncodeBytesDesc first encodes bytes using EncodeBytes, then bitwise reverses
// encoded value to guarentee the encoded value is in descending order for comparison,
// The encoded value is >= SmallestNoneNilValue and < InfiniteValue.
func EncodeBytesDesc(b []byte, data []byte) []byte {
	n := len(b)
	b = EncodeBytes(b, data)
	reverseBytes(b[n:])
	return b
}

// DecodeBytesDesc decodes bytes which is encoded by EncodeBytesDesc before,
// returns the leftover bytes and decoded value if no error.
func DecodeBytesDesc(b []byte) ([]byte, []byte, error) {
	var (
		r   []byte
		err error
	)
	b, r, err = decodeBytes(b, 0x00, 0xFF, ^byte(0x01))
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	reverseBytes(r)
	return b, r, nil
}

// See https://golang.org/src/crypto/cipher/xor.go
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64"

func fastReverseBytes(b []byte) {
	n := len(b)
	w := n / wordSize
	if w > 0 {
		bw := *(*[]uintptr)(unsafe.Pointer(&b))
		for i := 0; i < w; i++ {
			bw[i] = ^bw[i]
		}
	}

	for i := w * wordSize; i < n; i++ {
		b[i] = ^b[i]
	}
}

func safeReverseBytes(b []byte) {
	for i := range b {
		b[i] = ^b[i]
	}
}

func reverseBytes(b []byte) {
	if supportsUnaligned {
		fastReverseBytes(b)
		return
	}

	safeReverseBytes(b)
}

// like realloc.
func reallocBytes(b []byte, n int) []byte {
	if cap(b) < n {
		bs := make([]byte, len(b), len(b)+n)
		copy(bs, b)
		return bs
	}

	// slice b has capability to store n bytes
	return b
}
<commit_msg>codec: fix typo<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package codec

import (
	"bytes"
	"runtime"
	"unsafe"

	"github.com/juju/errors"
)

// EncodeBytes encodes the slice value using an escape based encoding,
// with the following rule:
//  \x00 -> \x00\xFF
//  \xFF -> \xFF\x00 if the first byte is \xFF
// EncodeBytes will append \x00\x01 at the end of the encoded value to
// indicate the termination.
// EncodeBytes guarantees the encoded value is in ascending order for comparison,
// The encoded value is >= SmallestNoneNilValue and < InfiniteValue.
func EncodeBytes(b []byte, data []byte) []byte {
	// Allocate more space to avoid unnecessary slice growing
	bs := reallocBytes(b, len(data)+20)
	if len(data) > 0 && data[0] == 0xFF {
		// we must escape 0xFF here to guarantee encoded value < InfiniteValue \xFF\xFF.
		bs = append(bs, 0xFF, 0x00)
		data = data[1:]
	}

	for {
		// find 0x00 and escape it.
		i := bytes.IndexByte(data, 0x00)
		if i == -1 {
			break
		}
		bs = append(bs, data[:i]...)
		bs = append(bs, 0x00, 0xFF)
		data = data[i+1:]
	}
	bs = append(bs, data...)
	return append(bs, 0x00, 0x01)
}

// DecodeBytes decodes bytes which is encoded by EncodeBytes before,
// returns the leftover bytes and decoded value if no error.
func DecodeBytes(b []byte) ([]byte, []byte, error) {
	return decodeBytes(b, 0xFF, 0x00, 0x01)
}

func decodeBytes(b []byte, escapeFirst byte, escape byte, term byte) ([]byte, []byte, error) {
	if len(b) < 2 {
		return nil, nil, errors.Errorf("insufficient bytes to decode value")
	}

	var r []byte

	if b[0] == escapeFirst {
		if b[1] != ^escapeFirst {
			return nil, nil, errors.Errorf("invalid escape byte, must 0x%x, but 0x%x", ^escapeFirst, b[1])
		}
		r = append(r, escapeFirst)
		b = b[2:]
	}

	for {
		i := bytes.IndexByte(b, escape)
		if i == -1 {
			return nil, nil, errors.Errorf("invalid termination in bytes")
		}

		if i+1 >= len(b) {
			return nil, nil, errors.Errorf("malformed escaped bytes")
		}

		if b[i+1] == term {
			if r == nil {
				r = b[:i]
			} else {
				r = append(r, b[:i]...)
			}

			return b[i+2:], r, nil
		}

		if b[i+1] != ^escape {
			return nil, nil, errors.Errorf("invalid escape byte, must 0x%x, but got 0x%0x", ^escape, b[i+1])
		}

		// here mean we have \x00 in origin slice, so realloc a large buffer
		// to avoid reallocation again, the final decoded slice length is < len(b) certainly.
		// TODO: we can record the escape offset and then do the alloc + copy in the end.
		r = reallocBytes(r, len(b))
		r = append(r, b[:i]...)
		r = append(r, escape)
		b = b[i+2:]
	}
}

// EncodeBytesDesc first encodes bytes using EncodeBytes, then bitwise reverses
// encoded value to guarentee the encoded value is in descending order for comparison,
// The encoded value is >= SmallestNoneNilValue and < InfiniteValue.
func EncodeBytesDesc(b []byte, data []byte) []byte {
	n := len(b)
	b = EncodeBytes(b, data)
	reverseBytes(b[n:])
	return b
}

// DecodeBytesDesc decodes bytes which is encoded by EncodeBytesDesc before,
// returns the leftover bytes and decoded value if no error.
func DecodeBytesDesc(b []byte) ([]byte, []byte, error) {
	var (
		r   []byte
		err error
	)
	b, r, err = decodeBytes(b, 0x00, 0xFF, ^byte(0x01))
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	reverseBytes(r)
	return b, r, nil
}

// See https://golang.org/src/crypto/cipher/xor.go
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64"

func fastReverseBytes(b []byte) {
	n := len(b)
	w := n / wordSize
	if w > 0 {
		bw := *(*[]uintptr)(unsafe.Pointer(&b))
		for i := 0; i < w; i++ {
			bw[i] = ^bw[i]
		}
	}

	for i := w * wordSize; i < n; i++ {
		b[i] = ^b[i]
	}
}

func safeReverseBytes(b []byte) {
	for i := range b {
		b[i] = ^b[i]
	}
}

func reverseBytes(b []byte) {
	if supportsUnaligned {
		fastReverseBytes(b)
		return
	}

	safeReverseBytes(b)
}

// like realloc.
func reallocBytes(b []byte, n int) []byte {
	if cap(b) < n {
		bs := make([]byte, len(b), len(b)+n)
		copy(bs, b)
		return bs
	}

	// slice b has capability to store n bytes
	return b
}
<|endoftext|>


<commit_before>package ss
// Is ss too short?
// Oh well.. http://golang.org/doc/effective_go.html#package-names
// Alias it if it bugs you

import (
  "errors"
  "fmt"
  "net"
  "os"
  "strconv"
  "strings"
)

type ListenTarget struct {
  Name  string // host:port | port. Currently unix sockets are not supported
  Fd    uintptr
}

func Ports() ([]ListenTarget, error) {
  ssport := os.Getenv("SERVER_STARTER_PORT")
  if ssport == "" {
    return nil, errors.New("No environment variable SERVER_STARTER_PORT available")
  }

  return ParsePorts(ssport)
}

func ParsePorts(ssport string) ([]ListenTarget, error) {
  ret := []ListenTarget{}
  for _, pairstring := range strings.Split(ssport, ";") {
    pair := strings.Split(pairstring, "=")
    port, err := strconv.ParseUint(pair[1], 10, 0)
    if err != nil {
      return nil, errors.New(fmt.Sprintf("Failed to parse '%s'", pairstring))
    }
    ret = append(ret, ListenTarget { pair[0], uintptr(port) })
  }
  return ret, nil
}

func NewListener() (net.Listener, error) {
  portmap, err := Ports()
  if err != nil {
    return nil, err
  }
  return ListenOn(portmap[0])
}

func AllListeners() ([]net.Listener, error) {
  portmap, err := Ports()
  if err != nil {
    return nil, err
  }
  return ListenersIn(portmap)
}

func ListenersIn (list []ListenTarget) ([]net.Listener, error) {
  ret := []net.Listener {}
  for _, t := range list {
    l, err := ListenOn(t)
    if err != nil {
      return nil, err
    }
    ret = append(ret, l)
  }
  return ret, nil
}

func ListenOn (t ListenTarget) (net.Listener, error) {
  f := os.NewFile(t.Fd, t.Name)
  return net.FileListener(f)
}
<commit_msg>Added docs, changed ListenOn to NewListenerOn, and ListenersIn to NewListenersOn<commit_after>package ss
// Is ss too short?
// Oh well.. http://golang.org/doc/effective_go.html#package-names
// Alias it if it bugs you

import (
  "errors"
  "fmt"
  "net"
  "os"
  "strconv"
  "strings"
)

type ListenTarget struct {
  Name  string // host:port | port. Currently unix sockets are not supported
  Fd    uintptr
}

/*
 * Parses SERVER_STARTER_PORT environment variable, and returns a list of
 * of ListenTarget structs that can be passed to NewListenerOn()
 */
func Ports() ([]ListenTarget, error) {
  ssport := os.Getenv("SERVER_STARTER_PORT")
  if ssport == "" {
    return nil, errors.New("No environment variable SERVER_STARTER_PORT available")
  }

  return ParsePorts(ssport)
}

/*
 * Parses the given string and returns a list of
 * of ListenTarget structs that can be passed to NewListenerOn()
 */
func ParsePorts(ssport string) ([]ListenTarget, error) {
  ret := []ListenTarget{}
  for _, pairstring := range strings.Split(ssport, ";") {
    pair := strings.Split(pairstring, "=")
    port, err := strconv.ParseUint(pair[1], 10, 0)
    if err != nil {
      return nil, errors.New(fmt.Sprintf("Failed to parse '%s'", pairstring))
    }
    ret = append(ret, ListenTarget { pair[0], uintptr(port) })
  }
  return ret, nil
}

/*
 * Creates a new listener from SERVER_STARTER_PORT environment variable
 *
 * Note that this binds to only ONE file descriptor (the first one found)
 */
func NewListener() (net.Listener, error) {
  portmap, err := Ports()
  if err != nil {
    return nil, err
  }
  return NewListenerOn(portmap[0])
}

/*
 * Creates new listeners from SERVER_STARTER_PORT environment variable.
 *
 * This binds to ALL file descriptors in SERVER_STARTER_PORT
 */
func AllListeners() ([]net.Listener, error) {
  portmap, err := Ports()
  if err != nil {
    return nil, err
  }
  return NewListenersOn(portmap)
}

/*
 * Given a list of ListenTargets, creates listeners for each one
 */
func NewListenersOn (list []ListenTarget) ([]net.Listener, error) {
  ret := []net.Listener {}
  for _, t := range list {
    l, err := NewListenerOn(t)
    if err != nil {
      return nil, err
    }
    ret = append(ret, l)
  }
  return ret, nil
}

/*
 * Given a ListenTarget, creates a listener
 */
func NewListenerOn (t ListenTarget) (net.Listener, error) {
  f := os.NewFile(t.Fd, t.Name)
  return net.FileListener(f)
}
<|endoftext|>


<commit_before>package main

import (
	"log"

	docker "github.com/fsouza/go-dockerclient"
)

type Listener struct {
	client   *docker.Client
	chEvents chan *docker.APIEvents
	gw       *Gateway
}

func NewListener(client *docker.Client, gw *Gateway) *Listener {
	return &Listener{
		client,
		make(chan *docker.APIEvents),
		gw,
	}
}

func (l *Listener) Init() {
	l.gw.Flush()

	err := l.gw.Load()
	if err != nil {
		log.Println(err)
	}
}

func (l *Listener) Start() error {
	err := l.client.AddEventListener(l.chEvents)
	if err != nil {
		return err
	}

	for {
		event := <-l.chEvents
		if event == nil {
			continue
		}

		l.handleEvent(event)
	}

	return nil
}

func (l *Listener) handleEvent(event *docker.APIEvents) {
	if event == nil {
		return
	}

	switch event.Status {
	case "start":
		container, err := l.client.InspectContainer(event.ID)
		if err == nil {
			l.gw.Remove(container)
			l.gw.Add(container)
		} else {
			log.Println(err)
		}
	case "stop", "destroy", "kill", "die":
		container, err := l.client.InspectContainer(event.ID)

		if err == nil {
			l.gw.Remove(container)
		} else {
			// Delete by ID in case if container was already deleted.
			// This usually happens with `docker rm -f ID`
			l.gw.RemoveByContainerId(event.ID)
		}
	}
}
<commit_msg>Handle docker events in goroutine<commit_after>package main

import (
	"log"

	docker "github.com/fsouza/go-dockerclient"
)

type Listener struct {
	client   *docker.Client
	chEvents chan *docker.APIEvents
	gw       *Gateway
}

func NewListener(client *docker.Client, gw *Gateway) *Listener {
	return &Listener{
		client,
		make(chan *docker.APIEvents),
		gw,
	}
}

func (l *Listener) Init() {
	l.gw.Flush()

	err := l.gw.Load()
	if err != nil {
		log.Println(err)
	}
}

func (l *Listener) Start() error {
	if err := l.client.AddEventListener(l.chEvents); err != nil {
		return err
	}

	for {
		event := <-l.chEvents
		if event == nil {
			continue
		}

		go l.handleEvent(event)
	}

	return nil
}

func (l *Listener) handleEvent(event *docker.APIEvents) {
	if event == nil {
		return
	}

	switch event.Status {
	case "start":
		container, err := l.client.InspectContainer(event.ID)
		if err == nil {
			l.gw.Remove(container)
			l.gw.Add(container)
		} else {
			log.Println(err)
		}
	case "stop", "destroy", "kill", "die":
		container, err := l.client.InspectContainer(event.ID)

		if err == nil {
			l.gw.Remove(container)
		} else {
			// Delete by ID in case if container was already deleted.
			// This usually happens with `docker rm -f ID`
			l.gw.RemoveByContainerId(event.ID)
		}
	}
}
<|endoftext|>


<commit_before>package utils

import (
	"bytes"
	crand "crypto/rand"
	"errors"
	"github.com/stretchr/testify/assert"
	"image"
	"image/color"
	"image/jpeg"
	"image/png"
	"io"
	"math/rand"
	"mime/multipart"
	"net/http"
	"testing"

	"github.com/eirka/eirka-libs/config"
)

func testPng(size int) *bytes.Buffer {

	output := new(bytes.Buffer)

	myimage := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{size, size}})

	// This loop just fills the image with random data
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			c := color.RGBA{uint8(rand.Intn(255)), uint8(rand.Intn(255)), uint8(rand.Intn(255)), 255}
			myimage.Set(x, y, c)
		}
	}

	png.Encode(output, myimage)

	return output
}

func testJpeg(size int) *bytes.Buffer {

	output := new(bytes.Buffer)

	myimage := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{size, size}})

	// This loop just fills the image with random data
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			c := color.RGBA{uint8(rand.Intn(255)), uint8(rand.Intn(255)), uint8(rand.Intn(255)), 255}
			myimage.Set(x, y, c)
		}
	}

	jpeg.Encode(output, myimage, nil)

	return output
}

func testRandom() []byte {
	bytes := make([]byte, 20000)

	if _, err := io.ReadFull(crand.Reader, bytes); err != nil {
		panic(err)
	}

	return bytes
}

func formJpegRequest(size int, filename string) *http.Request {

	var b bytes.Buffer

	w := multipart.NewWriter(&b)

	fw, _ := w.CreateFormFile("file", filename)

	io.Copy(fw, testJpeg(size))

	w.Close()

	req, _ := http.NewRequest("POST", "/reply", &b)
	req.Header.Set("Content-Type", w.FormDataContentType())

	return req
}

func formRandomRequest(filename string) *http.Request {

	var b bytes.Buffer

	w := multipart.NewWriter(&b)

	fw, _ := w.CreateFormFile("file", filename)

	io.Copy(fw, bytes.NewReader(testRandom()))

	w.Close()

	req, _ := http.NewRequest("POST", "/reply", &b)
	req.Header.Set("Content-Type", w.FormDataContentType())

	return req
}

func TestIsAllowedExt(t *testing.T) {

	assert.False(t, isAllowedExt(".png.exe"), "Should not be allowed")

	assert.False(t, isAllowedExt(".exe.png"), "Should not be allowed")

	assert.False(t, isAllowedExt(""), "Should not be allowed")

	assert.False(t, isAllowedExt("."), "Should not be allowed")

	assert.False(t, isAllowedExt(".pdf"), "Should not be allowed")

	assert.True(t, isAllowedExt(".jpg"), "Should be allowed")

	assert.True(t, isAllowedExt(".JPEG"), "Should be allowed")

}

func TestCheckReqGoodExt(t *testing.T) {

	var err error

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err = img.checkReqExt()
	assert.NoError(t, err, "An error was not expected")

}

func TestCheckReqBadExt(t *testing.T) {

	req := formJpegRequest(300, "test.crap")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("format not supported"), "Error should match")
	}

}

func TestCheckReqBadExtExploit1(t *testing.T) {

	req := formRandomRequest("test.exe.png")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	assert.NoError(t, err, "An error was not expected")

	err = img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("unknown file type"), "Error should match")
	}

}

func TestCheckReqBadExtExploit2(t *testing.T) {

	req := formRandomRequest("test.png.exe")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("format not supported"), "Error should match")
	}

}

func TestCheckReqNoExt(t *testing.T) {

	req := formJpegRequest(300, "test")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("no file extension"), "Error should match")
	}

}

func TestGetMD5(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

}

func TestCheckMagicGood(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.NoError(t, err, "An error was not expected") {
		assert.Equal(t, img.mime, "image/jpeg", "Mime type should be the same")
	}

}

func TestCheckMagicBad(t *testing.T) {

	req := formRandomRequest("test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("unknown file type"), "Error should match")
	}

}

func TestGetStatsGoodPng(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000000

	img := ImageType{}

	img.image = testPng(400)

	err := img.getStats()
	assert.NoError(t, err, "An error was not expected")

}

func TestGetStatsGoodJpeg(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000000

	img := ImageType{}

	img.image = testJpeg(400)

	err := img.getStats()
	assert.NoError(t, err, "An error was not expected")

}

func TestGetStatsBadSize(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000

	img := ImageType{}

	img.image = testPng(400)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image size too large"), "Error should match")
	}

}

func TestGetStatsBadMin(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 300000

	img := ImageType{}

	img.image = testPng(50)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image width too small"), "Error should match")
	}

}

func TestGetStatsBadMax(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 300000

	img := ImageType{}

	img.image = testPng(1200)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image width too large"), "Error should match")
	}

}

func TestMakeFilenames(t *testing.T) {

	img := ImageType{}

	img.makeFilenames()

	assert.NotEmpty(t, img.Filename, "Filename should be returned")

	assert.NotEmpty(t, img.Thumbnail, "Thumbnail name should be returned")

}

func TestSaveFile(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.ProcessFile()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
		assert.Equal(t, img.Ext, ".jpg", "Ext should be the same")
		assert.Equal(t, img.mime, "image/jpeg", "Mime type should be the same")
	}

	err = img.SaveImage()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.Filename, "Filename should be returned")
		assert.NotEmpty(t, img.Thumbnail, "Thumbnail name should be returned")
		assert.Equal(t, img.OrigHeight, 300, "Height should be the same")
		assert.Equal(t, img.OrigWidth, 300, "Width should be the same")
	}

}
<commit_msg>add tests and change image functions a bit<commit_after>package utils

import (
	"bytes"
	crand "crypto/rand"
	"errors"
	"github.com/stretchr/testify/assert"
	"image"
	"image/color"
	"image/jpeg"
	"image/png"
	"io"
	"math/rand"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"testing"

	"github.com/eirka/eirka-libs/config"
	local "github.com/eirka/eirka-post/config"
)

func testPng(size int) *bytes.Buffer {

	output := new(bytes.Buffer)

	myimage := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{size, size}})

	// This loop just fills the image with random data
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			c := color.RGBA{uint8(rand.Intn(255)), uint8(rand.Intn(255)), uint8(rand.Intn(255)), 255}
			myimage.Set(x, y, c)
		}
	}

	png.Encode(output, myimage)

	return output
}

func testJpeg(size int) *bytes.Buffer {

	output := new(bytes.Buffer)

	myimage := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{size, size}})

	// This loop just fills the image with random data
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			c := color.RGBA{uint8(rand.Intn(255)), uint8(rand.Intn(255)), uint8(rand.Intn(255)), 255}
			myimage.Set(x, y, c)
		}
	}

	jpeg.Encode(output, myimage, nil)

	return output
}

func testRandom() []byte {
	bytes := make([]byte, 20000)

	if _, err := io.ReadFull(crand.Reader, bytes); err != nil {
		panic(err)
	}

	return bytes
}

func formJpegRequest(size int, filename string) *http.Request {

	var b bytes.Buffer

	w := multipart.NewWriter(&b)

	fw, _ := w.CreateFormFile("file", filename)

	io.Copy(fw, testJpeg(size))

	w.Close()

	req, _ := http.NewRequest("POST", "/reply", &b)
	req.Header.Set("Content-Type", w.FormDataContentType())

	return req
}

func formRandomRequest(filename string) *http.Request {

	var b bytes.Buffer

	w := multipart.NewWriter(&b)

	fw, _ := w.CreateFormFile("file", filename)

	io.Copy(fw, bytes.NewReader(testRandom()))

	w.Close()

	req, _ := http.NewRequest("POST", "/reply", &b)
	req.Header.Set("Content-Type", w.FormDataContentType())

	return req
}

func TestIsAllowedExt(t *testing.T) {

	assert.False(t, isAllowedExt(".png.exe"), "Should not be allowed")

	assert.False(t, isAllowedExt(".exe.png"), "Should not be allowed")

	assert.False(t, isAllowedExt(""), "Should not be allowed")

	assert.False(t, isAllowedExt("."), "Should not be allowed")

	assert.False(t, isAllowedExt(".pdf"), "Should not be allowed")

	assert.True(t, isAllowedExt(".jpg"), "Should be allowed")

	assert.True(t, isAllowedExt(".JPEG"), "Should be allowed")

}

func TestCheckReqGoodExt(t *testing.T) {

	var err error

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err = img.checkReqExt()
	assert.NoError(t, err, "An error was not expected")

}

func TestCheckReqBadExt(t *testing.T) {

	req := formJpegRequest(300, "test.crap")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("format not supported"), "Error should match")
	}

}

func TestCheckReqBadExtExploit1(t *testing.T) {

	req := formRandomRequest("test.exe.png")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	assert.NoError(t, err, "An error was not expected")

	err = img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("unknown file type"), "Error should match")
	}

}

func TestCheckReqBadExtExploit2(t *testing.T) {

	req := formRandomRequest("test.png.exe")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("format not supported"), "Error should match")
	}

}

func TestCheckReqNoExt(t *testing.T) {

	req := formJpegRequest(300, "test")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("no file extension"), "Error should match")
	}

}

func TestGetMD5(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

}

func TestCheckMagicGood(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.NoError(t, err, "An error was not expected") {
		assert.Equal(t, img.mime, "image/jpeg", "Mime type should be the same")
	}

}

func TestCheckMagicBad(t *testing.T) {

	req := formRandomRequest("test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("unknown file type"), "Error should match")
	}

}

func TestGetStatsGoodPng(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000000

	img := ImageType{}

	img.image = testPng(400)

	err := img.getStats()
	assert.NoError(t, err, "An error was not expected")

}

func TestGetStatsGoodJpeg(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000000

	img := ImageType{}

	img.image = testJpeg(400)

	err := img.getStats()
	assert.NoError(t, err, "An error was not expected")

}

func TestGetStatsBadSize(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000

	img := ImageType{}

	img.image = testPng(400)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image size too large"), "Error should match")
	}

}

func TestGetStatsBadMin(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 300000

	img := ImageType{}

	img.image = testPng(50)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image width too small"), "Error should match")
	}

}

func TestGetStatsBadMax(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 300000

	img := ImageType{}

	img.image = testPng(1200)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image width too large"), "Error should match")
	}

}

func TestMakeFilenames(t *testing.T) {

	img := ImageType{}

	img.makeFilenames()

	assert.NotEmpty(t, img.Filename, "Filename should be returned")

	assert.NotEmpty(t, img.Thumbnail, "Thumbnail name should be returned")

}

func TestSaveFile(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.ProcessFile()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
		assert.Equal(t, img.Ext, ".jpg", "Ext should be the same")
		assert.Equal(t, img.mime, "image/jpeg", "Mime type should be the same")
	}

	filesize := img.image.Len()

	err = img.getStats()
	if assert.NoError(t, err, "An error was not expected") {
		assert.Equal(t, img.OrigHeight, 300, "Height should be the same")
		assert.Equal(t, img.OrigWidth, 300, "Width should be the same")
	}

	err = img.saveFile()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.Filename, "Filename should be returned")
		assert.NotEmpty(t, img.Thumbnail, "Thumbnail name should be returned")
	}

	file, err = os.Open(filepath.Join(local.Settings.Directories.ImageDir, img.Filename))
	assert.NoError(t, err, "An error was not expected")

	info, err = file.Stat()
	if assert.NoError(t, err, "An error was not expected") {
		assert.Equal(t, info.Name(), img.File, "Name should be the same")
		assert.Equal(t, info.Size(), filesize, "Size should be the same")
	}
}
<|endoftext|>


<commit_before>package logstash

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"

	"github.com/gliderlabs/logspout/router"
)

func init() {
	router.AdapterFactories.Register(NewLogstashAdapter, "logstash")
}

// LogstashAdapter is an adapter that streams UDP JSON to Logstash.
type LogstashAdapter struct {
	conn  net.Conn
	route *router.Route
}

func getopt(name, dfault string) string {
	value := os.Getenv(name)
	if value == "" {
		value = dfault
	}
	return value
}

// NewLogstashAdapter creates a LogstashAdapter with UDP as the default transport.
func NewLogstashAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}

	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &LogstashAdapter{
		route: route,
		conn:  conn,
	}, nil
}

// Stream implements the router.LogAdapter interface.
func (a *LogstashAdapter) Stream(logstream chan *router.Message) {

	opt_string := getopt("OPTIONS", "")
	var options map[string]string

	if opt_string != "" {
		b := []byte(opt_string)

		json.Unmarshal(b, &options)
	}

	resp, err := http.Get("http://169.254.169.254/latest/meta-data/instance-id")
	instance_id := ""
	if err == nil {
		value, err := ioutil.ReadAll(resp.Body)
		if err == nil {
			instance_id = value
		}
	}
	resp.Body.Close()

	for m := range logstream {
		msg := LogstashMessage{
			Message:    m.Data,
			Name:       m.Container.Name,
			ID:         m.Container.ID,
			Image:      m.Container.Config.Image,
			Hostname:   m.Container.Config.Hostname,
			Args:       m.Container.Args,
			InstanceId: instance_id,
			Options:    options,
		}
		js, err := json.Marshal(msg)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
		_, err = a.conn.Write(js)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
	}
}

// LogstashMessage is a simple JSON input to Logstash.
type LogstashMessage struct {
	Message    string            `json:"message"`
	Name       string            `json:"docker.name"`
	ID         string            `json:"docker.id"`
	Image      string            `json:"docker.image"`
	Hostname   string            `json:"docker.hostname"`
	Args       []string          `json:"docker.args,omitempty"`
	Options    map[string]string `json:"options,omitempty"`
	InstanceId string            `json:"instance-id,omitempty"`
}
<commit_msg>converting to string<commit_after>package logstash

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"

	"github.com/gliderlabs/logspout/router"
)

func init() {
	router.AdapterFactories.Register(NewLogstashAdapter, "logstash")
}

// LogstashAdapter is an adapter that streams UDP JSON to Logstash.
type LogstashAdapter struct {
	conn  net.Conn
	route *router.Route
}

func getopt(name, dfault string) string {
	value := os.Getenv(name)
	if value == "" {
		value = dfault
	}
	return value
}

// NewLogstashAdapter creates a LogstashAdapter with UDP as the default transport.
func NewLogstashAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}

	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &LogstashAdapter{
		route: route,
		conn:  conn,
	}, nil
}

// Stream implements the router.LogAdapter interface.
func (a *LogstashAdapter) Stream(logstream chan *router.Message) {

	opt_string := getopt("OPTIONS", "")
	var options map[string]string

	if opt_string != "" {
		b := []byte(opt_string)

		json.Unmarshal(b, &options)
	}

	resp, err := http.Get("http://169.254.169.254/latest/meta-data/instance-id")
	instance_id := ""
	if err == nil {
		value, err := ioutil.ReadAll(resp.Body)
		if err == nil {
			instance_id = string(value)
		}
	}
	resp.Body.Close()

	for m := range logstream {
		msg := LogstashMessage{
			Message:    m.Data,
			Name:       m.Container.Name,
			ID:         m.Container.ID,
			Image:      m.Container.Config.Image,
			Hostname:   m.Container.Config.Hostname,
			Args:       m.Container.Args,
			InstanceId: instance_id,
			Options:    options,
		}
		js, err := json.Marshal(msg)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
		_, err = a.conn.Write(js)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
	}
}

// LogstashMessage is a simple JSON input to Logstash.
type LogstashMessage struct {
	Message    string            `json:"message"`
	Name       string            `json:"docker.name"`
	ID         string            `json:"docker.id"`
	Image      string            `json:"docker.image"`
	Hostname   string            `json:"docker.hostname"`
	Args       []string          `json:"docker.args,omitempty"`
	Options    map[string]string `json:"options,omitempty"`
	InstanceId string            `json:"instance-id,omitempty"`
}
<|endoftext|>


<commit_before>package logstash

import (
	"encoding/json"
	"errors"
	"log"
	"net"

	"github.com/gliderlabs/logspout/router"
	"github.com/rancher/go-rancher-metadata/metadata"
)

const (
	metadataUrl = "http://rancher-metadata/2015-07-25"
)

func init() {
	router.AdapterFactories.Register(NewLogstashAdapter, "logstash")
}

// LogstashAdapter is an adapter that streams UDP JSON to Logstash.
type LogstashAdapter struct {
	conn  net.Conn
	route *router.Route
}

// NewLogstashAdapter creates a LogstashAdapter with UDP as the default transport.
func NewLogstashAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}

	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &LogstashAdapter{
		route: route,
		conn:  conn,
	}, nil
}

// Stream implements the router.LogAdapter interface.
func (a *LogstashAdapter) Stream(logstream chan *router.Message) {
	racherMetaData := metadata.NewClient(metadataUrl)
	stackname := ""
	for m := range logstream {
	    stack, err := racherMetaData.GetSelfStack()
		if err != nil {
			log.Println("Error reading metadata version: ", err)
        }else {
			stackname = stack.name
		}
		msg := LogstashMessage{
			Message: m.Data,
			Docker: DockerInfo{
				Name:     m.Container.Name,
				ID:       m.Container.ID,
				Image:    m.Container.Config.Image,
				Hostname: m.Container.Config.Hostname,
			},
			Rancher: RancherInfo{
				StackName: stackname,
			},
		}
		js, err := json.Marshal(msg)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
		_, err = a.conn.Write(js)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
	}
}

type DockerInfo struct {
	Name     string `json:"name"`
	ID       string `json:"id"`
	Image    string `json:"image"`
	Hostname string `json:"hostname"`
}

type RancherInfo struct {
    Stackname string `json:"stackname"`
}

// LogstashMessage is a simple JSON input to Logstash.
type LogstashMessage struct {
	Message string     `json:"message"`
	Docker  DockerInfo `json:"docker"`
	Rancher RancherInfo `json:"rancher"`
}
<commit_msg>Rafactoring<commit_after>package logstash

import (
	"encoding/json"
	"errors"
	"log"
	"net"

	"github.com/gliderlabs/logspout/router"
	"github.com/rancher/go-rancher-metadata/metadata"
)

const (
	metadataUrl = "http://rancher-metadata/2015-07-25"
)

func init() {
	router.AdapterFactories.Register(NewLogstashAdapter, "logstash")
}

// LogstashAdapter is an adapter that streams UDP JSON to Logstash.
type LogstashAdapter struct {
	conn  net.Conn
	route *router.Route
}

// NewLogstashAdapter creates a LogstashAdapter with UDP as the default transport.
func NewLogstashAdapter(route *router.Route) (router.LogAdapter, error) {
	transport, found := router.AdapterTransports.Lookup(route.AdapterTransport("udp"))
	if !found {
		return nil, errors.New("unable to find adapter: " + route.Adapter)
	}

	conn, err := transport.Dial(route.Address, route.Options)
	if err != nil {
		return nil, err
	}

	return &LogstashAdapter{
		route: route,
		conn:  conn,
	}, nil
}

// Stream implements the router.LogAdapter interface.
func (a *LogstashAdapter) Stream(logstream chan *router.Message) {
	racherMetaData := metadata.NewClient(metadataUrl)
	stackname := ""
	for m := range logstream {
	    stack, err := racherMetaData.GetSelfStack()
		if err != nil {
			log.Println("Error reading metadata version: ", err)
        }else {
			stackname = stack.Name
		}
		msg := LogstashMessage{
			Message: m.Data,
			Docker: DockerInfo{
				Name:     m.Container.Name,
				ID:       m.Container.ID,
				Image:    m.Container.Config.Image,
				Hostname: m.Container.Config.Hostname,
			},
			Rancher: RancherInfo{
				StackName: stackname,
			},
		}
		js, err := json.Marshal(msg)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
		_, err = a.conn.Write(js)
		if err != nil {
			log.Println("logstash:", err)
			continue
		}
	}
}

type DockerInfo struct {
	Name     string `json:"name"`
	ID       string `json:"id"`
	Image    string `json:"image"`
	Hostname string `json:"hostname"`
}

type RancherInfo struct {
    Stackname string `json:"stackname"`
}

// LogstashMessage is a simple JSON input to Logstash.
type LogstashMessage struct {
	Message string     `json:"message"`
	Docker  DockerInfo `json:"docker"`
	Rancher RancherInfo `json:"rancher"`
}
<|endoftext|>