<commit_before>package tchannel

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"io"
	"time"

	"github.com/uber/tchannel/golang/typed"
	"golang.org/x/net/context"
)

// maxOperationSize is the maximum size of arg1.
const maxOperationSize = 16 * 1024

// beginCall begins an outbound call on the connection
func (c *Connection) beginCall(ctx context.Context, serviceName string, callOptions *CallOptions) (*OutboundCall, error) {
	switch c.readState() {
	case connectionActive, connectionStartClose:
		break
	case connectionInboundClosed, connectionClosed:
		return nil, ErrConnectionClosed
	case connectionWaitingToRecvInitReq, connectionWaitingToSendInitReq, connectionWaitingToRecvInitRes:
		return nil, ErrConnectionNotReady
	default:
		return nil, errConnectionUnknownState
	}

	deadline, ok := ctx.Deadline()
	// No deadline was set, we should not support no deadlines.
	if !ok {
		return nil, ErrTimeoutRequired
	}
	timeToLive := deadline.Sub(time.Now())
	if timeToLive <= 0 {
		return nil, ErrTimeout
	}

	requestID := c.NextMessageID()
	mex, err := c.outbound.newExchange(ctx, c.framePool, messageTypeCallReq, requestID, 512)
	if err != nil {
		return nil, err
	}

	headers := transportHeaders{
		CallerName: c.localPeerInfo.ServiceName,
	}
	callOptions.setHeaders(headers)

	call := new(OutboundCall)
	call.mex = mex
	call.conn = c
	call.callReq = callReq{
		id:         requestID,
		Headers:    headers,
		Service:    serviceName,
		TimeToLive: timeToLive,
	}
	call.statsReporter = c.statsReporter
	call.createStatsTags(c.commonStatsTags)
	call.log = c.log.WithFields(LogFields{{"Out-Call", requestID}})

	// TODO(mmihic): It'd be nice to do this without an fptr
	call.messageForFragment = func(initial bool) message {
		if initial {
			return &call.callReq
		}

		return new(callReqContinue)
	}

	call.contents = newFragmentingWriter(call, c.checksumType.New())
	span := CurrentSpan(ctx)
	if span != nil {
		call.callReq.Tracing = *span.NewChildSpan()
	} else {
		// TODO(mmihic): Potentially reject calls that are made outside a root context?
		call.callReq.Tracing.EnableTracing(false)
	}

	call.AddBinaryAnnotation(BinaryAnnotation{Key: "cn", Value: call.callReq.Headers[CallerName]})
	call.AddBinaryAnnotation(BinaryAnnotation{Key: "as", Value: call.callReq.Headers[ArgScheme]})
	call.AddAnnotation(AnnotationKeyClientSend)

	response := new(OutboundCallResponse)
	response.startedAt = timeNow()
	response.mex = mex
	response.log = c.log.WithFields(LogFields{{"Out-Response", requestID}})
	response.messageForFragment = func(initial bool) message {
		if initial {
			call.AddAnnotation(AnnotationKeyClientReceive)
			call.Report(call.callReq.Tracing, c.traceReporter)
			return &response.callRes
		}

		return new(callResContinue)
	}
	response.contents = newFragmentingReader(response)
	response.statsReporter = call.statsReporter
	response.commonStatsTags = call.commonStatsTags
	call.response = response
	return call, nil
}

// handleCallRes handles an incoming call req message, forwarding the
// frame to the response channel waiting for it
func (c *Connection) handleCallRes(frame *Frame) bool {
	if err := c.outbound.forwardPeerFrame(frame); err != nil {
		c.outbound.removeExchange(frame.Header.ID)
		return true
	}
	return false
}

// handleCallResContinue handles an incoming call res continue message,
// forwarding the frame to the response channel waiting for it
func (c *Connection) handleCallResContinue(frame *Frame) bool {
	if err := c.outbound.forwardPeerFrame(frame); err != nil {
		c.outbound.removeExchange(frame.Header.ID)
		return true
	}
	return false
}

// An OutboundCall is an active call to a remote peer.  A client makes a call
// by calling BeginCall on the Channel, writing argument content via
// ArgWriter2() ArgWriter3(), and then reading reading response data via the
// ArgReader2() and ArgReader3() methods on the Response() object.
type OutboundCall struct {
	reqResWriter
	Annotations

	callReq         callReq
	response        *OutboundCallResponse
	statsReporter   StatsReporter
	commonStatsTags map[string]string
}

// Response provides access to the call's response object, which can be used to
// read response arguments
func (call *OutboundCall) Response() *OutboundCallResponse {
	return call.response
}

// createStatsTags creates the common stats tags, if they are not already created.
func (call *OutboundCall) createStatsTags(connectionTags map[string]string) {
	call.commonStatsTags = map[string]string{
		"target-service": call.callReq.Service,
	}
	for k, v := range connectionTags {
		call.commonStatsTags[k] = v
	}
}

// writeOperation writes the operation (arg1) to the call
func (call *OutboundCall) writeOperation(operation []byte) error {
	if len(operation) > maxOperationSize {
		return ErrOperationTooLarge
	}

	// TODO(prashant): Should operation become part of BeginCall so this can use Format directly.
	if call.callReq.Headers[ArgScheme] != HTTP.String() {
		call.commonStatsTags["target-endpoint"] = string(operation)
	}

	call.statsReporter.IncCounter("outbound.calls.send", call.commonStatsTags, 1)
	return NewArgWriter(call.arg1Writer()).Write(operation)
}

// Arg2Writer returns a WriteCloser that can be used to write the second argument.
// The returned writer must be closed once the write is complete.
func (call *OutboundCall) Arg2Writer() (ArgWriter, error) {
	return call.arg2Writer()
}

// Arg3Writer returns a WriteCloser that can be used to write the last argument.
// The returned writer must be closed once the write is complete.
func (call *OutboundCall) Arg3Writer() (ArgWriter, error) {
	return call.arg3Writer()
}

func (call *OutboundCall) doneSending() {}

// An OutboundCallResponse is the response to an outbound call
type OutboundCallResponse struct {
	reqResReader

	callRes callRes

	// startedAt is the time at which the outbound call was started.
	startedAt       time.Time
	statsReporter   StatsReporter
	commonStatsTags map[string]string
}

// ApplicationError returns true if the call resulted in an application level error
// TODO(mmihic): In current implementation, you must have called Arg2Reader before this
// method returns the proper value.  We should instead have this block until the first
// fragment is available, if the first fragment hasn't been received.
func (response *OutboundCallResponse) ApplicationError() bool {
	// TODO(mmihic): Wait for first fragment
	return response.callRes.ResponseCode == responseApplicationError
}

// Format the format of the request from the ArgScheme transport header.
func (response *OutboundCallResponse) Format() Format {
	return Format(response.callRes.Headers[ArgScheme])
}

// Arg2Reader returns an io.ReadCloser to read the second argument.
// The ReadCloser must be closed once the argument has been read.
func (response *OutboundCallResponse) Arg2Reader() (io.ReadCloser, error) {
	var operation []byte
	if err := NewArgReader(response.arg1Reader()).Read(&operation); err != nil {
		return nil, err
	}

	return response.arg2Reader()
}

// Arg3Reader returns an io.ReadCloser to read the last argument.
// The ReadCloser must be closed once the argument has been read.
func (response *OutboundCallResponse) Arg3Reader() (io.ReadCloser, error) {
	return response.arg3Reader()
}

// handleError andles an error coming back from the peer. If the error is a
// protocol level error, the entire connection will be closed.  If the error is
// a request specific error, it will be written to the request's response
// channel and converted into a SystemError returned from the next reader or
// access call.
func (c *Connection) handleError(frame *Frame) {
	var errorMessage errorMessage
	rbuf := typed.NewReadBuffer(frame.SizedPayload())
	if err := errorMessage.read(rbuf); err != nil {
		c.log.Warnf("Unable to read Error frame from %s: %v", c.remotePeerInfo, err)
		c.connectionError(err)
		return
	}

	if errorMessage.errCode == ErrCodeProtocol {
		c.log.Warnf("Peer %s reported protocol error: %s", c.remotePeerInfo, errorMessage.message)
		c.connectionError(errorMessage.AsSystemError())
		return
	}

	if err := c.outbound.forwardPeerFrame(frame); err != nil {
		c.outbound.removeExchange(frame.Header.ID)
	}
}

// doneReading shuts down the message exchange for this call.
// For outgoing calls, the last message is reading the call response.
func (response *OutboundCallResponse) doneReading() {
	if response.ApplicationError() {
		// TODO(prashant): Figure out how to add "type" to tags, which TChannel does not know about.
		response.statsReporter.IncCounter("outbound.calls.app-errors", response.commonStatsTags, 1)
	} else {
		response.statsReporter.IncCounter("outbound.calls.success", response.commonStatsTags, 1)
	}
	latency := timeNow().Sub(response.startedAt)
	response.statsReporter.RecordTimer("outbound.calls.latency", response.commonStatsTags, latency)

	response.mex.shutdown()
}
<commit_msg>Fail call if the operation is too large<commit_after>package tchannel

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"io"
	"time"

	"github.com/uber/tchannel/golang/typed"
	"golang.org/x/net/context"
)

// maxOperationSize is the maximum size of arg1.
const maxOperationSize = 16 * 1024

// beginCall begins an outbound call on the connection
func (c *Connection) beginCall(ctx context.Context, serviceName string, callOptions *CallOptions) (*OutboundCall, error) {
	switch c.readState() {
	case connectionActive, connectionStartClose:
		break
	case connectionInboundClosed, connectionClosed:
		return nil, ErrConnectionClosed
	case connectionWaitingToRecvInitReq, connectionWaitingToSendInitReq, connectionWaitingToRecvInitRes:
		return nil, ErrConnectionNotReady
	default:
		return nil, errConnectionUnknownState
	}

	deadline, ok := ctx.Deadline()
	// No deadline was set, we should not support no deadlines.
	if !ok {
		return nil, ErrTimeoutRequired
	}
	timeToLive := deadline.Sub(time.Now())
	if timeToLive <= 0 {
		return nil, ErrTimeout
	}

	requestID := c.NextMessageID()
	mex, err := c.outbound.newExchange(ctx, c.framePool, messageTypeCallReq, requestID, 512)
	if err != nil {
		return nil, err
	}

	headers := transportHeaders{
		CallerName: c.localPeerInfo.ServiceName,
	}
	callOptions.setHeaders(headers)

	call := new(OutboundCall)
	call.mex = mex
	call.conn = c
	call.callReq = callReq{
		id:         requestID,
		Headers:    headers,
		Service:    serviceName,
		TimeToLive: timeToLive,
	}
	call.statsReporter = c.statsReporter
	call.createStatsTags(c.commonStatsTags)
	call.log = c.log.WithFields(LogFields{{"Out-Call", requestID}})

	// TODO(mmihic): It'd be nice to do this without an fptr
	call.messageForFragment = func(initial bool) message {
		if initial {
			return &call.callReq
		}

		return new(callReqContinue)
	}

	call.contents = newFragmentingWriter(call, c.checksumType.New())
	span := CurrentSpan(ctx)
	if span != nil {
		call.callReq.Tracing = *span.NewChildSpan()
	} else {
		// TODO(mmihic): Potentially reject calls that are made outside a root context?
		call.callReq.Tracing.EnableTracing(false)
	}

	call.AddBinaryAnnotation(BinaryAnnotation{Key: "cn", Value: call.callReq.Headers[CallerName]})
	call.AddBinaryAnnotation(BinaryAnnotation{Key: "as", Value: call.callReq.Headers[ArgScheme]})
	call.AddAnnotation(AnnotationKeyClientSend)

	response := new(OutboundCallResponse)
	response.startedAt = timeNow()
	response.mex = mex
	response.log = c.log.WithFields(LogFields{{"Out-Response", requestID}})
	response.messageForFragment = func(initial bool) message {
		if initial {
			call.AddAnnotation(AnnotationKeyClientReceive)
			call.Report(call.callReq.Tracing, c.traceReporter)
			return &response.callRes
		}

		return new(callResContinue)
	}
	response.contents = newFragmentingReader(response)
	response.statsReporter = call.statsReporter
	response.commonStatsTags = call.commonStatsTags
	call.response = response
	return call, nil
}

// handleCallRes handles an incoming call req message, forwarding the
// frame to the response channel waiting for it
func (c *Connection) handleCallRes(frame *Frame) bool {
	if err := c.outbound.forwardPeerFrame(frame); err != nil {
		c.outbound.removeExchange(frame.Header.ID)
		return true
	}
	return false
}

// handleCallResContinue handles an incoming call res continue message,
// forwarding the frame to the response channel waiting for it
func (c *Connection) handleCallResContinue(frame *Frame) bool {
	if err := c.outbound.forwardPeerFrame(frame); err != nil {
		c.outbound.removeExchange(frame.Header.ID)
		return true
	}
	return false
}

// An OutboundCall is an active call to a remote peer.  A client makes a call
// by calling BeginCall on the Channel, writing argument content via
// ArgWriter2() ArgWriter3(), and then reading reading response data via the
// ArgReader2() and ArgReader3() methods on the Response() object.
type OutboundCall struct {
	reqResWriter
	Annotations

	callReq         callReq
	response        *OutboundCallResponse
	statsReporter   StatsReporter
	commonStatsTags map[string]string
}

// Response provides access to the call's response object, which can be used to
// read response arguments
func (call *OutboundCall) Response() *OutboundCallResponse {
	return call.response
}

// createStatsTags creates the common stats tags, if they are not already created.
func (call *OutboundCall) createStatsTags(connectionTags map[string]string) {
	call.commonStatsTags = map[string]string{
		"target-service": call.callReq.Service,
	}
	for k, v := range connectionTags {
		call.commonStatsTags[k] = v
	}
}

// writeOperation writes the operation (arg1) to the call
func (call *OutboundCall) writeOperation(operation []byte) error {
	if len(operation) > maxOperationSize {
		return call.failed(ErrOperationTooLarge)
	}

	// TODO(prashant): Should operation become part of BeginCall so this can use Format directly.
	if call.callReq.Headers[ArgScheme] != HTTP.String() {
		call.commonStatsTags["target-endpoint"] = string(operation)
	}

	call.statsReporter.IncCounter("outbound.calls.send", call.commonStatsTags, 1)
	return NewArgWriter(call.arg1Writer()).Write(operation)
}

// Arg2Writer returns a WriteCloser that can be used to write the second argument.
// The returned writer must be closed once the write is complete.
func (call *OutboundCall) Arg2Writer() (ArgWriter, error) {
	return call.arg2Writer()
}

// Arg3Writer returns a WriteCloser that can be used to write the last argument.
// The returned writer must be closed once the write is complete.
func (call *OutboundCall) Arg3Writer() (ArgWriter, error) {
	return call.arg3Writer()
}

func (call *OutboundCall) doneSending() {}

// An OutboundCallResponse is the response to an outbound call
type OutboundCallResponse struct {
	reqResReader

	callRes callRes

	// startedAt is the time at which the outbound call was started.
	startedAt       time.Time
	statsReporter   StatsReporter
	commonStatsTags map[string]string
}

// ApplicationError returns true if the call resulted in an application level error
// TODO(mmihic): In current implementation, you must have called Arg2Reader before this
// method returns the proper value.  We should instead have this block until the first
// fragment is available, if the first fragment hasn't been received.
func (response *OutboundCallResponse) ApplicationError() bool {
	// TODO(mmihic): Wait for first fragment
	return response.callRes.ResponseCode == responseApplicationError
}

// Format the format of the request from the ArgScheme transport header.
func (response *OutboundCallResponse) Format() Format {
	return Format(response.callRes.Headers[ArgScheme])
}

// Arg2Reader returns an io.ReadCloser to read the second argument.
// The ReadCloser must be closed once the argument has been read.
func (response *OutboundCallResponse) Arg2Reader() (io.ReadCloser, error) {
	var operation []byte
	if err := NewArgReader(response.arg1Reader()).Read(&operation); err != nil {
		return nil, err
	}

	return response.arg2Reader()
}

// Arg3Reader returns an io.ReadCloser to read the last argument.
// The ReadCloser must be closed once the argument has been read.
func (response *OutboundCallResponse) Arg3Reader() (io.ReadCloser, error) {
	return response.arg3Reader()
}

// handleError andles an error coming back from the peer. If the error is a
// protocol level error, the entire connection will be closed.  If the error is
// a request specific error, it will be written to the request's response
// channel and converted into a SystemError returned from the next reader or
// access call.
func (c *Connection) handleError(frame *Frame) {
	var errorMessage errorMessage
	rbuf := typed.NewReadBuffer(frame.SizedPayload())
	if err := errorMessage.read(rbuf); err != nil {
		c.log.Warnf("Unable to read Error frame from %s: %v", c.remotePeerInfo, err)
		c.connectionError(err)
		return
	}

	if errorMessage.errCode == ErrCodeProtocol {
		c.log.Warnf("Peer %s reported protocol error: %s", c.remotePeerInfo, errorMessage.message)
		c.connectionError(errorMessage.AsSystemError())
		return
	}

	if err := c.outbound.forwardPeerFrame(frame); err != nil {
		c.outbound.removeExchange(frame.Header.ID)
	}
}

// doneReading shuts down the message exchange for this call.
// For outgoing calls, the last message is reading the call response.
func (response *OutboundCallResponse) doneReading() {
	if response.ApplicationError() {
		// TODO(prashant): Figure out how to add "type" to tags, which TChannel does not know about.
		response.statsReporter.IncCounter("outbound.calls.app-errors", response.commonStatsTags, 1)
	} else {
		response.statsReporter.IncCounter("outbound.calls.success", response.commonStatsTags, 1)
	}
	latency := timeNow().Sub(response.startedAt)
	response.statsReporter.RecordTimer("outbound.calls.latency", response.commonStatsTags, latency)

	response.mex.shutdown()
}
<|endoftext|>


<commit_before>package gostrftime

import (
	"fmt"
	"testing"
	"time"

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

var formattests = []struct {
	format   string
	expected string
}{
	{
		"/this/is/a/test/%Y/%m/%d/test.log",
		"/this/is/a/test/2009/01/02/test.log",
	},
	{
		"/this/is/a/test/%Y%m%d/test.log",
		"/this/is/a/test/20090102/test.log",
	},
	{
		"/this/is/a/test/Ymd/test.log",
		"/this/is/a/test/Ymd/test.log",
	},
	{
		"/this/is/a/test/%%Y/test.log",
		"/this/is/a/test/%Y/test.log",
	},
	{
		"%/this/is/a/test/%9/test.log%",
		"%/this/is/a/test/%9/test.log%",
	},
	{
		"%ü-%Y-%m-%d-%%%m-%%%%m-ü-世%界",
		"%ü-2009-01-02-%01-%%m-ü-世%界",
	},
	{"%A", "Friday"},
	{"%a", "Fri"},
	{"%B", "January"},
	{"%b", "Jan"},
	{"%C", "2009"},
	{"%D", "01/02/09"},
	{"%d", "02"},
	{"%e", " 2"},
	{"%F", "2009-01-02"},
	{"%f", "000001"},
	{"%H", "03"},
	{"%h", "Jan"},
	{"%I", "03"},
	{"%j", "002"},
	{"%k", " 3"},
	{"%l", " 3"},
	{"%M", "04"},
	{"%m", "01"},
	{"%n", "\n"},
	{"%p", "AM"},
	{"%R", "03:04"},
	{"%r", "03:04:00 AM"},
	{"%S", "00"},
	{"%s", "1230865440"},
	{"%T", "03:04:00"},
	{"%t", "\t"},
	{"%v", " 2-Jan-2009"},
	{"%w", "5"},
	{"%Y", "2009"},
	{"%y", "09"},
	{"%Z", "UTC"},
	{"%z", "+0000"},
}

func TestStrfFormat(t *testing.T) {
	t.Parallel()
	assert := assert.New(t)

	tm := time.Date(2009, time.January, 2, 3, 4, 0, 1001, time.UTC)
	for _, tt := range formattests {
		output := Format(tt.format, tm)
		assert.Equal(tt.expected, output, fmt.Sprintf("%s not right", tt.format))
	}
}

func TestStrfFormatNotUTC(t *testing.T) {
	t.Parallel()
	assert := assert.New(t)

	// use a timezone that doesn't do daylight savings
	location := time.FixedZone("Saskatchewan", -6*60*60)
	tm := time.Date(2009, time.January, 2, 3, 4, 0, 1001, location)
	for _, tt := range formattests {
		var expected string
		switch tt.format {
		case "%Z":
			expected = "Saskatchewan"
		case "%z":
			expected = "-0600"
		case "%s":
			expected = "1230887040"
		default:
			expected = tt.expected
		}
		output := Format(tt.format, tm)
		assert.Equal(expected, output, fmt.Sprintf("%s not right", tt.format))
	}
}

func BenchmarkStrfFormatAll(b *testing.B) {
	tm := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Format("%A %a %B %b %C %D %d %e %F %f %H %h %I %j %k %l %M %m %n %p %R %r %S %s %T %t %v %w %Y %y %Z %z", tm)
	}
}

func BenchmarkStrfFormatSimple(b *testing.B) {
	tm := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Format(formattests[0].format, tm)
	}
}
<commit_msg>use larger nanoseconds for test cases<commit_after>package gostrftime

import (
	"fmt"
	"testing"
	"time"

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

var formattests = []struct {
	format   string
	expected string
}{
	{
		"/this/is/a/test/%Y/%m/%d/test.log",
		"/this/is/a/test/2009/01/02/test.log",
	},
	{
		"/this/is/a/test/%Y%m%d/test.log",
		"/this/is/a/test/20090102/test.log",
	},
	{
		"/this/is/a/test/Ymd/test.log",
		"/this/is/a/test/Ymd/test.log",
	},
	{
		"/this/is/a/test/%%Y/test.log",
		"/this/is/a/test/%Y/test.log",
	},
	{
		"%/this/is/a/test/%9/test.log%",
		"%/this/is/a/test/%9/test.log%",
	},
	{
		"%ü-%Y-%m-%d-%%%m-%%%%m-ü-世%界",
		"%ü-2009-01-02-%01-%%m-ü-世%界",
	},
	{"%A", "Friday"},
	{"%a", "Fri"},
	{"%B", "January"},
	{"%b", "Jan"},
	{"%C", "2009"},
	{"%D", "01/02/09"},
	{"%d", "02"},
	{"%e", " 2"},
	{"%F", "2009-01-02"},
	{"%f", "000123"},
	{"%H", "03"},
	{"%h", "Jan"},
	{"%I", "03"},
	{"%j", "002"},
	{"%k", " 3"},
	{"%l", " 3"},
	{"%M", "04"},
	{"%m", "01"},
	{"%n", "\n"},
	{"%p", "AM"},
	{"%R", "03:04"},
	{"%r", "03:04:00 AM"},
	{"%S", "00"},
	{"%s", "1230865440"},
	{"%T", "03:04:00"},
	{"%t", "\t"},
	{"%v", " 2-Jan-2009"},
	{"%w", "5"},
	{"%Y", "2009"},
	{"%y", "09"},
	{"%Z", "UTC"},
	{"%z", "+0000"},
}

func TestStrfFormat(t *testing.T) {
	t.Parallel()
	assert := assert.New(t)

	tm := time.Date(2009, time.January, 2, 3, 4, 0, 123456, time.UTC)
	for _, tt := range formattests {
		output := Format(tt.format, tm)
		assert.Equal(tt.expected, output, fmt.Sprintf("%s not right", tt.format))
	}
}

func TestStrfFormatNotUTC(t *testing.T) {
	t.Parallel()
	assert := assert.New(t)

	// use a timezone that doesn't do daylight savings
	location := time.FixedZone("Saskatchewan", -6*60*60)
	tm := time.Date(2009, time.January, 2, 3, 4, 0, 123456, location)
	for _, tt := range formattests {
		var expected string
		switch tt.format {
		case "%Z":
			expected = "Saskatchewan"
		case "%z":
			expected = "-0600"
		case "%s":
			expected = "1230887040"
		default:
			expected = tt.expected
		}
		output := Format(tt.format, tm)
		assert.Equal(expected, output, fmt.Sprintf("%s not right", tt.format))
	}
}

func BenchmarkStrfFormatAll(b *testing.B) {
	tm := time.Date(2009, time.November, 10, 23, 0, 0, 123456, time.UTC)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Format("%A %a %B %b %C %D %d %e %F %f %H %h %I %j %k %l %M %m %n %p %R %r %S %s %T %t %v %w %Y %y %Z %z", tm)
	}
}

func BenchmarkStrfFormatSimple(b *testing.B) {
	tm := time.Date(2009, time.November, 10, 23, 0, 0, 123456, time.UTC)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Format(formattests[0].format, tm)
	}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ls

import (
	"flag"
	"fmt"
	"io"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/list"
	"github.com/vmware/govmomi/vim25/mo"
	"golang.org/x/net/context"
)

type ls struct {
	*flags.DatacenterFlag

	Long bool
}

func init() {
	cli.Register("ls", &ls{})
}

func (cmd *ls) Register(f *flag.FlagSet) {
	f.BoolVar(&cmd.Long, "l", false, "Long listing format")
}

func (cmd *ls) Process() error { return nil }

func (cmd *ls) Usage() string {
	return "[PATH]..."
}

func (cmd *ls) Run(f *flag.FlagSet) error {
	finder, err := cmd.Finder()
	if err != nil {
		return err
	}

	lr := listResult{
		Elements: nil,
		Long:     cmd.Long,
	}

	args := f.Args()
	if len(args) == 0 {
		args = []string{"."}
	}

	for _, arg := range args {
		es, err := finder.ManagedObjectListChildren(context.TODO(), arg)
		if err != nil {
			return err
		}

		lr.Elements = append(lr.Elements, es...)
	}

	return cmd.WriteResult(lr)
}

type listResult struct {
	Elements []list.Element `json:"elements"`

	Long bool `json:"-"`
}

func (l listResult) Write(w io.Writer) error {
	var err error

	for _, e := range l.Elements {
		if !l.Long {
			fmt.Fprintf(w, "%s\n", e.Path)
			continue
		}

		switch e.Object.(type) {
		case mo.Folder:
			if _, err = fmt.Fprintf(w, "%s/\n", e.Path); err != nil {
				return err
			}
		case mo.Datacenter:
			if _, err = fmt.Fprintf(w, "%s (Datacenter)\n", e.Path); err != nil {
				return err
			}
		case mo.VirtualMachine:
			if _, err = fmt.Fprintf(w, "%s (VirtualMachine)\n", e.Path); err != nil {
				return err
			}
		case mo.Network:
			if _, err = fmt.Fprintf(w, "%s (Network)\n", e.Path); err != nil {
				return err
			}
		case mo.ComputeResource:
			if _, err = fmt.Fprintf(w, "%s (ComputeResource)\n", e.Path); err != nil {
				return err
			}
		case mo.Datastore:
			if _, err = fmt.Fprintf(w, "%s (Datastore)\n", e.Path); err != nil {
				return err
			}
		case mo.ResourcePool:
			if _, err = fmt.Fprintf(w, "%s (ResourcePool)\n", e.Path); err != nil {
				return err
			}
		}
	}

	return nil
}
<commit_msg>include VirtualApp in ls -l output<commit_after>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ls

import (
	"flag"
	"fmt"
	"io"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/list"
	"github.com/vmware/govmomi/vim25/mo"
	"golang.org/x/net/context"
)

type ls struct {
	*flags.DatacenterFlag

	Long bool
}

func init() {
	cli.Register("ls", &ls{})
}

func (cmd *ls) Register(f *flag.FlagSet) {
	f.BoolVar(&cmd.Long, "l", false, "Long listing format")
}

func (cmd *ls) Process() error { return nil }

func (cmd *ls) Usage() string {
	return "[PATH]..."
}

func (cmd *ls) Run(f *flag.FlagSet) error {
	finder, err := cmd.Finder()
	if err != nil {
		return err
	}

	lr := listResult{
		Elements: nil,
		Long:     cmd.Long,
	}

	args := f.Args()
	if len(args) == 0 {
		args = []string{"."}
	}

	for _, arg := range args {
		es, err := finder.ManagedObjectListChildren(context.TODO(), arg)
		if err != nil {
			return err
		}

		lr.Elements = append(lr.Elements, es...)
	}

	return cmd.WriteResult(lr)
}

type listResult struct {
	Elements []list.Element `json:"elements"`

	Long bool `json:"-"`
}

func (l listResult) Write(w io.Writer) error {
	var err error

	for _, e := range l.Elements {
		if !l.Long {
			fmt.Fprintf(w, "%s\n", e.Path)
			continue
		}

		switch e.Object.(type) {
		case mo.Folder:
			if _, err = fmt.Fprintf(w, "%s/\n", e.Path); err != nil {
				return err
			}
		case mo.Datacenter:
			if _, err = fmt.Fprintf(w, "%s (Datacenter)\n", e.Path); err != nil {
				return err
			}
		case mo.VirtualMachine:
			if _, err = fmt.Fprintf(w, "%s (VirtualMachine)\n", e.Path); err != nil {
				return err
			}
		case mo.Network:
			if _, err = fmt.Fprintf(w, "%s (Network)\n", e.Path); err != nil {
				return err
			}
		case mo.ComputeResource:
			if _, err = fmt.Fprintf(w, "%s (ComputeResource)\n", e.Path); err != nil {
				return err
			}
		case mo.Datastore:
			if _, err = fmt.Fprintf(w, "%s (Datastore)\n", e.Path); err != nil {
				return err
			}
		case mo.ResourcePool:
			if _, err = fmt.Fprintf(w, "%s (ResourcePool)\n", e.Path); err != nil {
				return err
			}
		case mo.VirtualApp:
			if _, err = fmt.Fprintf(w, "%s (VirtualApp)\n", e.Path); err != nil {
				return err
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"runtime"
	"sort"
	"strings"
	"sync/atomic"
	"time"
)

const (
	helloVersion = "0.3"
)

var knownPaths []string
var boottime time.Time
var banner string
var requests int64
var usr *user.User

func inc() int64 {
	return atomic.AddInt64(&requests, 1)
}

func get() int64 {
	return atomic.LoadInt64(&requests)
}

func main() {

	boottime = time.Now()

	tls := true

	log.Printf("version=%s runtime=%s pid=%d GOMAXPROCS=%d", helloVersion, runtime.Version(), os.Getpid(), runtime.GOMAXPROCS(0))

	defaultBanner := "banner default"
	if envBanner := os.Getenv("GWH_BANNER"); envBanner != "" {
		defaultBanner = envBanner
	}

	var errUser error
	usr, errUser = user.Current()
	if errUser != nil {
		log.Printf("current user error: %v", errUser)
	}
	if usr != nil {
		log.Printf("user: %s (uid: %s)", usr.Username, usr.Uid)
	}

	currDir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Getwd: %s", err)
	}

	var addr, httpsAddr, key, cert string
	var disableKeepalive bool

	flag.StringVar(&key, "key", "key.pem", "TLS key file")
	flag.StringVar(&cert, "cert", "cert.pem", "TLS cert file")
	flag.StringVar(&addr, "addr", ":8080", "HTTP listen address")
	flag.StringVar(&httpsAddr, "httpsAddr", ":8443", "HTTPS listen address")
	flag.StringVar(&banner, "banner", defaultBanner, "banner will be displayed (env var GWH_BANNER sets default banner)")
	flag.BoolVar(&disableKeepalive, "disableKeepalive", false, "disable keepalive")
	flag.Parse()

	keepalive := !disableKeepalive

	log.Print("banner: ", banner)
	log.Print("keepalive: ", keepalive)

	if !fileExists(key) {
		log.Printf("TLS key file not found: %s - disabling TLS", key)
		tls = false
	}

	if !fileExists(cert) {
		log.Printf("TLS cert file not found: %s - disabling TLS", cert)
		tls = false
	}

	// default handler
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { rootHandler(w, r, keepalive) })

	registerStatic("/www/", currDir)

	log.Printf("using TCP ports HTTP=%s HTTPS=%s TLS=%v", addr, httpsAddr, tls)

	if tls {
		serveHTTPS(addr, httpsAddr, cert, key, keepalive)
		return
	}

	log.Printf("serving HTTP on TCP %s", addr)
	if err := listenAndServe(addr, nil, keepalive); err != nil {
		log.Fatalf("listenAndServe: %s: %v", addr, err)
	}
}

func serveHTTPS(addr, httpsAddr, cert, key string, keepalive bool) {
	httpPort := "80"
	h := strings.Split(addr, ":")
	if len(h) > 1 {
		httpPort = h[1]
	}

	httpsPort := "443"
	hs := strings.Split(httpsAddr, ":")
	if len(hs) > 1 {
		httpsPort = hs[1]
	}

	if httpPort != httpsPort {
		log.Printf("installing redirect from HTTP=%s to HTTPS=%s", addr, httpsPort)

		redirectTLS := func(w http.ResponseWriter, r *http.Request) {
			host := strings.Split(r.Host, ":")[0]
			//code := http.StatusMovedPermanently // 301 permanent
			code := http.StatusFound // 302 temporary
			http.Redirect(w, r, "https://"+host+":"+httpsPort+r.RequestURI, code)
		}

		// http-to-https redirect server
		go func() {
			if err := listenAndServe(addr, http.HandlerFunc(redirectTLS), keepalive); err != nil {
				log.Fatalf("redirect: listenAndServe: %s: %v", addr, err)
			}
		}()
	}

	log.Printf("serving HTTPS on TCP %s", httpsAddr)
	if err := listenAndServeTLS(httpsAddr, cert, key, nil, keepalive); err != nil {
		log.Fatalf("listenAndServeTLS: %s: %v", httpsAddr, err)
	}
}

func listenAndServe(addr string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServe()
}

func listenAndServeTLS(addr, certFile, keyFile string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServeTLS(certFile, keyFile)
}

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

type staticHandler struct {
	innerHandler http.Handler
}

func registerStatic(path, dir string) {
	http.Handle(path, staticHandler{http.StripPrefix(path, http.FileServer(http.Dir(dir)))})
	knownPaths = append(knownPaths, path)
	log.Printf("mapping www path %s to directory %s", path, dir)
}

func (handler staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	count := inc()
	log.Printf("staticHandler.ServeHTTP req=%d url=%s from=%s", count, r.URL.Path, r.RemoteAddr)
	handler.innerHandler.ServeHTTP(w, r)
}

func rootHandler(w http.ResponseWriter, r *http.Request, keepalive bool) {
	count := inc()
	log.Printf("rootHandler: req=%d from=%s", count, r.RemoteAddr)
	log.Printf("rootHandler: req=%d method=%s host=%s path=%s", count, r.Method, r.Host, r.URL.Path)
	log.Printf("rootHandler: req=%d query=%s", count, r.URL.RawQuery)

	var paths string
	for _, p := range knownPaths {
		paths += fmt.Sprintf("%s
", p, p)
	}

	var errMsg string
	if r.URL.Path != "/" {
		format := `

Path not found!

Method: %s

Host: %s

Path not found: [%s]

Query: [%s]

`
		errMsg = fmt.Sprintf(format, r.Method, r.Host, r.URL.Path, r.URL.RawQuery)
	}

	header :=
		`<!DOCTYPE html>

    <title>gowebhello root page</title>

gowebhello root page

    https://github.com/udhos/gowebhello is a simple golang replacement for 'python -m SimpleHTTPServer'.

`
	bodyTempl :=
		`
Welcome!

	gowebhello version %s runtime %s

        Keepalive: %v

	Application banner: %s

	Application arguments: %v

	Application dir: %s

	Process: %d

	User: %s (uid: %s)

	Server hostname: %s

	Your address: %s

	Request method=%s host=%s path=[%s] query=[%s]

	Current time: %s

	Uptime: %s

	Requests: %d

    %s

All known paths:

    %s
`

	footer :=
		`

`

	cwd, errCwd := os.Getwd()
	if errCwd != nil {
		cwd = cwd + " (error: " + errCwd.Error() + ")"
	}

	host, errHost := os.Hostname()
	if errHost != nil {
		host = host + " (error: " + errHost.Error() + ")"
	}

	now := time.Now()

	username := "?"
	uid := "?"

	if usr != nil {
		username = usr.Username
		uid = usr.Uid
	}

	body := fmt.Sprintf(bodyTempl, helloVersion, runtime.Version(), keepalive, banner, os.Args, cwd, os.Getpid(), username, uid, host, r.RemoteAddr, r.Method, r.Host, r.URL.Path, r.URL.RawQuery, now, time.Since(boottime), get(), errMsg, paths)

	if !keepalive {
		w.Header().Set("Connection", "close")
	}

	io.WriteString(w, header)
	io.WriteString(w, body)
	showHeaders(w, r)
	showReqBody(w, r)
	io.WriteString(w, footer)
}

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

	io.WriteString(w, "
Request Body
\n")

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		m := fmt.Sprintf("request body: %v", err)
		log.Print(m)
		io.WriteString(w, m)
		return
	}

	io.WriteString(w, fmt.Sprintf("
request body size: %d
\n", len(buf)))

	io.WriteString(w, "
\n")
	io.WriteString(w, string(buf))
	io.WriteString(w, "\n")
	io.WriteString(w, "
\n")
}

func showHeaders(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "
Headers
\n")

	var headers []string
	for k, v := range r.Header {
		for _, vv := range v {
			headers = append(headers, k+": "+vv+"
\n")
		}
	}

	sort.Strings(headers)

	for _, h := range headers {
		io.WriteString(w, h)
	}
}
<commit_msg>Version up.<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"runtime"
	"sort"
	"strings"
	"sync/atomic"
	"time"
)

const (
	helloVersion = "0.4"
)

var knownPaths []string
var boottime time.Time
var banner string
var requests int64
var usr *user.User

func inc() int64 {
	return atomic.AddInt64(&requests, 1)
}

func get() int64 {
	return atomic.LoadInt64(&requests)
}

func main() {

	boottime = time.Now()

	tls := true

	log.Printf("version=%s runtime=%s pid=%d GOMAXPROCS=%d", helloVersion, runtime.Version(), os.Getpid(), runtime.GOMAXPROCS(0))

	defaultBanner := "banner default"
	if envBanner := os.Getenv("GWH_BANNER"); envBanner != "" {
		defaultBanner = envBanner
	}

	var errUser error
	usr, errUser = user.Current()
	if errUser != nil {
		log.Printf("current user error: %v", errUser)
	}
	if usr != nil {
		log.Printf("user: %s (uid: %s)", usr.Username, usr.Uid)
	}

	currDir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Getwd: %s", err)
	}

	var addr, httpsAddr, key, cert string
	var disableKeepalive bool

	flag.StringVar(&key, "key", "key.pem", "TLS key file")
	flag.StringVar(&cert, "cert", "cert.pem", "TLS cert file")
	flag.StringVar(&addr, "addr", ":8080", "HTTP listen address")
	flag.StringVar(&httpsAddr, "httpsAddr", ":8443", "HTTPS listen address")
	flag.StringVar(&banner, "banner", defaultBanner, "banner will be displayed (env var GWH_BANNER sets default banner)")
	flag.BoolVar(&disableKeepalive, "disableKeepalive", false, "disable keepalive")
	flag.Parse()

	keepalive := !disableKeepalive

	log.Print("banner: ", banner)
	log.Print("keepalive: ", keepalive)

	if !fileExists(key) {
		log.Printf("TLS key file not found: %s - disabling TLS", key)
		tls = false
	}

	if !fileExists(cert) {
		log.Printf("TLS cert file not found: %s - disabling TLS", cert)
		tls = false
	}

	// default handler
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { rootHandler(w, r, keepalive) })

	registerStatic("/www/", currDir)

	log.Printf("using TCP ports HTTP=%s HTTPS=%s TLS=%v", addr, httpsAddr, tls)

	if tls {
		serveHTTPS(addr, httpsAddr, cert, key, keepalive)
		return
	}

	log.Printf("serving HTTP on TCP %s", addr)
	if err := listenAndServe(addr, nil, keepalive); err != nil {
		log.Fatalf("listenAndServe: %s: %v", addr, err)
	}
}

func serveHTTPS(addr, httpsAddr, cert, key string, keepalive bool) {
	httpPort := "80"
	h := strings.Split(addr, ":")
	if len(h) > 1 {
		httpPort = h[1]
	}

	httpsPort := "443"
	hs := strings.Split(httpsAddr, ":")
	if len(hs) > 1 {
		httpsPort = hs[1]
	}

	if httpPort != httpsPort {
		log.Printf("installing redirect from HTTP=%s to HTTPS=%s", addr, httpsPort)

		redirectTLS := func(w http.ResponseWriter, r *http.Request) {
			host := strings.Split(r.Host, ":")[0]
			//code := http.StatusMovedPermanently // 301 permanent
			code := http.StatusFound // 302 temporary
			http.Redirect(w, r, "https://"+host+":"+httpsPort+r.RequestURI, code)
		}

		// http-to-https redirect server
		go func() {
			if err := listenAndServe(addr, http.HandlerFunc(redirectTLS), keepalive); err != nil {
				log.Fatalf("redirect: listenAndServe: %s: %v", addr, err)
			}
		}()
	}

	log.Printf("serving HTTPS on TCP %s", httpsAddr)
	if err := listenAndServeTLS(httpsAddr, cert, key, nil, keepalive); err != nil {
		log.Fatalf("listenAndServeTLS: %s: %v", httpsAddr, err)
	}
}

func listenAndServe(addr string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServe()
}

func listenAndServeTLS(addr, certFile, keyFile string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServeTLS(certFile, keyFile)
}

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

type staticHandler struct {
	innerHandler http.Handler
}

func registerStatic(path, dir string) {
	http.Handle(path, staticHandler{http.StripPrefix(path, http.FileServer(http.Dir(dir)))})
	knownPaths = append(knownPaths, path)
	log.Printf("mapping www path %s to directory %s", path, dir)
}

func (handler staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	count := inc()
	log.Printf("staticHandler.ServeHTTP req=%d url=%s from=%s", count, r.URL.Path, r.RemoteAddr)
	handler.innerHandler.ServeHTTP(w, r)
}

func rootHandler(w http.ResponseWriter, r *http.Request, keepalive bool) {
	count := inc()
	log.Printf("rootHandler: req=%d from=%s", count, r.RemoteAddr)
	log.Printf("rootHandler: req=%d method=%s host=%s path=%s", count, r.Method, r.Host, r.URL.Path)
	log.Printf("rootHandler: req=%d query=%s", count, r.URL.RawQuery)

	var paths string
	for _, p := range knownPaths {
		paths += fmt.Sprintf("%s
", p, p)
	}

	var errMsg string
	if r.URL.Path != "/" {
		format := `

Path not found!

Method: %s

Host: %s

Path not found: [%s]

Query: [%s]

`
		errMsg = fmt.Sprintf(format, r.Method, r.Host, r.URL.Path, r.URL.RawQuery)
	}

	header :=
		`<!DOCTYPE html>

    <title>gowebhello root page</title>

gowebhello root page

    https://github.com/udhos/gowebhello is a simple golang replacement for 'python -m SimpleHTTPServer'.

`
	bodyTempl :=
		`
Welcome!

	gowebhello version %s runtime %s

        Keepalive: %v

	Application banner: %s

	Application arguments: %v

	Application dir: %s

	Process: %d

	User: %s (uid: %s)

	Server hostname: %s

	Your address: %s

	Request method=%s host=%s path=[%s] query=[%s]

	Current time: %s

	Uptime: %s

	Requests: %d

    %s

All known paths:

    %s
`

	footer :=
		`

`

	cwd, errCwd := os.Getwd()
	if errCwd != nil {
		cwd = cwd + " (error: " + errCwd.Error() + ")"
	}

	host, errHost := os.Hostname()
	if errHost != nil {
		host = host + " (error: " + errHost.Error() + ")"
	}

	now := time.Now()

	username := "?"
	uid := "?"

	if usr != nil {
		username = usr.Username
		uid = usr.Uid
	}

	body := fmt.Sprintf(bodyTempl, helloVersion, runtime.Version(), keepalive, banner, os.Args, cwd, os.Getpid(), username, uid, host, r.RemoteAddr, r.Method, r.Host, r.URL.Path, r.URL.RawQuery, now, time.Since(boottime), get(), errMsg, paths)

	if !keepalive {
		w.Header().Set("Connection", "close")
	}

	io.WriteString(w, header)
	io.WriteString(w, body)
	showHeaders(w, r)
	showReqBody(w, r)
	io.WriteString(w, footer)
}

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

	io.WriteString(w, "
Request Body
\n")

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		m := fmt.Sprintf("request body: %v", err)
		log.Print(m)
		io.WriteString(w, m)
		return
	}

	io.WriteString(w, fmt.Sprintf("
request body size: %d
\n", len(buf)))

	io.WriteString(w, "
\n")
	io.WriteString(w, string(buf))
	io.WriteString(w, "\n")
	io.WriteString(w, "
\n")
}

func showHeaders(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "
Headers
\n")

	var headers []string
	for k, v := range r.Header {
		for _, vv := range v {
			headers = append(headers, k+": "+vv+"
\n")
		}
	}

	sort.Strings(headers)

	for _, h := range headers {
		io.WriteString(w, h)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes 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.
*/

package gcetasks

import (
	"fmt"
	"reflect"

	compute "google.golang.org/api/compute/v1"
	"k8s.io/klog/v2"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/gce"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter"
)

// +kops:fitask
type Network struct {
	Name      *string
	Lifecycle fi.Lifecycle
	Mode      string

	CIDR *string

	Shared *bool
}

var _ fi.CompareWithID = &Network{}

func (e *Network) CompareWithID() *string {
	return e.Name
}

func (e *Network) Find(c *fi.Context) (*Network, error) {
	cloud := c.Cloud.(gce.GCECloud)

	r, err := cloud.Compute().Networks().Get(cloud.Project(), *e.Name)
	if err != nil {
		if gce.IsNotFound(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error listing Networks: %v", err)
	}

	actual := &Network{}
	if r.IPv4Range != "" {
		actual.Mode = "legacy"
		actual.CIDR = &r.IPv4Range
	} else if r.AutoCreateSubnetworks {
		actual.Mode = "auto"
	} else {
		actual.Mode = "custom"
	}

	if r.SelfLink != e.URL(cloud.Project()) {
		klog.Warningf("SelfLink did not match URL: %q vs %q", r.SelfLink, e.URL(cloud.Project()))
	}

	// Ignore "system" fields
	actual.Lifecycle = e.Lifecycle
	actual.Shared = e.Shared
	actual.Name = e.Name

	// Match unspecified values
	if e.Mode == "" {
		e.Mode = actual.Mode
	}

	return actual, nil
}

func (e *Network) URL(project string) string {
	u := gce.GoogleCloudURL{
		Version: "v1",
		Project: project,
		Name:    *e.Name,
		Type:    "networks",
		Global:  true,
	}
	return u.BuildURL()
}

func (e *Network) Run(c *fi.Context) error {
	return fi.DefaultDeltaRunMethod(e, c)
}

func (_ *Network) CheckChanges(a, e, changes *Network) error {
	cidr := fi.StringValue(e.CIDR)
	switch e.Mode {
	case "legacy":
		if cidr == "" {
			return fmt.Errorf("CIDR must specified for networks where mode=legacy")
		}
		klog.Warningf("using legacy mode for GCE network %q", fi.StringValue(e.Name))
	default:
		if cidr != "" {
			return fmt.Errorf("CIDR cannot specified for networks where mode=%s", e.Mode)
		}
	}

	switch e.Mode {
	case "auto":
	case "custom":
	case "legacy":

	default:
		return fmt.Errorf("unknown mode %q for Network", e.Mode)
	}

	return nil
}

func (_ *Network) RenderGCE(t *gce.GCEAPITarget, a, e, changes *Network) error {
	shared := fi.BoolValue(e.Shared)
	if shared {
		// Verify the network was found
		if a == nil {
			return fmt.Errorf("Network with name %q not found", fi.StringValue(e.Name))
		}
	}

	if a == nil {
		klog.V(2).Infof("Creating Network with CIDR: %q", fi.StringValue(e.CIDR))

		network := &compute.Network{
			Name: *e.Name,
		}

		switch e.Mode {
		case "legacy":
			network.IPv4Range = fi.StringValue(e.CIDR)

		case "auto":
			network.AutoCreateSubnetworks = true

		case "custom":
			network.AutoCreateSubnetworks = false
			// The boolean default value of "false" is omitted when the struct
			// is serialized, which results in the network being created with
			// the auto-create subnetworks default of "true". Explicitly send
			// the default value.
			network.ForceSendFields = []string{"AutoCreateSubnetworks"}
		}
		op, err := t.Cloud.Compute().Networks().Insert(t.Cloud.Project(), network)
		if err != nil {
			return fmt.Errorf("error creating Network: %v", err)
		}
		if err := t.Cloud.WaitForOp(op); err != nil {
			return fmt.Errorf("error waiting for Network creation to complete: %w", err)
		}
	} else {
		if a.Mode == "legacy" {
			return fmt.Errorf("GCE networks in legacy mode are not supported.  Please convert to auto mode or specify a different network.")
		}
		empty := &Network{}
		if !reflect.DeepEqual(empty, changes) {
			return fmt.Errorf("cannot apply changes to Network: %v", changes)
		}
	}

	return nil
}

type terraformNetwork struct {
	Name                  *string `json:"name" cty:"name"`
	IPv4Range             *string `json:"ipv4_range,omitempty" cty:"ipv4_range"`
	AutoCreateSubnetworks *bool   `json:"auto_create_subnetworks,omitempty" cty:"auto_create_subnetworks"`
}

func (_ *Network) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *Network) error {
	shared := fi.BoolValue(e.Shared)
	if shared {
		// Not terraform owned / managed
		return nil
	}

	tf := &terraformNetwork{
		Name: e.Name,
	}

	switch e.Mode {
	case "legacy":
		tf.IPv4Range = e.CIDR

	case "auto":
		tf.AutoCreateSubnetworks = fi.Bool(true)

	case "custom":
		tf.AutoCreateSubnetworks = fi.Bool(false)
	}

	return t.RenderResource("google_compute_network", *e.Name, tf)
}

func (e *Network) TerraformLink() *terraformWriter.Literal {
	shared := fi.BoolValue(e.Shared)
	if shared {
		if e.Name == nil {
			klog.Fatalf("Name must be set, if network is shared: %#v", e)
		}

		klog.V(4).Infof("reusing existing network with name %q", *e.Name)
		return terraformWriter.LiteralFromStringValue(*e.Name)
	}

	return terraformWriter.LiteralProperty("google_compute_network", *e.Name, "name")
}
<commit_msg>gce: allow empty mode for shared Networks<commit_after>/*
Copyright 2019 The Kubernetes 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.
*/

package gcetasks

import (
	"fmt"
	"reflect"

	compute "google.golang.org/api/compute/v1"
	"k8s.io/klog/v2"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/gce"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter"
)

// +kops:fitask
type Network struct {
	Name      *string
	Lifecycle fi.Lifecycle
	Mode      string

	CIDR *string

	Shared *bool
}

var _ fi.CompareWithID = &Network{}

func (e *Network) CompareWithID() *string {
	return e.Name
}

func (e *Network) Find(c *fi.Context) (*Network, error) {
	cloud := c.Cloud.(gce.GCECloud)

	r, err := cloud.Compute().Networks().Get(cloud.Project(), *e.Name)
	if err != nil {
		if gce.IsNotFound(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("error listing Networks: %v", err)
	}

	actual := &Network{}
	if r.IPv4Range != "" {
		actual.Mode = "legacy"
		actual.CIDR = &r.IPv4Range
	} else if r.AutoCreateSubnetworks {
		actual.Mode = "auto"
	} else {
		actual.Mode = "custom"
	}

	if r.SelfLink != e.URL(cloud.Project()) {
		klog.Warningf("SelfLink did not match URL: %q vs %q", r.SelfLink, e.URL(cloud.Project()))
	}

	// Ignore "system" fields
	actual.Lifecycle = e.Lifecycle
	actual.Shared = e.Shared
	actual.Name = e.Name

	// Match unspecified values
	if e.Mode == "" {
		e.Mode = actual.Mode
	}

	return actual, nil
}

func (e *Network) URL(project string) string {
	u := gce.GoogleCloudURL{
		Version: "v1",
		Project: project,
		Name:    *e.Name,
		Type:    "networks",
		Global:  true,
	}
	return u.BuildURL()
}

func (e *Network) Run(c *fi.Context) error {
	return fi.DefaultDeltaRunMethod(e, c)
}

func (_ *Network) CheckChanges(a, e, changes *Network) error {
	cidr := fi.StringValue(e.CIDR)
	switch e.Mode {
	case "legacy":
		if cidr == "" {
			return fmt.Errorf("CIDR must specified for networks where mode=legacy")
		}
		klog.Warningf("using legacy mode for GCE network %q", fi.StringValue(e.Name))
	default:
		if cidr != "" {
			return fmt.Errorf("CIDR cannot specified for networks where mode=%s", e.Mode)
		}
	}

	switch e.Mode {
	case "auto":
	case "custom":
	case "legacy":
		// Known

	case "":
		// Treated as "keep existing", only allowed for shared mode
		if !fi.BoolValue(e.Shared) {
			return fmt.Errorf("must specify mode for (non-shared) Network")
		}

	default:
		return fmt.Errorf("unknown mode %q for Network", e.Mode)
	}

	return nil
}

func (_ *Network) RenderGCE(t *gce.GCEAPITarget, a, e, changes *Network) error {
	shared := fi.BoolValue(e.Shared)
	if shared {
		// Verify the network was found
		if a == nil {
			return fmt.Errorf("Network with name %q not found", fi.StringValue(e.Name))
		}
	}

	if a == nil {
		klog.V(2).Infof("Creating Network with CIDR: %q", fi.StringValue(e.CIDR))

		network := &compute.Network{
			Name: *e.Name,
		}

		switch e.Mode {
		case "legacy":
			network.IPv4Range = fi.StringValue(e.CIDR)

		case "auto":
			network.AutoCreateSubnetworks = true

		case "custom":
			network.AutoCreateSubnetworks = false
			// The boolean default value of "false" is omitted when the struct
			// is serialized, which results in the network being created with
			// the auto-create subnetworks default of "true". Explicitly send
			// the default value.
			network.ForceSendFields = []string{"AutoCreateSubnetworks"}

		default:
			return fmt.Errorf("unhandled mode %q", e.Mode)
		}

		op, err := t.Cloud.Compute().Networks().Insert(t.Cloud.Project(), network)
		if err != nil {
			return fmt.Errorf("error creating Network: %v", err)
		}
		if err := t.Cloud.WaitForOp(op); err != nil {
			return fmt.Errorf("error waiting for Network creation to complete: %w", err)
		}
	} else {
		if a.Mode == "legacy" {
			return fmt.Errorf("GCE networks in legacy mode are not supported.  Please convert to auto mode or specify a different network.")
		}
		empty := &Network{}
		if !reflect.DeepEqual(empty, changes) {
			return fmt.Errorf("cannot apply changes to Network: %v", changes)
		}
	}

	return nil
}

type terraformNetwork struct {
	Name                  *string `json:"name" cty:"name"`
	IPv4Range             *string `json:"ipv4_range,omitempty" cty:"ipv4_range"`
	AutoCreateSubnetworks *bool   `json:"auto_create_subnetworks,omitempty" cty:"auto_create_subnetworks"`
}

func (_ *Network) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *Network) error {
	shared := fi.BoolValue(e.Shared)
	if shared {
		// Not terraform owned / managed
		return nil
	}

	tf := &terraformNetwork{
		Name: e.Name,
	}

	switch e.Mode {
	case "legacy":
		tf.IPv4Range = e.CIDR

	case "auto":
		tf.AutoCreateSubnetworks = fi.Bool(true)

	case "custom":
		tf.AutoCreateSubnetworks = fi.Bool(false)
	}

	return t.RenderResource("google_compute_network", *e.Name, tf)
}

func (e *Network) TerraformLink() *terraformWriter.Literal {
	shared := fi.BoolValue(e.Shared)
	if shared {
		if e.Name == nil {
			klog.Fatalf("Name must be set, if network is shared: %#v", e)
		}

		klog.V(4).Infof("reusing existing network with name %q", *e.Name)
		return terraformWriter.LiteralFromStringValue(*e.Name)
	}

	return terraformWriter.LiteralProperty("google_compute_network", *e.Name, "name")
}
<|endoftext|>


<commit_before>package kong

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

/*
func TestAPI_marshal(t *testing.T) {
	testJSONMarshal(t, &Api{}, "{}")

	a := &Api{
		Name:             "n",
		CreatedAt:        1,
		ID:               "i",
		PreserveHost:     true,
		RequestPath:      "r",
		StripRequestPath: true,
		UpstreamURL:      "u",
	}
	want := `{
		"name": "n",
		"created_at": 1,
		"id": "i",
		"preserve_host": true,
		"request_path": "r",
		"strip_request_path": true,
		"upstream_url": "u"
	}`
	testJSONMarshal(t, a, want)
}
*/
func TestApisService_Get(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	mux.HandleFunc("/apis/i", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"id":"i"}`)
	})

	api, _, err := client.Apis.Get("i")
	if err != nil {
		t.Errorf("Apis.Get returned error: %v", err)
	}

	want := &Api{ID: "i"}
	if !reflect.DeepEqual(api, want) {
		t.Errorf("Apis.Get returned %+v, want %+v", api, want)
	}
}

func TestApisService_Get_invalidApi(t *testing.T) {
	_, _, err := client.Apis.Get("%")
	testURLParseError(t, err)
}

func TestApisService_Patch(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	input := &Api{ID: "i"}

	mux.HandleFunc("/apis/i", func(w http.ResponseWriter, r *http.Request) {
		v := new(Api)
		json.NewDecoder(r.Body).Decode(v)
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		testMethod(t, r, "PATCH")

	})
}

func TestApisService_Delete(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	mux.HandleFunc("/apis/i", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
	})

	_, err := client.Apis.Delete("i")
	if err != nil {
		t.Errorf("Apis.Get returned error: %v", err)
	}
}

func TestApisService_Post(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	input := &Api{ID: "i"}

	mux.HandleFunc("/apis", func(w http.ResponseWriter, r *http.Request) {
		v := new(Api)
		json.NewDecoder(r.Body).Decode(v)
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		testMethod(t, r, "POST")

	})
}

func TestApisService_GetAll(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	v := &Apis{Total: 1, Next: "n", Data: []Api{{ID: "i"}}}

	mux.HandleFunc("/apis", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"offset": "o", "request_host": "r"})
		json.NewEncoder(w).Encode(v)
	})

	opt := &ApisGetAllOptions{Offset: "o", RequestHost: "r"}
	apis, _, err := client.Apis.GetAll(opt)
	if err != nil {
		t.Errorf("Apis.GetAll returned error: %v", err)
	}

	want := &Apis{Total: 1, Next: "n", Data: []Api{{ID: "i"}}}
	if !reflect.DeepEqual(apis, want) {
		t.Errorf("Apis.GetAll returned %+v, want %+v", apis, want)
	}
}
<commit_msg>Added missing test lines in apis<commit_after>package kong

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

/*
func TestAPI_marshal(t *testing.T) {
	testJSONMarshal(t, &Api{}, "{}")

	a := &Api{
		Name:             "n",
		CreatedAt:        1,
		ID:               "i",
		PreserveHost:     true,
		RequestPath:      "r",
		StripRequestPath: true,
		UpstreamURL:      "u",
	}
	want := `{
		"name": "n",
		"created_at": 1,
		"id": "i",
		"preserve_host": true,
		"request_path": "r",
		"strip_request_path": true,
		"upstream_url": "u"
	}`
	testJSONMarshal(t, a, want)
}
*/
func TestApisService_Get(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	mux.HandleFunc("/apis/i", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"id":"i"}`)
	})

	api, _, err := client.Apis.Get("i")
	if err != nil {
		t.Errorf("Apis.Get returned error: %v", err)
	}

	want := &Api{ID: "i"}
	if !reflect.DeepEqual(api, want) {
		t.Errorf("Apis.Get returned %+v, want %+v", api, want)
	}
}

func TestApisService_Get_invalidApi(t *testing.T) {
	_, _, err := client.Apis.Get("%")
	testURLParseError(t, err)
}

func TestApisService_Patch(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	input := &Api{Name: "n"}

	mux.HandleFunc("/apis/n", func(w http.ResponseWriter, r *http.Request) {
		v := new(Api)
		json.NewDecoder(r.Body).Decode(v)
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		testMethod(t, r, "PATCH")

	})

	_, err := client.Apis.Patch(input)
	if err != nil {
		t.Errorf("Apis.Patch returned error: %v", err)
	}

}

func TestApisService_Delete(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	mux.HandleFunc("/apis/i", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
	})

	_, err := client.Apis.Delete("i")
	if err != nil {
		t.Errorf("Apis.Delete returned error: %v", err)
	}
}

func TestApisService_Post(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	input := &Api{ID: "i"}

	mux.HandleFunc("/apis", func(w http.ResponseWriter, r *http.Request) {
		v := new(Api)
		json.NewDecoder(r.Body).Decode(v)
		if !reflect.DeepEqual(v, input) {
			t.Errorf("Request body = %+v, want %+v", v, input)
		}

		testMethod(t, r, "POST")

	})

	_, err := client.Apis.Post(input)
	if err != nil {
		t.Errorf("Apis.Post returned error: %v", err)
	}
}

func TestApisService_GetAll(t *testing.T) {
	stubSetup()
	defer stubTeardown()

	v := &Apis{Total: 1, Next: "n", Data: []Api{{ID: "i"}}}

	mux.HandleFunc("/apis", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testFormValues(t, r, values{"offset": "o", "request_host": "r"})
		json.NewEncoder(w).Encode(v)
	})

	opt := &ApisGetAllOptions{Offset: "o", RequestHost: "r"}
	apis, _, err := client.Apis.GetAll(opt)
	if err != nil {
		t.Errorf("Apis.GetAll returned error: %v", err)
	}

	want := &Apis{Total: 1, Next: "n", Data: []Api{{ID: "i"}}}
	if !reflect.DeepEqual(apis, want) {
		t.Errorf("Apis.GetAll returned %+v, want %+v", apis, want)
	}
}
<|endoftext|>


<commit_before>package graph

import (
	"errors"
	"strings"
)

type TileGraph struct {
	tiles            []bool
	numRows, numCols int
}

func NewTileGraph(dimX, dimY int, isPassable bool) *TileGraph {
	tiles := make([]bool, dimX*dimY)
	if isPassable {
		for i, _ := range tiles {
			tiles[i] = true
		}
	}

	return &TileGraph{
		tiles:   tiles,
		numRows: dimX,
		numCols: dimY,
	}
}

func GenerateTileGraph(template string) (*TileGraph, error) {
	rows := strings.Split(strings.TrimSpace(template), "\n")

	tiles := make([]bool, 0)

	colCheck := -1
	for _, colString := range rows {
		colCount := 0
		cols := strings.NewReader(colString)
		for cols.Len() != 0 {
			colCount += 1
			ch, _, err := cols.ReadRune()
			if err != nil {
				return nil, errors.New("Error while reading rune from input string")
			}

			switch ch {
			case '\u2580':
				tiles = append(tiles, false)
			case ' ':
				tiles = append(tiles, true)
			default:
				return nil, errors.New("Unrecognized character while reading input string")
			}
		}

		if colCheck == -1 {
			colCheck = colCount
		} else if colCheck != colCount {
			return nil, errors.New("Jagged rows, cannot generate graph.")
		}
	}

	return &TileGraph{
		tiles:   tiles,
		numRows: len(rows),
		numCols: colCheck,
	}, nil
}

func (graph *TileGraph) SetPassability(row, col int, passability bool) {
	loc := row*graph.numCols + col
	if loc >= len(graph.tiles) || row < 0 || col < 0 {
		return
	}

	graph.tiles[loc] = passability
}

func (graph *TileGraph) String() string {
	var outString string
	for r := 0; r < graph.numRows; r++ {
		for c := 0; c < graph.numCols; c++ {
			if graph.tiles[r*graph.numCols+c] == false {
				outString += "\u2580" // Black square
			} else {
				outString += " " // Space
			}
		}

		outString += "\n"
	}

	return outString[:len(outString)-1] // Kill final newline
}

func (graph *TileGraph) PathString(path []int) string {
	if path == nil || len(path) == 0 {
		return graph.String()
	}

	var outString string
	for r := 0; r < graph.numRows; r++ {
		for c := 0; c < graph.numCols; c++ {
			if id := r*graph.numCols + c; graph.tiles[id] == false {
				outString += "\u2580" // Black square
			} else if id == path[0] {
				outString += "s"
			} else if id == path[len(path)-1] {
				outString += "g"
			} else {
				toAppend := " "
				for _, num := range path[1 : len(path)-1] {
					if id == num {
						toAppend = "♥"
					}
				}
				outString += toAppend
			}
		}

		outString += "\n"
	}

	return outString[:len(outString)-1]
}

func (graph *TileGraph) Dimensions() (rows, cols int) {
	return graph.numRows, graph.numCols
}

func (graph *TileGraph) IDToCoords(id int) (row, col int) {
	col = (id % graph.numCols)
	row = (id - col) / graph.numCols

	return row, col
}

func (graph *TileGraph) CoordsToID(row, col int) (id int) {
	if row < 0 || row >= graph.numRows || col < 0 || col >= graph.numCols {
		return -1
	}
	id = row*graph.numCols + col

	return id
}

func (graph *TileGraph) Successors(id int) []int {
	if id < 0 || id >= len(graph.tiles) || graph.tiles[id] == false {
		return nil
	}

	row, col := graph.IDToCoords(id)

	neighbors := []int{graph.CoordsToID(row-1, col), graph.CoordsToID(row+1, col), graph.CoordsToID(row, col-1), graph.CoordsToID(row, col+1)}
	realNeighbors := make([]int, 0, 4) // Will overallocate sometimes, but not by much. Not a big deal
	for _, neighbor := range neighbors {
		if neighbor != -1 && graph.tiles[neighbor] == true {
			realNeighbors = append(realNeighbors, neighbor)
		}
	}

	return realNeighbors
}

func (graph *TileGraph) IsSuccessor(id, succ int) bool {
	return (id >= 0 && id < len(graph.tiles) && graph.tiles[id] == true) && (succ >= 0 && succ < len(graph.tiles) && graph.tiles[succ] == true)
}

func (graph *TileGraph) Predecessors(id int) []int {
	return graph.Successors(id)
}

func (graph *TileGraph) IsPredecessor(id, pred int) bool {
	return graph.IsSuccessor(id, pred)
}

func (graph *TileGraph) IsAdjacent(id, neighbor int) bool {
	return graph.IsSuccessor(id, neighbor)
}

func (graph *TileGraph) NodeExists(id int) bool {
	return id >= 0 && id < len(graph.tiles) && graph.tiles[id] == true
}

func (graph *TileGraph) Degree(id int) int {
	return len(graph.Successors(id)) * 2
}

func (graph *TileGraph) EdgeList() [][2]int {
	edges := make([][2]int, 0)
	for id, passable := range graph.tiles {
		if !passable {
			continue
		}

		for _, succ := range graph.Successors(id) {
			edges = append(edges, [2]int{id, succ})
		}
	}

	return edges
}

func (graph *TileGraph) NodeList() []int {
	nodes := make([]int, 0)
	for id, passable := range graph.tiles {
		if !passable {
			continue
		}

		nodes = append(nodes, id)
	}

	return nodes
}

func (graph *TileGraph) IsDirected() bool {
	return false
}
<commit_msg>Brought tilegraph.go in line with new API<commit_after>package graph

import (
	"errors"
	"strings"
)

type TileGraph struct {
	tiles            []bool
	numRows, numCols int
}

func NewTileGraph(dimX, dimY int, isPassable bool) *TileGraph {
	tiles := make([]bool, dimX*dimY)
	if isPassable {
		for i, _ := range tiles {
			tiles[i] = true
		}
	}

	return &TileGraph{
		tiles:   tiles,
		numRows: dimX,
		numCols: dimY,
	}
}

func GenerateTileGraph(template string) (*TileGraph, error) {
	rows := strings.Split(strings.TrimSpace(template), "\n")

	tiles := make([]bool, 0)

	colCheck := -1
	for _, colString := range rows {
		colCount := 0
		cols := strings.NewReader(colString)
		for cols.Len() != 0 {
			colCount += 1
			ch, _, err := cols.ReadRune()
			if err != nil {
				return nil, errors.New("Error while reading rune from input string")
			}

			switch ch {
			case '\u2580':
				tiles = append(tiles, false)
			case ' ':
				tiles = append(tiles, true)
			default:
				return nil, errors.New("Unrecognized character while reading input string")
			}
		}

		if colCheck == -1 {
			colCheck = colCount
		} else if colCheck != colCount {
			return nil, errors.New("Jagged rows, cannot generate graph.")
		}
	}

	return &TileGraph{
		tiles:   tiles,
		numRows: len(rows),
		numCols: colCheck,
	}, nil
}

func (graph *TileGraph) SetPassability(row, col int, passability bool) {
	loc := row*graph.numCols + col
	if loc >= len(graph.tiles) || row < 0 || col < 0 {
		return
	}

	graph.tiles[loc] = passability
}

func (graph *TileGraph) String() string {
	var outString string
	for r := 0; r < graph.numRows; r++ {
		for c := 0; c < graph.numCols; c++ {
			if graph.tiles[r*graph.numCols+c] == false {
				outString += "\u2580" // Black square
			} else {
				outString += " " // Space
			}
		}

		outString += "\n"
	}

	return outString[:len(outString)-1] // Kill final newline
}

func (graph *TileGraph) PathString(path []int) string {
	if path == nil || len(path) == 0 {
		return graph.String()
	}

	var outString string
	for r := 0; r < graph.numRows; r++ {
		for c := 0; c < graph.numCols; c++ {
			if id := r*graph.numCols + c; graph.tiles[id] == false {
				outString += "\u2580" // Black square
			} else if id == path[0] {
				outString += "s"
			} else if id == path[len(path)-1] {
				outString += "g"
			} else {
				toAppend := " "
				for _, num := range path[1 : len(path)-1] {
					if id == num {
						toAppend = "♥"
					}
				}
				outString += toAppend
			}
		}

		outString += "\n"
	}

	return outString[:len(outString)-1]
}

func (graph *TileGraph) Dimensions() (rows, cols int) {
	return graph.numRows, graph.numCols
}

func (graph *TileGraph) IDToCoords(id int) (row, col int) {
	col = (id % graph.numCols)
	row = (id - col) / graph.numCols

	return row, col
}

func (graph *TileGraph) CoordsToID(row, col int) (id int) {
	if row < 0 || row >= graph.numRows || col < 0 || col >= graph.numCols {
		return -1
	}
	id = row*graph.numCols + col

	return id
}

func (graph *TileGraph) CoordsToNode(row, col int) (node Node) {
	id := graph.CoordsToID(row, col)
	if id == -1 {
		return nil
	} else {
		return GonumNode(id)
	}
}

func (graph *TileGraph) Successors(node Node) []Node {
	id := node.ID()
	if id < 0 || id >= len(graph.tiles) || graph.tiles[id] == false {
		return nil
	}

	row, col := graph.IDToCoords(id)

	neighbors := []Node{graph.CoordsToNode(row-1, col), graph.CoordsToNode(row+1, col), graph.CoordsToNode(row, col-1), graph.CoordsToNode(row, col+1)}
	realNeighbors := make([]Node, 0, 4) // Will overallocate sometimes, but not by much. Not a big deal
	for _, neighbor := range neighbors {
		if neighbor != nil && graph.tiles[neighbor.ID()] == true {
			realNeighbors = append(realNeighbors, neighbor)
		}
	}

	return realNeighbors
}

func (graph *TileGraph) IsSuccessor(node, successor Node) bool {
	id, succ := node.ID(), successor.ID()
	return (id >= 0 && id < len(graph.tiles) && graph.tiles[id] == true) && (succ >= 0 && succ < len(graph.tiles) && graph.tiles[succ] == true)
}

func (graph *TileGraph) Predecessors(node Node) []Node {
	return graph.Successors(node)
}

func (graph *TileGraph) IsPredecessor(node, pred Node) bool {
	return graph.IsSuccessor(node, pred)
}

func (graph *TileGraph) IsAdjacent(id, neighbor Node) bool {
	return graph.IsSuccessor(id, neighbor)
}

func (graph *TileGraph) NodeExists(node Node) bool {
	id := node.ID()
	return id >= 0 && id < len(graph.tiles) && graph.tiles[id] == true
}

func (graph *TileGraph) Degree(node Node) int {
	return len(graph.Successors(node)) * 2
}

func (graph *TileGraph) EdgeList() []Edge {
	edges := make([]Edge, 0)
	for id, passable := range graph.tiles {
		if !passable {
			continue
		}

		for _, succ := range graph.Successors(GonumNode(id)) {
			edges = append(edges, GonumEdge{GonumNode(id), succ})
		}
	}

	return edges
}

func (graph *TileGraph) NodeList() []Node {
	nodes := make([]Node, 0)
	for id, passable := range graph.tiles {
		if !passable {
			continue
		}

		nodes = append(nodes, GonumNode(id))
	}

	return nodes
}

func (graph *TileGraph) IsDirected() bool {
	return false
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package image

import (
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"path"
	"syscall"

	"gate.computer/gate/internal/file"
	"gate.computer/gate/internal/manifest"
	"gate.computer/wag/object"
	"golang.org/x/sys/unix"
	"google.golang.org/protobuf/proto"
)

const (
	programFileTag     = 0x4a5274bd
	instanceFileTag    = 0xb405dd05
	manifestHeaderSize = 8
)

// Filesystem implements Storage.  It supports program and instance
// persistence.
type Filesystem struct {
	progDir *file.File
	instDir *file.File
}

func NewFilesystem(root string) (fs *Filesystem, err error) {
	progPath := path.Join(root, "program")
	instPath := path.Join(root, "instance")

	// Don't use MkdirAll to get an error if root doesn't exist.
	for _, p := range []string{progPath, instPath} {
		if e := os.Mkdir(p, 0700); e != nil && !os.IsExist(e) {
			err = e
			return
		}
	}

	progDir, err := openat(unix.AT_FDCWD, progPath, syscall.O_DIRECTORY, 0)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			progDir.Close()
		}
	}()

	instDir, err := openat(unix.AT_FDCWD, instPath, syscall.O_DIRECTORY, 0)
	if err != nil {
		return
	}

	fs = &Filesystem{progDir, instDir}
	return
}

func (fs *Filesystem) Close() (err error) {
	fs.instDir.Close()
	fs.progDir.Close()
	return
}

func (fs *Filesystem) programBackend() interface{}  { return fs }
func (fs *Filesystem) instanceBackend() interface{} { return fs }

func (fs *Filesystem) newProgramFile() (f *file.File, err error) {
	f, err = openat(int(fs.progDir.Fd()), ".", unix.O_TMPFILE|syscall.O_RDWR, 0400)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()

	err = ftruncate(f.FD(), progMaxOffset)
	return
}

func (fs *Filesystem) protectProgramFile(*file.File) error { return nil }

func (fs *Filesystem) storeProgram(prog *Program, name string) (err error) {
	err = marshalManifest(prog.file, prog.man, progManifestOffset, programFileTag)
	if err != nil {
		return
	}

	err = fdatasync(prog.file.FD())
	if err != nil {
		return
	}

	err = linkTempFile(prog.file.Fd(), fs.progDir.Fd(), name)
	if err != nil {
		if !os.IsExist(err) {
			return
		}
		err = nil
	}

	err = fdatasync(fs.progDir.FD())
	return
}

func (fs *Filesystem) Programs() (names []string, err error) {
	return fs.listNames(fs.progDir.Fd())
}

func (fs *Filesystem) LoadProgram(name string) (prog *Program, err error) {
	return fs.loadProgram(fs, name)
}

func (fs *Filesystem) loadProgram(storage Storage, name string) (prog *Program, err error) {
	f, err := openat(int(fs.progDir.Fd()), name, syscall.O_RDONLY, 0)
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
		}
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()

	prog = &Program{
		storage: storage,
		man:     new(manifest.Program),
		file:    f,
	}

	err = unmarshalManifest(f, prog.man, progManifestOffset, programFileTag)
	if err != nil {
		return
	}

	var (
		progCallSitesOffset = progModuleOffset + align8(int64(prog.man.ModuleSize))
		progFuncAddrsOffset = progCallSitesOffset + int64(prog.man.CallSitesSize)
	)

	prog.Map.CallSites = make([]object.CallSite, prog.man.CallSitesSize/callSiteSize)
	prog.Map.FuncAddrs = make([]uint32, prog.man.FuncAddrsSize/4)

	// TODO: preadv

	_, err = io.ReadFull(io.NewSectionReader(f, progCallSitesOffset, int64(prog.man.CallSitesSize)), callSitesBytes(&prog.Map))
	if err != nil {
		return
	}

	_, err = io.ReadFull(io.NewSectionReader(f, progFuncAddrsOffset, int64(prog.man.FuncAddrsSize)), funcAddrsBytes(&prog.Map))
	return
}

func (fs *Filesystem) newInstanceFile() (f *file.File, err error) {
	f, err = openat(int(fs.instDir.Fd()), ".", unix.O_TMPFILE|syscall.O_RDWR, 0600)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()

	err = ftruncate(f.FD(), instMaxOffset)
	return
}

func (fs *Filesystem) instanceFileWriteSupported() bool { return true }
func (fs *Filesystem) storeInstanceSupported() bool     { return true }

func (fs *Filesystem) storeInstance(inst *Instance, name string) (err error) {
	if inst.manDirty {
		err = marshalManifest(inst.file, inst.man, instManifestOffset, instanceFileTag)
		if err != nil {
			return
		}
		inst.manDirty = false
	}

	err = fdatasync(inst.file.FD())
	if err != nil {
		return
	}

	err = linkTempFile(inst.file.Fd(), fs.instDir.Fd(), name)
	if err != nil {
		return
	}

	err = fdatasync(fs.instDir.FD())
	if err != nil {
		return
	}

	inst.dir = fs.instDir
	inst.name = name
	return
}

func (fs *Filesystem) Instances() (names []string, err error) {
	return fs.listNames(fs.instDir.Fd())
}

func (fs *Filesystem) LoadInstance(name string) (inst *Instance, err error) {
	f, err := openat(int(fs.instDir.Fd()), name, syscall.O_RDWR, 0)
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
		}
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()

	inst = &Instance{
		man:      new(manifest.Instance),
		coherent: true,
		file:     f,
		dir:      fs.instDir,
		name:     name,
	}

	err = unmarshalManifest(f, inst.man, instManifestOffset, instanceFileTag)
	return
}

func (fs *Filesystem) listNames(dirFD uintptr) (names []string, err error) {
	dir, err := os.Open(fmt.Sprintf("/proc/self/fd/%d", dirFD))
	if err != nil {
		return
	}
	defer dir.Close()

	infos, err := dir.Readdir(-1)
	if err != nil {
		return
	}

	for _, info := range infos {
		if info.Mode().IsRegular() {
			names = append(names, info.Name())
		}
	}
	return
}

func marshalManifest(f *file.File, man proto.Message, offset int64, tag uint32) (err error) {
	marsh, err := proto.Marshal(man)
	if err != nil {
		return
	}

	size := manifestHeaderSize + len(marsh)
	b := make([]byte, size)
	binary.LittleEndian.PutUint32(b, tag)
	binary.LittleEndian.PutUint32(b[4:], uint32(size))
	copy(b[manifestHeaderSize:], marsh)

	_, err = f.WriteAt(b, offset)
	return
}

func unmarshalManifest(f *file.File, man proto.Message, offset int64, tag uint32) (err error) {
	b := make([]byte, manifest.MaxSize)
	_, err = io.ReadFull(io.NewSectionReader(f, offset, manifest.MaxSize), b)
	if err != nil {
		return
	}

	if x := binary.LittleEndian.Uint32(b); x != tag {
		err = fmt.Errorf("incorrect file tag: %#x", x)
		return
	}

	size := binary.LittleEndian.Uint32(b[4:])
	if size < manifestHeaderSize || size > manifest.MaxSize {
		err = fmt.Errorf("manifest size out of bounds: %d", size)
		return
	}

	return proto.Unmarshal(b[manifestHeaderSize:size], man)
}
<commit_msg>image: don't load programs using different library version<commit_after>// Copyright (c) 2018 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package image

import (
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"path"
	"syscall"

	"gate.computer/gate/internal/file"
	"gate.computer/gate/internal/manifest"
	"gate.computer/gate/runtime/abi"
	"gate.computer/wag/object"
	"golang.org/x/sys/unix"
	"google.golang.org/protobuf/proto"
)

const (
	programFileTag     = 0x4a5274bd
	instanceFileTag    = 0xb405dd05
	manifestHeaderSize = 8
)

// Filesystem implements Storage.  It supports program and instance
// persistence.
type Filesystem struct {
	progDir *file.File
	instDir *file.File
}

func NewFilesystem(root string) (fs *Filesystem, err error) {
	progPath := path.Join(root, "program")
	instPath := path.Join(root, "instance")

	// Don't use MkdirAll to get an error if root doesn't exist.
	for _, p := range []string{progPath, instPath} {
		if e := os.Mkdir(p, 0700); e != nil && !os.IsExist(e) {
			err = e
			return
		}
	}

	progDir, err := openat(unix.AT_FDCWD, progPath, syscall.O_DIRECTORY, 0)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			progDir.Close()
		}
	}()

	instDir, err := openat(unix.AT_FDCWD, instPath, syscall.O_DIRECTORY, 0)
	if err != nil {
		return
	}

	fs = &Filesystem{progDir, instDir}
	return
}

func (fs *Filesystem) Close() (err error) {
	fs.instDir.Close()
	fs.progDir.Close()
	return
}

func (fs *Filesystem) programBackend() interface{}  { return fs }
func (fs *Filesystem) instanceBackend() interface{} { return fs }

func (fs *Filesystem) newProgramFile() (f *file.File, err error) {
	f, err = openat(int(fs.progDir.Fd()), ".", unix.O_TMPFILE|syscall.O_RDWR, 0400)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()

	err = ftruncate(f.FD(), progMaxOffset)
	return
}

func (fs *Filesystem) protectProgramFile(*file.File) error { return nil }

func (fs *Filesystem) storeProgram(prog *Program, name string) (err error) {
	err = marshalManifest(prog.file, prog.man, progManifestOffset, programFileTag)
	if err != nil {
		return
	}

	err = fdatasync(prog.file.FD())
	if err != nil {
		return
	}

	err = linkTempFile(prog.file.Fd(), fs.progDir.Fd(), name)
	if err != nil {
		if !os.IsExist(err) {
			return
		}
		err = nil
	}

	err = fdatasync(fs.progDir.FD())
	return
}

func (fs *Filesystem) Programs() (names []string, err error) {
	return fs.listNames(fs.progDir.Fd())
}

func (fs *Filesystem) LoadProgram(name string) (prog *Program, err error) {
	return fs.loadProgram(fs, name)
}

func (fs *Filesystem) loadProgram(storage Storage, name string) (*Program, error) {
	f, err := openat(int(fs.progDir.Fd()), name, syscall.O_RDONLY, 0)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}
	defer func() {
		if f != nil {
			f.Close()
		}
	}()

	prog := &Program{
		storage: storage,
		man:     new(manifest.Program),
	}

	if err := unmarshalManifest(f, prog.man, progManifestOffset, programFileTag); err != nil {
		return nil, err
	}

	if prog.man.LibraryChecksum != abi.LibraryChecksum() {
		return nil, nil
	}

	var (
		progCallSitesOffset = progModuleOffset + align8(int64(prog.man.ModuleSize))
		progFuncAddrsOffset = progCallSitesOffset + int64(prog.man.CallSitesSize)
	)

	prog.Map.CallSites = make([]object.CallSite, prog.man.CallSitesSize/callSiteSize)
	prog.Map.FuncAddrs = make([]uint32, prog.man.FuncAddrsSize/4)

	// TODO: preadv

	if _, err := io.ReadFull(io.NewSectionReader(f, progCallSitesOffset, int64(prog.man.CallSitesSize)), callSitesBytes(&prog.Map)); err != nil {
		return nil, err
	}

	if _, err := io.ReadFull(io.NewSectionReader(f, progFuncAddrsOffset, int64(prog.man.FuncAddrsSize)), funcAddrsBytes(&prog.Map)); err != nil {
		return nil, err
	}

	prog.file = f
	f = nil
	return prog, nil
}

func (fs *Filesystem) newInstanceFile() (f *file.File, err error) {
	f, err = openat(int(fs.instDir.Fd()), ".", unix.O_TMPFILE|syscall.O_RDWR, 0600)
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()

	err = ftruncate(f.FD(), instMaxOffset)
	return
}

func (fs *Filesystem) instanceFileWriteSupported() bool { return true }
func (fs *Filesystem) storeInstanceSupported() bool     { return true }

func (fs *Filesystem) storeInstance(inst *Instance, name string) (err error) {
	if inst.manDirty {
		err = marshalManifest(inst.file, inst.man, instManifestOffset, instanceFileTag)
		if err != nil {
			return
		}
		inst.manDirty = false
	}

	err = fdatasync(inst.file.FD())
	if err != nil {
		return
	}

	err = linkTempFile(inst.file.Fd(), fs.instDir.Fd(), name)
	if err != nil {
		return
	}

	err = fdatasync(fs.instDir.FD())
	if err != nil {
		return
	}

	inst.dir = fs.instDir
	inst.name = name
	return
}

func (fs *Filesystem) Instances() (names []string, err error) {
	return fs.listNames(fs.instDir.Fd())
}

func (fs *Filesystem) LoadInstance(name string) (inst *Instance, err error) {
	f, err := openat(int(fs.instDir.Fd()), name, syscall.O_RDWR, 0)
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
		}
		return
	}
	defer func() {
		if err != nil {
			f.Close()
		}
	}()

	inst = &Instance{
		man:      new(manifest.Instance),
		coherent: true,
		file:     f,
		dir:      fs.instDir,
		name:     name,
	}

	err = unmarshalManifest(f, inst.man, instManifestOffset, instanceFileTag)
	return
}

func (fs *Filesystem) listNames(dirFD uintptr) (names []string, err error) {
	dir, err := os.Open(fmt.Sprintf("/proc/self/fd/%d", dirFD))
	if err != nil {
		return
	}
	defer dir.Close()

	infos, err := dir.Readdir(-1)
	if err != nil {
		return
	}

	for _, info := range infos {
		if info.Mode().IsRegular() {
			names = append(names, info.Name())
		}
	}
	return
}

func marshalManifest(f *file.File, man proto.Message, offset int64, tag uint32) (err error) {
	marsh, err := proto.Marshal(man)
	if err != nil {
		return
	}

	size := manifestHeaderSize + len(marsh)
	b := make([]byte, size)
	binary.LittleEndian.PutUint32(b, tag)
	binary.LittleEndian.PutUint32(b[4:], uint32(size))
	copy(b[manifestHeaderSize:], marsh)

	_, err = f.WriteAt(b, offset)
	return
}

func unmarshalManifest(f *file.File, man proto.Message, offset int64, tag uint32) (err error) {
	b := make([]byte, manifest.MaxSize)
	_, err = io.ReadFull(io.NewSectionReader(f, offset, manifest.MaxSize), b)
	if err != nil {
		return
	}

	if x := binary.LittleEndian.Uint32(b); x != tag {
		err = fmt.Errorf("incorrect file tag: %#x", x)
		return
	}

	size := binary.LittleEndian.Uint32(b[4:])
	if size < manifestHeaderSize || size > manifest.MaxSize {
		err = fmt.Errorf("manifest size out of bounds: %d", size)
		return
	}

	return proto.Unmarshal(b[manifestHeaderSize:size], man)
}
<|endoftext|>


<commit_before>package pubsub

import (
	"errors"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/dynport/dgtk/pubsub"
)

const (
	colorDryRun = 226
	colorCached = 33
	colorExec   = 34
)

var colorMapping = map[string]int{
	StatusCached:       colorCached,
	StatusExecFinished: colorExec,
}

var ignoredMessagesError = errors.New("ignored published messages (subscriber buffer full)")

// Create a logging facility for urknall using urknall's default formatter.
// Note that this resource must be closed afterwards!
func OpenLogger(w io.Writer) io.Closer {
	logger := &logger{}
	logger.Output = w
	logger.Formatter = logger.DefaultFormatter
	// Ignore the error from Start. It would only be triggered if the formatter wouldn't be set.
	_ = logger.Start()
	return logger
}

type logger struct {
	Output       io.Writer
	Formatter    formatter
	maxLengths   map[int]int
	started      time.Time
	finished     chan interface{}
	pubSub       *pubsub.PubSub
	subscription *pubsub.Subscription
}

func (logger *logger) Started() time.Time {
	if logger.started.IsZero() {
		logger.started = time.Now()
	}
	return logger.started
}

func (logger *logger) formatCommandOuput(message *Message) string {
	prefix := fmt.Sprintf("[%s][%s][%s]", formatIp(message.Hostname), formatTaskName(message.TaskName, 12), formatDuration(logger.sinceStarted()))
	line := message.Line
	if message.IsStderr() {
		line = colorize(13, line)
	}
	return prefix + " " + line
}

func formatIp(ip string) string {
	return fmt.Sprintf("%15s", ip)
}

type formatter func(urknallMessage *Message) string

func (logger *logger) DefaultFormatter(message *Message) string {
	ignoreKeys := []string{MessageTasksPrecompile, MessageCleanupCacheEntries, MessageTasksProvision, MessageUrknallInternal}
	for _, k := range ignoreKeys {
		if strings.HasPrefix(message.Key, k) {
			return ""
		}
	}
	if len(message.Line) > 0 {
		return logger.formatCommandOuput(message)
	}
	ip := message.Hostname
	taskName := message.TaskName
	payload := ""
	if message.Message != "" {
		payload = message.Message
	}
	execStatus := fmt.Sprintf("%-8s", message.ExecStatus)
	if color := colorMapping[message.ExecStatus]; color > 0 {
		execStatus = colorize(color, execStatus)
	}
	parts := []string{
		fmt.Sprintf("[%s][%s][%s][%s]%s",
			formatIp(ip),
			formatTaskName(taskName, 12),
			formatDuration(logger.sinceStarted()),
			execStatus,
			payload,
		),
	}
	return strings.Join(parts, " ")
}

func formatTaskName(name string, maxLen int) string {
	if len(name) > maxLen {
		name = name[0:maxLen]
	}
	return fmt.Sprintf("%-*s", maxLen, name)
}

func formatDuration(dur time.Duration) string {
	durString := ""
	if dur >= 1*time.Millisecond {
		durString = fmt.Sprintf("%.03f", dur.Seconds())
	}
	return fmt.Sprintf("%7s", durString)
}

func (logger *logger) sinceStarted() time.Duration {
	return time.Now().Sub(logger.Started())
}

func (logger *logger) Start() error {
	logger.started = time.Now()
	if logger.Formatter == nil {
		return fmt.Errorf("Formatter must be set")
	}
	logger.pubSub = pubsub.New()
	RegisterPubSub(logger.pubSub)
	logger.subscription = logger.pubSub.Subscribe(func(m *Message) {
		if message := logger.Formatter(m); message != "" {
			fmt.Fprintln(logger.Output, message)
		}
	})
	return nil
}

func (logger *logger) Close() (e error) {
	e = logger.subscription.Close()
	if d := logger.pubSub.Stats.Ignored(); e == nil && d > 0 {
		return ignoredMessagesError
	}
	return e
}

func colorize(c int, s string) string {
	return fmt.Sprintf("\033[38;5;%dm%s\033[0m", c, s)
}
<commit_msg>change command log color<commit_after>package pubsub

import (
	"errors"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/dynport/dgtk/pubsub"
)

const (
	colorDryRun = 226
	colorCached = 33
	colorExec   = 46
)

var colorMapping = map[string]int{
	StatusCached:       colorCached,
	StatusExecFinished: colorExec,
}

var ignoredMessagesError = errors.New("ignored published messages (subscriber buffer full)")

// Create a logging facility for urknall using urknall's default formatter.
// Note that this resource must be closed afterwards!
func OpenLogger(w io.Writer) io.Closer {
	logger := &logger{}
	logger.Output = w
	logger.Formatter = logger.DefaultFormatter
	// Ignore the error from Start. It would only be triggered if the formatter wouldn't be set.
	_ = logger.Start()
	return logger
}

type logger struct {
	Output       io.Writer
	Formatter    formatter
	maxLengths   map[int]int
	started      time.Time
	finished     chan interface{}
	pubSub       *pubsub.PubSub
	subscription *pubsub.Subscription
}

func (logger *logger) Started() time.Time {
	if logger.started.IsZero() {
		logger.started = time.Now()
	}
	return logger.started
}

func (logger *logger) formatCommandOuput(message *Message) string {
	prefix := fmt.Sprintf("[%s][%s][%s]", formatIp(message.Hostname), formatTaskName(message.TaskName, 12), formatDuration(logger.sinceStarted()))
	line := message.Line
	if message.IsStderr() {
		line = colorize(34, line)
	}
	return prefix + " " + line
}

func formatIp(ip string) string {
	return fmt.Sprintf("%s", ip)
}

type formatter func(urknallMessage *Message) string

func (logger *logger) DefaultFormatter(message *Message) string {
	ignoreKeys := []string{MessageTasksPrecompile, MessageCleanupCacheEntries, MessageTasksProvision, MessageUrknallInternal}
	for _, k := range ignoreKeys {
		if strings.HasPrefix(message.Key, k) {
			return ""
		}
	}
	if len(message.Line) > 0 {
		return logger.formatCommandOuput(message)
	}
	ip := message.Hostname
	taskName := message.TaskName
	payload := ""
	if message.Message != "" {
		payload = message.Message
	}
	execStatus := fmt.Sprintf("%-8s", message.ExecStatus)
	if color := colorMapping[message.ExecStatus]; color > 0 {
		execStatus = colorize(color, execStatus)
	}
	parts := []string{
		fmt.Sprintf("[%s][%s][%s][%s]%s",
			formatIp(ip),
			formatTaskName(taskName, 12),
			formatDuration(logger.sinceStarted()),
			execStatus,
			payload,
		),
	}
	return strings.Join(parts, " ")
}

func formatTaskName(name string, maxLen int) string {
	if len(name) > maxLen {
		name = name[0:maxLen]
	}
	return fmt.Sprintf("%-*s", maxLen, name)
}

func formatDuration(dur time.Duration) string {
	durString := ""
	if dur >= 1*time.Millisecond {
		durString = fmt.Sprintf("%.03f", dur.Seconds())
	}
	return fmt.Sprintf("%7s", durString)
}

func (logger *logger) sinceStarted() time.Duration {
	return time.Now().Sub(logger.Started())
}

func (logger *logger) Start() error {
	logger.started = time.Now()
	if logger.Formatter == nil {
		return fmt.Errorf("Formatter must be set")
	}
	logger.pubSub = pubsub.New()
	RegisterPubSub(logger.pubSub)
	logger.subscription = logger.pubSub.Subscribe(func(m *Message) {
		if message := logger.Formatter(m); message != "" {
			fmt.Fprintln(logger.Output, message)
		}
	})
	return nil
}

func (logger *logger) Close() (e error) {
	e = logger.subscription.Close()
	if d := logger.pubSub.Stats.Ignored(); e == nil && d > 0 {
		return ignoredMessagesError
	}
	return e
}

func colorize(c int, s string) string {
	return fmt.Sprintf("\033[38;5;%dm%s\033[0m", c, s)
}
<|endoftext|>