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

package steven

import (
	"fmt"
	"math"
	"runtime"
	"time"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol/mojang"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/ui"
)

var (
	profile       mojang.Profile
	server        string
	loadChan      = make(chan struct{})
	currentScreen screen
	connected     bool
)

type screen interface {
	tick(delta float64)
	hover(x, y float64, w, h int)
	click(down bool, x, y float64, w, h int)
	remove()
}

func setScreen(s screen) {
	if currentScreen != nil {
		currentScreen.remove()
	}
	currentScreen = s
	if s != nil {
		lockMouse = false
		window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
	}
}

func Main(username, uuid, accessToken, s string) {
	profile = mojang.Profile{
		Username:    username,
		ID:          uuid,
		AccessToken: accessToken,
	}
	server = s

	go func() {
		render.LoadTextures()
		initBlocks()
		loadChan <- struct{}{}
	}()

	if profile.IsComplete() && server != "" {
		// Start connecting whilst starting the renderer
		connect()
	} else {
		Client.valid = false
	}

	startWindow()
}

func connect() {
	connected = true
	disconnectReason.Value = nil
	go startConnection(mojang.Profile{
		Username:    profile.Username,
		ID:          profile.ID,
		AccessToken: profile.AccessToken,
	}, server)
	server = ""
}

func start() {
	<-loadChan
	if Client.valid {
		Client.init()
	} else {
		fakeGen()
		if !profile.IsComplete() {
			setScreen(newLoginScreen())
		} else {
			setScreen(newServerList())
		}
	}
	render.FOV = Config.Render.FOV
	render.Start()
}

func rotate(x, y float64) {
	Client.Yaw -= x
	Client.Pitch -= y
}

var maxBuilders = runtime.NumCPU() * 2

var (
	ready            bool
	freeBuilders     = maxBuilders
	completeBuilders = make(chan buildPos, maxBuilders)
	syncChan         = make(chan func(), 200)
	ticker           = time.NewTicker(time.Second / 20)
	lastFrame        = time.Now()
)

func handleErrors() {
handle:
	for {
		select {
		case err := <-errorChan:
			if !connected {
				continue
			}
			connected = false
			if conn != nil {
				conn.Close()
			}
			fmt.Printf("Disconnected: %s\n", err)
			ready = false
			if err != errManualDisconnect && disconnectReason.Value == nil {
				txt := &chat.TextComponent{Text: err.Error()}
				txt.Color = chat.Red
				disconnectReason.Value = txt
			}
			setScreen(newServerList())
		default:
			break handle
		}
	}
}

func draw() {
	now := time.Now()
	diff := now.Sub(lastFrame)
	lastFrame = now
	delta := float64(diff.Nanoseconds()) / (float64(time.Second) / 60)
	delta = math.Min(delta, 1.6)
handle:
	for {
		select {
		case packet := <-readChan:
			defaultHandler.Handle(packet)
		case pos := <-completeBuilders:
			c := chunkMap[chunkPosition{pos.X, pos.Z}]
			freeBuilders++
			if c != nil {
				s := c.Sections[pos.Y]
				if s != nil {
					s.building = false
				}
			}
		case f := <-syncChan:
			f()
		default:
			break handle
		}
	}
	handleErrors()

	width, height := window.GetFramebufferSize()

	if currentScreen != nil {
		currentScreen.tick(delta)
	}

	if ready && Client.valid {
		Client.renderTick(delta)
		select {
		case <-ticker.C:
			tick()
		default:
		}
	} else {
		render.Camera.Yaw += 0.005 * delta
		if render.Camera.Yaw > math.Pi*2 {
			render.Camera.Yaw = 0
		}
	}
	ui.Draw(width, height, delta)

	render.Draw(width, height, delta)
	chunks := sortedChunks()

	// Search for 'dirty' chunk sections and start building
	// them if we have any builders free. To prevent race conditions
	// two flags are used, dirty and building, to allow a second
	// build to be requested whilst the chunk is still building
	// without either losing the change or having two builds
	// for the same section going on at once (where the second
	// could finish quicker causing the old version to be
	// displayed.
dirtyClean:
	for _, c := range chunks {
		for _, s := range c.Sections {
			if s == nil {
				continue
			}
			if freeBuilders <= 0 {
				break dirtyClean
			}
			if s.dirty && !s.building {
				freeBuilders--
				s.dirty = false
				s.building = true
				s.build(completeBuilders)
			}
		}
	}
}

// tick is called 20 times a second (bar any preformance issues).
// Minecraft is built around this fact so we have to follow it
// as well.
func tick() {
	Client.tick()
}
<commit_msg>steven: force show the client ui when all screens are closed<commit_after>// Copyright 2015 Matthew Collins
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package steven

import (
	"fmt"
	"math"
	"runtime"
	"time"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol/mojang"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/ui"
)

var (
	profile       mojang.Profile
	server        string
	loadChan      = make(chan struct{})
	currentScreen screen
	connected     bool
)

type screen interface {
	tick(delta float64)
	hover(x, y float64, w, h int)
	click(down bool, x, y float64, w, h int)
	remove()
}

func setScreen(s screen) {
	if currentScreen != nil {
		currentScreen.remove()
	}
	currentScreen = s
	if s != nil {
		lockMouse = false
		window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
	} else {
		Client.scene.Show()
	}
}

func Main(username, uuid, accessToken, s string) {
	profile = mojang.Profile{
		Username:    username,
		ID:          uuid,
		AccessToken: accessToken,
	}
	server = s

	go func() {
		render.LoadTextures()
		initBlocks()
		loadChan <- struct{}{}
	}()

	if profile.IsComplete() && server != "" {
		// Start connecting whilst starting the renderer
		connect()
	} else {
		Client.valid = false
	}

	startWindow()
}

func connect() {
	connected = true
	disconnectReason.Value = nil
	go startConnection(mojang.Profile{
		Username:    profile.Username,
		ID:          profile.ID,
		AccessToken: profile.AccessToken,
	}, server)
	server = ""
}

func start() {
	<-loadChan
	if Client.valid {
		Client.init()
	} else {
		fakeGen()
		if !profile.IsComplete() {
			setScreen(newLoginScreen())
		} else {
			setScreen(newServerList())
		}
	}
	render.FOV = Config.Render.FOV
	render.Start()
}

func rotate(x, y float64) {
	Client.Yaw -= x
	Client.Pitch -= y
}

var maxBuilders = runtime.NumCPU() * 2

var (
	ready            bool
	freeBuilders     = maxBuilders
	completeBuilders = make(chan buildPos, maxBuilders)
	syncChan         = make(chan func(), 200)
	ticker           = time.NewTicker(time.Second / 20)
	lastFrame        = time.Now()
)

func handleErrors() {
handle:
	for {
		select {
		case err := <-errorChan:
			if !connected {
				continue
			}
			connected = false
			if conn != nil {
				conn.Close()
			}
			fmt.Printf("Disconnected: %s\n", err)
			ready = false
			if err != errManualDisconnect && disconnectReason.Value == nil {
				txt := &chat.TextComponent{Text: err.Error()}
				txt.Color = chat.Red
				disconnectReason.Value = txt
			}
			setScreen(newServerList())
		default:
			break handle
		}
	}
}

func draw() {
	now := time.Now()
	diff := now.Sub(lastFrame)
	lastFrame = now
	delta := float64(diff.Nanoseconds()) / (float64(time.Second) / 60)
	delta = math.Min(delta, 1.6)
handle:
	for {
		select {
		case packet := <-readChan:
			defaultHandler.Handle(packet)
		case pos := <-completeBuilders:
			c := chunkMap[chunkPosition{pos.X, pos.Z}]
			freeBuilders++
			if c != nil {
				s := c.Sections[pos.Y]
				if s != nil {
					s.building = false
				}
			}
		case f := <-syncChan:
			f()
		default:
			break handle
		}
	}
	handleErrors()

	width, height := window.GetFramebufferSize()

	if currentScreen != nil {
		currentScreen.tick(delta)
	}

	if ready && Client.valid {
		Client.renderTick(delta)
		select {
		case <-ticker.C:
			tick()
		default:
		}
	} else {
		render.Camera.Yaw += 0.005 * delta
		if render.Camera.Yaw > math.Pi*2 {
			render.Camera.Yaw = 0
		}
	}
	ui.Draw(width, height, delta)

	render.Draw(width, height, delta)
	chunks := sortedChunks()

	// Search for 'dirty' chunk sections and start building
	// them if we have any builders free. To prevent race conditions
	// two flags are used, dirty and building, to allow a second
	// build to be requested whilst the chunk is still building
	// without either losing the change or having two builds
	// for the same section going on at once (where the second
	// could finish quicker causing the old version to be
	// displayed.
dirtyClean:
	for _, c := range chunks {
		for _, s := range c.Sections {
			if s == nil {
				continue
			}
			if freeBuilders <= 0 {
				break dirtyClean
			}
			if s.dirty && !s.building {
				freeBuilders--
				s.dirty = false
				s.building = true
				s.build(completeBuilders)
			}
		}
	}
}

// tick is called 20 times a second (bar any preformance issues).
// Minecraft is built around this fact so we have to follow it
// as well.
func tick() {
	Client.tick()
}
<|endoftext|>


<commit_before>package logrus

import (
	"bytes"
	"encoding/json"
	"strconv"
	"strings"
	"sync"
	"testing"

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

func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
	var buffer bytes.Buffer
	var fields Fields

	logger := New()
	logger.Out = &buffer
	logger.Formatter = new(JSONFormatter)

	log(logger)

	err := json.Unmarshal(buffer.Bytes(), &fields)
	assert.Nil(t, err)

	assertions(fields)
}

func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
	var buffer bytes.Buffer

	logger := New()
	logger.Out = &buffer
	logger.Formatter = &TextFormatter{
		DisableColors: true,
	}

	log(logger)

	fields := make(map[string]string)
	for _, kv := range strings.Split(buffer.String(), " ") {
		if !strings.Contains(kv, "=") {
			continue
		}
		kvArr := strings.Split(kv, "=")
		key := strings.TrimSpace(kvArr[0])
		val := kvArr[1]
		if kvArr[1][0] == '"' {
			var err error
			val, err = strconv.Unquote(val)
			assert.NoError(t, err)
		}
		fields[key] = val
	}
	assertions(fields)
}

// TestReportCaller verifies that when ReportCaller is set, the 'method' field
// is added, and when it is unset it is not set or modified
func TestReportCaller(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		SetReportCaller(false)
		log.Print("testNoCaller")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "testNoCaller")
		assert.Equal(t, fields["level"], "info")
		assert.Equal(t, fields["method"], nil)
	})

	LogAndAssertJSON(t, func(log *Logger) {
		SetReportCaller(true)
		log.Print("testWithCaller")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "testWithCaller")
		assert.Equal(t, fields["level"], "info")
		assert.Equal(t, fields["method"], "testing.tRunner")
	})

	SetReportCaller(false) // return to default value
}

func TestPrint(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Print("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
		assert.Equal(t, fields["level"], "info")
	})
}

func TestInfo(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
		assert.Equal(t, fields["level"], "info")
	})
}

func TestWarn(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Warn("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
		assert.Equal(t, fields["level"], "warning")
	})
}

func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Infoln("test", "test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test test")
	})
}

func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Infoln("test", 10)
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test 10")
	})
}

func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Infoln(10, 10)
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "10 10")
	})
}

func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Infoln(10, 10)
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "10 10")
	})
}

func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Info("test", 10)
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test10")
	})
}

func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Info("test", "test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "testtest")
	})
}

func TestWithFieldsShouldAllowAssignments(t *testing.T) {
	var buffer bytes.Buffer
	var fields Fields

	logger := New()
	logger.Out = &buffer
	logger.Formatter = new(JSONFormatter)

	localLog := logger.WithFields(Fields{
		"key1": "value1",
	})

	localLog.WithField("key2", "value2").Info("test")
	err := json.Unmarshal(buffer.Bytes(), &fields)
	assert.Nil(t, err)

	assert.Equal(t, "value2", fields["key2"])
	assert.Equal(t, "value1", fields["key1"])

	buffer = bytes.Buffer{}
	fields = Fields{}
	localLog.Info("test")
	err = json.Unmarshal(buffer.Bytes(), &fields)
	assert.Nil(t, err)

	_, ok := fields["key2"]
	assert.Equal(t, false, ok)
	assert.Equal(t, "value1", fields["key1"])
}

func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.WithField("msg", "hello").Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
	})
}

func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.WithField("msg", "hello").Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
		assert.Equal(t, fields["fields.msg"], "hello")
	})
}

func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.WithField("time", "hello").Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["fields.time"], "hello")
	})
}

func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.WithField("level", 1).Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["level"], "info")
		assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
	})
}

func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
	LogAndAssertText(t, func(log *Logger) {
		ll := log.WithField("herp", "derp")
		ll.Info("hello")
		ll.Info("bye")
	}, func(fields map[string]string) {
		for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
			if _, ok := fields[fieldName]; ok {
				t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
			}
		}
	})
}

func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {

	var buffer bytes.Buffer
	var fields Fields

	logger := New()
	logger.Out = &buffer
	logger.Formatter = new(JSONFormatter)

	llog := logger.WithField("context", "eating raw fish")

	llog.Info("looks delicious")

	err := json.Unmarshal(buffer.Bytes(), &fields)
	assert.NoError(t, err, "should have decoded first message")
	assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
	assert.Equal(t, fields["msg"], "looks delicious")
	assert.Equal(t, fields["context"], "eating raw fish")

	buffer.Reset()

	llog.Warn("omg it is!")

	err = json.Unmarshal(buffer.Bytes(), &fields)
	assert.NoError(t, err, "should have decoded second message")
	assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
	assert.Equal(t, fields["msg"], "omg it is!")
	assert.Equal(t, fields["context"], "eating raw fish")
	assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")

}

func TestNestedLoggingReportsCorrectCaller(t *testing.T) {
	var buffer bytes.Buffer
	var fields Fields

	SetReportCaller(true)
	logger := New()
	logger.Out = &buffer
	logger.Formatter = new(JSONFormatter)

	llog := logger.WithField("context", "eating raw fish")

	llog.Info("looks delicious")

	err := json.Unmarshal(buffer.Bytes(), &fields)
	assert.NoError(t, err, "should have decoded first message")
	assert.Equal(t, len(fields), 5, "should have msg/time/level/method/context fields")
	assert.Equal(t, fields["msg"], "looks delicious")
	assert.Equal(t, fields["context"], "eating raw fish")
	assert.Equal(t, fields["method"], "testing.tRunner")

	buffer.Reset()

	logger.WithFields(Fields{
		"foo": "a",
	}).WithFields(Fields{
		"bar": "b",
	}).WithFields(Fields{
		"baz": "c",
	}).WithFields(Fields{
		"method": "man",
	}).WithFields(Fields{
		"clan": "Wu Tang",
	}).Print("omg it is!")

	err = json.Unmarshal(buffer.Bytes(), &fields)
	assert.NoError(t, err, "should have decoded second message")
	assert.Equal(t, 10, len(fields), "should have all builtin fields plus foo,bar,baz")
	assert.Equal(t, "omg it is!", fields["msg"])
	assert.Equal(t, "a", fields["foo"])
	assert.Equal(t, "b", fields["bar"])
	assert.Equal(t, "c", fields["baz"])
	assert.Equal(t, "man", fields["fields.method"])
	assert.Equal(t, "Wu Tang", fields["clan"])
	assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
	assert.Equal(t, "testing.tRunner", fields["method"])

	SetReportCaller(false) // return to default value
}

func TestConvertLevelToString(t *testing.T) {
	assert.Equal(t, "debug", DebugLevel.String())
	assert.Equal(t, "info", InfoLevel.String())
	assert.Equal(t, "warning", WarnLevel.String())
	assert.Equal(t, "error", ErrorLevel.String())
	assert.Equal(t, "fatal", FatalLevel.String())
	assert.Equal(t, "panic", PanicLevel.String())
}

func TestParseLevel(t *testing.T) {
	l, err := ParseLevel("panic")
	assert.Nil(t, err)
	assert.Equal(t, PanicLevel, l)

	l, err = ParseLevel("PANIC")
	assert.Nil(t, err)
	assert.Equal(t, PanicLevel, l)

	l, err = ParseLevel("fatal")
	assert.Nil(t, err)
	assert.Equal(t, FatalLevel, l)

	l, err = ParseLevel("FATAL")
	assert.Nil(t, err)
	assert.Equal(t, FatalLevel, l)

	l, err = ParseLevel("error")
	assert.Nil(t, err)
	assert.Equal(t, ErrorLevel, l)

	l, err = ParseLevel("ERROR")
	assert.Nil(t, err)
	assert.Equal(t, ErrorLevel, l)

	l, err = ParseLevel("warn")
	assert.Nil(t, err)
	assert.Equal(t, WarnLevel, l)

	l, err = ParseLevel("WARN")
	assert.Nil(t, err)
	assert.Equal(t, WarnLevel, l)

	l, err = ParseLevel("warning")
	assert.Nil(t, err)
	assert.Equal(t, WarnLevel, l)

	l, err = ParseLevel("WARNING")
	assert.Nil(t, err)
	assert.Equal(t, WarnLevel, l)

	l, err = ParseLevel("info")
	assert.Nil(t, err)
	assert.Equal(t, InfoLevel, l)

	l, err = ParseLevel("INFO")
	assert.Nil(t, err)
	assert.Equal(t, InfoLevel, l)

	l, err = ParseLevel("debug")
	assert.Nil(t, err)
	assert.Equal(t, DebugLevel, l)

	l, err = ParseLevel("DEBUG")
	assert.Nil(t, err)
	assert.Equal(t, DebugLevel, l)

	l, err = ParseLevel("invalid")
	assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
}

func TestGetSetLevelRace(t *testing.T) {
	wg := sync.WaitGroup{}
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			if i%2 == 0 {
				SetLevel(InfoLevel)
			} else {
				GetLevel()
			}
		}(i)

	}
	wg.Wait()
}

func TestLoggingRace(t *testing.T) {
	logger := New()

	var wg sync.WaitGroup
	wg.Add(100)

	for i := 0; i < 100; i++ {
		go func() {
			logger.Info("info")
			wg.Done()
		}()
	}
	wg.Wait()
}

// Compile test
func TestLogrusInterface(t *testing.T) {
	var buffer bytes.Buffer
	fn := func(l FieldLogger) {
		b := l.WithField("key", "value")
		b.Debug("Test")
	}
	// test logger
	logger := New()
	logger.Out = &buffer
	fn(logger)

	// test Entry
	e := logger.WithField("another", "value")
	fn(e)
}
<commit_msg>fix test description<commit_after>package logrus

import (
	"bytes"
	"encoding/json"
	"strconv"
	"strings"
	"sync"
	"testing"

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

func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
	var buffer bytes.Buffer
	var fields Fields

	logger := New()
	logger.Out = &buffer
	logger.Formatter = new(JSONFormatter)

	log(logger)

	err := json.Unmarshal(buffer.Bytes(), &fields)
	assert.Nil(t, err)

	assertions(fields)
}

func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
	var buffer bytes.Buffer

	logger := New()
	logger.Out = &buffer
	logger.Formatter = &TextFormatter{
		DisableColors: true,
	}

	log(logger)

	fields := make(map[string]string)
	for _, kv := range strings.Split(buffer.String(), " ") {
		if !strings.Contains(kv, "=") {
			continue
		}
		kvArr := strings.Split(kv, "=")
		key := strings.TrimSpace(kvArr[0])
		val := kvArr[1]
		if kvArr[1][0] == '"' {
			var err error
			val, err = strconv.Unquote(val)
			assert.NoError(t, err)
		}
		fields[key] = val
	}
	assertions(fields)
}

// TestReportCaller verifies that when ReportCaller is set, the 'method' field
// is added, and when it is unset it is not set or modified
func TestReportCaller(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		SetReportCaller(false)
		log.Print("testNoCaller")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "testNoCaller")
		assert.Equal(t, fields["level"], "info")
		assert.Equal(t, fields["method"], nil)
	})

	LogAndAssertJSON(t, func(log *Logger) {
		SetReportCaller(true)
		log.Print("testWithCaller")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "testWithCaller")
		assert.Equal(t, fields["level"], "info")
		assert.Equal(t, fields["method"], "testing.tRunner")
	})

	SetReportCaller(false) // return to default value
}

func TestPrint(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Print("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
		assert.Equal(t, fields["level"], "info")
	})
}

func TestInfo(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
		assert.Equal(t, fields["level"], "info")
	})
}

func TestWarn(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Warn("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
		assert.Equal(t, fields["level"], "warning")
	})
}

func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Infoln("test", "test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test test")
	})
}

func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Infoln("test", 10)
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test 10")
	})
}

func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Infoln(10, 10)
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "10 10")
	})
}

func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Infoln(10, 10)
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "10 10")
	})
}

func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Info("test", 10)
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test10")
	})
}

func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.Info("test", "test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "testtest")
	})
}

func TestWithFieldsShouldAllowAssignments(t *testing.T) {
	var buffer bytes.Buffer
	var fields Fields

	logger := New()
	logger.Out = &buffer
	logger.Formatter = new(JSONFormatter)

	localLog := logger.WithFields(Fields{
		"key1": "value1",
	})

	localLog.WithField("key2", "value2").Info("test")
	err := json.Unmarshal(buffer.Bytes(), &fields)
	assert.Nil(t, err)

	assert.Equal(t, "value2", fields["key2"])
	assert.Equal(t, "value1", fields["key1"])

	buffer = bytes.Buffer{}
	fields = Fields{}
	localLog.Info("test")
	err = json.Unmarshal(buffer.Bytes(), &fields)
	assert.Nil(t, err)

	_, ok := fields["key2"]
	assert.Equal(t, false, ok)
	assert.Equal(t, "value1", fields["key1"])
}

func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.WithField("msg", "hello").Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
	})
}

func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.WithField("msg", "hello").Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["msg"], "test")
		assert.Equal(t, fields["fields.msg"], "hello")
	})
}

func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.WithField("time", "hello").Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["fields.time"], "hello")
	})
}

func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
	LogAndAssertJSON(t, func(log *Logger) {
		log.WithField("level", 1).Info("test")
	}, func(fields Fields) {
		assert.Equal(t, fields["level"], "info")
		assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
	})
}

func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
	LogAndAssertText(t, func(log *Logger) {
		ll := log.WithField("herp", "derp")
		ll.Info("hello")
		ll.Info("bye")
	}, func(fields map[string]string) {
		for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
			if _, ok := fields[fieldName]; ok {
				t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
			}
		}
	})
}

func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {

	var buffer bytes.Buffer
	var fields Fields

	logger := New()
	logger.Out = &buffer
	logger.Formatter = new(JSONFormatter)

	llog := logger.WithField("context", "eating raw fish")

	llog.Info("looks delicious")

	err := json.Unmarshal(buffer.Bytes(), &fields)
	assert.NoError(t, err, "should have decoded first message")
	assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
	assert.Equal(t, fields["msg"], "looks delicious")
	assert.Equal(t, fields["context"], "eating raw fish")

	buffer.Reset()

	llog.Warn("omg it is!")

	err = json.Unmarshal(buffer.Bytes(), &fields)
	assert.NoError(t, err, "should have decoded second message")
	assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
	assert.Equal(t, fields["msg"], "omg it is!")
	assert.Equal(t, fields["context"], "eating raw fish")
	assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")

}

func TestNestedLoggingReportsCorrectCaller(t *testing.T) {
	var buffer bytes.Buffer
	var fields Fields

	SetReportCaller(true)
	logger := New()
	logger.Out = &buffer
	logger.Formatter = new(JSONFormatter)

	llog := logger.WithField("context", "eating raw fish")

	llog.Info("looks delicious")

	err := json.Unmarshal(buffer.Bytes(), &fields)
	assert.NoError(t, err, "should have decoded first message")
	assert.Equal(t, len(fields), 5, "should have msg/time/level/method/context fields")
	assert.Equal(t, fields["msg"], "looks delicious")
	assert.Equal(t, fields["context"], "eating raw fish")
	assert.Equal(t, fields["method"], "testing.tRunner")

	buffer.Reset()

	logger.WithFields(Fields{
		"foo": "a",
	}).WithFields(Fields{
		"bar": "b",
	}).WithFields(Fields{
		"baz": "c",
	}).WithFields(Fields{
		"method": "man",
	}).WithFields(Fields{
		"clan": "Wu Tang",
	}).Print("omg it is!")

	err = json.Unmarshal(buffer.Bytes(), &fields)
	assert.NoError(t, err, "should have decoded second message")
	assert.Equal(t, 10, len(fields), "should have all builtin fields plus foo,bar,baz,...")
	assert.Equal(t, "omg it is!", fields["msg"])
	assert.Equal(t, "a", fields["foo"])
	assert.Equal(t, "b", fields["bar"])
	assert.Equal(t, "c", fields["baz"])
	assert.Equal(t, "man", fields["fields.method"])
	assert.Equal(t, "Wu Tang", fields["clan"])
	assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
	assert.Equal(t, "testing.tRunner", fields["method"])

	SetReportCaller(false) // return to default value
}

func TestConvertLevelToString(t *testing.T) {
	assert.Equal(t, "debug", DebugLevel.String())
	assert.Equal(t, "info", InfoLevel.String())
	assert.Equal(t, "warning", WarnLevel.String())
	assert.Equal(t, "error", ErrorLevel.String())
	assert.Equal(t, "fatal", FatalLevel.String())
	assert.Equal(t, "panic", PanicLevel.String())
}

func TestParseLevel(t *testing.T) {
	l, err := ParseLevel("panic")
	assert.Nil(t, err)
	assert.Equal(t, PanicLevel, l)

	l, err = ParseLevel("PANIC")
	assert.Nil(t, err)
	assert.Equal(t, PanicLevel, l)

	l, err = ParseLevel("fatal")
	assert.Nil(t, err)
	assert.Equal(t, FatalLevel, l)

	l, err = ParseLevel("FATAL")
	assert.Nil(t, err)
	assert.Equal(t, FatalLevel, l)

	l, err = ParseLevel("error")
	assert.Nil(t, err)
	assert.Equal(t, ErrorLevel, l)

	l, err = ParseLevel("ERROR")
	assert.Nil(t, err)
	assert.Equal(t, ErrorLevel, l)

	l, err = ParseLevel("warn")
	assert.Nil(t, err)
	assert.Equal(t, WarnLevel, l)

	l, err = ParseLevel("WARN")
	assert.Nil(t, err)
	assert.Equal(t, WarnLevel, l)

	l, err = ParseLevel("warning")
	assert.Nil(t, err)
	assert.Equal(t, WarnLevel, l)

	l, err = ParseLevel("WARNING")
	assert.Nil(t, err)
	assert.Equal(t, WarnLevel, l)

	l, err = ParseLevel("info")
	assert.Nil(t, err)
	assert.Equal(t, InfoLevel, l)

	l, err = ParseLevel("INFO")
	assert.Nil(t, err)
	assert.Equal(t, InfoLevel, l)

	l, err = ParseLevel("debug")
	assert.Nil(t, err)
	assert.Equal(t, DebugLevel, l)

	l, err = ParseLevel("DEBUG")
	assert.Nil(t, err)
	assert.Equal(t, DebugLevel, l)

	l, err = ParseLevel("invalid")
	assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
}

func TestGetSetLevelRace(t *testing.T) {
	wg := sync.WaitGroup{}
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			if i%2 == 0 {
				SetLevel(InfoLevel)
			} else {
				GetLevel()
			}
		}(i)

	}
	wg.Wait()
}

func TestLoggingRace(t *testing.T) {
	logger := New()

	var wg sync.WaitGroup
	wg.Add(100)

	for i := 0; i < 100; i++ {
		go func() {
			logger.Info("info")
			wg.Done()
		}()
	}
	wg.Wait()
}

// Compile test
func TestLogrusInterface(t *testing.T) {
	var buffer bytes.Buffer
	fn := func(l FieldLogger) {
		b := l.WithField("key", "value")
		b.Debug("Test")
	}
	// test logger
	logger := New()
	logger.Out = &buffer
	fn(logger)

	// test Entry
	e := logger.WithField("another", "value")
	fn(e)
}
<|endoftext|>


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

package gremlin

import (
	"errors"
	"fmt"
	"sort"
	"time"

	"github.com/robertkrimen/otto"

	"github.com/google/cayley/graph"
)

type Session struct {
	ts                   graph.TripleStore
	currentChannel       chan interface{}
	env                  *otto.Otto
	debug                bool
	limit                int
	count                int
	dataOutput           []interface{}
	lookingForQueryShape bool
	queryShape           map[string]interface{}
	err                  error
	script               *otto.Script
	doHalt               bool
	timeoutSec           time.Duration
	emptyEnv             *otto.Otto
}

func NewSession(inputTripleStore graph.TripleStore, timeoutSec int, persist bool) *Session {
	var g Session
	g.ts = inputTripleStore
	g.env = BuildEnviron(&g)
	g.limit = -1
	g.count = 0
	g.lookingForQueryShape = false
	if persist {
		g.emptyEnv = g.env
	}
	if timeoutSec < 0 {
		g.timeoutSec = time.Duration(-1)
	} else {
		g.timeoutSec = time.Duration(timeoutSec)
	}
	g.ClearJson()
	return &g
}

type GremlinResult struct {
	metaresult    bool
	err           string
	val           *otto.Value
	actualResults *map[string]graph.TSVal
}

func (g *Session) ToggleDebug() {
	g.debug = !g.debug
}

func (g *Session) GetQuery(input string, output_struct chan map[string]interface{}) {
	defer close(output_struct)
	g.queryShape = make(map[string]interface{})
	g.lookingForQueryShape = true
	g.env.Run(input)
	output_struct <- g.queryShape
	g.queryShape = nil
}

func (g *Session) InputParses(input string) (graph.ParseResult, error) {
	script, err := g.env.Compile("", input)
	if err != nil {
		return graph.ParseFail, err
	}
	g.script = script
	return graph.Parsed, nil
}

func (g *Session) SendResult(result *GremlinResult) bool {
	if g.limit >= 0 && g.limit == g.count {
		return false
	}
	if g.doHalt {
		return false
	}
	if g.currentChannel != nil {
		g.currentChannel <- result
		g.count++
		if g.limit >= 0 && g.limit == g.count {
			return false
		} else {
			return true
		}
	}
	return false
}

var halt = errors.New("Query Timeout")

func (g *Session) runUnsafe(input interface{}) (otto.Value, error) {
	g.doHalt = false
	defer func() {
		if caught := recover(); caught != nil {
			if caught == halt {
				g.err = halt
				return
			}
			panic(caught) // Something else happened, repanic!
		}
	}()

	g.env.Interrupt = make(chan func(), 1) // The buffer prevents blocking

	if g.timeoutSec != -1 {
		go func() {
			time.Sleep(g.timeoutSec * time.Second) // Stop after two seconds
			g.doHalt = true
			if g.env != nil {
				g.env.Interrupt <- func() {
					panic(halt)
				}
				g.env = g.emptyEnv
			}
		}()
	}

	return g.env.Run(input) // Here be dragons (risky code)
}

func (g *Session) ExecInput(input string, out chan interface{}, limit int) {
	defer close(out)
	g.err = nil
	g.currentChannel = out
	var err error
	var value otto.Value
	if g.script == nil {
		value, err = g.runUnsafe(input)
	} else {
		value, err = g.runUnsafe(g.script)
	}
	if err != nil {
		out <- &GremlinResult{metaresult: true,
			err:           err.Error(),
			val:           &value,
			actualResults: nil}
	} else {
		out <- &GremlinResult{metaresult: true,
			err:           "",
			val:           &value,
			actualResults: nil}
	}
	g.currentChannel = nil
	g.script = nil
	g.env = g.emptyEnv
	return
}

func (s *Session) ToText(result interface{}) string {
	data := result.(*GremlinResult)
	if data.metaresult {
		if data.err != "" {
			return fmt.Sprintln("Error: ", data.err)
		}
		if data.val != nil {
			s, _ := data.val.Export()
			if data.val.IsObject() {
				typeVal, _ := data.val.Object().Get("_gremlin_type")
				if !typeVal.IsUndefined() {
					s = "[internal Iterator]"
				}
			}
			return fmt.Sprintln("=>", s)
		}
		return ""
	}
	var out string
	out = fmt.Sprintln("****")
	if data.val == nil {
		tags := data.actualResults
		tagKeys := make([]string, len(*tags))
		i := 0
		for k, _ := range *tags {
			tagKeys[i] = k
			i++
		}
		sort.Strings(tagKeys)
		for _, k := range tagKeys {
			if k == "$_" {
				continue
			}
			out += fmt.Sprintf("%s : %s\n", k, s.ts.GetNameFor((*tags)[k]))
		}
	} else {
		if data.val.IsObject() {
			export, _ := data.val.Export()
			mapExport := export.(map[string]string)
			for k, v := range mapExport {
				out += fmt.Sprintf("%s : %v\n", k, v)
			}
		} else {
			strVersion, _ := data.val.ToString()
			out += fmt.Sprintf("%s\n", strVersion)
		}
	}
	return out
}

// Web stuff
func (ses *Session) BuildJson(result interface{}) {
	data := result.(*GremlinResult)
	if !data.metaresult {
		if data.val == nil {
			obj := make(map[string]string)
			tags := data.actualResults
			tagKeys := make([]string, len(*tags))
			i := 0
			for k, _ := range *tags {
				tagKeys[i] = k
				i++
			}
			sort.Strings(tagKeys)
			for _, k := range tagKeys {
				obj[k] = ses.ts.GetNameFor((*tags)[k])
			}
			ses.dataOutput = append(ses.dataOutput, obj)
		} else {
			if data.val.IsObject() {
				export, _ := data.val.Export()
				ses.dataOutput = append(ses.dataOutput, export)
			} else {
				strVersion, _ := data.val.ToString()
				ses.dataOutput = append(ses.dataOutput, strVersion)
			}
		}
	}

}

func (ses *Session) GetJson() (interface{}, error) {
	defer ses.ClearJson()
	if ses.err != nil {
		return nil, ses.err
	}
	if ses.doHalt {
		return nil, halt
	}
	return ses.dataOutput, nil
}

func (ses *Session) ClearJson() {
	ses.dataOutput = nil
}
<commit_msg>Canonicalise gremlin receiver names<commit_after>// Copyright 2014 The Cayley Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gremlin

import (
	"errors"
	"fmt"
	"sort"
	"time"

	"github.com/robertkrimen/otto"

	"github.com/google/cayley/graph"
)

type Session struct {
	ts                   graph.TripleStore
	currentChannel       chan interface{}
	env                  *otto.Otto
	debug                bool
	limit                int
	count                int
	dataOutput           []interface{}
	lookingForQueryShape bool
	queryShape           map[string]interface{}
	err                  error
	script               *otto.Script
	doHalt               bool
	timeoutSec           time.Duration
	emptyEnv             *otto.Otto
}

func NewSession(inputTripleStore graph.TripleStore, timeoutSec int, persist bool) *Session {
	var g Session
	g.ts = inputTripleStore
	g.env = BuildEnviron(&g)
	g.limit = -1
	g.count = 0
	g.lookingForQueryShape = false
	if persist {
		g.emptyEnv = g.env
	}
	if timeoutSec < 0 {
		g.timeoutSec = time.Duration(-1)
	} else {
		g.timeoutSec = time.Duration(timeoutSec)
	}
	g.ClearJson()
	return &g
}

type GremlinResult struct {
	metaresult    bool
	err           string
	val           *otto.Value
	actualResults *map[string]graph.TSVal
}

func (s *Session) ToggleDebug() {
	s.debug = !s.debug
}

func (s *Session) GetQuery(input string, output_struct chan map[string]interface{}) {
	defer close(output_struct)
	s.queryShape = make(map[string]interface{})
	s.lookingForQueryShape = true
	s.env.Run(input)
	output_struct <- s.queryShape
	s.queryShape = nil
}

func (s *Session) InputParses(input string) (graph.ParseResult, error) {
	script, err := s.env.Compile("", input)
	if err != nil {
		return graph.ParseFail, err
	}
	s.script = script
	return graph.Parsed, nil
}

func (s *Session) SendResult(result *GremlinResult) bool {
	if s.limit >= 0 && s.limit == s.count {
		return false
	}
	if s.doHalt {
		return false
	}
	if s.currentChannel != nil {
		s.currentChannel <- result
		s.count++
		if s.limit >= 0 && s.limit == s.count {
			return false
		} else {
			return true
		}
	}
	return false
}

var halt = errors.New("Query Timeout")

func (s *Session) runUnsafe(input interface{}) (otto.Value, error) {
	s.doHalt = false
	defer func() {
		if caught := recover(); caught != nil {
			if caught == halt {
				s.err = halt
				return
			}
			panic(caught) // Something else happened, repanic!
		}
	}()

	s.env.Interrupt = make(chan func(), 1) // The buffer prevents blocking

	if s.timeoutSec != -1 {
		go func() {
			time.Sleep(s.timeoutSec * time.Second) // Stop after two seconds
			s.doHalt = true
			if s.env != nil {
				s.env.Interrupt <- func() {
					panic(halt)
				}
				s.env = s.emptyEnv
			}
		}()
	}

	return s.env.Run(input) // Here be dragons (risky code)
}

func (s *Session) ExecInput(input string, out chan interface{}, limit int) {
	defer close(out)
	s.err = nil
	s.currentChannel = out
	var err error
	var value otto.Value
	if s.script == nil {
		value, err = s.runUnsafe(input)
	} else {
		value, err = s.runUnsafe(s.script)
	}
	if err != nil {
		out <- &GremlinResult{metaresult: true,
			err:           err.Error(),
			val:           &value,
			actualResults: nil}
	} else {
		out <- &GremlinResult{metaresult: true,
			err:           "",
			val:           &value,
			actualResults: nil}
	}
	s.currentChannel = nil
	s.script = nil
	s.env = s.emptyEnv
	return
}

func (s *Session) ToText(result interface{}) string {
	data := result.(*GremlinResult)
	if data.metaresult {
		if data.err != "" {
			return fmt.Sprintln("Error: ", data.err)
		}
		if data.val != nil {
			s, _ := data.val.Export()
			if data.val.IsObject() {
				typeVal, _ := data.val.Object().Get("_gremlin_type")
				if !typeVal.IsUndefined() {
					s = "[internal Iterator]"
				}
			}
			return fmt.Sprintln("=>", s)
		}
		return ""
	}
	var out string
	out = fmt.Sprintln("****")
	if data.val == nil {
		tags := data.actualResults
		tagKeys := make([]string, len(*tags))
		i := 0
		for k, _ := range *tags {
			tagKeys[i] = k
			i++
		}
		sort.Strings(tagKeys)
		for _, k := range tagKeys {
			if k == "$_" {
				continue
			}
			out += fmt.Sprintf("%s : %s\n", k, s.ts.GetNameFor((*tags)[k]))
		}
	} else {
		if data.val.IsObject() {
			export, _ := data.val.Export()
			mapExport := export.(map[string]string)
			for k, v := range mapExport {
				out += fmt.Sprintf("%s : %v\n", k, v)
			}
		} else {
			strVersion, _ := data.val.ToString()
			out += fmt.Sprintf("%s\n", strVersion)
		}
	}
	return out
}

// Web stuff
func (ses *Session) BuildJson(result interface{}) {
	data := result.(*GremlinResult)
	if !data.metaresult {
		if data.val == nil {
			obj := make(map[string]string)
			tags := data.actualResults
			tagKeys := make([]string, len(*tags))
			i := 0
			for k, _ := range *tags {
				tagKeys[i] = k
				i++
			}
			sort.Strings(tagKeys)
			for _, k := range tagKeys {
				obj[k] = ses.ts.GetNameFor((*tags)[k])
			}
			ses.dataOutput = append(ses.dataOutput, obj)
		} else {
			if data.val.IsObject() {
				export, _ := data.val.Export()
				ses.dataOutput = append(ses.dataOutput, export)
			} else {
				strVersion, _ := data.val.ToString()
				ses.dataOutput = append(ses.dataOutput, strVersion)
			}
		}
	}

}

func (ses *Session) GetJson() (interface{}, error) {
	defer ses.ClearJson()
	if ses.err != nil {
		return nil, ses.err
	}
	if ses.doHalt {
		return nil, halt
	}
	return ses.dataOutput, nil
}

func (ses *Session) ClearJson() {
	ses.dataOutput = nil
}
<|endoftext|>


<commit_before>package gitter

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

	"github.com/mreiferson/go-httpclient"
)

var defaultConnectionWaitTime time.Duration = 3000 // millis
var defaultConnectionMaxRetries = 5

// Stream initialize stream
func (gitter *Gitter) Stream(roomID string) *Stream {
	return &Stream{
		url:    streamBaseURL + "rooms/" + roomID + "/chatMessages",
		Event:  make(chan Event),
		gitter: gitter,
		streamConnection: gitter.newStreamConnection(
			defaultConnectionWaitTime,
			defaultConnectionMaxRetries),
	}
}

// Implemented to conform with https://developer.gitter.im/docs/streaming-api
func (gitter *Gitter) Listen(stream *Stream) {

	defer stream.destroy()

	var reader *bufio.Reader
	var gitterMessage Message
	lastKeepalive := time.Now().Unix()

	// connect
	stream.connect()

Loop:
	for {

		// if closed then stop trying
		if stream.isClosed() {
			stream.Event <- Event{
				Data: &GitterConnectionClosed{},
			}
			break Loop
		}

		resp := stream.getResponse()
		if resp.StatusCode != 200 {
			gitter.log(fmt.Sprintf("Unexpected response code %v", resp.StatusCode))
			continue
		}

		//"The JSON stream returns messages as JSON objects that are delimited by carriage return (\r)" <- Not true crap it's (\n) only
		reader = bufio.NewReader(resp.Body)
		line, err := reader.ReadBytes('\n')
		if err != nil {
			gitter.log("ReadBytes error: " + err.Error())
			stream.connect()
			continue
		}

		//Check if the line only consists of whitespace
		onlyWhitespace := true
		for _, b := range line {
			if b != ' ' && b != '\t' && b != '\r' && b != '\n' {
				onlyWhitespace = false
			}
		}

		if onlyWhitespace {
			//"Parsers must be tolerant of occasional extra newline characters placed between messages."
			currentKeepalive := time.Now().Unix() //interesting behavior of 100+ keepalives per seconds was observed
			if currentKeepalive-lastKeepalive > 10 {
				lastKeepalive = currentKeepalive
				gitter.log("Keepalive was received")
			}
			continue
		} else if stream.isClosed() {
			gitter.log("Stream closed")
			continue
		}

		// unmarshal the streamed data
		err = json.Unmarshal(line, &gitterMessage)
		if err != nil {
			gitter.log("JSON Unmarshal error: " + err.Error())
			continue
		}

		// we are here, then we got the good message. pipe it forward.
		stream.Event <- Event{
			Data: &MessageReceived{
				Message: gitterMessage,
			},
		}
	}

	gitter.log("Listening was completed")
}

// Stream holds stream data.
type Stream struct {
	url              string
	Event            chan Event
	streamConnection *streamConnection
	gitter           *Gitter
}

func (stream *Stream) destroy() {
	close(stream.Event)
	stream.streamConnection.currentRetries = 0
}

type Event struct {
	Data interface{}
}

type GitterConnectionClosed struct {
}

type MessageReceived struct {
	Message Message
}

// connect and try to reconnect with
func (stream *Stream) connect() {

	if stream.streamConnection.retries == stream.streamConnection.currentRetries {
		stream.Close()
		stream.gitter.log("Number of retries exceeded the max retries number, we are done here")
		return
	}

	res, err := stream.gitter.getResponse(stream.url, stream)
	if err != nil || res.StatusCode != 200 {
		stream.gitter.log(fmt.Sprintf("Failed to get response, trying reconnect (Status code: %v)", res.StatusCode))
		stream.gitter.log(err)

		// sleep and wait
		stream.streamConnection.currentRetries++
		time.Sleep(time.Millisecond * stream.streamConnection.wait * time.Duration(stream.streamConnection.currentRetries))

		// connect again
		stream.Close()
		stream.connect()
	} else {
		stream.gitter.log("Response was received")
		stream.streamConnection.currentRetries = 0
		stream.streamConnection.closed = false
		stream.streamConnection.response = res
	}
}

type streamConnection struct {

	// connection was closed
	closed bool

	// wait time till next try
	wait time.Duration

	// max tries to recover
	retries int

	// current streamed response
	response *http.Response

	// current request
	request *http.Request

	// current status
	currentRetries int
}

// Close the stream connection and stop receiving streamed data
func (stream *Stream) Close() {
	conn := stream.streamConnection
	conn.closed = true
	if conn.response != nil {
		stream.gitter.log("Stream connection close response")
		defer conn.response.Body.Close()
	}
	if conn.request != nil {
		stream.gitter.log("Stream connection close request")
		switch transport := stream.gitter.config.client.Transport.(type) {
		case *httpclient.Transport:
			transport.CancelRequest(conn.request)
		default:
		}
	}
}

func (stream *Stream) isClosed() bool {
	return stream.streamConnection.closed
}

func (stream *Stream) getResponse() *http.Response {
	return stream.streamConnection.response
}

// Optional, set stream connection properties
// wait - time in milliseconds of waiting between reconnections. Will grow exponentially.
// retries - number of reconnections retries before dropping the stream.
func (gitter *Gitter) newStreamConnection(wait time.Duration, retries int) *streamConnection {
	return &streamConnection{
		closed:  true,
		wait:    wait,
		retries: retries,
	}
}
<commit_msg>response can be nil, we shouldn't crash #15<commit_after>package gitter

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

	"github.com/mreiferson/go-httpclient"
)

var defaultConnectionWaitTime time.Duration = 3000 // millis
var defaultConnectionMaxRetries = 5

// Stream initialize stream
func (gitter *Gitter) Stream(roomID string) *Stream {
	return &Stream{
		url:    streamBaseURL + "rooms/" + roomID + "/chatMessages",
		Event:  make(chan Event),
		gitter: gitter,
		streamConnection: gitter.newStreamConnection(
			defaultConnectionWaitTime,
			defaultConnectionMaxRetries),
	}
}

// Implemented to conform with https://developer.gitter.im/docs/streaming-api
func (gitter *Gitter) Listen(stream *Stream) {

	defer stream.destroy()

	var reader *bufio.Reader
	var gitterMessage Message
	lastKeepalive := time.Now().Unix()

	// connect
	stream.connect()

Loop:
	for {

		// if closed then stop trying
		if stream.isClosed() {
			stream.Event <- Event{
				Data: &GitterConnectionClosed{},
			}
			break Loop
		}

		resp := stream.getResponse()
		if resp.StatusCode != 200 {
			gitter.log(fmt.Sprintf("Unexpected response code %v", resp.StatusCode))
			continue
		}

		//"The JSON stream returns messages as JSON objects that are delimited by carriage return (\r)" <- Not true crap it's (\n) only
		reader = bufio.NewReader(resp.Body)
		line, err := reader.ReadBytes('\n')
		if err != nil {
			gitter.log("ReadBytes error: " + err.Error())
			stream.connect()
			continue
		}

		//Check if the line only consists of whitespace
		onlyWhitespace := true
		for _, b := range line {
			if b != ' ' && b != '\t' && b != '\r' && b != '\n' {
				onlyWhitespace = false
			}
		}

		if onlyWhitespace {
			//"Parsers must be tolerant of occasional extra newline characters placed between messages."
			currentKeepalive := time.Now().Unix() //interesting behavior of 100+ keepalives per seconds was observed
			if currentKeepalive-lastKeepalive > 10 {
				lastKeepalive = currentKeepalive
				gitter.log("Keepalive was received")
			}
			continue
		} else if stream.isClosed() {
			gitter.log("Stream closed")
			continue
		}

		// unmarshal the streamed data
		err = json.Unmarshal(line, &gitterMessage)
		if err != nil {
			gitter.log("JSON Unmarshal error: " + err.Error())
			continue
		}

		// we are here, then we got the good message. pipe it forward.
		stream.Event <- Event{
			Data: &MessageReceived{
				Message: gitterMessage,
			},
		}
	}

	gitter.log("Listening was completed")
}

// Stream holds stream data.
type Stream struct {
	url              string
	Event            chan Event
	streamConnection *streamConnection
	gitter           *Gitter
}

func (stream *Stream) destroy() {
	close(stream.Event)
	stream.streamConnection.currentRetries = 0
}

type Event struct {
	Data interface{}
}

type GitterConnectionClosed struct {
}

type MessageReceived struct {
	Message Message
}

// connect and try to reconnect with
func (stream *Stream) connect() {

	if stream.streamConnection.retries == stream.streamConnection.currentRetries {
		stream.Close()
		stream.gitter.log("Number of retries exceeded the max retries number, we are done here")
		return
	}

	res, err := stream.gitter.getResponse(stream.url, stream)
	if err != nil || res.StatusCode != 200 {
		stream.gitter.log("Failed to get response, trying reconnect")
		if res != nil {
			stream.gitter.log(fmt.Sprintf("Status code: %v", res.StatusCode))
		}
		stream.gitter.log(err)

		// sleep and wait
		stream.streamConnection.currentRetries++
		time.Sleep(time.Millisecond * stream.streamConnection.wait * time.Duration(stream.streamConnection.currentRetries))

		// connect again
		stream.Close()
		stream.connect()
	} else {
		stream.gitter.log("Response was received")
		stream.streamConnection.currentRetries = 0
		stream.streamConnection.closed = false
		stream.streamConnection.response = res
	}
}

type streamConnection struct {

	// connection was closed
	closed bool

	// wait time till next try
	wait time.Duration

	// max tries to recover
	retries int

	// current streamed response
	response *http.Response

	// current request
	request *http.Request

	// current status
	currentRetries int
}

// Close the stream connection and stop receiving streamed data
func (stream *Stream) Close() {
	conn := stream.streamConnection
	conn.closed = true
	if conn.response != nil {
		stream.gitter.log("Stream connection close response")
		defer conn.response.Body.Close()
	}
	if conn.request != nil {
		stream.gitter.log("Stream connection close request")
		switch transport := stream.gitter.config.client.Transport.(type) {
		case *httpclient.Transport:
			transport.CancelRequest(conn.request)
		default:
		}
	}
}

func (stream *Stream) isClosed() bool {
	return stream.streamConnection.closed
}

func (stream *Stream) getResponse() *http.Response {
	return stream.streamConnection.response
}

// Optional, set stream connection properties
// wait - time in milliseconds of waiting between reconnections. Will grow exponentially.
// retries - number of reconnections retries before dropping the stream.
func (gitter *Gitter) newStreamConnection(wait time.Duration, retries int) *streamConnection {
	return &streamConnection{
		closed:  true,
		wait:    wait,
		retries: retries,
	}
}
<|endoftext|>


<commit_before>// Package stripe provides a client for Stripe REST APIs.
package stripe

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// url is the public Stripe URL for APIs.
const defaultUrl = "https://api.stripe.com/v1"

// apiversion is the currently supported API version
const apiversion = "2014-08-20"

// clientversion is the binding version
const clientversion = "1.0"

// Backend is an interface for making calls against a Stripe service.
// This interface exists to enable mocking for during testing if needed.
type Backend interface {
	Call(method, path, token string, body *url.Values, v interface{}) error
}

// InternalBackend is the internal implementation for making HTTP calls to Stripe.
type InternalBackend struct {
	Url        string
	HttpClient *http.Client
}

// Key is the Stripe API key used globally in the binding.
var Key string

var debug bool
var backend Backend
var httpClient *http.Client

// SetDebug enables additional tracing globally.
// The method is designed for used during testing.
func SetDebug(value bool) {
	debug = value
}

// GetBackend returns the currently used backend in the binding.
func GetBackend() Backend {
	if backend == nil {
		backend = &InternalBackend{
			Url:        defaultUrl,
			HttpClient: GetHttpClient(),
		}
	}

	return backend
}

// SetBackend sets the backend used in the binding.
func SetBackend(b Backend) {
	backend = b
}

// SetHttpClient sets the HTTP client used in the binding.
func SetHttpClient(c *http.Client) {
	httpClient = c
}

// GetHttpClient returns the HTTP client used in the binding.
func GetHttpClient() *http.Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	return httpClient
}

// Call is the Backend.Call implementation for invoking Stripe APIs.
func (s *InternalBackend) Call(method, path, token string, body *url.Values, v interface{}) error {
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}

	path = s.Url + path

	if body != nil && len(*body) > 0 {
		path += "?" + body.Encode()
	}

	req, err := http.NewRequest(method, path, nil)
	if err != nil {
		log.Printf("Cannot create Stripe request: %v\n", err)
		return err
	}

	req.SetBasicAuth(token, "")
	req.Header.Add("Stripe-Version", apiversion)
	req.Header.Add("X-Stripe-Client-User-Agent", "Stripe.Go-"+clientversion)

	log.Printf("Requesting %v %q\n", method, path)
	start := time.Now()

	res, err := s.HttpClient.Do(req)

	if debug {
		log.Printf("Completed in %v\n", time.Since(start))
	}

	if err != nil {
		log.Printf("Request to Stripe failed: %v\n", err)
		return err
	}
	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Printf("Cannot parse Stripe response: %v\n", err)
		return err
	}

	if res.StatusCode >= 400 {
		// for some odd reason, the Erro structure doesn't unmarshal
		// initially I thought it was because it's a struct inside of a struct
		// but even after trying that, it still didn't work
		// so unmarshalling to a map for now and parsing the results manually
		// but should investigate later
		var errMap map[string]interface{}
		json.Unmarshal(resBody, &errMap)

		if e, found := errMap["error"]; !found {
			err := errors.New(string(resBody))
			log.Printf("Unparsable error returned from Stripe: %v\n", err)
			return err
		} else {
			root := e.(map[string]interface{})
			err := &Error{
				Type: ErrorType(root["type"].(string)),
				Msg:  root["message"].(string),
			}

			if code, found := root["code"]; found {
				err.Code = ErrorCode(code.(string))
			}

			if param, found := root["param"]; found {
				err.Param = param.(string)
			}

			log.Printf("Error encountered from Stripe: %v\n", err)
			return err
		}
	}

	if debug {
		log.Printf("Stripe Response: %q\n", resBody)
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}
<commit_msg>Create backends with a custom Url through NewInternalBackend<commit_after>// Package stripe provides a client for Stripe REST APIs.
package stripe

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// url is the public Stripe URL for APIs.
const defaultUrl = "https://api.stripe.com/v1"

// apiversion is the currently supported API version
const apiversion = "2014-08-20"

// clientversion is the binding version
const clientversion = "1.0"

// Backend is an interface for making calls against a Stripe service.
// This interface exists to enable mocking for during testing if needed.
type Backend interface {
	Call(method, path, token string, body *url.Values, v interface{}) error
}

// InternalBackend is the internal implementation for making HTTP calls to Stripe.
type InternalBackend struct {
	url        string
	httpClient *http.Client
}

func NewInternalBackend(httpClient *http.Client, url string) *InternalBackend {
	if url == "" {
		url = defaultUrl
	}

	return &InternalBackend{
		url:        url,
		httpClient: GetHttpClient(),
	}
}

// Key is the Stripe API key used globally in the binding.
var Key string

var debug bool
var backend Backend
var httpClient *http.Client

// SetDebug enables additional tracing globally.
// The method is designed for used during testing.
func SetDebug(value bool) {
	debug = value
}

// GetBackend returns the currently used backend in the binding.
func GetBackend() Backend {
	if backend == nil {
		backend = NewInternalBackend(GetHttpClient(), "")
	}

	return backend
}

// SetBackend sets the backend used in the binding.
func SetBackend(b Backend) {
	backend = b
}

// SetHttpClient sets the HTTP client used in the binding.
func SetHttpClient(c *http.Client) {
	httpClient = c
}

// GetHttpClient returns the HTTP client used in the binding.
func GetHttpClient() *http.Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	return httpClient
}

// Call is the Backend.Call implementation for invoking Stripe APIs.
func (s *InternalBackend) Call(method, path, token string, body *url.Values, v interface{}) error {
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}

	path = s.url + path

	if body != nil && len(*body) > 0 {
		path += "?" + body.Encode()
	}

	req, err := http.NewRequest(method, path, nil)
	if err != nil {
		log.Printf("Cannot create Stripe request: %v\n", err)
		return err
	}

	req.SetBasicAuth(token, "")
	req.Header.Add("Stripe-Version", apiversion)
	req.Header.Add("X-Stripe-Client-User-Agent", "Stripe.Go-"+clientversion)

	log.Printf("Requesting %v %q\n", method, path)
	start := time.Now()

	res, err := s.httpClient.Do(req)

	if debug {
		log.Printf("Completed in %v\n", time.Since(start))
	}

	if err != nil {
		log.Printf("Request to Stripe failed: %v\n", err)
		return err
	}
	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Printf("Cannot parse Stripe response: %v\n", err)
		return err
	}

	if res.StatusCode >= 400 {
		// for some odd reason, the Erro structure doesn't unmarshal
		// initially I thought it was because it's a struct inside of a struct
		// but even after trying that, it still didn't work
		// so unmarshalling to a map for now and parsing the results manually
		// but should investigate later
		var errMap map[string]interface{}
		json.Unmarshal(resBody, &errMap)

		if e, found := errMap["error"]; !found {
			err := errors.New(string(resBody))
			log.Printf("Unparsable error returned from Stripe: %v\n", err)
			return err
		} else {
			root := e.(map[string]interface{})
			err := &Error{
				Type: ErrorType(root["type"].(string)),
				Msg:  root["message"].(string),
			}

			if code, found := root["code"]; found {
				err.Code = ErrorCode(code.(string))
			}

			if param, found := root["param"]; found {
				err.Param = param.(string)
			}

			log.Printf("Error encountered from Stripe: %v\n", err)
			return err
		}
	}

	if debug {
		log.Printf("Stripe Response: %q\n", resBody)
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"

	"gopkg.in/lxc/go-lxc.v2"

	lxd "github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/config"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/node"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/osarch"
	"github.com/lxc/lxd/shared/version"
	"github.com/pkg/errors"
)

var api10Cmd = Command{
	name:         "",
	untrustedGet: true,
	get:          api10Get,
	put:          api10Put,
	patch:        api10Patch,
}

var api10 = []APIEndpoint{
	api10Cmd,
	api10ResourcesCmd,
	certificateCmd,
	certificatesCmd,
	clusterCmd,
	clusterNodeCmd,
	clusterNodesCmd,
	containerBackupCmd,
	containerBackupExportCmd,
	containerBackupsCmd,
	containerCmd,
	containerConsoleCmd,
	containerExecCmd,
	containerFileCmd,
	containerLogCmd,
	containerLogsCmd,
	containerMetadataCmd,
	containerMetadataTemplatesCmd,
	containersCmd,
	containerSnapshotCmd,
	containerSnapshotsCmd,
	containerStateCmd,
	eventsCmd,
	imageAliasCmd,
	imageAliasesCmd,
	imageCmd,
	imageExportCmd,
	imageRefreshCmd,
	imagesCmd,
	imageSecretCmd,
	networkCmd,
	networkLeasesCmd,
	networksCmd,
	networkStateCmd,
	operationCmd,
	operationsCmd,
	operationWait,
	operationWebsocket,
	profileCmd,
	profilesCmd,
	projectCmd,
	projectsCmd,
	storagePoolCmd,
	storagePoolResourcesCmd,
	storagePoolsCmd,
	storagePoolVolumesCmd,
	storagePoolVolumeSnapshotsTypeCmd,
	storagePoolVolumeSnapshotTypeCmd,
	storagePoolVolumesTypeCmd,
	storagePoolVolumeTypeContainerCmd,
	storagePoolVolumeTypeCustomCmd,
	storagePoolVolumeTypeImageCmd,
}

func api10Get(d *Daemon, r *http.Request) Response {
	authMethods := []string{"tls"}
	err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
		config, err := cluster.ConfigLoad(tx)
		if err != nil {
			return err
		}

		candidURL, _, _, _ := config.CandidServer()
		if candidURL != "" {
			authMethods = append(authMethods, "candid")
		}

		return nil
	})
	if err != nil {
		return SmartError(err)
	}
	srv := api.ServerUntrusted{
		APIExtensions: version.APIExtensions,
		APIStatus:     "stable",
		APIVersion:    version.APIVersion,
		Public:        false,
		Auth:          "untrusted",
		AuthMethods:   authMethods,
	}

	// If untrusted, return now
	if d.checkTrustedClient(r) != nil {
		return SyncResponseETag(true, srv, nil)
	}

	// If a target was specified, forward the request to the relevant node.
	response := ForwardedResponseIfTargetIsRemote(d, r)
	if response != nil {
		return response
	}

	srv.Auth = "trusted"

	uname, err := shared.Uname()
	if err != nil {
		return InternalError(err)
	}

	address, err := node.HTTPSAddress(d.db)
	if err != nil {
		return InternalError(err)
	}
	addresses, err := util.ListenAddresses(address)
	if err != nil {
		return InternalError(err)
	}

	clustered, err := cluster.Enabled(d.db)
	if err != nil {
		return SmartError(err)
	}

	// When clustered, use the node name, otherwise use the hostname.
	var serverName string
	if clustered {
		err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
			serverName, err = tx.NodeName()
			return err
		})
		if err != nil {
			return SmartError(err)
		}
	} else {
		hostname, err := os.Hostname()
		if err != nil {
			return SmartError(err)
		}
		serverName = hostname
	}

	certificate := string(d.endpoints.NetworkPublicKey())
	var certificateFingerprint string
	if certificate != "" {
		certificateFingerprint, err = shared.CertFingerprintStr(certificate)
		if err != nil {
			return InternalError(err)
		}
	}

	architectures := []string{}

	for _, architecture := range d.os.Architectures {
		architectureName, err := osarch.ArchitectureName(architecture)
		if err != nil {
			return InternalError(err)
		}
		architectures = append(architectures, architectureName)
	}

	project := r.FormValue("project")
	if project == "" {
		project = "default"
	}

	env := api.ServerEnvironment{
		Addresses:              addresses,
		Architectures:          architectures,
		Certificate:            certificate,
		CertificateFingerprint: certificateFingerprint,
		Driver:                 "lxc",
		DriverVersion:          lxc.Version(),
		Kernel:                 uname.Sysname,
		KernelArchitecture:     uname.Machine,
		KernelVersion:          uname.Release,
		Project:                project,
		Server:                 "lxd",
		ServerPid:              os.Getpid(),
		ServerVersion:          version.Version,
		ServerClustered:        clustered,
		ServerName:             serverName,
	}

	env.KernelFeatures = map[string]string{
		"netnsid_getifaddrs": fmt.Sprintf("%v", d.os.NetnsGetifaddrs),
		"uevent_injection":   fmt.Sprintf("%v", d.os.UeventInjection),
		"unpriv_fscaps":      fmt.Sprintf("%v", d.os.VFS3Fscaps),
		"seccomp_listener":   fmt.Sprintf("%v", d.os.SeccompListener),
		"shiftfs":            fmt.Sprintf("%v", d.os.Shiftfs),
	}

	drivers := readStoragePoolDriversCache()
	for driver, version := range drivers {
		if env.Storage != "" {
			env.Storage = env.Storage + " | " + driver
		} else {
			env.Storage = driver
		}

		// Get the version of the storage drivers in use.
		if env.StorageVersion != "" {
			env.StorageVersion = env.StorageVersion + " | " + version
		} else {
			env.StorageVersion = version
		}
	}

	fullSrv := api.Server{ServerUntrusted: srv}
	fullSrv.Environment = env
	fullSrv.Config, err = daemonConfigRender(d.State())
	if err != nil {
		return InternalError(err)
	}

	return SyncResponseETag(true, fullSrv, fullSrv.Config)
}

func api10Put(d *Daemon, r *http.Request) Response {
	// If a target was specified, forward the request to the relevant node.
	response := ForwardedResponseIfTargetIsRemote(d, r)
	if response != nil {
		return response
	}

	req := api.ServerPut{}
	if err := shared.ReadToJSON(r.Body, &req); err != nil {
		return BadRequest(err)
	}

	// If this is a notification from a cluster node, just run the triggers
	// for reacting to the values that changed.
	if isClusterNotification(r) {
		logger.Debugf("Handling config changed notification")
		changed := make(map[string]string)
		for key, value := range req.Config {
			changed[key] = value.(string)
		}
		var config *cluster.Config
		err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
			var err error
			config, err = cluster.ConfigLoad(tx)
			return err
		})
		if err != nil {
			return SmartError(err)
		}
		err = doApi10UpdateTriggers(d, nil, changed, nil, config)
		if err != nil {
			return SmartError(err)
		}
		return EmptySyncResponse
	}

	render, err := daemonConfigRender(d.State())
	if err != nil {
		return SmartError(err)
	}
	err = util.EtagCheck(r, render)
	if err != nil {
		return PreconditionFailed(err)
	}

	return doApi10Update(d, req, false)
}

func api10Patch(d *Daemon, r *http.Request) Response {
	// If a target was specified, forward the request to the relevant node.
	response := ForwardedResponseIfTargetIsRemote(d, r)
	if response != nil {
		return response
	}

	render, err := daemonConfigRender(d.State())
	if err != nil {
		return InternalError(err)
	}
	err = util.EtagCheck(r, render)
	if err != nil {
		return PreconditionFailed(err)
	}

	req := api.ServerPut{}
	if err := shared.ReadToJSON(r.Body, &req); err != nil {
		return BadRequest(err)
	}

	if req.Config == nil {
		return EmptySyncResponse
	}

	return doApi10Update(d, req, true)
}

func doApi10Update(d *Daemon, req api.ServerPut, patch bool) Response {
	// First deal with config specific to the local daemon
	nodeValues := map[string]interface{}{}

	for key := range node.ConfigSchema {
		value, ok := req.Config[key]
		if ok {
			nodeValues[key] = value
			delete(req.Config, key)
		}
	}

	nodeChanged := map[string]string{}
	var newNodeConfig *node.Config
	err := d.db.Transaction(func(tx *db.NodeTx) error {
		var err error
		newNodeConfig, err = node.ConfigLoad(tx)
		if err != nil {
			return errors.Wrap(err, "Failed to load node config")
		}

		// We currently don't allow changing the cluster.https_address
		// once it's set.
		curClusterAddress := newNodeConfig.ClusterAddress()
		newClusterAddress, ok := nodeValues["cluster.https_address"]

		if ok && curClusterAddress != "" && !util.IsAddressCovered(newClusterAddress.(string), curClusterAddress) {
			return fmt.Errorf("Changing cluster.https_address is currently not supported")
		}

		if patch {
			nodeChanged, err = newNodeConfig.Patch(nodeValues)
		} else {
			nodeChanged, err = newNodeConfig.Replace(nodeValues)
		}
		return err
	})
	if err != nil {
		switch err.(type) {
		case config.ErrorList:
			return BadRequest(err)
		default:
			return SmartError(err)
		}
	}

	// Then deal with cluster wide configuration
	var clusterChanged map[string]string
	var newClusterConfig *cluster.Config
	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
		var err error
		newClusterConfig, err = cluster.ConfigLoad(tx)
		if err != nil {
			return errors.Wrap(err, "Failed to load cluster config")
		}
		if patch {
			clusterChanged, err = newClusterConfig.Patch(req.Config)
		} else {
			clusterChanged, err = newClusterConfig.Replace(req.Config)
		}
		return err
	})
	if err != nil {
		switch err.(type) {
		case config.ErrorList:
			return BadRequest(err)
		default:
			return SmartError(err)
		}
	}

	// Notify the other nodes about changes
	notifier, err := cluster.NewNotifier(d.State(), d.endpoints.NetworkCert(), cluster.NotifyAlive)
	if err != nil {
		return SmartError(err)
	}
	err = notifier(func(client lxd.ContainerServer) error {
		server, etag, err := client.GetServer()
		if err != nil {
			return err
		}
		serverPut := server.Writable()
		serverPut.Config = make(map[string]interface{})
		// Only propagated cluster-wide changes
		for key, value := range clusterChanged {
			serverPut.Config[key] = value
		}
		return client.UpdateServer(serverPut, etag)
	})
	if err != nil {
		logger.Debugf("Failed to notify other nodes about config change: %v", err)
		return SmartError(err)
	}

	err = doApi10UpdateTriggers(d, nodeChanged, clusterChanged, newNodeConfig, newClusterConfig)
	if err != nil {
		return SmartError(err)
	}

	return EmptySyncResponse
}

func doApi10UpdateTriggers(d *Daemon, nodeChanged, clusterChanged map[string]string, nodeConfig *node.Config, clusterConfig *cluster.Config) error {
	maasChanged := false
	candidChanged := false

	for key := range clusterChanged {
		switch key {
		case "core.proxy_http":
			fallthrough
		case "core.proxy_https":
			fallthrough
		case "core.proxy_ignore_hosts":
			daemonConfigSetProxy(d, clusterConfig)
		case "maas.api.url":
			fallthrough
		case "maas.api.key":
			maasChanged = true
		case "candid.domains":
			fallthrough
		case "candid.expiry":
			fallthrough
		case "candid.api.key":
			fallthrough
		case "candid.api.url":
			candidChanged = true
		case "images.auto_update_interval":
			if !d.os.MockMode {
				d.taskAutoUpdate.Reset()
			}
		case "images.remote_cache_expiry":
			if !d.os.MockMode {
				d.taskPruneImages.Reset()
			}
		}
	}

	// Look for changed values. We do it sequentially because some keys are
	// correlated with others, and need to be processed first (for example
	// core.https_address need to be processed before
	// cluster.https_address).

	_, ok := nodeChanged["maas.machine"]
	if ok {
		maasChanged = true
	}

	value, ok := nodeChanged["core.https_address"]
	if ok {
		err := d.endpoints.NetworkUpdateAddress(value)
		if err != nil {
			return err
		}
	}

	value, ok = nodeChanged["cluster.https_address"]
	if ok {
		err := d.endpoints.ClusterUpdateAddress(value)
		if err != nil {
			return err
		}
	}

	value, ok = nodeChanged["core.debug_address"]
	if ok {
		err := d.endpoints.PprofUpdateAddress(value)
		if err != nil {
			return err
		}
	}

	if maasChanged {
		url, key := clusterConfig.MAASController()
		machine := nodeConfig.MAASMachine()
		err := d.setupMAASController(url, key, machine)
		if err != nil {
			return err
		}
	}

	if candidChanged {
		apiURL, apiKey, expiry, domains := clusterConfig.CandidServer()
		err := d.setupExternalAuthentication(apiURL, apiKey, expiry, domains)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>lxd/daemon: Port to APIEndpoint<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"

	"gopkg.in/lxc/go-lxc.v2"

	lxd "github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/config"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/node"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/osarch"
	"github.com/lxc/lxd/shared/version"
	"github.com/pkg/errors"
)

var api10Cmd = APIEndpoint{
	Get:   APIEndpointAction{Handler: api10Get, AllowUntrusted: true},
	Patch: APIEndpointAction{Handler: api10Patch},
	Put:   APIEndpointAction{Handler: api10Put},
}

var api10 = []APIEndpoint{
	api10Cmd,
	api10ResourcesCmd,
	certificateCmd,
	certificatesCmd,
	clusterCmd,
	clusterNodeCmd,
	clusterNodesCmd,
	containerBackupCmd,
	containerBackupExportCmd,
	containerBackupsCmd,
	containerCmd,
	containerConsoleCmd,
	containerExecCmd,
	containerFileCmd,
	containerLogCmd,
	containerLogsCmd,
	containerMetadataCmd,
	containerMetadataTemplatesCmd,
	containersCmd,
	containerSnapshotCmd,
	containerSnapshotsCmd,
	containerStateCmd,
	eventsCmd,
	imageAliasCmd,
	imageAliasesCmd,
	imageCmd,
	imageExportCmd,
	imageRefreshCmd,
	imagesCmd,
	imageSecretCmd,
	networkCmd,
	networkLeasesCmd,
	networksCmd,
	networkStateCmd,
	operationCmd,
	operationsCmd,
	operationWait,
	operationWebsocket,
	profileCmd,
	profilesCmd,
	projectCmd,
	projectsCmd,
	storagePoolCmd,
	storagePoolResourcesCmd,
	storagePoolsCmd,
	storagePoolVolumesCmd,
	storagePoolVolumeSnapshotsTypeCmd,
	storagePoolVolumeSnapshotTypeCmd,
	storagePoolVolumesTypeCmd,
	storagePoolVolumeTypeContainerCmd,
	storagePoolVolumeTypeCustomCmd,
	storagePoolVolumeTypeImageCmd,
}

func api10Get(d *Daemon, r *http.Request) Response {
	authMethods := []string{"tls"}
	err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
		config, err := cluster.ConfigLoad(tx)
		if err != nil {
			return err
		}

		candidURL, _, _, _ := config.CandidServer()
		if candidURL != "" {
			authMethods = append(authMethods, "candid")
		}

		return nil
	})
	if err != nil {
		return SmartError(err)
	}
	srv := api.ServerUntrusted{
		APIExtensions: version.APIExtensions,
		APIStatus:     "stable",
		APIVersion:    version.APIVersion,
		Public:        false,
		Auth:          "untrusted",
		AuthMethods:   authMethods,
	}

	// If untrusted, return now
	if d.checkTrustedClient(r) != nil {
		return SyncResponseETag(true, srv, nil)
	}

	// If a target was specified, forward the request to the relevant node.
	response := ForwardedResponseIfTargetIsRemote(d, r)
	if response != nil {
		return response
	}

	srv.Auth = "trusted"

	uname, err := shared.Uname()
	if err != nil {
		return InternalError(err)
	}

	address, err := node.HTTPSAddress(d.db)
	if err != nil {
		return InternalError(err)
	}
	addresses, err := util.ListenAddresses(address)
	if err != nil {
		return InternalError(err)
	}

	clustered, err := cluster.Enabled(d.db)
	if err != nil {
		return SmartError(err)
	}

	// When clustered, use the node name, otherwise use the hostname.
	var serverName string
	if clustered {
		err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
			serverName, err = tx.NodeName()
			return err
		})
		if err != nil {
			return SmartError(err)
		}
	} else {
		hostname, err := os.Hostname()
		if err != nil {
			return SmartError(err)
		}
		serverName = hostname
	}

	certificate := string(d.endpoints.NetworkPublicKey())
	var certificateFingerprint string
	if certificate != "" {
		certificateFingerprint, err = shared.CertFingerprintStr(certificate)
		if err != nil {
			return InternalError(err)
		}
	}

	architectures := []string{}

	for _, architecture := range d.os.Architectures {
		architectureName, err := osarch.ArchitectureName(architecture)
		if err != nil {
			return InternalError(err)
		}
		architectures = append(architectures, architectureName)
	}

	project := r.FormValue("project")
	if project == "" {
		project = "default"
	}

	env := api.ServerEnvironment{
		Addresses:              addresses,
		Architectures:          architectures,
		Certificate:            certificate,
		CertificateFingerprint: certificateFingerprint,
		Driver:                 "lxc",
		DriverVersion:          lxc.Version(),
		Kernel:                 uname.Sysname,
		KernelArchitecture:     uname.Machine,
		KernelVersion:          uname.Release,
		Project:                project,
		Server:                 "lxd",
		ServerPid:              os.Getpid(),
		ServerVersion:          version.Version,
		ServerClustered:        clustered,
		ServerName:             serverName,
	}

	env.KernelFeatures = map[string]string{
		"netnsid_getifaddrs": fmt.Sprintf("%v", d.os.NetnsGetifaddrs),
		"uevent_injection":   fmt.Sprintf("%v", d.os.UeventInjection),
		"unpriv_fscaps":      fmt.Sprintf("%v", d.os.VFS3Fscaps),
		"seccomp_listener":   fmt.Sprintf("%v", d.os.SeccompListener),
		"shiftfs":            fmt.Sprintf("%v", d.os.Shiftfs),
	}

	drivers := readStoragePoolDriversCache()
	for driver, version := range drivers {
		if env.Storage != "" {
			env.Storage = env.Storage + " | " + driver
		} else {
			env.Storage = driver
		}

		// Get the version of the storage drivers in use.
		if env.StorageVersion != "" {
			env.StorageVersion = env.StorageVersion + " | " + version
		} else {
			env.StorageVersion = version
		}
	}

	fullSrv := api.Server{ServerUntrusted: srv}
	fullSrv.Environment = env
	fullSrv.Config, err = daemonConfigRender(d.State())
	if err != nil {
		return InternalError(err)
	}

	return SyncResponseETag(true, fullSrv, fullSrv.Config)
}

func api10Put(d *Daemon, r *http.Request) Response {
	// If a target was specified, forward the request to the relevant node.
	response := ForwardedResponseIfTargetIsRemote(d, r)
	if response != nil {
		return response
	}

	req := api.ServerPut{}
	if err := shared.ReadToJSON(r.Body, &req); err != nil {
		return BadRequest(err)
	}

	// If this is a notification from a cluster node, just run the triggers
	// for reacting to the values that changed.
	if isClusterNotification(r) {
		logger.Debugf("Handling config changed notification")
		changed := make(map[string]string)
		for key, value := range req.Config {
			changed[key] = value.(string)
		}
		var config *cluster.Config
		err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
			var err error
			config, err = cluster.ConfigLoad(tx)
			return err
		})
		if err != nil {
			return SmartError(err)
		}
		err = doApi10UpdateTriggers(d, nil, changed, nil, config)
		if err != nil {
			return SmartError(err)
		}
		return EmptySyncResponse
	}

	render, err := daemonConfigRender(d.State())
	if err != nil {
		return SmartError(err)
	}
	err = util.EtagCheck(r, render)
	if err != nil {
		return PreconditionFailed(err)
	}

	return doApi10Update(d, req, false)
}

func api10Patch(d *Daemon, r *http.Request) Response {
	// If a target was specified, forward the request to the relevant node.
	response := ForwardedResponseIfTargetIsRemote(d, r)
	if response != nil {
		return response
	}

	render, err := daemonConfigRender(d.State())
	if err != nil {
		return InternalError(err)
	}
	err = util.EtagCheck(r, render)
	if err != nil {
		return PreconditionFailed(err)
	}

	req := api.ServerPut{}
	if err := shared.ReadToJSON(r.Body, &req); err != nil {
		return BadRequest(err)
	}

	if req.Config == nil {
		return EmptySyncResponse
	}

	return doApi10Update(d, req, true)
}

func doApi10Update(d *Daemon, req api.ServerPut, patch bool) Response {
	// First deal with config specific to the local daemon
	nodeValues := map[string]interface{}{}

	for key := range node.ConfigSchema {
		value, ok := req.Config[key]
		if ok {
			nodeValues[key] = value
			delete(req.Config, key)
		}
	}

	nodeChanged := map[string]string{}
	var newNodeConfig *node.Config
	err := d.db.Transaction(func(tx *db.NodeTx) error {
		var err error
		newNodeConfig, err = node.ConfigLoad(tx)
		if err != nil {
			return errors.Wrap(err, "Failed to load node config")
		}

		// We currently don't allow changing the cluster.https_address
		// once it's set.
		curClusterAddress := newNodeConfig.ClusterAddress()
		newClusterAddress, ok := nodeValues["cluster.https_address"]

		if ok && curClusterAddress != "" && !util.IsAddressCovered(newClusterAddress.(string), curClusterAddress) {
			return fmt.Errorf("Changing cluster.https_address is currently not supported")
		}

		if patch {
			nodeChanged, err = newNodeConfig.Patch(nodeValues)
		} else {
			nodeChanged, err = newNodeConfig.Replace(nodeValues)
		}
		return err
	})
	if err != nil {
		switch err.(type) {
		case config.ErrorList:
			return BadRequest(err)
		default:
			return SmartError(err)
		}
	}

	// Then deal with cluster wide configuration
	var clusterChanged map[string]string
	var newClusterConfig *cluster.Config
	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
		var err error
		newClusterConfig, err = cluster.ConfigLoad(tx)
		if err != nil {
			return errors.Wrap(err, "Failed to load cluster config")
		}
		if patch {
			clusterChanged, err = newClusterConfig.Patch(req.Config)
		} else {
			clusterChanged, err = newClusterConfig.Replace(req.Config)
		}
		return err
	})
	if err != nil {
		switch err.(type) {
		case config.ErrorList:
			return BadRequest(err)
		default:
			return SmartError(err)
		}
	}

	// Notify the other nodes about changes
	notifier, err := cluster.NewNotifier(d.State(), d.endpoints.NetworkCert(), cluster.NotifyAlive)
	if err != nil {
		return SmartError(err)
	}
	err = notifier(func(client lxd.ContainerServer) error {
		server, etag, err := client.GetServer()
		if err != nil {
			return err
		}
		serverPut := server.Writable()
		serverPut.Config = make(map[string]interface{})
		// Only propagated cluster-wide changes
		for key, value := range clusterChanged {
			serverPut.Config[key] = value
		}
		return client.UpdateServer(serverPut, etag)
	})
	if err != nil {
		logger.Debugf("Failed to notify other nodes about config change: %v", err)
		return SmartError(err)
	}

	err = doApi10UpdateTriggers(d, nodeChanged, clusterChanged, newNodeConfig, newClusterConfig)
	if err != nil {
		return SmartError(err)
	}

	return EmptySyncResponse
}

func doApi10UpdateTriggers(d *Daemon, nodeChanged, clusterChanged map[string]string, nodeConfig *node.Config, clusterConfig *cluster.Config) error {
	maasChanged := false
	candidChanged := false

	for key := range clusterChanged {
		switch key {
		case "core.proxy_http":
			fallthrough
		case "core.proxy_https":
			fallthrough
		case "core.proxy_ignore_hosts":
			daemonConfigSetProxy(d, clusterConfig)
		case "maas.api.url":
			fallthrough
		case "maas.api.key":
			maasChanged = true
		case "candid.domains":
			fallthrough
		case "candid.expiry":
			fallthrough
		case "candid.api.key":
			fallthrough
		case "candid.api.url":
			candidChanged = true
		case "images.auto_update_interval":
			if !d.os.MockMode {
				d.taskAutoUpdate.Reset()
			}
		case "images.remote_cache_expiry":
			if !d.os.MockMode {
				d.taskPruneImages.Reset()
			}
		}
	}

	// Look for changed values. We do it sequentially because some keys are
	// correlated with others, and need to be processed first (for example
	// core.https_address need to be processed before
	// cluster.https_address).

	_, ok := nodeChanged["maas.machine"]
	if ok {
		maasChanged = true
	}

	value, ok := nodeChanged["core.https_address"]
	if ok {
		err := d.endpoints.NetworkUpdateAddress(value)
		if err != nil {
			return err
		}
	}

	value, ok = nodeChanged["cluster.https_address"]
	if ok {
		err := d.endpoints.ClusterUpdateAddress(value)
		if err != nil {
			return err
		}
	}

	value, ok = nodeChanged["core.debug_address"]
	if ok {
		err := d.endpoints.PprofUpdateAddress(value)
		if err != nil {
			return err
		}
	}

	if maasChanged {
		url, key := clusterConfig.MAASController()
		machine := nodeConfig.MAASMachine()
		err := d.setupMAASController(url, key, machine)
		if err != nil {
			return err
		}
	}

	if candidChanged {
		apiURL, apiKey, expiry, domains := clusterConfig.CandidServer()
		err := d.setupExternalAuthentication(apiURL, apiKey, expiry, domains)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/yosisa/craft/docker"
	"github.com/yosisa/craft/rpc"
)

type CmdSubmit struct {
	Args struct {
		Manifest string `positional-arg-name:"MANIFEST"`
	} `positional-args:"yes" required:"yes"`
}

func (opts *CmdSubmit) Execute(args []string) error {
	m, err := docker.ParseManifest(opts.Args.Manifest)
	if err != nil {
		log.WithField("error", err).Fatal("Could not parse manifest")
	}

	caps := gatherCapabilities(gopts.agents())
	agent := findBestAgent(m, caps.Copy())
	if agent == "" {
		log.WithField("error", "No available agents").Fatal("Could not find best agent")
	}
	exlinks, err := resolveExLinks(m, caps)
	if err != nil {
		log.WithField("error", err).Fatal("Failed to resolve exlinks")
	}

	resp, err := rpc.Submit(agent, m, exlinks)
	if err != nil {
		log.WithField("error", err).Fatal("RPC failed")
	}
	log.WithFields(log.Fields{"name": m.Name, "agent": resp.Agent}).Info("Container running")
	return nil
}

func init() {
	parser.AddCommand("submit", "Run a container by the manifest", "", &CmdSubmit{})
}
<commit_msg>Add options to modify container names<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/yosisa/craft/docker"
	"github.com/yosisa/craft/rpc"
)

type CmdSubmit struct {
	NameSuffix    string `long:"name-suffix" description:"Append to name value"`
	ReplaceSuffix string `long:"replace-suffix" description:"Append to replace value"`
	Args          struct {
		Manifest string `positional-arg-name:"MANIFEST"`
	} `positional-args:"yes" required:"yes"`
}

func (opts *CmdSubmit) Execute(args []string) error {
	m, err := docker.ParseManifest(opts.Args.Manifest)
	if err != nil {
		log.WithField("error", err).Fatal("Could not parse manifest")
	}
	m.Name += opts.NameSuffix
	m.Replace += opts.ReplaceSuffix

	caps := gatherCapabilities(gopts.agents())
	agent := findBestAgent(m, caps.Copy())
	if agent == "" {
		log.WithField("error", "No available agents").Fatal("Could not find best agent")
	}
	exlinks, err := resolveExLinks(m, caps)
	if err != nil {
		log.WithField("error", err).Fatal("Failed to resolve exlinks")
	}

	resp, err := rpc.Submit(agent, m, exlinks)
	if err != nil {
		log.WithField("error", err).Fatal("RPC failed")
	}
	log.WithFields(log.Fields{"name": m.Name, "agent": resp.Agent}).Info("Container running")
	return nil
}

func init() {
	parser.AddCommand("submit", "Run a container by the manifest", "", &CmdSubmit{})
}
<|endoftext|>