<commit_before>// Package raygun4go adds Raygun-based error handling to your golang code.
//
// It basically adds an error-handler that recovers from all panics that
// might occur and sends information about that error to Raygun. The amount
// of data being sent is configurable.
//
// Basic example:
//   raygun, err := raygun4go.New("appName", "apiKey")
//   if err != nil {
//     log.Println("Unable to create Raygun client:", err.Error())
//   }
//   defer raygun.HandleError()
//
// This will send the error message together with a stack trace to Raygun.
//
// However, raygun4go really starts to shine if used in a webserver context.
// By calling
//
//   raygun.Request(*http.Request)
//
// you can set a request to be analyzed in case of an error. If an error
// occurs, this will send the request details to Raygun, including
//
//   * hostname
//   * url
//   * http method
//   * ip adress
//   * url parameters
//   * POSTed form fields
//   * headers
//   * cookies
//
// giving you a lot more leverage on your errors than the plain error message
// could provide you with.
//
// Chainable configuration methods are available (see below) to set the
// affected version, user, tags or custom data.
package raygun4go

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"

	goerrors "github.com/go-errors/errors"
	"github.com/kaeuferportal/stack2struct"
	"github.com/pborman/uuid"
)

// Client is the struct holding your Raygun configuration and context
// information that is needed if an error occurs.
type Client struct {
	appName      string             // the name of the app
	apiKey       string             // the api key for your raygun app
	context      contextInformation // optional context information
	silent       bool               // if true, the error is printed instead of sent to Raygun
	logToStdOut  bool               // if true, the client will print debug messages
	asynchronous bool               // if true, reports are sent to Raygun from a new go routine
}

// contextInformation holds optional information on the context the error
// occured in.
type contextInformation struct {
	Request    *http.Request                          // the request associated to the error
	Version    string                                 // the version of the package
	Tags       []string                               // tags that you would like to use to filter this error
	CustomData interface{}                            // whatever you like Raygun to know about this error
	User       string                                 // the user that saw the error
	GetCustomGroupingKey func(error, PostData) string // A function that takes the original error and Raygun payload and returns a key for grouping errors together in Raygun.
	identifier string                                 // a unique identifier for the running process, automatically set by New()
}

// raygunAPIEndpoint  holds the REST - JSON API Endpoint address
var raygunEndpoint = "https://api.raygun.io"

// Identifier returns the otherwise private identifier property from the
// Client's context. It is set by the New()-method and represents a unique
// identifier for your running program.
func (ci *contextInformation) Identifier() string {
	return ci.identifier
}

// New creates and returns a Client, needing an appName and an apiKey. It also
// creates a unique identifier for your program.
func New(appName, apiKey string) (c *Client, err error) {
	context := contextInformation{identifier: uuid.New()}
	if appName == "" || apiKey == "" {
		return nil, errors.New("appName and apiKey are required")
	}
	c = &Client{appName, apiKey, context, false, false, false}
	return c, nil
}

func (c *Client) Clone() *Client {
	contextInfoClone := contextInformation{
		Request: c.context.Request,
		Version: c.context.Version,
		Tags: c.context.Tags,
		CustomData: c.context.CustomData,
		User: c.context.User,
		GetCustomGroupingKey: c.context.GetCustomGroupingKey,
		identifier: c.context.identifier,
	}

	clientClone := &Client{
		appName: c.appName,
		apiKey: c.apiKey,
		context: contextInfoClone,
		silent: c.silent,
		logToStdOut: c.logToStdOut,
		asynchronous: c.asynchronous,
	}
	return clientClone
}

// Silent sets the silent-property on the Client. If true, errors will not be
// sent to Raygun but printed instead.
func (c *Client) Silent(s bool) *Client {
	c.silent = s
	return c
}

// LogToStdOut sets the logToStdOut-property on the Client.  If true, errors will
// be printed to std out as they are submitted to raygun.  This will also log
// any errors that occur when submiting to raygun to std out
func (c *Client) LogToStdOut(l bool) *Client {
	c.logToStdOut = l
	return c
}

// Sets whether or not this client submits reports to Raygun asynchronously.
// The default is false.
func (c *Client) Asynchronous(a bool) *Client {
	c.asynchronous = a
	return c
}

// Request is a chainable option-setting method to add a request to the context.
func (c *Client) Request(r *http.Request) *Client {
	c.context.Request = r
	return c
}

// Version is a chainable option-setting method to add a version to the context.
func (c *Client) Version(v string) *Client {
	c.context.Version = v
	return c
}

// Tags is a chainable option-setting method to add tags to the context. You
// can use tags to filter errors in Raygun.
func (c *Client) Tags(tags []string) *Client {
	c.context.Tags = tags
	return c
}

// CustomData is a chainable option-setting method to add arbitrary custom data
// to the context. Note that the given type (or at least parts of it)
// must implement the Marshaler-interface for this to work.
func (c *Client) CustomData(data interface{}) *Client {
	c.context.CustomData = data
	return c
}

// User is a chainable option-setting method to add an affected Username to the
// context.
func (c *Client) User(u string) *Client {
	c.context.User = u
	return c
}

// CustomGroupingKeyFunction is a chainable option-setting method to provide a callback function
// that returns a custom grouping key. This function is passed the original error and Raygun
// payload just before it is serialized and sent to Raygun. This lets you control how errors
// are grouped in your Raygun account. Returning null will result in Raygun grouping the errors
// for you. This allows you to pick and choose which errors you want to control the grouping for.
func (c *Client) CustomGroupingKeyFunction(getCustomGroupingKey func(error, PostData) string) *Client {
	c.context.GetCustomGroupingKey = getCustomGroupingKey
	return c
}

// HandleError sets up the error handling code. It needs to be called with
//
//   defer c.HandleError()
//
// to handle all panics inside the calling function and all calls made from it.
// Be sure to call this in your main function or (if it is webserver) in your
// request handler as soon as possible.
func (c *Client) HandleError() error {
	e := recover()
	if e == nil {
		return nil
	}

	err, ok := e.(error)
	if !ok {
		err = errors.New(e.(string))
	}

	if c.logToStdOut {
		log.Println("Recovering from:", err.Error())
	}

	post := c.createPost(err, currentStack())
	err = c.submit(post)

	if c.logToStdOut && err != nil {
		log.Println(err.Error())
	}
	return err
}

// createPost creates the data structure that will be sent to Raygun.
func (c *Client) createPost(err error, stack StackTrace) PostData {
	postData := newPostData(c.context, err, stack)

	if c.context.GetCustomGroupingKey != nil {
	    customGroupingKey := c.context.GetCustomGroupingKey(err, postData)
	    if customGroupingKey != "" {
	        postData.Details.GroupingKey = &customGroupingKey
	    }
	}

	return postData
}

// Manually send a new error with the given message to Raygun. This will use the current execution stacktrace.
func (c *Client) CreateError(message string) error {
	err := errors.New(message)
	post := c.createPost(err, currentStack())

	return c.submit(post)
}

// Manually send the given error to Raygun.
// If the given error is a "github.com/go-errors/errors".Error, then its stacktrace will be used in the Raygun report.
// For other errors, the current execution stacktrace is used in the Raygun report.
func (c *Client) SendError(error error) error {
	err := errors.New(error.Error())

	var st StackTrace = nil
	if goerror, ok := error.(*goerrors.Error); ok {
		st = make(StackTrace, 0, 0)
		stack2struct.Parse(goerror.Stack(), &st)
	} else {
		st = currentStack()
	}

	post := c.createPost(err, st)
	return c.submit(post)
}

// submit takes care of actually sending the error to Raygun unless the silent
// option is set.
func (c *Client) submit(post PostData) error {
	if c.silent {
		enc, _ := json.MarshalIndent(post, "", "\t")
		fmt.Println(string(enc))
		return nil
	}

  if c.asynchronous {
  	go c.submitCore(post)
  	return nil
  }

  return c.submitCore(post)
}

func (c *Client) submitCore(post PostData) error {
	json, err := json.Marshal(post)
	if err != nil {
		errMsg := fmt.Sprintf("Unable to convert to JSON (%s): %#v", err.Error(), post)
		return errors.New(errMsg)
	}

	r, err := http.NewRequest("POST", raygunEndpoint+"/entries", bytes.NewBuffer(json))
	if err != nil {
		errMsg := fmt.Sprintf("Unable to create request (%s)", err.Error())
		return errors.New(errMsg)
	}
	r.Header.Add("X-ApiKey", c.apiKey)
	httpClient := http.Client{}
	resp, err := httpClient.Do(r)

	if err != nil {
		errMsg := fmt.Sprintf("Failed to request (%s)", err.Error())
		return errors.New(errMsg)
	}

	defer resp.Body.Close()
	if resp.StatusCode == 202 {
		if c.logToStdOut {
			log.Println("Successfully sent message to Raygun")
		}
		return nil
	}

	errMsg := fmt.Sprintf("Unexpected answer from Raygun %d", resp.StatusCode)
	if err != nil {
		errMsg = fmt.Sprintf("%s: %s", errMsg, err.Error())
	}

	return errors.New(errMsg)
}
<commit_msg>Switch to raygun.com<commit_after>// Package raygun4go adds Raygun-based error handling to your golang code.
//
// It basically adds an error-handler that recovers from all panics that
// might occur and sends information about that error to Raygun. The amount
// of data being sent is configurable.
//
// Basic example:
//   raygun, err := raygun4go.New("appName", "apiKey")
//   if err != nil {
//     log.Println("Unable to create Raygun client:", err.Error())
//   }
//   defer raygun.HandleError()
//
// This will send the error message together with a stack trace to Raygun.
//
// However, raygun4go really starts to shine if used in a webserver context.
// By calling
//
//   raygun.Request(*http.Request)
//
// you can set a request to be analyzed in case of an error. If an error
// occurs, this will send the request details to Raygun, including
//
//   * hostname
//   * url
//   * http method
//   * ip adress
//   * url parameters
//   * POSTed form fields
//   * headers
//   * cookies
//
// giving you a lot more leverage on your errors than the plain error message
// could provide you with.
//
// Chainable configuration methods are available (see below) to set the
// affected version, user, tags or custom data.
package raygun4go

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"

	goerrors "github.com/go-errors/errors"
	"github.com/kaeuferportal/stack2struct"
	"github.com/pborman/uuid"
)

// Client is the struct holding your Raygun configuration and context
// information that is needed if an error occurs.
type Client struct {
	appName      string             // the name of the app
	apiKey       string             // the api key for your raygun app
	context      contextInformation // optional context information
	silent       bool               // if true, the error is printed instead of sent to Raygun
	logToStdOut  bool               // if true, the client will print debug messages
	asynchronous bool               // if true, reports are sent to Raygun from a new go routine
}

// contextInformation holds optional information on the context the error
// occured in.
type contextInformation struct {
	Request    *http.Request                          // the request associated to the error
	Version    string                                 // the version of the package
	Tags       []string                               // tags that you would like to use to filter this error
	CustomData interface{}                            // whatever you like Raygun to know about this error
	User       string                                 // the user that saw the error
	GetCustomGroupingKey func(error, PostData) string // A function that takes the original error and Raygun payload and returns a key for grouping errors together in Raygun.
	identifier string                                 // a unique identifier for the running process, automatically set by New()
}

// raygunAPIEndpoint  holds the REST - JSON API Endpoint address
var raygunEndpoint = "https://api.raygun.com"

// Identifier returns the otherwise private identifier property from the
// Client's context. It is set by the New()-method and represents a unique
// identifier for your running program.
func (ci *contextInformation) Identifier() string {
	return ci.identifier
}

// New creates and returns a Client, needing an appName and an apiKey. It also
// creates a unique identifier for your program.
func New(appName, apiKey string) (c *Client, err error) {
	context := contextInformation{identifier: uuid.New()}
	if appName == "" || apiKey == "" {
		return nil, errors.New("appName and apiKey are required")
	}
	c = &Client{appName, apiKey, context, false, false, false}
	return c, nil
}

func (c *Client) Clone() *Client {
	contextInfoClone := contextInformation{
		Request: c.context.Request,
		Version: c.context.Version,
		Tags: c.context.Tags,
		CustomData: c.context.CustomData,
		User: c.context.User,
		GetCustomGroupingKey: c.context.GetCustomGroupingKey,
		identifier: c.context.identifier,
	}

	clientClone := &Client{
		appName: c.appName,
		apiKey: c.apiKey,
		context: contextInfoClone,
		silent: c.silent,
		logToStdOut: c.logToStdOut,
		asynchronous: c.asynchronous,
	}
	return clientClone
}

// Silent sets the silent-property on the Client. If true, errors will not be
// sent to Raygun but printed instead.
func (c *Client) Silent(s bool) *Client {
	c.silent = s
	return c
}

// LogToStdOut sets the logToStdOut-property on the Client.  If true, errors will
// be printed to std out as they are submitted to raygun.  This will also log
// any errors that occur when submiting to raygun to std out
func (c *Client) LogToStdOut(l bool) *Client {
	c.logToStdOut = l
	return c
}

// Sets whether or not this client submits reports to Raygun asynchronously.
// The default is false.
func (c *Client) Asynchronous(a bool) *Client {
	c.asynchronous = a
	return c
}

// Request is a chainable option-setting method to add a request to the context.
func (c *Client) Request(r *http.Request) *Client {
	c.context.Request = r
	return c
}

// Version is a chainable option-setting method to add a version to the context.
func (c *Client) Version(v string) *Client {
	c.context.Version = v
	return c
}

// Tags is a chainable option-setting method to add tags to the context. You
// can use tags to filter errors in Raygun.
func (c *Client) Tags(tags []string) *Client {
	c.context.Tags = tags
	return c
}

// CustomData is a chainable option-setting method to add arbitrary custom data
// to the context. Note that the given type (or at least parts of it)
// must implement the Marshaler-interface for this to work.
func (c *Client) CustomData(data interface{}) *Client {
	c.context.CustomData = data
	return c
}

// User is a chainable option-setting method to add an affected Username to the
// context.
func (c *Client) User(u string) *Client {
	c.context.User = u
	return c
}

// CustomGroupingKeyFunction is a chainable option-setting method to provide a callback function
// that returns a custom grouping key. This function is passed the original error and Raygun
// payload just before it is serialized and sent to Raygun. This lets you control how errors
// are grouped in your Raygun account. Returning null will result in Raygun grouping the errors
// for you. This allows you to pick and choose which errors you want to control the grouping for.
func (c *Client) CustomGroupingKeyFunction(getCustomGroupingKey func(error, PostData) string) *Client {
	c.context.GetCustomGroupingKey = getCustomGroupingKey
	return c
}

// HandleError sets up the error handling code. It needs to be called with
//
//   defer c.HandleError()
//
// to handle all panics inside the calling function and all calls made from it.
// Be sure to call this in your main function or (if it is webserver) in your
// request handler as soon as possible.
func (c *Client) HandleError() error {
	e := recover()
	if e == nil {
		return nil
	}

	err, ok := e.(error)
	if !ok {
		err = errors.New(e.(string))
	}

	if c.logToStdOut {
		log.Println("Recovering from:", err.Error())
	}

	post := c.createPost(err, currentStack())
	err = c.submit(post)

	if c.logToStdOut && err != nil {
		log.Println(err.Error())
	}
	return err
}

// createPost creates the data structure that will be sent to Raygun.
func (c *Client) createPost(err error, stack StackTrace) PostData {
	postData := newPostData(c.context, err, stack)

	if c.context.GetCustomGroupingKey != nil {
	    customGroupingKey := c.context.GetCustomGroupingKey(err, postData)
	    if customGroupingKey != "" {
	        postData.Details.GroupingKey = &customGroupingKey
	    }
	}

	return postData
}

// Manually send a new error with the given message to Raygun. This will use the current execution stacktrace.
func (c *Client) CreateError(message string) error {
	err := errors.New(message)
	post := c.createPost(err, currentStack())

	return c.submit(post)
}

// Manually send the given error to Raygun.
// If the given error is a "github.com/go-errors/errors".Error, then its stacktrace will be used in the Raygun report.
// For other errors, the current execution stacktrace is used in the Raygun report.
func (c *Client) SendError(error error) error {
	err := errors.New(error.Error())

	var st StackTrace = nil
	if goerror, ok := error.(*goerrors.Error); ok {
		st = make(StackTrace, 0, 0)
		stack2struct.Parse(goerror.Stack(), &st)
	} else {
		st = currentStack()
	}

	post := c.createPost(err, st)
	return c.submit(post)
}

// submit takes care of actually sending the error to Raygun unless the silent
// option is set.
func (c *Client) submit(post PostData) error {
	if c.silent {
		enc, _ := json.MarshalIndent(post, "", "\t")
		fmt.Println(string(enc))
		return nil
	}

  if c.asynchronous {
  	go c.submitCore(post)
  	return nil
  }

  return c.submitCore(post)
}

func (c *Client) submitCore(post PostData) error {
	json, err := json.Marshal(post)
	if err != nil {
		errMsg := fmt.Sprintf("Unable to convert to JSON (%s): %#v", err.Error(), post)
		return errors.New(errMsg)
	}

	r, err := http.NewRequest("POST", raygunEndpoint+"/entries", bytes.NewBuffer(json))
	if err != nil {
		errMsg := fmt.Sprintf("Unable to create request (%s)", err.Error())
		return errors.New(errMsg)
	}
	r.Header.Add("X-ApiKey", c.apiKey)
	httpClient := http.Client{}
	resp, err := httpClient.Do(r)

	if err != nil {
		errMsg := fmt.Sprintf("Failed to request (%s)", err.Error())
		return errors.New(errMsg)
	}

	defer resp.Body.Close()
	if resp.StatusCode == 202 {
		if c.logToStdOut {
			log.Println("Successfully sent message to Raygun")
		}
		return nil
	}

	errMsg := fmt.Sprintf("Unexpected answer from Raygun %d", resp.StatusCode)
	if err != nil {
		errMsg = fmt.Sprintf("%s: %s", errMsg, err.Error())
	}

	return errors.New(errMsg)
}
<|endoftext|>


<commit_before>package plugin

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/golang/mock/gomock"
	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/terraform/config/hcl2shim"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"

	proto "github.com/hashicorp/terraform/internal/tfplugin5"
	mockproto "github.com/hashicorp/terraform/plugin/mock_proto"
)

var _ providers.Interface = (*GRPCProvider)(nil)

func mockProviderClient(t *testing.T) *mockproto.MockProviderClient {
	ctrl := gomock.NewController(t)
	client := mockproto.NewMockProviderClient(ctrl)

	// we always need a GetSchema method
	client.EXPECT().GetSchema(
		gomock.Any(),
		gomock.Any(),
	).Return(providerProtoSchema(), nil)

	return client
}

func checkDiags(t *testing.T, d tfdiags.Diagnostics) {
	t.Helper()
	if d.HasErrors() {
		t.Fatal(d.Err())
	}
}

func providerProtoSchema() *proto.GetProviderSchema_Response {
	return &proto.GetProviderSchema_Response{
		Provider: &proto.Schema{
			Block: &proto.Schema_Block{
				Attributes: []*proto.Schema_Attribute{
					{
						Name:     "attr",
						Type:     []byte(`"string"`),
						Required: true,
					},
				},
			},
		},
		ResourceSchemas: map[string]*proto.Schema{
			"resource": &proto.Schema{
				Version: 1,
				Block: &proto.Schema_Block{
					Attributes: []*proto.Schema_Attribute{
						{
							Name:     "attr",
							Type:     []byte(`"string"`),
							Required: true,
						},
					},
				},
			},
		},
		DataSourceSchemas: map[string]*proto.Schema{
			"data": &proto.Schema{
				Version: 1,
				Block: &proto.Schema_Block{
					Attributes: []*proto.Schema_Attribute{
						{
							Name:     "attr",
							Type:     []byte(`"string"`),
							Required: true,
						},
					},
				},
			},
		},
	}
}

func TestGRPCProvider_GetSchema(t *testing.T) {
	p := &GRPCProvider{
		client: mockProviderClient(t),
	}

	resp := p.GetSchema()
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_PrepareProviderConfig(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().PrepareProviderConfig(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.PrepareProviderConfig_Response{}, nil)

	cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"})
	resp := p.PrepareProviderConfig(providers.PrepareProviderConfigRequest{Config: cfg})
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_ValidateResourceTypeConfig(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().ValidateResourceTypeConfig(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ValidateResourceTypeConfig_Response{}, nil)

	cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"})
	resp := p.ValidateResourceTypeConfig(providers.ValidateResourceTypeConfigRequest{
		TypeName: "resource",
		Config:   cfg,
	})
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_ValidateDataSourceConfig(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().ValidateDataSourceConfig(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ValidateDataSourceConfig_Response{}, nil)

	cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"})
	resp := p.ValidateDataSourceConfig(providers.ValidateDataSourceConfigRequest{
		TypeName: "data",
		Config:   cfg,
	})
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_UpgradeResourceState(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().UpgradeResourceState(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.UpgradeResourceState_Response{
		UpgradedState: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
	}, nil)

	resp := p.UpgradeResourceState(providers.UpgradeResourceStateRequest{
		TypeName:     "resource",
		Version:      0,
		RawStateJSON: []byte(`{"old_attr":"bar"}`),
	})
	checkDiags(t, resp.Diagnostics)

	expected := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty))
	}
}

func TestGRPCProvider_Configure(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().Configure(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.Configure_Response{}, nil)

	resp := p.Configure(providers.ConfigureRequest{
		Config: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
	})
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_Stop(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().Stop(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.Stop_Response{}, nil)

	err := p.Stop()
	if err != nil {
		t.Fatal(err)
	}
}

func TestGRPCProvider_ReadResource(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().ReadResource(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ReadResource_Response{
		NewState: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
	}, nil)

	resp := p.ReadResource(providers.ReadResourceRequest{
		TypeName: "resource",
		PriorState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
	})

	checkDiags(t, resp.Diagnostics)

	expected := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty))
	}
}

func TestGRPCProvider_PlanResourceChange(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	expectedPrivate := []byte(`{"meta": "data"}`)

	client.EXPECT().PlanResourceChange(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.PlanResourceChange_Response{
		PlannedState: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
		RequiresReplace: []*proto.AttributePath{
			{
				Steps: []*proto.AttributePath_Step{
					{
						Selector: &proto.AttributePath_Step_AttributeName{
							AttributeName: "attr",
						},
					},
				},
			},
		},
		PlannedPrivate: expectedPrivate,
	}, nil)

	resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{
		TypeName: "resource",
		PriorState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
		ProposedNewState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
		Config: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
	})

	checkDiags(t, resp.Diagnostics)

	expectedState := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty))
	}

	expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}`
	replace := fmt.Sprintf("%#v", resp.RequiresReplace)
	if expectedReplace != replace {
		t.Fatalf("expected %q, got %q", expectedReplace, replace)
	}

	if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) {
		t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate)
	}
}

func TestGRPCProvider_ApplyResourceChange(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	expectedPrivate := []byte(`{"meta": "data"}`)

	client.EXPECT().ApplyResourceChange(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ApplyResourceChange_Response{
		NewState: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
		Private: expectedPrivate,
	}, nil)

	resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{
		TypeName: "resource",
		PriorState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
		PlannedState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
		Config: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
		PlannedPrivate: expectedPrivate,
	})

	checkDiags(t, resp.Diagnostics)

	expectedState := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty))
	}

	if !bytes.Equal(expectedPrivate, resp.Private) {
		t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private)
	}
}

func TestGRPCProvider_ImportResourceState(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	expectedPrivate := []byte(`{"meta": "data"}`)

	client.EXPECT().ImportResourceState(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ImportResourceState_Response{
		ImportedResources: []*proto.ImportResourceState_ImportedResource{
			{
				TypeName: "resource",
				State: &proto.DynamicValue{
					Msgpack: []byte("\x81\xa4attr\xa3bar"),
				},
				Private: expectedPrivate,
			},
		},
	}, nil)

	resp := p.ImportResourceState(providers.ImportResourceStateRequest{
		TypeName: "resource",
		ID:       "foo",
	})

	checkDiags(t, resp.Diagnostics)

	expectedResource := providers.ImportedResource{
		TypeName: "resource",
		State: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
		Private: expectedPrivate,
	}

	imported := resp.ImportedResources[0]
	if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty))
	}
}

func TestGRPCProvider_ReadDataSource(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().ReadDataSource(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ReadDataSource_Response{
		State: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
	}, nil)

	resp := p.ReadDataSource(providers.ReadDataSourceRequest{
		TypeName: "data",
		Config: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
	})

	checkDiags(t, resp.Diagnostics)

	expected := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty))
	}
}
<commit_msg>add 3rd param to mock call<commit_after>package plugin

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/golang/mock/gomock"
	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/terraform/config/hcl2shim"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"

	proto "github.com/hashicorp/terraform/internal/tfplugin5"
	mockproto "github.com/hashicorp/terraform/plugin/mock_proto"
)

var _ providers.Interface = (*GRPCProvider)(nil)

func mockProviderClient(t *testing.T) *mockproto.MockProviderClient {
	ctrl := gomock.NewController(t)
	client := mockproto.NewMockProviderClient(ctrl)

	// we always need a GetSchema method
	client.EXPECT().GetSchema(
		gomock.Any(),
		gomock.Any(),
		gomock.Any(),
	).Return(providerProtoSchema(), nil)

	return client
}

func checkDiags(t *testing.T, d tfdiags.Diagnostics) {
	t.Helper()
	if d.HasErrors() {
		t.Fatal(d.Err())
	}
}

func providerProtoSchema() *proto.GetProviderSchema_Response {
	return &proto.GetProviderSchema_Response{
		Provider: &proto.Schema{
			Block: &proto.Schema_Block{
				Attributes: []*proto.Schema_Attribute{
					{
						Name:     "attr",
						Type:     []byte(`"string"`),
						Required: true,
					},
				},
			},
		},
		ResourceSchemas: map[string]*proto.Schema{
			"resource": &proto.Schema{
				Version: 1,
				Block: &proto.Schema_Block{
					Attributes: []*proto.Schema_Attribute{
						{
							Name:     "attr",
							Type:     []byte(`"string"`),
							Required: true,
						},
					},
				},
			},
		},
		DataSourceSchemas: map[string]*proto.Schema{
			"data": &proto.Schema{
				Version: 1,
				Block: &proto.Schema_Block{
					Attributes: []*proto.Schema_Attribute{
						{
							Name:     "attr",
							Type:     []byte(`"string"`),
							Required: true,
						},
					},
				},
			},
		},
	}
}

func TestGRPCProvider_GetSchema(t *testing.T) {
	p := &GRPCProvider{
		client: mockProviderClient(t),
	}

	resp := p.GetSchema()
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_PrepareProviderConfig(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().PrepareProviderConfig(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.PrepareProviderConfig_Response{}, nil)

	cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"})
	resp := p.PrepareProviderConfig(providers.PrepareProviderConfigRequest{Config: cfg})
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_ValidateResourceTypeConfig(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().ValidateResourceTypeConfig(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ValidateResourceTypeConfig_Response{}, nil)

	cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"})
	resp := p.ValidateResourceTypeConfig(providers.ValidateResourceTypeConfigRequest{
		TypeName: "resource",
		Config:   cfg,
	})
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_ValidateDataSourceConfig(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().ValidateDataSourceConfig(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ValidateDataSourceConfig_Response{}, nil)

	cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"})
	resp := p.ValidateDataSourceConfig(providers.ValidateDataSourceConfigRequest{
		TypeName: "data",
		Config:   cfg,
	})
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_UpgradeResourceState(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().UpgradeResourceState(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.UpgradeResourceState_Response{
		UpgradedState: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
	}, nil)

	resp := p.UpgradeResourceState(providers.UpgradeResourceStateRequest{
		TypeName:     "resource",
		Version:      0,
		RawStateJSON: []byte(`{"old_attr":"bar"}`),
	})
	checkDiags(t, resp.Diagnostics)

	expected := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty))
	}
}

func TestGRPCProvider_Configure(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().Configure(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.Configure_Response{}, nil)

	resp := p.Configure(providers.ConfigureRequest{
		Config: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
	})
	checkDiags(t, resp.Diagnostics)
}

func TestGRPCProvider_Stop(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().Stop(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.Stop_Response{}, nil)

	err := p.Stop()
	if err != nil {
		t.Fatal(err)
	}
}

func TestGRPCProvider_ReadResource(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().ReadResource(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ReadResource_Response{
		NewState: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
	}, nil)

	resp := p.ReadResource(providers.ReadResourceRequest{
		TypeName: "resource",
		PriorState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
	})

	checkDiags(t, resp.Diagnostics)

	expected := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty))
	}
}

func TestGRPCProvider_PlanResourceChange(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	expectedPrivate := []byte(`{"meta": "data"}`)

	client.EXPECT().PlanResourceChange(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.PlanResourceChange_Response{
		PlannedState: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
		RequiresReplace: []*proto.AttributePath{
			{
				Steps: []*proto.AttributePath_Step{
					{
						Selector: &proto.AttributePath_Step_AttributeName{
							AttributeName: "attr",
						},
					},
				},
			},
		},
		PlannedPrivate: expectedPrivate,
	}, nil)

	resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{
		TypeName: "resource",
		PriorState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
		ProposedNewState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
		Config: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
	})

	checkDiags(t, resp.Diagnostics)

	expectedState := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty))
	}

	expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}`
	replace := fmt.Sprintf("%#v", resp.RequiresReplace)
	if expectedReplace != replace {
		t.Fatalf("expected %q, got %q", expectedReplace, replace)
	}

	if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) {
		t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate)
	}
}

func TestGRPCProvider_ApplyResourceChange(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	expectedPrivate := []byte(`{"meta": "data"}`)

	client.EXPECT().ApplyResourceChange(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ApplyResourceChange_Response{
		NewState: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
		Private: expectedPrivate,
	}, nil)

	resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{
		TypeName: "resource",
		PriorState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
		PlannedState: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
		Config: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
		PlannedPrivate: expectedPrivate,
	})

	checkDiags(t, resp.Diagnostics)

	expectedState := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty))
	}

	if !bytes.Equal(expectedPrivate, resp.Private) {
		t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private)
	}
}

func TestGRPCProvider_ImportResourceState(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	expectedPrivate := []byte(`{"meta": "data"}`)

	client.EXPECT().ImportResourceState(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ImportResourceState_Response{
		ImportedResources: []*proto.ImportResourceState_ImportedResource{
			{
				TypeName: "resource",
				State: &proto.DynamicValue{
					Msgpack: []byte("\x81\xa4attr\xa3bar"),
				},
				Private: expectedPrivate,
			},
		},
	}, nil)

	resp := p.ImportResourceState(providers.ImportResourceStateRequest{
		TypeName: "resource",
		ID:       "foo",
	})

	checkDiags(t, resp.Diagnostics)

	expectedResource := providers.ImportedResource{
		TypeName: "resource",
		State: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("bar"),
		}),
		Private: expectedPrivate,
	}

	imported := resp.ImportedResources[0]
	if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty))
	}
}

func TestGRPCProvider_ReadDataSource(t *testing.T) {
	client := mockProviderClient(t)
	p := &GRPCProvider{
		client: client,
	}

	client.EXPECT().ReadDataSource(
		gomock.Any(),
		gomock.Any(),
	).Return(&proto.ReadDataSource_Response{
		State: &proto.DynamicValue{
			Msgpack: []byte("\x81\xa4attr\xa3bar"),
		},
	}, nil)

	resp := p.ReadDataSource(providers.ReadDataSourceRequest{
		TypeName: "data",
		Config: cty.ObjectVal(map[string]cty.Value{
			"attr": cty.StringVal("foo"),
		}),
	})

	checkDiags(t, resp.Diagnostics)

	expected := cty.ObjectVal(map[string]cty.Value{
		"attr": cty.StringVal("bar"),
	})

	if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) {
		t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty))
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess 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 mysql

import (
	"fmt"
	"io"
	"time"

	"context"

	"vitess.io/vitess/go/vt/proto/vtrpc"
	"vitess.io/vitess/go/vt/vterrors"
)

// mysqlFlavor implements the Flavor interface for Mysql.
type mysqlFlavor struct{}
type mysqlFlavor56 struct {
	mysqlFlavor
}
type mysqlFlavor57 struct {
	mysqlFlavor
}
type mysqlFlavor80 struct {
	mysqlFlavor
}

var _ flavor = (*mysqlFlavor56)(nil)
var _ flavor = (*mysqlFlavor57)(nil)
var _ flavor = (*mysqlFlavor80)(nil)

// masterGTIDSet is part of the Flavor interface.
func (mysqlFlavor) masterGTIDSet(c *Conn) (GTIDSet, error) {
	// keep @@global as lowercase, as some servers like the Ripple binlog server only honors a lowercase `global` value
	qr, err := c.ExecuteFetch("SELECT @@global.gtid_executed", 1, false)
	if err != nil {
		return nil, err
	}
	if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 {
		return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected result format for gtid_executed: %#v", qr)
	}
	return parseMysql56GTIDSet(qr.Rows[0][0].ToString())
}

func (mysqlFlavor) startReplicationCommand() string {
	return "START SLAVE"
}

func (mysqlFlavor) restartReplicationCommands() []string {
	return []string{
		"STOP SLAVE",
		"RESET SLAVE",
		"START SLAVE",
	}
}

func (mysqlFlavor) startReplicationUntilAfter(pos Position) string {
	return fmt.Sprintf("START SLAVE UNTIL SQL_AFTER_GTIDS = '%s'", pos)
}

func (mysqlFlavor) stopReplicationCommand() string {
	return "STOP SLAVE"
}

func (mysqlFlavor) stopIOThreadCommand() string {
	return "STOP SLAVE IO_THREAD"
}

// sendBinlogDumpCommand is part of the Flavor interface.
func (mysqlFlavor) sendBinlogDumpCommand(c *Conn, serverID uint32, startPos Position) error {
	gtidSet, ok := startPos.GTIDSet.(Mysql56GTIDSet)
	if !ok {
		return vterrors.Errorf(vtrpc.Code_INTERNAL, "startPos.GTIDSet is wrong type - expected Mysql56GTIDSet, got: %#v", startPos.GTIDSet)
	}

	// Build the command.
	sidBlock := gtidSet.SIDBlock()
	return c.WriteComBinlogDumpGTID(serverID, "", 4, 0, sidBlock)
}

// resetReplicationCommands is part of the Flavor interface.
func (mysqlFlavor) resetReplicationCommands(c *Conn) []string {
	resetCommands := []string{
		"STOP SLAVE",
		"RESET SLAVE ALL", // "ALL" makes it forget master host:port.
		"RESET MASTER",    // This will also clear gtid_executed and gtid_purged.
	}
	if c.SemiSyncExtensionLoaded() {
		resetCommands = append(resetCommands, "SET GLOBAL rpl_semi_sync_master_enabled = false, GLOBAL rpl_semi_sync_slave_enabled = false") // semi-sync will be enabled if needed when replica is started.
	}
	return resetCommands
}

// setReplicationPositionCommands is part of the Flavor interface.
func (mysqlFlavor) setReplicationPositionCommands(pos Position) []string {
	return []string{
		"RESET MASTER", // We must clear gtid_executed before setting gtid_purged.
		fmt.Sprintf("SET GLOBAL gtid_purged = '%s'", pos),
	}
}

// setReplicationPositionCommands is part of the Flavor interface.
func (mysqlFlavor) changeMasterArg() string {
	return "MASTER_AUTO_POSITION = 1"
}

// status is part of the Flavor interface.
func (mysqlFlavor) status(c *Conn) (ReplicationStatus, error) {
	qr, err := c.ExecuteFetch("SHOW SLAVE STATUS", 100, true /* wantfields */)
	if err != nil {
		return ReplicationStatus{}, err
	}
	if len(qr.Rows) == 0 {
		// The query returned no data, meaning the server
		// is not configured as a replica.
		return ReplicationStatus{}, ErrNotReplica
	}

	resultMap, err := resultToMap(qr)
	if err != nil {
		return ReplicationStatus{}, err
	}

	return parseMysqlReplicationStatus(resultMap)
}

func parseMysqlReplicationStatus(resultMap map[string]string) (ReplicationStatus, error) {
	status := parseReplicationStatus(resultMap)
	uuidString := resultMap["Master_UUID"]
	if uuidString != "" {
		sid, err := ParseSID(uuidString)
		if err != nil {
			return ReplicationStatus{}, vterrors.Wrapf(err, "cannot decode MasterUUID")
		}
		status.MasterUUID = sid
	}

	var err error
	status.Position.GTIDSet, err = parseMysql56GTIDSet(resultMap["Executed_Gtid_Set"])
	if err != nil {
		return ReplicationStatus{}, vterrors.Wrapf(err, "ReplicationStatus can't parse MySQL 5.6 GTID (Executed_Gtid_Set: %#v)", resultMap["Executed_Gtid_Set"])
	}
	relayLogGTIDSet, err := parseMysql56GTIDSet(resultMap["Retrieved_Gtid_Set"])
	if err != nil {
		return ReplicationStatus{}, vterrors.Wrapf(err, "ReplicationStatus can't parse MySQL 5.6 GTID (Retrieved_Gtid_Set: %#v)", resultMap["Retrieved_Gtid_Set"])
	}
	// We take the union of the executed and retrieved gtidset, because the retrieved gtidset only represents GTIDs since
	// the relay log has been reset. To get the full Position, we need to take a union of executed GTIDSets, since these would
	// have been in the relay log's GTIDSet in the past, prior to a reset.
	status.RelayLogPosition.GTIDSet = status.Position.GTIDSet.Union(relayLogGTIDSet)

	return status, nil
}

// masterStatus is part of the Flavor interface.
func (mysqlFlavor) masterStatus(c *Conn) (MasterStatus, error) {
	qr, err := c.ExecuteFetch("SHOW MASTER STATUS", 100, true /* wantfields */)
	if err != nil {
		return MasterStatus{}, err
	}
	if len(qr.Rows) == 0 {
		// The query returned no data. We don't know how this could happen.
		return MasterStatus{}, ErrNoMasterStatus
	}

	resultMap, err := resultToMap(qr)
	if err != nil {
		return MasterStatus{}, err
	}

	return parseMysqlMasterStatus(resultMap)
}

func parseMysqlMasterStatus(resultMap map[string]string) (MasterStatus, error) {
	status := parseMasterStatus(resultMap)

	var err error
	status.Position.GTIDSet, err = parseMysql56GTIDSet(resultMap["Executed_Gtid_Set"])
	if err != nil {
		return MasterStatus{}, vterrors.Wrapf(err, "MasterStatus can't parse MySQL 5.6 GTID (Executed_Gtid_Set: %#v)", resultMap["Executed_Gtid_Set"])
	}

	return status, nil
}

// waitUntilPositionCommand is part of the Flavor interface.

// waitUntilPositionCommand is part of the Flavor interface.
func (mysqlFlavor) waitUntilPositionCommand(ctx context.Context, pos Position) (string, error) {
	// A timeout of 0 means wait indefinitely.
	timeoutSeconds := 0
	if deadline, ok := ctx.Deadline(); ok {
		timeout := time.Until(deadline)
		if timeout <= 0 {
			return "", vterrors.Errorf(vtrpc.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos)
		}

		// Only whole numbers of seconds are supported.
		timeoutSeconds = int(timeout.Seconds())
		if timeoutSeconds == 0 {
			// We don't want a timeout <1.0s to truncate down to become infinite.
			timeoutSeconds = 1
		}
	}

	return fmt.Sprintf("SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('%s', %v)", pos, timeoutSeconds), nil
}

// readBinlogEvent is part of the Flavor interface.
func (mysqlFlavor) readBinlogEvent(c *Conn) (BinlogEvent, error) {
	result, err := c.ReadPacket()
	if err != nil {
		return nil, err
	}
	switch result[0] {
	case EOFPacket:
		return nil, NewSQLError(CRServerLost, SSUnknownSQLState, "%v", io.EOF)
	case ErrPacket:
		return nil, ParseErrorPacket(result)
	}
	return NewMysql56BinlogEvent(result[1:]), nil
}

// enableBinlogPlaybackCommand is part of the Flavor interface.
func (mysqlFlavor) enableBinlogPlaybackCommand() string {
	return ""
}

// disableBinlogPlaybackCommand is part of the Flavor interface.
func (mysqlFlavor) disableBinlogPlaybackCommand() string {
	return ""
}

// TablesWithSize56 is a query to select table along with size for mysql 5.6
const TablesWithSize56 = `SELECT table_name, table_type, unix_timestamp(create_time), table_comment, SUM( data_length + index_length), SUM( data_length + index_length)
		FROM information_schema.tables WHERE table_schema = database() group by table_name`

// TablesWithSize57 is a query to select table along with size for mysql 5.7.
// It's a little weird, because the JOIN predicate only works if the table and databases do not contain weird characters.
// As a fallback, we use the mysql 5.6 query, which is not always up to date, but works for all table/db names.
const TablesWithSize57 = `
	SELECT t.table_name, t.table_type, unix_timestamp(t.create_time), t.table_comment, i.file_size, i.allocated_size
	FROM information_schema.tables t, information_schema.innodb_sys_tablespaces i
	WHERE t.table_schema = database() and i.name = concat(t.table_schema,'/',t.table_name)
UNION ALL
	SELECT table_name, table_type, unix_timestamp(create_time), table_comment, SUM( data_length + index_length), SUM( data_length + index_length)
	FROM information_schema.tables t
	WHERE table_schema = database() AND NOT EXISTS(SELECT * FROM information_schema.innodb_sys_tablespaces i WHERE i.name = concat(t.table_schema,'/',t.table_name))
	group by table_name, table_type, unix_timestamp(create_time), table_comment
`

// TablesWithSize80 is a query to select table along with size for mysql 8.0
const TablesWithSize80 = `SELECT t.table_name, t.table_type, unix_timestamp(t.create_time), t.table_comment, i.file_size, i.allocated_size
		FROM information_schema.tables t, information_schema.innodb_tablespaces i
		WHERE t.table_schema = database() and i.name = concat(t.table_schema,'/',t.table_name)`

// baseShowTablesWithSizes is part of the Flavor interface.
func (mysqlFlavor56) baseShowTablesWithSizes() string {
	return TablesWithSize56
}

// baseShowTablesWithSizes is part of the Flavor interface.
func (mysqlFlavor57) baseShowTablesWithSizes() string {
	return TablesWithSize57
}

// baseShowTablesWithSizes is part of the Flavor interface.
func (mysqlFlavor80) baseShowTablesWithSizes() string {
	return TablesWithSize80
}
<commit_msg>fix schema unit test<commit_after>/*
Copyright 2019 The Vitess 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 mysql

import (
	"fmt"
	"io"
	"time"

	"context"

	"vitess.io/vitess/go/vt/proto/vtrpc"
	"vitess.io/vitess/go/vt/vterrors"
)

// mysqlFlavor implements the Flavor interface for Mysql.
type mysqlFlavor struct{}
type mysqlFlavor56 struct {
	mysqlFlavor
}
type mysqlFlavor57 struct {
	mysqlFlavor
}
type mysqlFlavor80 struct {
	mysqlFlavor
}

var _ flavor = (*mysqlFlavor56)(nil)
var _ flavor = (*mysqlFlavor57)(nil)
var _ flavor = (*mysqlFlavor80)(nil)

// masterGTIDSet is part of the Flavor interface.
func (mysqlFlavor) masterGTIDSet(c *Conn) (GTIDSet, error) {
	// keep @@global as lowercase, as some servers like the Ripple binlog server only honors a lowercase `global` value
	qr, err := c.ExecuteFetch("SELECT @@global.gtid_executed", 1, false)
	if err != nil {
		return nil, err
	}
	if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 {
		return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "unexpected result format for gtid_executed: %#v", qr)
	}
	return parseMysql56GTIDSet(qr.Rows[0][0].ToString())
}

func (mysqlFlavor) startReplicationCommand() string {
	return "START SLAVE"
}

func (mysqlFlavor) restartReplicationCommands() []string {
	return []string{
		"STOP SLAVE",
		"RESET SLAVE",
		"START SLAVE",
	}
}

func (mysqlFlavor) startReplicationUntilAfter(pos Position) string {
	return fmt.Sprintf("START SLAVE UNTIL SQL_AFTER_GTIDS = '%s'", pos)
}

func (mysqlFlavor) stopReplicationCommand() string {
	return "STOP SLAVE"
}

func (mysqlFlavor) stopIOThreadCommand() string {
	return "STOP SLAVE IO_THREAD"
}

// sendBinlogDumpCommand is part of the Flavor interface.
func (mysqlFlavor) sendBinlogDumpCommand(c *Conn, serverID uint32, startPos Position) error {
	gtidSet, ok := startPos.GTIDSet.(Mysql56GTIDSet)
	if !ok {
		return vterrors.Errorf(vtrpc.Code_INTERNAL, "startPos.GTIDSet is wrong type - expected Mysql56GTIDSet, got: %#v", startPos.GTIDSet)
	}

	// Build the command.
	sidBlock := gtidSet.SIDBlock()
	return c.WriteComBinlogDumpGTID(serverID, "", 4, 0, sidBlock)
}

// resetReplicationCommands is part of the Flavor interface.
func (mysqlFlavor) resetReplicationCommands(c *Conn) []string {
	resetCommands := []string{
		"STOP SLAVE",
		"RESET SLAVE ALL", // "ALL" makes it forget master host:port.
		"RESET MASTER",    // This will also clear gtid_executed and gtid_purged.
	}
	if c.SemiSyncExtensionLoaded() {
		resetCommands = append(resetCommands, "SET GLOBAL rpl_semi_sync_master_enabled = false, GLOBAL rpl_semi_sync_slave_enabled = false") // semi-sync will be enabled if needed when replica is started.
	}
	return resetCommands
}

// setReplicationPositionCommands is part of the Flavor interface.
func (mysqlFlavor) setReplicationPositionCommands(pos Position) []string {
	return []string{
		"RESET MASTER", // We must clear gtid_executed before setting gtid_purged.
		fmt.Sprintf("SET GLOBAL gtid_purged = '%s'", pos),
	}
}

// setReplicationPositionCommands is part of the Flavor interface.
func (mysqlFlavor) changeMasterArg() string {
	return "MASTER_AUTO_POSITION = 1"
}

// status is part of the Flavor interface.
func (mysqlFlavor) status(c *Conn) (ReplicationStatus, error) {
	qr, err := c.ExecuteFetch("SHOW SLAVE STATUS", 100, true /* wantfields */)
	if err != nil {
		return ReplicationStatus{}, err
	}
	if len(qr.Rows) == 0 {
		// The query returned no data, meaning the server
		// is not configured as a replica.
		return ReplicationStatus{}, ErrNotReplica
	}

	resultMap, err := resultToMap(qr)
	if err != nil {
		return ReplicationStatus{}, err
	}

	return parseMysqlReplicationStatus(resultMap)
}

func parseMysqlReplicationStatus(resultMap map[string]string) (ReplicationStatus, error) {
	status := parseReplicationStatus(resultMap)
	uuidString := resultMap["Master_UUID"]
	if uuidString != "" {
		sid, err := ParseSID(uuidString)
		if err != nil {
			return ReplicationStatus{}, vterrors.Wrapf(err, "cannot decode MasterUUID")
		}
		status.MasterUUID = sid
	}

	var err error
	status.Position.GTIDSet, err = parseMysql56GTIDSet(resultMap["Executed_Gtid_Set"])
	if err != nil {
		return ReplicationStatus{}, vterrors.Wrapf(err, "ReplicationStatus can't parse MySQL 5.6 GTID (Executed_Gtid_Set: %#v)", resultMap["Executed_Gtid_Set"])
	}
	relayLogGTIDSet, err := parseMysql56GTIDSet(resultMap["Retrieved_Gtid_Set"])
	if err != nil {
		return ReplicationStatus{}, vterrors.Wrapf(err, "ReplicationStatus can't parse MySQL 5.6 GTID (Retrieved_Gtid_Set: %#v)", resultMap["Retrieved_Gtid_Set"])
	}
	// We take the union of the executed and retrieved gtidset, because the retrieved gtidset only represents GTIDs since
	// the relay log has been reset. To get the full Position, we need to take a union of executed GTIDSets, since these would
	// have been in the relay log's GTIDSet in the past, prior to a reset.
	status.RelayLogPosition.GTIDSet = status.Position.GTIDSet.Union(relayLogGTIDSet)

	return status, nil
}

// masterStatus is part of the Flavor interface.
func (mysqlFlavor) masterStatus(c *Conn) (MasterStatus, error) {
	qr, err := c.ExecuteFetch("SHOW MASTER STATUS", 100, true /* wantfields */)
	if err != nil {
		return MasterStatus{}, err
	}
	if len(qr.Rows) == 0 {
		// The query returned no data. We don't know how this could happen.
		return MasterStatus{}, ErrNoMasterStatus
	}

	resultMap, err := resultToMap(qr)
	if err != nil {
		return MasterStatus{}, err
	}

	return parseMysqlMasterStatus(resultMap)
}

func parseMysqlMasterStatus(resultMap map[string]string) (MasterStatus, error) {
	status := parseMasterStatus(resultMap)

	var err error
	status.Position.GTIDSet, err = parseMysql56GTIDSet(resultMap["Executed_Gtid_Set"])
	if err != nil {
		return MasterStatus{}, vterrors.Wrapf(err, "MasterStatus can't parse MySQL 5.6 GTID (Executed_Gtid_Set: %#v)", resultMap["Executed_Gtid_Set"])
	}

	return status, nil
}

// waitUntilPositionCommand is part of the Flavor interface.

// waitUntilPositionCommand is part of the Flavor interface.
func (mysqlFlavor) waitUntilPositionCommand(ctx context.Context, pos Position) (string, error) {
	// A timeout of 0 means wait indefinitely.
	timeoutSeconds := 0
	if deadline, ok := ctx.Deadline(); ok {
		timeout := time.Until(deadline)
		if timeout <= 0 {
			return "", vterrors.Errorf(vtrpc.Code_DEADLINE_EXCEEDED, "timed out waiting for position %v", pos)
		}

		// Only whole numbers of seconds are supported.
		timeoutSeconds = int(timeout.Seconds())
		if timeoutSeconds == 0 {
			// We don't want a timeout <1.0s to truncate down to become infinite.
			timeoutSeconds = 1
		}
	}

	return fmt.Sprintf("SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('%s', %v)", pos, timeoutSeconds), nil
}

// readBinlogEvent is part of the Flavor interface.
func (mysqlFlavor) readBinlogEvent(c *Conn) (BinlogEvent, error) {
	result, err := c.ReadPacket()
	if err != nil {
		return nil, err
	}
	switch result[0] {
	case EOFPacket:
		return nil, NewSQLError(CRServerLost, SSUnknownSQLState, "%v", io.EOF)
	case ErrPacket:
		return nil, ParseErrorPacket(result)
	}
	return NewMysql56BinlogEvent(result[1:]), nil
}

// enableBinlogPlaybackCommand is part of the Flavor interface.
func (mysqlFlavor) enableBinlogPlaybackCommand() string {
	return ""
}

// disableBinlogPlaybackCommand is part of the Flavor interface.
func (mysqlFlavor) disableBinlogPlaybackCommand() string {
	return ""
}

// TablesWithSize56 is a query to select table along with size for mysql 5.6
const TablesWithSize56 = `SELECT table_name, table_type, unix_timestamp(create_time), table_comment, SUM( data_length + index_length), SUM( data_length + index_length)
		FROM information_schema.tables WHERE table_schema = database() group by table_name`

// TablesWithSize57 is a query to select table along with size for mysql 5.7.
// It's a little weird, because the JOIN predicate only works if the table and databases do not contain weird characters.
// As a fallback, we use the mysql 5.6 query, which is not always up to date, but works for all table/db names.
const TablesWithSize57 = `SELECT t.table_name, t.table_type, unix_timestamp(t.create_time), t.table_comment, i.file_size, i.allocated_size
	FROM information_schema.tables t, information_schema.innodb_sys_tablespaces i
	WHERE t.table_schema = database() and i.name = concat(t.table_schema,'/',t.table_name)
UNION ALL
	SELECT table_name, table_type, unix_timestamp(create_time), table_comment, SUM( data_length + index_length), SUM( data_length + index_length)
	FROM information_schema.tables t
	WHERE table_schema = database() AND NOT EXISTS(SELECT * FROM information_schema.innodb_sys_tablespaces i WHERE i.name = concat(t.table_schema,'/',t.table_name))
	group by table_name, table_type, unix_timestamp(create_time), table_comment
`

// TablesWithSize80 is a query to select table along with size for mysql 8.0
const TablesWithSize80 = `SELECT t.table_name, t.table_type, unix_timestamp(t.create_time), t.table_comment, i.file_size, i.allocated_size
		FROM information_schema.tables t, information_schema.innodb_tablespaces i
		WHERE t.table_schema = database() and i.name = concat(t.table_schema,'/',t.table_name)`

// baseShowTablesWithSizes is part of the Flavor interface.
func (mysqlFlavor56) baseShowTablesWithSizes() string {
	return TablesWithSize56
}

// baseShowTablesWithSizes is part of the Flavor interface.
func (mysqlFlavor57) baseShowTablesWithSizes() string {
	return TablesWithSize57
}

// baseShowTablesWithSizes is part of the Flavor interface.
func (mysqlFlavor80) baseShowTablesWithSizes() string {
	return TablesWithSize80
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"logs_topic", // name
		"topic",      // type
		true,         // durable
		false,        // auto-deleted
		false,        // internal
		false,        // no-wait
		nil,          // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	for _, s := range os.Args {
		log.Printf("Binding queue %s to exchange %s with routing key %s", q.Name, "logs_topic", s)
		err = ch.QueueBind(
			q.Name,       // queue name
			s,            // routing key
			"logs_topic", // exchange
			false,
			nil)
		failOnError(err, "Failed to bind a queue")
	}

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto ack
		false,  // exclusive
		false,  // no local
		false,  // no wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	forever := make(chan bool)

	go func() {
		for d := range msgs {
			log.Printf(" [x] %s", d.Body)
		}
	}()

	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
	<-forever
}
<commit_msg>Corrected argument handling<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"logs_topic", // name
		"topic",      // type
		true,         // durable
		false,        // auto-deleted
		false,        // internal
		false,        // no-wait
		nil,          // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	if len(os.Args) < 2 {
		log.Printf("Usage: %s [binding_key]...", os.Args[0])
		os.Exit(0)
	}
	for _, s := range os.Args[1:] {
		log.Printf("Binding queue %s to exchange %s with routing key %s", q.Name, "logs_topic", s)
		err = ch.QueueBind(
			q.Name,       // queue name
			s,            // routing key
			"logs_topic", // exchange
			false,
			nil)
		failOnError(err, "Failed to bind a queue")
	}

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto ack
		false,  // exclusive
		false,  // no local
		false,  // no wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	forever := make(chan bool)

	go func() {
		for d := range msgs {
			log.Printf(" [x] %s", d.Body)
		}
	}()

	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
	<-forever
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"net"
	"net/http"
	"strings"

	yaml "gopkg.in/yaml.v1"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

type MyJSONIPInfo struct {
	XMLName   xml.Name `json:"-" xml:"myjsonip.com" yaml:"-"`
	IPAddress string   `json:"ip,omitempty" xml:"ip,omitempty" yaml:"ip,omitempty"`
	Agent     string   `json:"agent,omitempty" xml:"agent,omitempty" yaml:"agent,omitempty"`
}

var e = createMux()

func init() {
	//e.SetHTTPErrorHandler(httpErrorHandler)
	e.Pre(middleware.RemoveTrailingSlash())

	e.GET("/", ipAddress)

	e.GET("/ip", ipAddress)
	e.GET("/ip/:format", ipAddress)

	e.GET("/agent", agent)
	e.GET("/agent/:format", agent)

	e.GET("/all", all)
	e.GET("/all/:format", all)
}

func httpErrorHandler(err error, c echo.Context) {
	code := http.StatusInternalServerError
	msg := http.StatusText(code)
	if he, ok := err.(*echo.HTTPError); ok {
		code = he.Code
		msg = he.Message.(string)
	}

	if c.Echo().Debug {
		msg = err.Error()
	}
	if !c.Response().Committed {
		if c.Request().Method == echo.HEAD { // Issue #608
			c.NoContent(code)
		} else {
			switch code {
			case http.StatusNotFound:
				c.Redirect(http.StatusMovedPermanently, "/404")
			default:
				c.String(code, msg)
			}
		}
	}
	c.Echo().Logger.Error(err)
}

func formatOutput(c echo.Context, m MyJSONIPInfo) (err error) {
	f := strings.ToLower(c.Param("format"))

	if f == "" {
		//w.Header().Set("Content-Type", "application/json")
		return c.JSON(http.StatusOK, m)
	} else if f == "json" {
		return c.JSON(http.StatusOK, m)
	} else if f == "yaml" || f == "yml" {
		c.Response().Header().Set(echo.HeaderContentType, "text/yaml; charset=utf-8")
		c.Response().WriteHeader(http.StatusOK)
		bodyFormatted, _ := yaml.Marshal(m)
		_, err = c.Response().Write(bodyFormatted)
		return
	} else if f == "xml" {
		return c.XML(http.StatusOK, m)
	}

	return c.String(http.StatusNotImplemented, "Format not recognized")
}

func parseRemoteAddr(s string) (ipType string, ip string) {
	if ip := net.ParseIP(s); ip != nil {
		if ip.To4() != nil {
			return "ipv4", ip.String()
		}
		// Return IPv6 if not IPv4
		return "ipv6", ip.String()
	}

	if ip := net.ParseIP(strings.Split(s, ":")[0]); ip != nil {
		return "ipv4", ip.String()
	}

	return "ipv?", "not found"
}

func ipAddress(c echo.Context) error {
	_, ip := parseRemoteAddr(c.Request().RemoteAddr)

	info := MyJSONIPInfo{}
	info.IPAddress = ip

	return formatOutput(c, info)
}

func agent(c echo.Context) error {
	agent := c.Request().UserAgent()

	info := MyJSONIPInfo{}
	info.Agent = agent

	return formatOutput(c, info)
}

func all(c echo.Context) error {
	agent := c.Request().UserAgent()
	_, ip := parseRemoteAddr(c.Request().RemoteAddr)

	info := MyJSONIPInfo{}
	info.Agent = agent
	info.IPAddress = ip

	return formatOutput(c, info)
}
<commit_msg>update to yaml.v2<commit_after>package main

import (
	"encoding/xml"
	"net"
	"net/http"
	"strings"

	yaml "gopkg.in/yaml.v2"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

type MyJSONIPInfo struct {
	XMLName   xml.Name `json:"-" xml:"myjsonip.com" yaml:"-"`
	IPAddress string   `json:"ip,omitempty" xml:"ip,omitempty" yaml:"ip,omitempty"`
	Agent     string   `json:"agent,omitempty" xml:"agent,omitempty" yaml:"agent,omitempty"`
}

var e = createMux()

func init() {
	//e.SetHTTPErrorHandler(httpErrorHandler)
	e.Pre(middleware.RemoveTrailingSlash())

	e.GET("/", ipAddress)

	e.GET("/ip", ipAddress)
	e.GET("/ip/:format", ipAddress)

	e.GET("/agent", agent)
	e.GET("/agent/:format", agent)

	e.GET("/all", all)
	e.GET("/all/:format", all)
}

func httpErrorHandler(err error, c echo.Context) {
	code := http.StatusInternalServerError
	msg := http.StatusText(code)
	if he, ok := err.(*echo.HTTPError); ok {
		code = he.Code
		msg = he.Message.(string)
	}

	if c.Echo().Debug {
		msg = err.Error()
	}
	if !c.Response().Committed {
		if c.Request().Method == echo.HEAD { // Issue #608
			c.NoContent(code)
		} else {
			switch code {
			case http.StatusNotFound:
				c.Redirect(http.StatusMovedPermanently, "/404")
			default:
				c.String(code, msg)
			}
		}
	}
	c.Echo().Logger.Error(err)
}

func formatOutput(c echo.Context, m MyJSONIPInfo) (err error) {
	f := strings.ToLower(c.Param("format"))

	if f == "" {
		//w.Header().Set("Content-Type", "application/json")
		return c.JSON(http.StatusOK, m)
	} else if f == "json" {
		return c.JSON(http.StatusOK, m)
	} else if f == "yaml" || f == "yml" {
		c.Response().Header().Set(echo.HeaderContentType, "text/yaml; charset=utf-8")
		c.Response().WriteHeader(http.StatusOK)
		bodyFormatted, _ := yaml.Marshal(m)
		_, err = c.Response().Write(bodyFormatted)
		return
	} else if f == "xml" {
		return c.XML(http.StatusOK, m)
	}

	return c.String(http.StatusNotImplemented, "Format not recognized")
}

func parseRemoteAddr(s string) (ipType string, ip string) {
	if ip := net.ParseIP(s); ip != nil {
		if ip.To4() != nil {
			return "ipv4", ip.String()
		}
		// Return IPv6 if not IPv4
		return "ipv6", ip.String()
	}

	if ip := net.ParseIP(strings.Split(s, ":")[0]); ip != nil {
		return "ipv4", ip.String()
	}

	return "ipv?", "not found"
}

func ipAddress(c echo.Context) error {
	_, ip := parseRemoteAddr(c.Request().RemoteAddr)

	info := MyJSONIPInfo{}
	info.IPAddress = ip

	return formatOutput(c, info)
}

func agent(c echo.Context) error {
	agent := c.Request().UserAgent()

	info := MyJSONIPInfo{}
	info.Agent = agent

	return formatOutput(c, info)
}

func all(c echo.Context) error {
	agent := c.Request().UserAgent()
	_, ip := parseRemoteAddr(c.Request().RemoteAddr)

	info := MyJSONIPInfo{}
	info.Agent = agent
	info.IPAddress = ip

	return formatOutput(c, info)
}
<|endoftext|>


<commit_before><commit_msg>bugfix #159<commit_after><|endoftext|>


<commit_before>// Copyright 2016 The Netstack Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tmutex

import (
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

func TestBasicLock(t *testing.T) {
	var m Mutex
	m.Init()

	m.Lock()

	// Try blocking lock the mutex from a different goroutine. This must
	// not block because the mutex is held.
	ch := make(chan struct{}, 1)
	go func() {
		m.Lock()
		ch <- struct{}{}
		m.Unlock()
		ch <- struct{}{}
	}()

	select {
	case <-ch:
		t.Fatalf("Lock succeeded on locked mutex")
	case <-time.After(100 * time.Millisecond):
	}

	// Unlock the mutex and make sure that the goroutine waiting on Lock()
	// unblocks and succeeds.
	m.Unlock()

	select {
	case <-ch:
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("Lock failed to acquire unlocked mutex")
	}

	// Make sure we can lock and unlock again.
	m.Lock()
	m.Unlock()
}

func TestTryLock(t *testing.T) {
	var m Mutex
	m.Init()

	// Try to lock. It should succeed.
	if !m.TryLock() {
		t.Fatalf("TryLock failed on unlocked mutex")
	}

	// Try to lock again, it should now fail.
	if m.TryLock() {
		t.Fatalf("TryLock succeeded on locked mutex")
	}

	// Try blocking lock the mutex from a different goroutine. This must
	// not block because the mutex is held.
	ch := make(chan struct{}, 1)
	go func() {
		m.Lock()
		ch <- struct{}{}
		m.Unlock()
	}()

	select {
	case <-ch:
		t.Fatalf("Lock succeeded on locked mutex")
	case <-time.After(100 * time.Millisecond):
	}

	// Unlock the mutex and make sure that the goroutine waiting on Lock()
	// unblocks and succeeds.
	m.Unlock()

	select {
	case <-ch:
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("Lock failed to acquire unlocked mutex")
	}
}

func TestMutualExclusion(t *testing.T) {
	var m Mutex
	m.Init()

	// Test mutual exclusion by running "gr" goroutines concurrently, and
	// have each one increment a counter "iters" times within the critical
	// section established by the mutex.
	//
	// If at the end the counter is not gr * iters, then we know that
	// goroutines ran concurrently within the critical section.
	//
	// If one of the goroutines doesn't complete, it's likely a bug that
	// causes to it to wait forever.
	const gr = 1000
	const iters = 100000
	v := 0
	var wg sync.WaitGroup
	for i := 0; i < gr; i++ {
		wg.Add(1)
		go func() {
			for j := 0; j < iters; j++ {
				m.Lock()
				v++
				m.Unlock()
			}
			wg.Done()
		}()
	}

	wg.Wait()

	if v != gr*iters {
		t.Fatalf("Bad count: got %v, want %v", v, gr*iters)
	}
}

func TestMutualExclusionWithTryLock(t *testing.T) {
	var m Mutex
	m.Init()

	// Similar to the previous, with the addition of some goroutines that
	// only increment the count if TryLock succeeds.
	const gr = 1000
	const iters = 100000
	total := int64(gr * iters)
	v := int64(0)
	var wg sync.WaitGroup
	for i := 0; i < gr; i++ {
		wg.Add(2)
		go func() {
			for j := 0; j < iters; j++ {
				m.Lock()
				v++
				m.Unlock()
			}
			wg.Done()
		}()
		go func() {
			local := int64(0)
			for j := 0; j < iters; j++ {
				if m.TryLock() {
					v++
					m.Unlock()
					local++
				}
			}
			atomic.AddInt64(&total, local)
			wg.Done()
		}()
	}

	wg.Wait()

	if v != total {
		t.Fatalf("Bad count: got %v, want %v", v, total)
	}
}
<commit_msg>Improve tmutex_test.<commit_after>// Copyright 2016 The Netstack Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tmutex

import (
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

func TestBasicLock(t *testing.T) {
	var m Mutex
	m.Init()

	m.Lock()

	// Try blocking lock the mutex from a different goroutine. This must
	// not block because the mutex is held.
	ch := make(chan struct{}, 1)
	go func() {
		m.Lock()
		ch <- struct{}{}
		m.Unlock()
		ch <- struct{}{}
	}()

	select {
	case <-ch:
		t.Fatalf("Lock succeeded on locked mutex")
	case <-time.After(100 * time.Millisecond):
	}

	// Unlock the mutex and make sure that the goroutine waiting on Lock()
	// unblocks and succeeds.
	m.Unlock()

	select {
	case <-ch:
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("Lock failed to acquire unlocked mutex")
	}

	// Make sure we can lock and unlock again.
	m.Lock()
	m.Unlock()
}

func TestTryLock(t *testing.T) {
	var m Mutex
	m.Init()

	// Try to lock. It should succeed.
	if !m.TryLock() {
		t.Fatalf("TryLock failed on unlocked mutex")
	}

	// Try to lock again, it should now fail.
	if m.TryLock() {
		t.Fatalf("TryLock succeeded on locked mutex")
	}

	// Try blocking lock the mutex from a different goroutine. This must
	// not block because the mutex is held.
	ch := make(chan struct{}, 1)
	go func() {
		m.Lock()
		ch <- struct{}{}
		m.Unlock()
	}()

	select {
	case <-ch:
		t.Fatalf("Lock succeeded on locked mutex")
	case <-time.After(100 * time.Millisecond):
	}

	// Unlock the mutex and make sure that the goroutine waiting on Lock()
	// unblocks and succeeds.
	m.Unlock()

	select {
	case <-ch:
	case <-time.After(100 * time.Millisecond):
		t.Fatalf("Lock failed to acquire unlocked mutex")
	}
}

func TestMutualExclusion(t *testing.T) {
	var m Mutex
	m.Init()

	// Test mutual exclusion by running "gr" goroutines concurrently, and
	// have each one increment a counter "iters" times within the critical
	// section established by the mutex.
	//
	// If at the end the counter is not gr * iters, then we know that
	// goroutines ran concurrently within the critical section.
	//
	// If one of the goroutines doesn't complete, it's likely a bug that
	// causes to it to wait forever.
	const gr = 1000
	const iters = 100000
	v := 0
	var wg sync.WaitGroup
	for i := 0; i < gr; i++ {
		wg.Add(1)
		go func() {
			for j := 0; j < iters; j++ {
				m.Lock()
				v++
				m.Unlock()
			}
			wg.Done()
		}()
	}

	wg.Wait()

	if v != gr*iters {
		t.Fatalf("Bad count: got %v, want %v", v, gr*iters)
	}
}

func TestMutualExclusionWithTryLock(t *testing.T) {
	var m Mutex
	m.Init()

	// Similar to the previous, with the addition of some goroutines that
	// only increment the count if TryLock succeeds.
	const gr = 1000
	const iters = 100000
	total := int64(gr * iters)
	var tryTotal int64
	v := int64(0)
	var wg sync.WaitGroup
	for i := 0; i < gr; i++ {
		wg.Add(2)
		go func() {
			for j := 0; j < iters; j++ {
				m.Lock()
				v++
				m.Unlock()
			}
			wg.Done()
		}()
		go func() {
			local := int64(0)
			for j := 0; j < iters; j++ {
				if m.TryLock() {
					v++
					m.Unlock()
					local++
				}
			}
			atomic.AddInt64(&tryTotal, local)
			wg.Done()
		}()
	}

	wg.Wait()

	t.Logf("tryTotal = %d", tryTotal)
	total += tryTotal

	if v != total {
		t.Fatalf("Bad count: got %v, want %v", v, total)
	}
}

// BenchmarkTmutex is equivalent to TestMutualExclusion, with the following
// differences:
//
// - The number of goroutines is variable, with the maximum value depending on
// GOMAXPROCS.
//
// - The number of iterations per benchmark is controlled by the benchmarking
// framework.
//
// - Care is taken to ensure that all goroutines participating in the benchmark
// have been created before the benchmark begins.
func BenchmarkTmutex(b *testing.B) {
	for n, max := 1, 4*runtime.GOMAXPROCS(0); n > 0 && n <= max; n *= 2 {
		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
			var m Mutex
			m.Init()

			var ready sync.WaitGroup
			begin := make(chan struct{})
			var end sync.WaitGroup
			for i := 0; i < n; i++ {
				ready.Add(1)
				end.Add(1)
				go func() {
					ready.Done()
					<-begin
					for j := 0; j < b.N; j++ {
						m.Lock()
						m.Unlock()
					}
					end.Done()
				}()
			}

			ready.Wait()
			b.ResetTimer()
			close(begin)
			end.Wait()
		})
	}
}

// BenchmarkSyncMutex is equivalent to BenchmarkTmutex, but uses sync.Mutex as
// a comparison point.
func BenchmarkSyncMutex(b *testing.B) {
	for n, max := 1, 4*runtime.GOMAXPROCS(0); n > 0 && n <= max; n *= 2 {
		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
			var m sync.Mutex

			var ready sync.WaitGroup
			begin := make(chan struct{})
			var end sync.WaitGroup
			for i := 0; i < n; i++ {
				ready.Add(1)
				end.Add(1)
				go func() {
					ready.Done()
					<-begin
					for j := 0; j < b.N; j++ {
						m.Lock()
						m.Unlock()
					}
					end.Done()
				}()
			}

			ready.Wait()
			b.ResetTimer()
			close(begin)
			end.Wait()
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fakestorage

import "github.com/fsouza/fake-gcs-server/internal/backend"

const timestampFormat = "2006-01-02T15:04:05.999999Z07:00"

type listResponse struct {
	Kind     string        `json:"kind"`
	Items    []interface{} `json:"items"`
	Prefixes []string      `json:"prefixes,omitempty"`
}

func newListBucketsResponse(buckets []backend.Bucket) listResponse {
	resp := listResponse{
		Kind:  "storage#buckets",
		Items: make([]interface{}, len(buckets)),
	}
	for i, bucket := range buckets {
		resp.Items[i] = newBucketResponse(bucket)
	}
	return resp
}

type bucketResponse struct {
	Kind        string            `json:"kind"`
	ID          string            `json:"id"`
	Name        string            `json:"name"`
	Versioning  *bucketVersioning `json:"versioning,omitempty"`
	TimeCreated string            `json:"timeCreated,omitempty"`
}

type bucketVersioning struct {
	Enabled bool `json:"enabled,omitempty"`
}

func newBucketResponse(bucket backend.Bucket) bucketResponse {
	return bucketResponse{
		Kind:        "storage#bucket",
		ID:          bucket.Name,
		Name:        bucket.Name,
		Versioning:  &bucketVersioning{bucket.VersioningEnabled},
		TimeCreated: bucket.TimeCreated.Format(timestampFormat),
	}
}

func newListObjectsResponse(objs []Object, prefixes []string) listResponse {
	resp := listResponse{
		Kind:     "storage#objects",
		Items:    make([]interface{}, len(objs)),
		Prefixes: prefixes,
	}
	for i, obj := range objs {
		resp.Items[i] = newObjectResponse(obj)
	}
	return resp
}

// objectAccessControl is copied from the Google SDK to avoid direct
// dependency.
type objectAccessControl struct {
	Bucket      string `json:"bucket,omitempty"`
	Domain      string `json:"domain,omitempty"`
	Email       string `json:"email,omitempty"`
	Entity      string `json:"entity,omitempty"`
	EntityID    string `json:"entityId,omitempty"`
	Etag        string `json:"etag,omitempty"`
	Generation  int64  `json:"generation,omitempty,string"`
	ID          string `json:"id,omitempty"`
	Kind        string `json:"kind,omitempty"`
	Object      string `json:"object,omitempty"`
	ProjectTeam struct {
		// ProjectNumber: The project number.
		ProjectNumber string `json:"projectNumber,omitempty"`

		// Team: The team.
		Team string `json:"team,omitempty"`

		// ForceSendFields is a list of field names (e.g. "ProjectNumber") to
		// unconditionally include in API requests. By default, fields with
		// empty values are omitted from API requests. However, any non-pointer,
		// non-interface field appearing in ForceSendFields will be sent to the
		// server regardless of whether the field is empty or not. This may be
		// used to include empty fields in Patch requests.
		ForceSendFields []string `json:"-"`

		// NullFields is a list of field names (e.g. "ProjectNumber") to include
		// in API requests with the JSON null value. By default, fields with
		// empty values are omitted from API requests. However, any field with
		// an empty value appearing in NullFields will be sent to the server as
		// null. It is an error if a field in this list has a non-empty value.
		// This may be used to include null fields in Patch requests.
		NullFields []string `json:"-"`
	} `json:"projectTeam,omitempty"`
	Role     string `json:"role,omitempty"`
	SelfLink string `json:"selfLink,omitempty"`
}

type objectResponse struct {
	Kind            string                 `json:"kind"`
	Name            string                 `json:"name"`
	ID              string                 `json:"id"`
	Bucket          string                 `json:"bucket"`
	Size            int64                  `json:"size,string"`
	ContentType     string                 `json:"contentType,omitempty"`
	ContentEncoding string                 `json:"contentEncoding,omitempty"`
	Crc32c          string                 `json:"crc32c,omitempty"`
	ACL             []*objectAccessControl `json:"acl,omitempty"`
	Md5Hash         string                 `json:"md5Hash,omitempty"`
	TimeCreated     string                 `json:"timeCreated,omitempty"`
	TimeDeleted     string                 `json:"timeDeleted,omitempty"`
	Updated         string                 `json:"updated,omitempty"`
	Generation      int64                  `json:"generation,string"`
	Metadata        map[string]string      `json:"metadata,omitempty"`
}

func newObjectResponse(obj Object) objectResponse {
	acl := getAccessControlsListFromObject(obj)

	return objectResponse{
		Kind:            "storage#object",
		ID:              obj.id(),
		Bucket:          obj.BucketName,
		Name:            obj.Name,
		Size:            int64(len(obj.Content)),
		ContentType:     obj.ContentType,
		ContentEncoding: obj.ContentEncoding,
		Crc32c:          obj.Crc32c,
		Md5Hash:         obj.Md5Hash,
		ACL:             acl,
		Metadata:        obj.Metadata,
		TimeCreated:     obj.Created.Format(timestampFormat),
		TimeDeleted:     obj.Deleted.Format(timestampFormat),
		Updated:         obj.Updated.Format(timestampFormat),
		Generation:      obj.Generation,
	}
}

type aclListResponse struct {
	Items []*objectAccessControl `json:"items"`
}

func newACLListResponse(obj Object) aclListResponse {
	if len(obj.ACL) == 0 {
		return aclListResponse{}
	}
	return aclListResponse{Items: getAccessControlsListFromObject(obj)}
}

func getAccessControlsListFromObject(obj Object) []*objectAccessControl {
	aclItems := make([]*objectAccessControl, len(obj.ACL))
	for idx, aclRule := range obj.ACL {
		aclItems[idx] = &objectAccessControl{
			Bucket: obj.BucketName,
			Entity: string(aclRule.Entity),
			Object: obj.Name,
			Role:   string(aclRule.Role),
		}
	}
	return aclItems
}

type rewriteResponse struct {
	Kind                string         `json:"kind"`
	TotalBytesRewritten int64          `json:"totalBytesRewritten,string"`
	ObjectSize          int64          `json:"objectSize,string"`
	Done                bool           `json:"done"`
	RewriteToken        string         `json:"rewriteToken"`
	Resource            objectResponse `json:"resource"`
}

func newObjectRewriteResponse(obj Object) rewriteResponse {
	return rewriteResponse{
		Kind:                "storage#rewriteResponse",
		TotalBytesRewritten: int64(len(obj.Content)),
		ObjectSize:          int64(len(obj.Content)),
		Done:                true,
		RewriteToken:        "",
		Resource:            newObjectResponse(obj),
	}
}

type errorResponse struct {
	Error httpError `json:"error"`
}

type httpError struct {
	Code    int        `json:"code"`
	Message string     `json:"message"`
	Errors  []apiError `json:"errors"`
}

type apiError struct {
	Domain  string `json:"domain"`
	Reason  string `json:"reason"`
	Message string `json:"message"`
}

func newErrorResponse(code int, message string, errs []apiError) errorResponse {
	return errorResponse{
		Error: httpError{
			Code:    code,
			Message: message,
			Errors:  errs,
		},
	}
}
<commit_msg>response: avoid some unused fields in the objectAccessControl struct<commit_after>// Copyright 2017 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fakestorage

import "github.com/fsouza/fake-gcs-server/internal/backend"

const timestampFormat = "2006-01-02T15:04:05.999999Z07:00"

type listResponse struct {
	Kind     string        `json:"kind"`
	Items    []interface{} `json:"items"`
	Prefixes []string      `json:"prefixes,omitempty"`
}

func newListBucketsResponse(buckets []backend.Bucket) listResponse {
	resp := listResponse{
		Kind:  "storage#buckets",
		Items: make([]interface{}, len(buckets)),
	}
	for i, bucket := range buckets {
		resp.Items[i] = newBucketResponse(bucket)
	}
	return resp
}

type bucketResponse struct {
	Kind        string            `json:"kind"`
	ID          string            `json:"id"`
	Name        string            `json:"name"`
	Versioning  *bucketVersioning `json:"versioning,omitempty"`
	TimeCreated string            `json:"timeCreated,omitempty"`
}

type bucketVersioning struct {
	Enabled bool `json:"enabled,omitempty"`
}

func newBucketResponse(bucket backend.Bucket) bucketResponse {
	return bucketResponse{
		Kind:        "storage#bucket",
		ID:          bucket.Name,
		Name:        bucket.Name,
		Versioning:  &bucketVersioning{bucket.VersioningEnabled},
		TimeCreated: bucket.TimeCreated.Format(timestampFormat),
	}
}

func newListObjectsResponse(objs []Object, prefixes []string) listResponse {
	resp := listResponse{
		Kind:     "storage#objects",
		Items:    make([]interface{}, len(objs)),
		Prefixes: prefixes,
	}
	for i, obj := range objs {
		resp.Items[i] = newObjectResponse(obj)
	}
	return resp
}

// objectAccessControl is copied from the Google SDK to avoid direct
// dependency.
type objectAccessControl struct {
	Bucket      string `json:"bucket,omitempty"`
	Domain      string `json:"domain,omitempty"`
	Email       string `json:"email,omitempty"`
	Entity      string `json:"entity,omitempty"`
	EntityID    string `json:"entityId,omitempty"`
	Etag        string `json:"etag,omitempty"`
	Generation  int64  `json:"generation,omitempty,string"`
	ID          string `json:"id,omitempty"`
	Kind        string `json:"kind,omitempty"`
	Object      string `json:"object,omitempty"`
	ProjectTeam struct {
		ProjectNumber string `json:"projectNumber,omitempty"`
		Team          string `json:"team,omitempty"`
	} `json:"projectTeam,omitempty"`
	Role     string `json:"role,omitempty"`
	SelfLink string `json:"selfLink,omitempty"`
}

type objectResponse struct {
	Kind            string                 `json:"kind"`
	Name            string                 `json:"name"`
	ID              string                 `json:"id"`
	Bucket          string                 `json:"bucket"`
	Size            int64                  `json:"size,string"`
	ContentType     string                 `json:"contentType,omitempty"`
	ContentEncoding string                 `json:"contentEncoding,omitempty"`
	Crc32c          string                 `json:"crc32c,omitempty"`
	ACL             []*objectAccessControl `json:"acl,omitempty"`
	Md5Hash         string                 `json:"md5Hash,omitempty"`
	TimeCreated     string                 `json:"timeCreated,omitempty"`
	TimeDeleted     string                 `json:"timeDeleted,omitempty"`
	Updated         string                 `json:"updated,omitempty"`
	Generation      int64                  `json:"generation,string"`
	Metadata        map[string]string      `json:"metadata,omitempty"`
}

func newObjectResponse(obj Object) objectResponse {
	acl := getAccessControlsListFromObject(obj)

	return objectResponse{
		Kind:            "storage#object",
		ID:              obj.id(),
		Bucket:          obj.BucketName,
		Name:            obj.Name,
		Size:            int64(len(obj.Content)),
		ContentType:     obj.ContentType,
		ContentEncoding: obj.ContentEncoding,
		Crc32c:          obj.Crc32c,
		Md5Hash:         obj.Md5Hash,
		ACL:             acl,
		Metadata:        obj.Metadata,
		TimeCreated:     obj.Created.Format(timestampFormat),
		TimeDeleted:     obj.Deleted.Format(timestampFormat),
		Updated:         obj.Updated.Format(timestampFormat),
		Generation:      obj.Generation,
	}
}

type aclListResponse struct {
	Items []*objectAccessControl `json:"items"`
}

func newACLListResponse(obj Object) aclListResponse {
	if len(obj.ACL) == 0 {
		return aclListResponse{}
	}
	return aclListResponse{Items: getAccessControlsListFromObject(obj)}
}

func getAccessControlsListFromObject(obj Object) []*objectAccessControl {
	aclItems := make([]*objectAccessControl, len(obj.ACL))
	for idx, aclRule := range obj.ACL {
		aclItems[idx] = &objectAccessControl{
			Bucket: obj.BucketName,
			Entity: string(aclRule.Entity),
			Object: obj.Name,
			Role:   string(aclRule.Role),
		}
	}
	return aclItems
}

type rewriteResponse struct {
	Kind                string         `json:"kind"`
	TotalBytesRewritten int64          `json:"totalBytesRewritten,string"`
	ObjectSize          int64          `json:"objectSize,string"`
	Done                bool           `json:"done"`
	RewriteToken        string         `json:"rewriteToken"`
	Resource            objectResponse `json:"resource"`
}

func newObjectRewriteResponse(obj Object) rewriteResponse {
	return rewriteResponse{
		Kind:                "storage#rewriteResponse",
		TotalBytesRewritten: int64(len(obj.Content)),
		ObjectSize:          int64(len(obj.Content)),
		Done:                true,
		RewriteToken:        "",
		Resource:            newObjectResponse(obj),
	}
}

type errorResponse struct {
	Error httpError `json:"error"`
}

type httpError struct {
	Code    int        `json:"code"`
	Message string     `json:"message"`
	Errors  []apiError `json:"errors"`
}

type apiError struct {
	Domain  string `json:"domain"`
	Reason  string `json:"reason"`
	Message string `json:"message"`
}

func newErrorResponse(code int, message string, errs []apiError) errorResponse {
	return errorResponse{
		Error: httpError{
			Code:    code,
			Message: message,
			Errors:  errs,
		},
	}
}
<|endoftext|>


<commit_before>package ndp

import (
	"bytes"
	"reflect"
	"testing"
)

func TestEncDecDomainName(t *testing.T) {
	tests := []struct {
		name    []string
		encoded []byte
	}{
		// TODO: fix this
		//{[]string{"."}, []byte{0, 0, 0, 0, 0, 0, 0, 0}},
		{[]string{"foo.bar."}, []byte{3, 102, 111, 111, 3, 98, 97, 114, 0, 0, 0, 0, 0, 0, 0, 0}},
		{[]string{"golang.org."}, []byte{6, 103, 111, 108, 97, 110, 103, 3, 111, 114, 103, 0, 0, 0, 0, 0}},
		{[]string{"basement.golang.org."}, []byte{8, 98, 97, 115, 101, 109, 101, 110, 116, 6, 103, 111, 108, 97, 110, 103, 3, 111, 114, 103, 0, 0, 0, 0}},
		{[]string{"basement.golang.org.", "golang.org."}, []byte{8, 98, 97, 115, 101, 109, 101, 110, 116, 6, 103, 111, 108, 97, 110, 103, 3, 111, 114, 103, 0, 6, 103, 111, 108, 97, 110, 103, 3, 111, 114, 103, 0, 0, 0, 0, 0, 0, 0, 0}},
	}

	for _, test := range tests {
		// encoding
		encoded := encDomainName(test.name)
		if bytes.Compare(encoded, test.encoded) != 0 {
			t.Errorf("failed to encode %s to %v, result was %v", test.name, test.encoded, encoded)
		}
		// decoding
		name := decDomainName(test.encoded)
		if !reflect.DeepEqual(name, test.name) {
			t.Errorf("failed to decode %v to %s, result was %s", test.encoded, test.name, name)
		}
	}
}
<commit_msg>additional encDomainName tests<commit_after>package ndp

import (
	"bytes"
	"reflect"
	"testing"
)

func TestEncDecDomainName(t *testing.T) {
	tests := []struct {
		name    []string
		encoded []byte
	}{
		// TODO: fix this
		//{[]string{"."}, []byte{0, 0, 0, 0, 0, 0, 0, 0}},
		{[]string{"foo.bar."}, []byte{3, 102, 111, 111, 3, 98, 97, 114, 0, 0, 0, 0, 0, 0, 0, 0}},
		{[]string{"golang.org."}, []byte{6, 103, 111, 108, 97, 110, 103, 3, 111, 114, 103, 0, 0, 0, 0, 0}},
		{[]string{"basement.golang.org."}, []byte{8, 98, 97, 115, 101, 109, 101, 110, 116, 6, 103, 111, 108, 97, 110, 103, 3, 111, 114, 103, 0, 0, 0, 0}},
		{[]string{"basement.golang.org.", "golang.org."}, []byte{8, 98, 97, 115, 101, 109, 101, 110, 116, 6, 103, 111, 108, 97, 110, 103, 3, 111, 114, 103, 0, 6, 103, 111, 108, 97, 110, 103, 3, 111, 114, 103, 0, 0, 0, 0, 0, 0, 0, 0}},
	}

	for _, test := range tests {
		// encoding
		encoded := encDomainName(test.name)
		if bytes.Compare(encoded, test.encoded) != 0 {
			t.Errorf("failed to encode %s to %v, result was %v", test.name, test.encoded, encoded)
		}
		// decoding
		name := decDomainName(test.encoded)
		if !reflect.DeepEqual(name, test.name) {
			t.Errorf("failed to decode %v to %s, result was %s", test.encoded, test.name, name)
		}
	}

	// total length may not exceed 255 bytes
	encoded := encDomainName([]string{
		// many labels
		"aaaa.aaaa.aaaa",
		"bbbb.bbbb.bbbb",
		"cccc.cccc.cccc",
		"dddd.dddd.dddd",
		"eeee.eeee.eeee",
		"ffff.ffff.ffff",
		"gggg.gggg.gggg",
		"hhhh.hhhh.hhhh",
		"iiii.iiii.iiii",
		"jjjj.jjjj.jjjj",
		"kkkk.kkkk.kkkk",
		"llll.llll.llll",
		"mmmm.mmmm.mmmm",
		"nnnn.nnnn.nnnn",
		"oooo.oooo.oooo",
		"pppp.pppp.pppp",
		"qqqq.qqqq.qqqq",
	})
	if len(encoded) != 255 {
		t.Errorf("expected truncated encoding of 255, not %d", len(encoded))
	}

	// individual label length may not exceed 63 bytes
	encoded = encDomainName([]string{
		// very long label
		"abcdefghijlmnopqrstuvwyxzabcdefghijlmnopqrstuvwyxzabcdefghijlmnopqrstuvwyxz.foo",
	})
	if len(encoded) != 72 {
		t.Errorf("expected truncated encoding of 72, not %d", len(encoded))
	}
}
<|endoftext|>