<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"
)

//Recordset contains Route53 Recordset
type Recordset struct {
	Record       *route53.ResourceRecordSet
	HostZoneName string
	HostZoneID   string
}

//Not every ResourceRecordSet memember is supported yet
var hostedzonenameFlag string
var hostnameFlag string
var recordtypeFlag string
var ipaddressFlag string
var ttlFlag int64

//init all flag values
func init() {
	flag.StringVar(&hostedzonenameFlag, "hostedzone", "", "The hosted zone name that will be updated (Required)")
	flag.StringVar(&hostnameFlag, "recordname", "", "The name of the FQDN you want to perform the action on (Required)")
	flag.StringVar(&recordtypeFlag, "type", "", "Valid values for basic resource record sets: \n A | AAAA | CNAME | MX | NS | PTR | SOA | SPF | SRV | TXT (Required)")
	flag.StringVar(&ipaddressFlag, "ip", "", "The record's IP if record type is A ")
	flag.Int64Var(&ttlFlag, "ttl", 300, "The cache time to live for the current resource record set - default 300 seconds")
}

func main() {
	flag.Parse()

	arglen := len(os.Args)
	record := &Recordset{}
	record.Record = &route53.ResourceRecordSet{}
	record.Record.ResourceRecords = make([]*route53.ResourceRecord, 1)

	if arglen <= 1 {
		fmt.Println("No option set try --help for more information")
	}

	if (hostedzonenameFlag != "") && (hostnameFlag != "") && (recordtypeFlag != "") {
		//debug
		fmt.Println("hosted name: ", hostedzonenameFlag, " recordname: ", hostnameFlag, " recordtype: ", recordtypeFlag)
		// TODO: Validate FLAGS!!!
		//set the vars from flags
		record.HostZoneName = hostedzonenameFlag
		record.Record.Name = aws.String(hostnameFlag)
		record.Record.TTL = aws.Int64(ttlFlag)
		record.Record.Type = aws.String(recordtypeFlag)
		record.Record.ResourceRecords[0] = &route53.ResourceRecord{Value: aws.String(ipaddressFlag)}
		//get the hosted zone id from the name
		record.getzoneid()
		//upsert record
		record.recordupsert()
	}

}

func (record *Recordset) getzoneid() {
	svc := route53.New(session.New())
	fmt.Println("looking for the record: ", record.HostZoneName)

	params := &route53.ListHostedZonesByNameInput{
		DNSName:  aws.String(record.HostZoneName),
		MaxItems: aws.String("1"),
	}

	resp, err := svc.ListHostedZonesByName(params)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(-1)
	}
	//if result does not match error out
	if record.HostZoneName+"." == *resp.HostedZones[0].Name {
		record.HostZoneID = *resp.HostedZones[0].Id
	} else {
		fmt.Println("Error: Could not find Hostzone: ", record.HostZoneName)
		os.Exit(-1)
		return
	}

}

//update zone record
func (record *Recordset) recordupsert() {
	svc := route53.New(session.New())

	//initialise upsert params
	parameters := &route53.ChangeResourceRecordSetsInput{}
	parameters.ChangeBatch = &route53.ChangeBatch{}
	parameters.HostedZoneId = aws.String(record.HostZoneID)
	parameters.ChangeBatch.Changes = make([]*route53.Change, 1)
	parameters.ChangeBatch.Changes[0] = &route53.Change{
		Action: aws.String("UPSERT"),
		ResourceRecordSet: &route53.ResourceRecordSet{
			Type: aws.String(*record.Record.Type),
			TTL:  aws.Int64(*record.Record.TTL),
			Name: aws.String(*record.Record.Name),
			ResourceRecords: []*route53.ResourceRecord{&route53.ResourceRecord{
				Value: record.Record.ResourceRecords[0].Value,
			}},
		},
	}

	//fmt.Println(*record.Parameters.ChangeBatch.Changes[0].ResourceRecordSet)
	resp, err := svc.ChangeResourceRecordSets(parameters)

	if err != nil {
		// Print the error, cast err to awserr.Error to get the Code and
		// Message from an error.
		fmt.Println(err.Error())
		return
	}
	// Pretty-print the response data.
	fmt.Println(resp)
}
<commit_msg>Code Tidy<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"
)

//Recordset contains Route53 Recordset
type Recordset struct {
	Record       *route53.ResourceRecordSet
	HostZoneName string
	HostZoneID   string
}

//Not every ResourceRecordSet memember is supported yet
var hostedzonenameFlag string
var hostnameFlag string
var recordtypeFlag string
var ipaddressFlag string
var ttlFlag int64

//init all flag values
func init() {
	flag.StringVar(&hostedzonenameFlag, "hostedzone", "", "The hosted zone name that will be updated (Required)")
	flag.StringVar(&hostnameFlag, "recordname", "", "The name of the FQDN you want to perform the action on (Required)")
	flag.StringVar(&recordtypeFlag, "type", "", "Valid values for basic resource record sets: \n A | AAAA | CNAME | MX | NS | PTR | SOA | SPF | SRV | TXT (Required)")
	flag.StringVar(&ipaddressFlag, "ip", "", "The record's IP if record type is A ")
	flag.Int64Var(&ttlFlag, "ttl", 300, "The cache time to live for the current resource record set - default 300 seconds")
}

func main() {
	flag.Parse()

	arglen := len(os.Args)
	record := &Recordset{}
	record.Record = &route53.ResourceRecordSet{}
	record.Record.ResourceRecords = make([]*route53.ResourceRecord, 1)

	if arglen <= 1 {
		fmt.Println("No option set try --help for more information")
		os.Exit(-1)
	}

	if (hostedzonenameFlag != "") && (hostnameFlag != "") && (recordtypeFlag != "") {
		//debug
		fmt.Println("hosted name: ", hostedzonenameFlag, " recordname: ", hostnameFlag, " recordtype: ", recordtypeFlag)
		// TODO: Validate FLAGS!!!
		//set the vars from flags
		record.HostZoneName = hostedzonenameFlag
		record.Record.Name = aws.String(hostnameFlag)
		record.Record.TTL = aws.Int64(ttlFlag)
		record.Record.Type = aws.String(recordtypeFlag)
		record.Record.ResourceRecords[0] = &route53.ResourceRecord{Value: aws.String(ipaddressFlag)}
		//get the hosted zone id from the name
		record.getZoneID()
		//upsert record
		record.recordUpsert()
	} else {
		fmt.Println("Option missing or not set, try --help for more information")
		os.Exit(-1)
	}

}

func (record *Recordset) getZoneID() {
	svc := route53.New(session.New())
	//intialise search params
	params := &route53.ListHostedZonesByNameInput{
		DNSName:  aws.String(record.HostZoneName),
		MaxItems: aws.String("1"), //limit records to 1
	}

	resp, err := svc.ListHostedZonesByName(params)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	//if result does not match error out
	if record.HostZoneName+"." == *resp.HostedZones[0].Name {
		record.HostZoneID = *resp.HostedZones[0].Id
	} else {
		fmt.Println("Error: Could not find Hostzone: ", record.HostZoneName)
		os.Exit(-1)
		return
	}

}

//update zone record
func (record *Recordset) recordUpsert() {
	svc := route53.New(session.New())

	//initialise upsert params
	parameters := &route53.ChangeResourceRecordSetsInput{}
	parameters.ChangeBatch = &route53.ChangeBatch{}
	parameters.HostedZoneId = aws.String(record.HostZoneID)
	parameters.ChangeBatch.Changes = make([]*route53.Change, 1)
	parameters.ChangeBatch.Changes[0] = &route53.Change{
		Action: aws.String("UPSERT"),
		ResourceRecordSet: &route53.ResourceRecordSet{
			Type: aws.String(*record.Record.Type),
			TTL:  aws.Int64(*record.Record.TTL),
			Name: aws.String(*record.Record.Name),
			ResourceRecords: []*route53.ResourceRecord{&route53.ResourceRecord{
				Value: record.Record.ResourceRecords[0].Value,
			}},
		},
	}

	//fmt.Println(*record.Parameters.ChangeBatch.Changes[0].ResourceRecordSet)
	resp, err := svc.ChangeResourceRecordSets(parameters)

	if err != nil {
		// Print the error, cast err to awserr.Error to get the Code and
		// Message from an error.
		fmt.Println("Error: Could not upsert record - ", err.Error())
		os.Exit(-1)
		return
	}
	// Pretty-print the response data.
	fmt.Println(resp)
}
<|endoftext|>


<commit_before>package kiwi

/*
Copyright (c) 2016-2019, Alexander I.Grafov <grafov@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of kvlog nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"bytes"
	"testing"
)

/* All tests consists of three parts:

- arrange structures and initialize objects for use in tests
- act on testing object
- check and assert on results

These parts separated by empty lines in each test function.
*/

// Test logging of string value.
func TestRace_LogStringValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := SinkTo(output, AsLogfmt()).Start()
	defer out.Close()
	for i := 0; i < 100; i++ {
		go func(i int) {
			log := New()
			log.Log("k", "The sample string.", "instance", i)
		}(i)
	}

	Log("k", "The sample string.", "instance", 0)

	out.Flush()
}

// // Test logging of byte array.
// func TestLogger_LogBytesValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", []byte("The sample string with a lot of spaces."))

//	out.Flush()
//	if strings.TrimSpace(output.String()) != `k="The sample string with a lot of spaces."` {
//		t.Fail()
//	}
// }

// // Test logging of integer value.
// func TestLogger_LogIntValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", 123)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=123" {
//		t.Fail()
//	}
// }

// // Test logging of negative integer value.
// func TestLogger_LogNegativeIntValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", -123)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=-123" {
//		t.Fail()
//	}
// }

// // Test logging of float value in default (scientific) format.
// func TestLogger_LogFloatValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", 3.14159265359)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=3.14159265359e+00" {
//		t.Fail()
//	}
// }

// // Test logging of float value in fixed format.
// func TestLogger_LogFixedFloatValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	FloatFormat = 'f'
//	log.Log("k", 3.14159265359)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=3.14159265359" {
//		t.Fail()
//	}
//	// Turn back to default format.
//	FloatFormat = 'e'
// }

// // Test logging of boolean value.
// func TestLogger_LogBoolValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", true, "k2", false)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=true k2=false" {
//		t.Fail()
//	}
// }

// // Test logging of complex number.
// func TestLogger_LogComplexValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", .12345E+5i, "k2", 1.e+0i)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=(0.000000+12345.000000i) k2=(0.000000+1.000000i)" {
//		t.Fail()
//	}
// }

// // Test logging of time literal.
// func TestLogger_LogTimeValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	value := time.Now()
//	valueString := value.Format(TimeLayout)
//	defer out.Close()

//	log.Log("k", value)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != fmt.Sprintf("k=%s", valueString) {
//		t.Fail()
//	}
// }

// func TestLogger_LogIntKeyInvalid_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log(123, 456)

//	out.Flush()
//	expected := `kiwi-error="non a string type (int) for the key (123)" message=456`
//	if strings.TrimSpace(output.String()) != expected {
//		t.Logf("expected %s got %v", expected, output.String())
//		t.Fail()
//	}
// }

// // The logger accepts string keys. In other cases it complains about
// // the wrong key type. It also assumes that each key followed by the
// // value.
// func TestLogger_LogThreeIntKeysInvalid_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log(123, 456, 789)

//	out.Flush()
//	expected := `kiwi-error="non a string type (int) for the key (123)" message=456 kiwi-error="non a string type (int) for the key (789)"`
//	if strings.TrimSpace(output.String()) != expected {
//		t.Logf("expected %s got %v", expected, output.String())
//		t.Fail()
//	}
// }

// // The logger accepts string keys. In other cases it complains about
// // the wrong key type. It also assumes that each key followed by the
// // value.
// func TestLogger_LogFourIntKeysInvalid_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log(12, 34, 56, 78)

//	out.Flush()
//	expected := `kiwi-error="non a string type (int) for the key (12)" message=34 kiwi-error="non a string type (int) for the key (56)" message=78`
//	if strings.TrimSpace(output.String()) != expected {
//		t.Logf("expected %s got %v", expected, output.String())
//		t.Fail()
//	}
// }

// // Test chaining for Add()
// func TestLogger_AddMixChained_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Add("k", "value2").Add("k2", 123).Add("k3", 3.14159265359).Log()

//	out.Flush()
//	expected := `k="value2" k2=123 k3=3.14159265359e+00`
//	if strings.TrimSpace(output.String()) != expected {
//		t.Logf("expected %s got %v", expected, output.String())
//		t.Fail()
//	}
// }

// // Test log with the context value.
// func TestLogger_WithContextPassed_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.With("key1", "value")
//	log.Log("key2", "value")

//	out.Flush()
//	if strings.TrimSpace(output.String()) != `key1="value" key2="value"` {
//		t.Fail()
//	}
// }

// // Test log with adding then removing the context.
// func TestLogger_WithoutContextPassed_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	// add the context
//	log.With("key1", "value")
//	// add regular pair
//	log.Add("key2", "value")
//	// remove the context and flush the record
//	log.Without("key1").Log()

//	out.Flush()
//	if strings.TrimSpace(output.String()) != `key2="value"` {
//		t.Fail()
//	}
// }

// // Test log with adding then reset the context.
// func TestLogger_ResetContext_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	// add the context
//	log.With("key1", "value")
//	// add regular pair
//	log.Add("key2", "value")
//	// reset the context and flush the record
//	log.ResetContext().Log()

//	out.Flush()
//	if strings.TrimSpace(output.String()) != `key2="value"` {
//		t.Fail()
//	}
// }
<commit_msg>Add more race tests<commit_after>package kiwi

/*
Copyright (c) 2016-2019, Alexander I.Grafov <grafov@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of kvlog nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"bytes"
	"sync"
	"testing"
)

/* All tests consists of three parts:

- arrange structures and initialize objects for use in tests
- act on testing object
- check and assert on results

These parts separated by empty lines in each test function.
*/

// Test logging of string value.
func TestRace_NewFromGlobal_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := SinkTo(output, AsLogfmt()).Start()
	defer out.Close()

	for i := 0; i < 100; i++ {
		go func(i int) {
			log := New()
			log.Log("k", "The sample string.", "instance", i)
		}(i)
	}
	Log("k", "The sample string.", "instance", 0)

	out.Flush()
}

// Test logging of string value.
func TestRace_ForkFromGlobal_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := SinkTo(output, AsLogfmt()).Start()
	defer out.Close()

	for i := 0; i < 100; i++ {
		go func(i int) {
			log := Fork()
			log.Log("k", "The sample string.", "instance", i)
		}(i)
	}
	Log("k", "The sample string.", "instance", 0)

	out.Flush()
}

// Test logging of string value.
func TestRace_Fork_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := SinkTo(output, AsLogfmt()).Start()
	defer out.Close()
	var (
		wg sync.WaitGroup
	)

	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			log := Fork()
			log.Log("k", "The sample string.", "instance", i)
			wg.Done()
		}(i)
	}
	Log("k", "The sample string.", "instance", 0)
	wg.Wait()

	out.Flush()
}

// Test logging of string value.
func TestRace_ForkFork_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := SinkTo(output, AsLogfmt()).Start()
	defer out.Close()
	var (
		wg sync.WaitGroup
	)

	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			log := Fork().Fork()
			log.Log("k", "The sample string.", "instance", i)
			wg.Done()
		}(i)
	}
	Log("k", "The sample string.", "instance", 0)
	wg.Wait()

	out.Flush()
}

// // Test logging of byte array.
// func TestLogger_LogBytesValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", []byte("The sample string with a lot of spaces."))

//	out.Flush()
//	if strings.TrimSpace(output.String()) != `k="The sample string with a lot of spaces."` {
//		t.Fail()
//	}
// }

// // Test logging of integer value.
// func TestLogger_LogIntValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", 123)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=123" {
//		t.Fail()
//	}
// }

// // Test logging of negative integer value.
// func TestLogger_LogNegativeIntValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", -123)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=-123" {
//		t.Fail()
//	}
// }

// // Test logging of float value in default (scientific) format.
// func TestLogger_LogFloatValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", 3.14159265359)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=3.14159265359e+00" {
//		t.Fail()
//	}
// }

// // Test logging of float value in fixed format.
// func TestLogger_LogFixedFloatValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	FloatFormat = 'f'
//	log.Log("k", 3.14159265359)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=3.14159265359" {
//		t.Fail()
//	}
//	// Turn back to default format.
//	FloatFormat = 'e'
// }

// // Test logging of boolean value.
// func TestLogger_LogBoolValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", true, "k2", false)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=true k2=false" {
//		t.Fail()
//	}
// }

// // Test logging of complex number.
// func TestLogger_LogComplexValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log("k", .12345E+5i, "k2", 1.e+0i)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != "k=(0.000000+12345.000000i) k2=(0.000000+1.000000i)" {
//		t.Fail()
//	}
// }

// // Test logging of time literal.
// func TestLogger_LogTimeValue_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	value := time.Now()
//	valueString := value.Format(TimeLayout)
//	defer out.Close()

//	log.Log("k", value)

//	out.Flush()
//	if strings.TrimSpace(output.String()) != fmt.Sprintf("k=%s", valueString) {
//		t.Fail()
//	}
// }

// func TestLogger_LogIntKeyInvalid_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log(123, 456)

//	out.Flush()
//	expected := `kiwi-error="non a string type (int) for the key (123)" message=456`
//	if strings.TrimSpace(output.String()) != expected {
//		t.Logf("expected %s got %v", expected, output.String())
//		t.Fail()
//	}
// }

// // The logger accepts string keys. In other cases it complains about
// // the wrong key type. It also assumes that each key followed by the
// // value.
// func TestLogger_LogThreeIntKeysInvalid_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log(123, 456, 789)

//	out.Flush()
//	expected := `kiwi-error="non a string type (int) for the key (123)" message=456 kiwi-error="non a string type (int) for the key (789)"`
//	if strings.TrimSpace(output.String()) != expected {
//		t.Logf("expected %s got %v", expected, output.String())
//		t.Fail()
//	}
// }

// // The logger accepts string keys. In other cases it complains about
// // the wrong key type. It also assumes that each key followed by the
// // value.
// func TestLogger_LogFourIntKeysInvalid_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Log(12, 34, 56, 78)

//	out.Flush()
//	expected := `kiwi-error="non a string type (int) for the key (12)" message=34 kiwi-error="non a string type (int) for the key (56)" message=78`
//	if strings.TrimSpace(output.String()) != expected {
//		t.Logf("expected %s got %v", expected, output.String())
//		t.Fail()
//	}
// }

// // Test chaining for Add()
// func TestLogger_AddMixChained_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.Add("k", "value2").Add("k2", 123).Add("k3", 3.14159265359).Log()

//	out.Flush()
//	expected := `k="value2" k2=123 k3=3.14159265359e+00`
//	if strings.TrimSpace(output.String()) != expected {
//		t.Logf("expected %s got %v", expected, output.String())
//		t.Fail()
//	}
// }

// // Test log with the context value.
// func TestLogger_WithContextPassed_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	log.With("key1", "value")
//	log.Log("key2", "value")

//	out.Flush()
//	if strings.TrimSpace(output.String()) != `key1="value" key2="value"` {
//		t.Fail()
//	}
// }

// // Test log with adding then removing the context.
// func TestLogger_WithoutContextPassed_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	// add the context
//	log.With("key1", "value")
//	// add regular pair
//	log.Add("key2", "value")
//	// remove the context and flush the record
//	log.Without("key1").Log()

//	out.Flush()
//	if strings.TrimSpace(output.String()) != `key2="value"` {
//		t.Fail()
//	}
// }

// // Test log with adding then reset the context.
// func TestLogger_ResetContext_Logfmt(t *testing.T) {
//	output := bytes.NewBufferString("")
//	log := New()
//	out := SinkTo(output, AsLogfmt()).Start()
//	defer out.Close()

//	// add the context
//	log.With("key1", "value")
//	// add regular pair
//	log.Add("key2", "value")
//	// reset the context and flush the record
//	log.ResetContext().Log()

//	out.Flush()
//	if strings.TrimSpace(output.String()) != `key2="value"` {
//		t.Fail()
//	}
// }
<|endoftext|>


<commit_before>// Copyright 2013 bee 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 main

import (
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/howeyc/fsnotify"
)

var (
	cmd         *exec.Cmd
	state       sync.Mutex
	eventTime   = make(map[string]int64)
	buildPeriod time.Time
)

func NewWatcher(paths []string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		ColorLog("[ERRO] Fail to create new Watcher[ %s ]\n", err)
		os.Exit(2)
	}

	go func() {
		for {
			select {
			case e := <-watcher.Event:
				isbuild := true

				// Skip TMP files for Sublime Text.
				if checkTMPFile(e.Name) {
					continue
				}
				if !chekcIfWatchExt(e.Name) {
					continue
				}

				// Prevent duplicated builds.
				if buildPeriod.Add(1 * time.Second).After(time.Now()) {
					continue
				}
				buildPeriod = time.Now()

				mt := getFileModTime(e.Name)
				if t := eventTime[e.Name]; mt == t {
					ColorLog("[SKIP] # %s #\n", e.String())
					isbuild = false
				}

				eventTime[e.Name] = mt

				if isbuild {
					ColorLog("[EVEN] %s\n", e)
					go Autobuild()
				}
			case err := <-watcher.Error:
				ColorLog("[WARN] %s\n", err.Error()) // No need to exit here
			}
		}
	}()

	ColorLog("[INFO] Initializing watcher...\n")
	for _, path := range paths {
		ColorLog("[TRAC] Directory( %s )\n", path)
		err = watcher.Watch(path)
		if err != nil {
			ColorLog("[ERRO] Fail to watch directory[ %s ]\n", err)
			os.Exit(2)
		}
	}

}

// getFileModTime retuens unix timestamp of `os.File.ModTime` by given path.
func getFileModTime(path string) int64 {
	path = strings.Replace(path, "\\", "/", -1)
	f, err := os.Open(path)
	if err != nil {
		ColorLog("[ERRO] Fail to open file[ %s ]\n", err)
		return time.Now().Unix()
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		ColorLog("[ERRO] Fail to get file information[ %s ]\n", err)
		return time.Now().Unix()
	}

	return fi.ModTime().Unix()
}

func Autobuild() {
	state.Lock()
	defer state.Unlock()

	ColorLog("[INFO] Start building...\n")
	path, _ := os.Getwd()
	os.Chdir(path)

	cmdName := "go"
	if conf.Gopm.Enable {
		cmdName = "gopm"
	}

	var err error
	// For applications use full import path like "github.com/.../.."
	// are able to use "go install" to reduce build time.
	if conf.GoInstall || conf.Gopm.Install {
		icmd := exec.Command(cmdName, "install")
		icmd.Stdout = os.Stdout
		icmd.Stderr = os.Stderr
		err = icmd.Run()
	}

	if err == nil {
		appName := appname
		if runtime.GOOS == "windows" {
			appName += ".exe"
		}
		if isExist(appName) {
			os.Remove(appName)
		}
		binPath := GetGOPATHs()[0] + "/bin/" + appName

		if conf.GoInstall && isExist(binPath) {
			os.Rename(binPath, appName)
			ColorLog("[INFO] Build command reduced\n")
		} else {
			bcmd := exec.Command(cmdName, "build")
			bcmd.Stdout = os.Stdout
			bcmd.Stderr = os.Stderr
			err = bcmd.Run()
		}
	}

	if err != nil {
		ColorLog("[ERRO] ============== Build failed ===================\n")
		return
	}
	ColorLog("[SUCC] Build was successful\n")
	Restart(appname)
}

func Kill() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("Kill -> ", e)
		}
	}()
	if cmd != nil && cmd.Process != nil {
		cmd.Process.Kill()
	}
}

func Restart(appname string) {
	Debugf("kill running process")
	Kill()
	go Start(appname)
}

func Start(appname string) {
	ColorLog("[INFO] Restarting %s ...\n", appname)
	if strings.Index(appname, "./") == -1 {
		appname = "./" + appname
	}

	cmd = exec.Command(appname)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Args = append([]string{appname}, conf.CmdArgs...)
	cmd.Env = append(os.Environ(), conf.Envs...)

	go cmd.Run()
	ColorLog("[INFO] %s is running...\n", appname)
	started <- true
}

// checkTMPFile returns true if the event was for TMP files.
func checkTMPFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}

var watchExts = []string{".go"}

// chekcIfWatchExt returns true if the name HasSuffix <watch_ext>.
func chekcIfWatchExt(name string) bool {
	for _, s := range watchExts {
		if strings.HasSuffix(name, s) {
			return true
		}
	}
	return false
}
<commit_msg>ug fixed<commit_after>// Copyright 2013 bee 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 main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/howeyc/fsnotify"
)

var (
	cmd         *exec.Cmd
	state       sync.Mutex
	eventTime   = make(map[string]int64)
	buildPeriod time.Time
)

func NewWatcher(paths []string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		ColorLog("[ERRO] Fail to create new Watcher[ %s ]\n", err)
		os.Exit(2)
	}

	go func() {
		for {
			select {
			case e := <-watcher.Event:
				isbuild := true

				// Skip TMP files for Sublime Text.
				if checkTMPFile(e.Name) {
					continue
				}
				if !chekcIfWatchExt(e.Name) {
					continue
				}

				// Prevent duplicated builds.
				if buildPeriod.Add(1 * time.Second).After(time.Now()) {
					continue
				}
				buildPeriod = time.Now()

				mt := getFileModTime(e.Name)
				if t := eventTime[e.Name]; mt == t {
					ColorLog("[SKIP] # %s #\n", e.String())
					isbuild = false
				}

				eventTime[e.Name] = mt

				if isbuild {
					ColorLog("[EVEN] %s\n", e)
					go Autobuild()
				}
			case err := <-watcher.Error:
				ColorLog("[WARN] %s\n", err.Error()) // No need to exit here
			}
		}
	}()

	ColorLog("[INFO] Initializing watcher...\n")
	for _, path := range paths {
		ColorLog("[TRAC] Directory( %s )\n", path)
		err = watcher.Watch(path)
		if err != nil {
			ColorLog("[ERRO] Fail to watch directory[ %s ]\n", err)
			os.Exit(2)
		}
	}

}

// getFileModTime retuens unix timestamp of `os.File.ModTime` by given path.
func getFileModTime(path string) int64 {
	path = strings.Replace(path, "\\", "/", -1)
	f, err := os.Open(path)
	if err != nil {
		ColorLog("[ERRO] Fail to open file[ %s ]\n", err)
		return time.Now().Unix()
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		ColorLog("[ERRO] Fail to get file information[ %s ]\n", err)
		return time.Now().Unix()
	}

	return fi.ModTime().Unix()
}

func Autobuild() {
	state.Lock()
	defer state.Unlock()

	ColorLog("[INFO] Start building...\n")
	path, _ := os.Getwd()
	os.Chdir(path)

	cmdName := "go"
	if conf.Gopm.Enable {
		cmdName = "gopm"
	}

	var err error
	// For applications use full import path like "github.com/.../.."
	// are able to use "go install" to reduce build time.
	if conf.GoInstall || conf.Gopm.Install {
		icmd := exec.Command("go", "list", "./...")
		buf := bytes.NewBuffer([]byte(""))
		icmd.Stdout = buf
		err = icmd.Run()
		if err == nil {
			list := strings.Split(buf.String(), "\n")[1:]
			for _, pkg := range list {
				if len(pkg) == 0 {
					continue
				}
				fmt.Println(pkg)
				icmd = exec.Command(cmdName, "install", pkg)
				err = icmd.Run()
				if err != nil {
					break
				}
			}
		}
	}

	if err == nil {
		appName := appname
		if runtime.GOOS == "windows" {
			appName += ".exe"
		}

		bcmd := exec.Command(cmdName, "build")
		bcmd.Stdout = os.Stdout
		bcmd.Stderr = os.Stderr
		err = bcmd.Run()
	}

	if err != nil {
		ColorLog("[ERRO] ============== Build failed ===================\n")
		return
	}
	ColorLog("[SUCC] Build was successful\n")
	Restart(appname)
}

func Kill() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("Kill.recover -> ", e)
		}
	}()
	if cmd != nil && cmd.Process != nil {
		err := cmd.Process.Kill()
		if err != nil {
			fmt.Println("Kill -> ", err)
		}
	}
}

func Restart(appname string) {
	Debugf("kill running process")
	Kill()
	go Start(appname)
}

func Start(appname string) {
	ColorLog("[INFO] Restarting %s ...\n", appname)
	if strings.Index(appname, "./") == -1 {
		appname = "./" + appname
	}

	cmd = exec.Command(appname)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Args = append([]string{appname}, conf.CmdArgs...)
	cmd.Env = append(os.Environ(), conf.Envs...)

	go cmd.Run()
	ColorLog("[INFO] %s is running...\n", appname)
	started <- true
}

// checkTMPFile returns true if the event was for TMP files.
func checkTMPFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}

var watchExts = []string{".go"}

// chekcIfWatchExt returns true if the name HasSuffix <watch_ext>.
func chekcIfWatchExt(name string) bool {
	for _, s := range watchExts {
		if strings.HasSuffix(name, s) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package raft

import (
	"fmt"
	"reflect"
	"testing"
)

func TestLeaderElection(t *testing.T) {
	tests := []struct {
		network
		state stateType
	}{
		{newNetwork(nil, nil, nil), stateLeader},
		{newNetwork(nil, nil, nopStepper), stateLeader},
		{newNetwork(nil, nopStepper, nopStepper), stateCandidate},
		{newNetwork(nil, nopStepper, nopStepper, nil), stateCandidate},
		{newNetwork(nil, nopStepper, nopStepper, nil, nil), stateLeader},
	}

	for i, tt := range tests {
		tt.step(Message{To: 0, Type: msgHup})
		sm := tt.network[0].(*stateMachine)
		if sm.state != tt.state {
			t.Errorf("#%d: state = %s, want %s", i, sm.state, tt.state)
		}
		if g := sm.term; g != 1 {
			t.Errorf("#%d: term = %d, want %d", i, g, 1)
		}
	}
}

func TestProposal(t *testing.T) {
	tests := []struct {
		network
		success bool
	}{
		{newNetwork(nil, nil, nil), true},
		{newNetwork(nil, nil, nopStepper), true},
		{newNetwork(nil, nopStepper, nopStepper), false},
		{newNetwork(nil, nopStepper, nopStepper, nil), false},
		{newNetwork(nil, nopStepper, nopStepper, nil, nil), true},
	}

	for i, tt := range tests {
		step := stepperFunc(func(m Message) {
			defer func() {
				if !tt.success {
					// not expected success implies there
					// will be no known leader which will
					// cause step to panic - swallow it.
					e := recover()
					if e != nil {
						t.Logf("#%d: err: %s", i, e)
					}
				}
			}()
			t.Logf("#%d: m = %+v", i, m)
			tt.step(m)
		})

		var data = []byte("somedata")

		// promote 0 the leader
		step(Message{To: 0, Type: msgHup})
		step(Message{To: 0, Type: msgProp, Data: data})

		w := []Entry{{}}
		if tt.success {
			w = append(w, Entry{Term: 1, Data: data})
		}
		ls := append([][]Entry{w}, tt.logs()...)

		if g := diffLogs(ls); g != nil {
			for _, diff := range g {
				t.Errorf("#%d: bag log:\n%s", i, diff)
			}
		}
		sm := tt.network[0].(*stateMachine)
		if g := sm.term; g != 1 {
			t.Errorf("#%d: term = %d, want %d", i, g, 1)
		}
	}
}

func TestProposalByProxy(t *testing.T) {
	tests := []struct {
		network
		success bool
	}{
		{newNetwork(nil, nil, nil), true},
		{newNetwork(nil, nil, nopStepper), true},
	}

	for i, tt := range tests {
		step := stepperFunc(func(m Message) {
			t.Logf("#%d: m = %+v", i, m)
			tt.step(m)
		})

		// promote 0 the leader
		step(Message{To: 0, Type: msgHup})

		// propose via follower
		step(Message{To: 1, Type: msgProp, Data: []byte("somedata")})

		if g := diffLogs(tt.logs()); g != nil {
			for _, diff := range g {
				t.Errorf("#%d: bag log:\n%s", i, diff)
			}
		}
		sm := tt.network[0].(*stateMachine)
		if g := sm.term; g != 1 {
			t.Errorf("#%d: term = %d, want %d", i, g, 1)
		}
	}
}

type network []stepper

// newNetwork initializes a network from nodes. A nil node will be replaced
// with a new *stateMachine. A *stateMachine will get its k, addr, and next
// fields set.
func newNetwork(nodes ...stepper) network {
	nt := network(nodes)
	for i, n := range nodes {
		switch v := n.(type) {
		case nil:
			nt[i] = newStateMachine(len(nodes), i, &nt)
		case *stateMachine:
			v.k = len(nodes)
			v.addr = i
			v.next = &nt
		}
	}
	return nt
}

func (nt network) step(m Message) {
	nt[m.To].step(m)
}

func (nt network) logs() [][]Entry {
	ls := make([][]Entry, len(nt))
	for i, node := range nt {
		if sm, ok := node.(*stateMachine); ok {
			ls[i] = sm.log
		}
	}
	return ls
}

type diff struct {
	i    int
	ents []*Entry // pointers so they can be nil for N/A
}

var naEntry = &Entry{}
var nologEntry = &Entry{}

func (d diff) String() string {
	s := fmt.Sprintf("[%d] ", d.i)
	for i, e := range d.ents {
		switch e {
		case nologEntry:
			s += fmt.Sprintf("<NL>")
		case naEntry:
			s += fmt.Sprintf("<N/A>")
		case nil:
			s += fmt.Sprintf("<nil>")
		default:
			s += fmt.Sprintf("<%d:%q>", e.Term, string(e.Data))
		}
		if i != len(d.ents)-1 {
			s += "\t\t"
		}
	}
	return s
}

func diffLogs(logs [][]Entry) []diff {
	var (
		d   []diff
		max int
	)
	for _, log := range logs {
		if l := len(log); l > max {
			max = l
		}
	}
	ediff := func(i int) (result []*Entry) {
		e := make([]*Entry, len(logs))
		found := false
		for j, log := range logs {
			if log == nil {
				e[j] = nologEntry
				continue
			}
			if len(log) <= i {
				e[j] = naEntry
				found = true
				continue
			}
			e[j] = &log[i]
			if j > 0 {
				switch prev := e[j-1]; {
				case prev == nologEntry:
				case prev == naEntry:
				case !reflect.DeepEqual(prev, e[j]):
					found = true
				}
			}
		}
		if found {
			return e
		}
		return nil
	}
	for i := 0; i < max; i++ {
		if e := ediff(i); e != nil {
			d = append(d, diff{i, e})
		}
	}
	return d
}

type stepperFunc func(Message)

func (f stepperFunc) step(m Message) { f(m) }

var nopStepper = stepperFunc(func(Message) {})
<commit_msg>raft: make testing logs easier<commit_after>package raft

import (
	"fmt"
	"reflect"
	"testing"
)

var defaultLog = []Entry{{}}

func TestLeaderElection(t *testing.T) {
	tests := []struct {
		network
		state stateType
	}{
		{newNetwork(nil, nil, nil), stateLeader},
		{newNetwork(nil, nil, nopStepper), stateLeader},
		{newNetwork(nil, nopStepper, nopStepper), stateCandidate},
		{newNetwork(nil, nopStepper, nopStepper, nil), stateCandidate},
		{newNetwork(nil, nopStepper, nopStepper, nil, nil), stateLeader},
	}

	for i, tt := range tests {
		tt.step(Message{To: 0, Type: msgHup})
		sm := tt.network[0].(*stateMachine)
		if sm.state != tt.state {
			t.Errorf("#%d: state = %s, want %s", i, sm.state, tt.state)
		}
		if g := sm.term; g != 1 {
			t.Errorf("#%d: term = %d, want %d", i, g, 1)
		}
	}
}

func TestProposal(t *testing.T) {
	data := []byte("somedata")
	successLog := []Entry{{}, {Term: 1, Data: data}}

	tests := []struct {
		network
		log       []Entry
		willpanic bool
	}{
		{newNetwork(nil, nil, nil), successLog, false},
		{newNetwork(nil, nil, nopStepper), successLog, false},
		{newNetwork(nil, nopStepper, nopStepper), defaultLog, true},
		{newNetwork(nil, nopStepper, nopStepper, nil), defaultLog, true},
		{newNetwork(nil, nopStepper, nopStepper, nil, nil), successLog, false},
	}

	for i, tt := range tests {
		step := stepperFunc(func(m Message) {
			defer func() {
				if tt.willpanic {
					e := recover()
					if e != nil {
						t.Logf("#%d: err: %s", i, e)
					}
				}
			}()
			t.Logf("#%d: m = %+v", i, m)
			tt.step(m)
		})

		var data = []byte("somedata")

		// promote 0 the leader
		step(Message{To: 0, Type: msgHup})
		step(Message{To: 0, Type: msgProp, Data: data})

		if g := diffLogs(tt.logs(tt.log)); g != nil {
			for _, diff := range g {
				t.Errorf("#%d: bag log:\n%s", i, diff)
			}
		}
		sm := tt.network[0].(*stateMachine)
		if g := sm.term; g != 1 {
			t.Errorf("#%d: term = %d, want %d", i, g, 1)
		}
	}
}

func TestProposalByProxy(t *testing.T) {
	data := []byte("somedata")
	successLog := []Entry{{}, {Term: 1, Data: data}}

	tests := []struct {
		network
		log []Entry
	}{
		{newNetwork(nil, nil, nil), successLog},
		{newNetwork(nil, nil, nopStepper), successLog},
	}

	for i, tt := range tests {
		step := stepperFunc(func(m Message) {
			t.Logf("#%d: m = %+v", i, m)
			tt.step(m)
		})

		// promote 0 the leader
		step(Message{To: 0, Type: msgHup})

		// propose via follower
		step(Message{To: 1, Type: msgProp, Data: []byte("somedata")})

		if g := diffLogs(tt.logs(tt.log)); g != nil {
			for _, diff := range g {
				t.Errorf("#%d: bag log:\n%s", i, diff)
			}
		}
		sm := tt.network[0].(*stateMachine)
		if g := sm.term; g != 1 {
			t.Errorf("#%d: term = %d, want %d", i, g, 1)
		}
	}
}

type network []stepper

// newNetwork initializes a network from nodes. A nil node will be replaced
// with a new *stateMachine. A *stateMachine will get its k, addr, and next
// fields set.
func newNetwork(nodes ...stepper) network {
	nt := network(nodes)
	for i, n := range nodes {
		switch v := n.(type) {
		case nil:
			nt[i] = newStateMachine(len(nodes), i, &nt)
		case *stateMachine:
			v.k = len(nodes)
			v.addr = i
			v.next = &nt
		}
	}
	return nt
}

func (nt network) step(m Message) {
	nt[m.To].step(m)
}

// logs returns all logs in nt prepended with want. If a node is not a
// *stateMachine, its log will be nil.
func (nt network) logs(want []Entry) [][]Entry {
	ls := make([][]Entry, len(nt)+1)
	ls[0] = want
	for i, node := range nt {
		if sm, ok := node.(*stateMachine); ok {
			ls[i] = sm.log
		}
	}
	return ls
}

type diff struct {
	i    int
	ents []*Entry // pointers so they can be nil for N/A
}

var naEntry = &Entry{}
var nologEntry = &Entry{}

func (d diff) String() string {
	s := fmt.Sprintf("[%d] ", d.i)
	for i, e := range d.ents {
		switch e {
		case nologEntry:
			s += fmt.Sprintf("<NL>")
		case naEntry:
			s += fmt.Sprintf("<N/A>")
		case nil:
			s += fmt.Sprintf("<nil>")
		default:
			s += fmt.Sprintf("<%d:%q>", e.Term, string(e.Data))
		}
		if i != len(d.ents)-1 {
			s += "\t\t"
		}
	}
	return s
}

func diffLogs(logs [][]Entry) []diff {
	var (
		d   []diff
		max int
	)
	for _, log := range logs {
		if l := len(log); l > max {
			max = l
		}
	}
	ediff := func(i int) (result []*Entry) {
		e := make([]*Entry, len(logs))
		found := false
		for j, log := range logs {
			if log == nil {
				e[j] = nologEntry
				continue
			}
			if len(log) <= i {
				e[j] = naEntry
				found = true
				continue
			}
			e[j] = &log[i]
			if j > 0 {
				switch prev := e[j-1]; {
				case prev == nologEntry:
				case prev == naEntry:
				case !reflect.DeepEqual(prev, e[j]):
					found = true
				}
			}
		}
		if found {
			return e
		}
		return nil
	}
	for i := 0; i < max; i++ {
		if e := ediff(i); e != nil {
			d = append(d, diff{i, e})
		}
	}
	return d
}

type stepperFunc func(Message)

func (f stepperFunc) step(m Message) { f(m) }

var nopStepper = stepperFunc(func(Message) {})
<|endoftext|>


<commit_before>package rain

import (
	"net"
	"time"

	"github.com/cenkalti/log"
)

// All current implementations use 2^14 (16 kiB), and close connections which request an amount greater than that.
const blockSize = 16 * 1024

// http://www.bittorrent.org/beps/bep_0020.html
var peerIDPrefix = []byte("-RN0001-")

type peerID [20]byte

const bitTorrent10pstrLen = 19

var bitTorrent10pstr = []byte("BitTorrent protocol")

func (r *Rain) servePeerConn(conn net.Conn) {
	defer conn.Close()
	log.Debugln("Serving peer", conn.RemoteAddr())

	// Give a minute for completing handshake.
	err := conn.SetDeadline(time.Now().Add(time.Minute))
	if err != nil {
		return
	}

	var d *download

	resultC := make(chan interface{}, 2)
	go r.readHandShake(conn, resultC)

	// Send handshake as soon as you see info_hash.
	i := <-resultC
	switch res := i.(type) {
	case *infoHash:
		// Do not continue if we don't have a torrent with this infoHash.
		r.downloadsM.Lock()
		var ok bool
		if d, ok = r.downloads[*res]; !ok {
			log.Error("unexpected info_hash")
			r.downloadsM.Unlock()
			return
		}
		r.downloadsM.Unlock()

		if err = r.sendHandShake(conn, res); err != nil {
			log.Error(err)
			return
		}
	case error:
		log.Error(res)
		return
	}

	i = <-resultC
	switch res := i.(type) {
	case *peerID:
		if *res == *r.peerID {
			log.Warning("Rejected own connection: server")
			return
		}
		// TODO save peer_id
	case error:
		log.Error(res)
		return
	}

	log.Debugln("servePeerConn: Handshake completed", conn.RemoteAddr())
	r.communicateWithPeer(conn, d)
}

func (r *Rain) connectToPeer(p *Peer, d *download) {
	log.Debugln("Connecting to peer", p.TCPAddr())

	conn, err := net.DialTCP("tcp4", nil, p.TCPAddr())
	if err != nil {
		log.Error(err)
		return
	}
	defer conn.Close()

	log.Infoln("Connected to peer", conn.RemoteAddr())

	// Give a minute for completing handshake.
	err = conn.SetDeadline(time.Now().Add(time.Minute))
	if err != nil {
		return
	}

	err = r.sendHandShake(conn, &d.TorrentFile.InfoHash)
	if err != nil {
		log.Error(err)
		return
	}

	ih, id, err := r.readHandShakeBlocking(conn)
	if err != nil {
		log.Error(err)
		return
	}
	if *ih != d.TorrentFile.InfoHash {
		log.Error("unexpected info_hash")
		return
	}
	if *id == *r.peerID {
		log.Warning("Rejected own connection: client")
		return
	}

	log.Debugln("connectToPeer: Handshake completed", conn.RemoteAddr())
	r.communicateWithPeer(conn, d)
}

// communicateWithPeer is the common method that is called after handshake.
// Peer connections are symmetrical.
func (r *Rain) communicateWithPeer(conn net.Conn, d *download) {
	log.Debugln("Communicating peer", conn.RemoteAddr())
	// TODO adjust deadline to heartbeat
	err := conn.SetDeadline(time.Time{})
	if err != nil {
		return
	}

	b := make([]byte, 1)
	_, err = conn.Read(b)
	if err != nil {
		log.Error(err)
		return
	}

	select {}
}
<commit_msg>read messages<commit_after>package rain

import (
	"encoding/binary"
	"net"
	"time"

	"github.com/cenkalti/log"
	"github.com/willf/bitset"
)

// All current implementations use 2^14 (16 kiB), and close connections which request an amount greater than that.
const blockSize = 16 * 1024

// http://www.bittorrent.org/beps/bep_0020.html
var peerIDPrefix = []byte("-RN0001-")

type peerID [20]byte

const bitTorrent10pstrLen = 19

var bitTorrent10pstr = []byte("BitTorrent protocol")

func (r *Rain) servePeerConn(conn net.Conn) {
	defer conn.Close()
	log.Debugln("Serving peer", conn.RemoteAddr())

	// Give a minute for completing handshake.
	err := conn.SetDeadline(time.Now().Add(time.Minute))
	if err != nil {
		return
	}

	var d *download

	resultC := make(chan interface{}, 2)
	go r.readHandShake(conn, resultC)

	// Send handshake as soon as you see info_hash.
	i := <-resultC
	switch res := i.(type) {
	case *infoHash:
		// Do not continue if we don't have a torrent with this infoHash.
		r.downloadsM.Lock()
		var ok bool
		if d, ok = r.downloads[*res]; !ok {
			log.Error("unexpected info_hash")
			r.downloadsM.Unlock()
			return
		}
		r.downloadsM.Unlock()

		if err = r.sendHandShake(conn, res); err != nil {
			log.Error(err)
			return
		}
	case error:
		log.Error(res)
		return
	}

	i = <-resultC
	switch res := i.(type) {
	case *peerID:
		if *res == *r.peerID {
			log.Warning("Rejected own connection: server")
			return
		}
		// TODO save peer_id
	case error:
		log.Error(res)
		return
	}

	log.Debugln("servePeerConn: Handshake completed", conn.RemoteAddr())
	r.communicateWithPeer(conn, d)
}

func (r *Rain) connectToPeer(p *Peer, d *download) {
	log.Debugln("Connecting to peer", p.TCPAddr())

	conn, err := net.DialTCP("tcp4", nil, p.TCPAddr())
	if err != nil {
		log.Error(err)
		return
	}
	defer conn.Close()

	log.Infoln("Connected to peer", conn.RemoteAddr())

	// Give a minute for completing handshake.
	err = conn.SetDeadline(time.Now().Add(time.Minute))
	if err != nil {
		return
	}

	err = r.sendHandShake(conn, &d.TorrentFile.InfoHash)
	if err != nil {
		log.Error(err)
		return
	}

	ih, id, err := r.readHandShakeBlocking(conn)
	if err != nil {
		log.Error(err)
		return
	}
	if *ih != d.TorrentFile.InfoHash {
		log.Error("unexpected info_hash")
		return
	}
	if *id == *r.peerID {
		log.Warning("Rejected own connection: client")
		return
	}

	log.Debugln("connectToPeer: Handshake completed", conn.RemoteAddr())
	r.communicateWithPeer(conn, d)
}

const (
	choke = iota
	unchoke
	interested
	notInterested
	have
	bitfield
	request
	piece
	cancel
)

// communicateWithPeer is the common method that is called after handshake.
// Peer connections are symmetrical.
func (r *Rain) communicateWithPeer(conn net.Conn, d *download) {
	log.Debugln("Communicating peer", conn.RemoteAddr())
	// TODO adjust deadline to heartbeat
	err := conn.SetDeadline(time.Time{})
	if err != nil {
		return
	}

	for {
		var length uint32
		err = binary.Read(conn, binary.BigEndian, &length)
		if err != nil {
			log.Error(err)
			return
		}

		if length == 0 { // heartbeat
			log.Debug("came heartbeat")
			continue
		}

		var msgType byte
		err = binary.Read(conn, binary.BigEndian, &msgType)
		if err != nil {
			log.Error(err)
			return
		}
		length--
		log.Debug("Received message of type ", msgType)

		switch msgType {
		case choke:
		case unchoke:
		case interested:
		case notInterested:
		case have:
		case bitfield:

		case request:
		case piece:
		case cancel:
		}

	}
}
<|endoftext|>


<commit_before>package manager

import (
	ds "github.com/Comcast/traffic_control/traffic_monitor/experimental/traffic_monitor/deliveryservice"
	"sync"
)

type DSStatsThreadsafe struct {
	dsStats *ds.Stats
	m       *sync.RWMutex
}

func NewDSStatsThreadsafe() DSStatsThreadsafe {
	s := ds.NewStats()
	return DSStatsThreadsafe{m: &sync.RWMutex{}, dsStats: &s}
}

func (o *DSStatsThreadsafe) Get() ds.Stats {
	o.m.Lock()
	defer o.m.Unlock()
	return o.dsStats.Copy()
}

func (o *DSStatsThreadsafe) Set(newDsStats ds.Stats) {
	o.m.Lock()
	*o.dsStats = newDsStats
	o.m.Unlock()
}
<commit_msg>Change TM2 DSStatsThreadsafe to not copy<commit_after>package manager

import (
	ds "github.com/Comcast/traffic_control/traffic_monitor/experimental/traffic_monitor/deliveryservice"
	"sync"
)

type DSStatsThreadsafe struct {
	dsStats *ds.Stats
	m       *sync.RWMutex
}

func NewDSStatsThreadsafe() DSStatsThreadsafe {
	s := ds.NewStats()
	return DSStatsThreadsafe{m: &sync.RWMutex{}, dsStats: &s}
}

func (o *DSStatsThreadsafe) Get() ds.Stats {
	o.m.Lock()
	defer o.m.Unlock()
	return *o.dsStats
}

func (o *DSStatsThreadsafe) Set(newDsStats ds.Stats) {
	o.m.Lock()
	*o.dsStats = newDsStats
	o.m.Unlock()
}
<|endoftext|>


<commit_before>// weedo.go
package weedo

import (
	"bytes"
	"encoding/json"
	"errors"
	//"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"strconv"
	"strings"
)

const (
	AssignUri             = "/dir/assign"
	UploadUri             = "/submit"
	LookupUri             = "/dir/lookup?volumeId="
	VolumeServerStatusUri = "/status"
	SystemStatusUri       = "/dir/status"
)

type Client struct {
	Url     string
	volumes map[uint64]string
}

type assignResp struct {
	Count     int
	Fid       string
	Url       string
	PublicUrl string
	Size      int
	Error     string
}

type lookupResp struct {
	Locations []location
	Error     string
}

type location struct {
	Url       string
	PublicUrl string
}

type uploadResp struct {
	Fid      string
	FileName string
	FileUrl  string
	Size     int
	Error    string
}

type systemStatus struct {
	Topology topology
	Version  string
	Error    string
}

type topology struct {
	DataCenters dataCenter
	Free        int
	Max         int
	Layouts     []layout
}

type dataCenter struct {
	Free  int
	Max   int
	Racks []rack
}

type rack struct {
	DataNodes []dataNode
	Free      int
	Max       int
}

type dataNode struct {
	Free      int
	Max       int
	PublicUrl string
	Url       string
	Volumes   int
}

type layout struct {
	Replication string
	Writables   []uint64
}

type volumeServerStatus struct {
	Version string
	volumes []volume
	Error   string
}

type volume struct {
	Id               uint64
	Size             uint64
	RepType          string
	Version          int
	FileCount        uint64
	DeleteCount      uint64
	DeletedByteCount uint64
	ReadOnly         bool
}

var defaultClient *Client

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	defaultClient = NewClient("localhost", 9333)
}

func ParseFid(fid string) (id, key, cookie uint64, err error) {
	s := strings.Split(fid, ",")
	if len(s) != 2 || len(s[1]) <= 8 {
		err = errors.New("Fid format invalid")
		//log.Println(err)
		return
	}
	if id, err = strconv.ParseUint(s[0], 10, 32); err != nil {
		log.Println(err)
		return
	}
	//log.Println(s, len(s[1]))
	index := len(s[1]) - 8
	if key, err = strconv.ParseUint(s[1][:index], 16, 64); err != nil {
		log.Println(err)
		return
	}
	if cookie, err = strconv.ParseUint(s[1][index:], 16, 32); err != nil {
		log.Println(err)
		return
	}
	return
}

func GetUrl(fid string) (url string, err error) {
	return defaultClient.GetUrl(fid)
}

func VolumeUpload(fid string, version int, filename string, file io.Reader) (size int, err error) {
	return defaultClient.VolumeUpload(fid, version, filename, file)
}

// Upload file directly to default master server: localhost:9333
// It is same as: curl -F file=@/home/chris/myphoto.jpg http://localhost:9333/submit
func MasterUpload(filename string, file io.Reader) (fid string, size int, err error) {
	return defaultClient.MasterUpload(filename, file)
}

// First, contact with master server and assign a fid, then upload to volume server
// It is same as the follow steps
// curl http://localhost:9333/dir/assign
// curl -F file=@example.jpg http://127.0.0.1:8080/3,01637037d6
func AssignUpload(filename string, file io.Reader) (fid string, size int, err error) {
	return defaultClient.AssignUpload(filename, file)
}

func Download(fid string) (file io.ReadCloser, err error) {
	return defaultClient.Download(fid)
}

func Delete(fid string) (err error) {
	return defaultClient.Delete(fid)
}

// Get a fresh new weed client, with it's master url is ip:port
func NewClient(ip string, port int) *Client {
	masterUrl := ip + ":" + strconv.Itoa(port)
	if !strings.HasPrefix(masterUrl, "http://") {
		masterUrl = "http://" + masterUrl
	}
	return &Client{masterUrl, make(map[uint64]string)}
}

func (c *Client) Assign() (fid string, err error) {
	return c.AssignN(1)
}

func (c *Client) AssignN(count int) (fid string, err error) {
	if count <= 0 {
		count = 1
	}
	url := c.Url + AssignUri
	if count > 1 {
		url = url + "?count=" + strconv.Itoa(count)
	}
	resp, err := http.Get(url)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	assign := new(assignResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(assign); err != nil {
		log.Println(err)
		return
	}
	if assign.Error != "" {
		err = errors.New(assign.Error)
		log.Println(err)
		return
	}

	fid = assign.Fid
	log.Printf("assign fid:%s at %s, count:%d", fid, assign.Url, assign.Count)

	return
}

func (c *Client) AssignUpload(filename string, file io.Reader) (fid string, size int, err error) {
	fid, err = c.Assign()
	if err != nil {
		return
	}

	size, err = c.VolumeUpload(fid, 0, filename, file)

	return
}

func (c *Client) VolumeUpload(fid string, version int, filename string, file io.Reader) (size int, err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}
	if version > 0 {
		url = url + "_" + strconv.Itoa(version)
	}

	formData, contentType, err := c.makeFormData(filename, file)
	if err != nil {
		return
	}

	resp, err := c.upload(url, contentType, formData)
	if err == nil {
		size = resp.Size
	}
	return
}

func (c *Client) MasterUpload(filename string, file io.Reader) (fid string, size int, err error) {
	data, contentType, err := c.makeFormData(filename, file)
	if err != nil {
		return
	}
	resp, err := c.upload(c.Url+UploadUri, contentType, data)
	if err == nil {
		fid = resp.Fid
		size = resp.Size
	}
	return
}

func (_ *Client) upload(url string, contentType string, formData io.Reader) (r *uploadResp, err error) {
	resp, err := http.Post(url, contentType, formData)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()

	upload := new(uploadResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(upload); err != nil {
		log.Println(err)
		return
	}

	if upload.Error != "" {
		err = errors.New(upload.Error)
		log.Println(err)
		return
	}

	r = upload

	return
}

func (c *Client) lookup(volumeId uint64) (url string, err error) {
	if v, ok := c.volumes[volumeId]; ok {
		return v, nil
	}

	resp, err := http.Get(c.Url + LookupUri + strconv.FormatUint(volumeId, 10))
	//log.Println(c.Url + LookupUri + strconv.FormatUint(volumeId, 10))
	if err != nil {
		return
	}
	defer resp.Body.Close()

	lookup := new(lookupResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(lookup); err != nil {
		log.Println(err)
		return
	}

	if lookup.Error != "" {
		err = errors.New(lookup.Error)
		log.Println(err)
		return
	}

	c.volumes[volumeId] = lookup.Locations[0].Url

	return lookup.Locations[0].Url, nil
}

func (c *Client) GetUrl(fid string) (url string, err error) {
	id, _, _, err := ParseFid(fid)
	if err != nil {
		log.Println(err)
		return
	}
	//log.Printf("id:%d, key:%x, cookie:%x\n", id, key, cookie)

	if url, err = c.lookup(id); err != nil {
		log.Println(err)
		return
	}

	url = "http://" + url + "/" + fid

	return
}

func (_ *Client) makeFormData(filename string, content io.Reader) (formData io.Reader, contentType string, err error) {
	buf := new(bytes.Buffer)
	writer := multipart.NewWriter(buf)

	part, err := writer.CreateFormFile("file", filename)
	if err != nil {
		log.Println(err)
		return
	}
	_, err = io.Copy(part, content)
	if err != nil {
		log.Println(err)
		return
	}

	formData = buf
	contentType = writer.FormDataContentType()
	writer.Close()

	return
}

func (c *Client) Download(fid string) (file io.ReadCloser, err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	//log.Println(resp.Header.Get("Content-Type"))
	if resp.ContentLength == 0 {
		return nil, errors.New("File Not Found")
	}

	return resp.Body, nil
}

func (c *Client) Delete(fid string) (err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}

	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return
	}
	client := http.Client{}

	_, err = client.Do(request)

	return
}

func (c *Client) VolumeServerStatus(ip string, port uint16) (err error) {
	url := ip + ":" + strconv.Itoa(int(port))
	if !strings.HasPrefix(url, "http://") {
		url = "http://" + url
	}
	resp, err := http.Get(url + VolumeServerStatusUri)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	status := new(volumeServerStatus)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(status); err != nil {
		log.Println(err)
		return
	}

	if status.Error != "" {
		err = errors.New(status.Error)
		log.Println(err)
		return
	}
	return
}

func (c *Client) SystemStatus() (err error) {
	resp, err := http.Get(c.Url + SystemStatusUri)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	status := new(systemStatus)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(status); err != nil {
		log.Println(err)
		return
	}

	if status.Error != "" {
		err = errors.New(status.Error)
		log.Println(err)
		return
	}
	return
}
<commit_msg>add content-length for download<commit_after>// weedo.go
package weedo

import (
	"bytes"
	"encoding/json"
	"errors"
	//"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"strconv"
	"strings"
)

const (
	AssignUri             = "/dir/assign"
	UploadUri             = "/submit"
	LookupUri             = "/dir/lookup?volumeId="
	VolumeServerStatusUri = "/status"
	SystemStatusUri       = "/dir/status"
)

type Client struct {
	Url     string
	volumes map[uint64]string
}

type assignResp struct {
	Count     int
	Fid       string
	Url       string
	PublicUrl string
	Size      int
	Error     string
}

type lookupResp struct {
	Locations []location
	Error     string
}

type location struct {
	Url       string
	PublicUrl string
}

type uploadResp struct {
	Fid      string
	FileName string
	FileUrl  string
	Size     int
	Error    string
}

type systemStatus struct {
	Topology topology
	Version  string
	Error    string
}

type topology struct {
	DataCenters dataCenter
	Free        int
	Max         int
	Layouts     []layout
}

type dataCenter struct {
	Free  int
	Max   int
	Racks []rack
}

type rack struct {
	DataNodes []dataNode
	Free      int
	Max       int
}

type dataNode struct {
	Free      int
	Max       int
	PublicUrl string
	Url       string
	Volumes   int
}

type layout struct {
	Replication string
	Writables   []uint64
}

type volumeServerStatus struct {
	Version string
	volumes []volume
	Error   string
}

type volume struct {
	Id               uint64
	Size             uint64
	RepType          string
	Version          int
	FileCount        uint64
	DeleteCount      uint64
	DeletedByteCount uint64
	ReadOnly         bool
}

var defaultClient *Client

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	defaultClient = NewClient("localhost", 9333)
}

func ParseFid(fid string) (id, key, cookie uint64, err error) {
	s := strings.Split(fid, ",")
	if len(s) != 2 || len(s[1]) <= 8 {
		err = errors.New("Fid format invalid")
		//log.Println(err)
		return
	}
	if id, err = strconv.ParseUint(s[0], 10, 32); err != nil {
		log.Println(err)
		return
	}
	//log.Println(s, len(s[1]))
	index := len(s[1]) - 8
	if key, err = strconv.ParseUint(s[1][:index], 16, 64); err != nil {
		log.Println(err)
		return
	}
	if cookie, err = strconv.ParseUint(s[1][index:], 16, 32); err != nil {
		log.Println(err)
		return
	}
	return
}

func GetUrl(fid string) (url string, err error) {
	return defaultClient.GetUrl(fid)
}

func VolumeUpload(fid string, version int, filename string, file io.Reader) (size int, err error) {
	return defaultClient.VolumeUpload(fid, version, filename, file)
}

// Upload file directly to default master server: localhost:9333
// It is same as: curl -F file=@/home/chris/myphoto.jpg http://localhost:9333/submit
func MasterUpload(filename string, file io.Reader) (fid string, size int, err error) {
	return defaultClient.MasterUpload(filename, file)
}

// First, contact with master server and assign a fid, then upload to volume server
// It is same as the follow steps
// curl http://localhost:9333/dir/assign
// curl -F file=@example.jpg http://127.0.0.1:8080/3,01637037d6
func AssignUpload(filename string, file io.Reader) (fid string, size int, err error) {
	return defaultClient.AssignUpload(filename, file)
}

func Download(fid string) (file io.ReadCloser, length int64, err error) {
	return defaultClient.Download(fid)
}

func Delete(fid string) (err error) {
	return defaultClient.Delete(fid)
}

// Get a fresh new weed client, with it's master url is ip:port
func NewClient(ip string, port int) *Client {
	masterUrl := ip + ":" + strconv.Itoa(port)
	if !strings.HasPrefix(masterUrl, "http://") {
		masterUrl = "http://" + masterUrl
	}
	return &Client{masterUrl, make(map[uint64]string)}
}

func (c *Client) Assign() (fid string, err error) {
	return c.AssignN(1)
}

func (c *Client) AssignN(count int) (fid string, err error) {
	if count <= 0 {
		count = 1
	}
	url := c.Url + AssignUri
	if count > 1 {
		url = url + "?count=" + strconv.Itoa(count)
	}
	resp, err := http.Get(url)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	assign := new(assignResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(assign); err != nil {
		log.Println(err)
		return
	}
	if assign.Error != "" {
		err = errors.New(assign.Error)
		log.Println(err)
		return
	}

	fid = assign.Fid
	log.Printf("assign fid:%s at %s, count:%d", fid, assign.Url, assign.Count)

	return
}

func (c *Client) AssignUpload(filename string, file io.Reader) (fid string, size int, err error) {
	fid, err = c.Assign()
	if err != nil {
		return
	}

	size, err = c.VolumeUpload(fid, 0, filename, file)

	return
}

func (c *Client) VolumeUpload(fid string, version int, filename string, file io.Reader) (size int, err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}
	if version > 0 {
		url = url + "_" + strconv.Itoa(version)
	}

	formData, contentType, err := c.makeFormData(filename, file)
	if err != nil {
		return
	}

	resp, err := c.upload(url, contentType, formData)
	if err == nil {
		size = resp.Size
	}
	return
}

func (c *Client) MasterUpload(filename string, file io.Reader) (fid string, size int, err error) {
	data, contentType, err := c.makeFormData(filename, file)
	if err != nil {
		return
	}
	resp, err := c.upload(c.Url+UploadUri, contentType, data)
	if err == nil {
		fid = resp.Fid
		size = resp.Size
	}
	return
}

func (_ *Client) upload(url string, contentType string, formData io.Reader) (r *uploadResp, err error) {
	resp, err := http.Post(url, contentType, formData)
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()

	upload := new(uploadResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(upload); err != nil {
		log.Println(err)
		return
	}

	if upload.Error != "" {
		err = errors.New(upload.Error)
		log.Println(err)
		return
	}

	r = upload

	return
}

func (c *Client) lookup(volumeId uint64) (url string, err error) {
	if v, ok := c.volumes[volumeId]; ok {
		return v, nil
	}

	resp, err := http.Get(c.Url + LookupUri + strconv.FormatUint(volumeId, 10))
	//log.Println(c.Url + LookupUri + strconv.FormatUint(volumeId, 10))
	if err != nil {
		return
	}
	defer resp.Body.Close()

	lookup := new(lookupResp)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(lookup); err != nil {
		log.Println(err)
		return
	}

	if lookup.Error != "" {
		err = errors.New(lookup.Error)
		log.Println(err)
		return
	}

	c.volumes[volumeId] = lookup.Locations[0].Url

	return lookup.Locations[0].Url, nil
}

func (c *Client) GetUrl(fid string) (url string, err error) {
	id, _, _, err := ParseFid(fid)
	if err != nil {
		log.Println(err)
		return
	}
	//log.Printf("id:%d, key:%x, cookie:%x\n", id, key, cookie)

	if url, err = c.lookup(id); err != nil {
		log.Println(err)
		return
	}

	url = "http://" + url + "/" + fid

	return
}

func (_ *Client) makeFormData(filename string, content io.Reader) (formData io.Reader, contentType string, err error) {
	buf := new(bytes.Buffer)
	writer := multipart.NewWriter(buf)

	part, err := writer.CreateFormFile("file", filename)
	if err != nil {
		log.Println(err)
		return
	}
	_, err = io.Copy(part, content)
	if err != nil {
		log.Println(err)
		return
	}

	formData = buf
	contentType = writer.FormDataContentType()
	writer.Close()

	return
}

func (c *Client) Download(fid string) (file io.ReadCloser, length int64, err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}

	resp, err := http.Get(url)
	if err != nil {
		return
	}

	//log.Println(resp.Header.Get("Content-Type"))
	if resp.ContentLength == 0 {
		return nil, 0, errors.New("File Not Found")
	}

	return resp.Body, resp.ContentLength, nil
}

func (c *Client) Delete(fid string) (err error) {
	url, err := c.GetUrl(fid)
	if err != nil {
		return
	}

	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return
	}
	client := http.Client{}

	_, err = client.Do(request)

	return
}

func (c *Client) VolumeServerStatus(ip string, port uint16) (err error) {
	url := ip + ":" + strconv.Itoa(int(port))
	if !strings.HasPrefix(url, "http://") {
		url = "http://" + url
	}
	resp, err := http.Get(url + VolumeServerStatusUri)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	status := new(volumeServerStatus)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(status); err != nil {
		log.Println(err)
		return
	}

	if status.Error != "" {
		err = errors.New(status.Error)
		log.Println(err)
		return
	}
	return
}

func (c *Client) SystemStatus() (err error) {
	resp, err := http.Get(c.Url + SystemStatusUri)
	if err != nil {
		return
	}

	defer resp.Body.Close()

	status := new(systemStatus)
	decoder := json.NewDecoder(resp.Body)
	if err = decoder.Decode(status); err != nil {
		log.Println(err)
		return
	}

	if status.Error != "" {
		err = errors.New(status.Error)
		log.Println(err)
		return
	}
	return
}
<|endoftext|>


<commit_before>package v2

import (
	"fmt"
	"math"
	"sort"
	"strings"

	"code.cloudfoundry.org/cli/actors/v2actions"
	"code.cloudfoundry.org/cli/cf"
	"code.cloudfoundry.org/cli/commands"
	"code.cloudfoundry.org/cli/commands/flags"
	"code.cloudfoundry.org/cli/commands/v2/internal"
	"code.cloudfoundry.org/cli/utils/config"
	"code.cloudfoundry.org/cli/utils/sortutils"
)

//go:generate counterfeiter . HelpActor

// HelpActor handles the business logic of the help command
type HelpActor interface {
	// CommandInfoByName returns back a help command information for the given
	// command
	CommandInfoByName(interface{}, string) (v2actions.CommandInfo, error)

	// CommandInfos returns a list of all commands
	CommandInfos(interface{}) map[string]v2actions.CommandInfo
}

type HelpCommand struct {
	UI     commands.UI
	Actor  HelpActor
	Config commands.Config

	OptionalArgs flags.CommandName `positional-args:"yes"`
	AllCommands  bool              `short:"a" description:"All available CLI commands"`
	usage        interface{}       `usage:"CF_NAME help [COMMAND]"`
}

func (cmd *HelpCommand) Setup(config commands.Config, ui commands.UI) error {
	cmd.Actor = v2actions.NewActor()
	cmd.Config = config
	cmd.UI = ui

	return nil
}

func (cmd HelpCommand) Execute(args []string) error {
	var err error
	if cmd.OptionalArgs.CommandName == "" {
		cmd.displayFullHelp()
	} else {
		err = cmd.displayCommand()
	}

	return err
}

func (cmd HelpCommand) displayFullHelp() {
	cmd.displayHelpPreamble()
	if cmd.AllCommands {
		cmd.displayAllCommands()
		cmd.displayHelpFooter()
	} else {
		cmd.displayCommonCommands()
	}
}

func (cmd HelpCommand) displayHelpPreamble() {
	cmd.UI.DisplayHelpHeader("NAME:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
		[]string{"CommandDescription"},
		map[string]interface{}{
			"CommandName":        cmd.Config.BinaryName(),
			"CommandDescription": "A command line tool to interact with Cloud Foundry",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("USAGE:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} {{.CommandUsage}}",
		[]string{"CommandUsage"},
		map[string]interface{}{
			"CommandName":  cmd.Config.BinaryName(),
			"CommandUsage": "[global options] command [arguments...] [command options]",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("VERSION:")
	cmd.UI.DisplayText("   {{.Version}}-{{.Time}}", map[string]interface{}{
		"Version": cf.Version,
		"Time":    cf.BuiltOnDate,
	})
	cmd.UI.DisplayNewline()
}

func (cmd HelpCommand) displayCommonCommands() {
	cmdInfo := cmd.Actor.CommandInfos(Commands)

	for _, category := range internal.CommonHelpCategoryList {
		cmd.UI.DisplayHelpHeader(category.CategoryName)
		table := [][]string{}

		for _, row := range category.CommandList {
			finalRow := []string{}

			for _, command := range row {
				separator := ""
				if info, ok := cmdInfo[command]; ok {
					if len(info.Alias) > 0 {
						separator = ","
					}
					finalRow = append(finalRow, fmt.Sprintf("%s%s%s\t", info.Name, separator, info.Alias))
				}
			}

			table = append(table, finalRow)
		}

		cmd.UI.DisplayTable("   ", table)
		cmd.UI.DisplayNewline()
	}

	pluginCommands := cmd.getSortedPluginCommands()
	cmd.UI.DisplayHelpHeader("Commands offered by installed plugins:")

	size := int(math.Ceil(float64(len(pluginCommands)) / 3))
	table := make([][]string, size)
	for i := 0; i < size; i++ {
		table[i] = make([]string, 3)
		for j := 0; j < 3; j++ {
			index := i + j*size
			if index < len(pluginCommands) {
				table[i][j] = pluginCommands[index].Name
			}
		}
	}

	cmd.UI.DisplayTable("   ", table)
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("Global options:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "--help, -h",
			"Description": "Show help",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "-v",
			"Description": "Print API request diagnostics to stdout",
		})
	cmd.UI.DisplayNewline()
	cmd.UI.DisplayText("'cf help -a' lists all commands with short descriptions. See 'cf help <command>' to read about a specific command.")
}

func (cmd HelpCommand) displayAllCommands() {
	pluginCommands := cmd.getSortedPluginCommands()
	cmdInfo := cmd.Actor.CommandInfos(Commands)
	longestCmd := internal.LongestCommandName(cmdInfo, pluginCommands)

	for _, category := range internal.HelpCategoryList {
		cmd.UI.DisplayHelpHeader(category.CategoryName)

		for _, row := range category.CommandList {
			for _, command := range row {
				cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}",
					[]string{"CommandDescription"},
					map[string]interface{}{
						"CommandName":        cmdInfo[command].Name,
						"CommandDescription": cmdInfo[command].Description,
						"Gap":                strings.Repeat(" ", longestCmd+1-len(command)),
					})
			}
		}

		cmd.UI.DisplayNewline()
	}

	cmd.UI.DisplayHelpHeader("INSTALLED PLUGIN COMMANDS:")
	for _, pluginCommand := range pluginCommands {
		cmd.UI.DisplayText("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}", map[string]interface{}{
			"CommandName":        pluginCommand.Name,
			"CommandDescription": pluginCommand.HelpText,
			"Gap":                strings.Repeat(" ", longestCmd+1-len(pluginCommand.Name)),
		})
	}
	cmd.UI.DisplayNewline()
}

func (cmd HelpCommand) displayHelpFooter() {
	cmd.UI.DisplayHelpHeader("ENVIRONMENT VARIABLES:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                     {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_COLOR=false",
			"Description": "Do not colorize output",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_HOME=path/to/dir/",
			"Description": "Override path to default config directory",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}        {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_PLUGIN_HOME=path/to/dir/",
			"Description": "Override path to default plugin config directory",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}              {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_STAGING_TIMEOUT=15",
			"Description": "Max wait time for buildpack staging, in minutes",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_STARTUP_TIMEOUT=5",
			"Description": "Max wait time for app instance startup, in minutes",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                      {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_TRACE=true",
			"Description": "Print API request diagnostics to stdout",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_TRACE=path/to/trace.log",
			"Description": "Append API request diagnostics to a log file",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}} {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "https_proxy=proxy.example.com:8080",
			"Description": "Enable HTTP proxying for API requests",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("GLOBAL OPTIONS:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "--help, -h",
			"Description": "Show help",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "-v",
			"Description": "Print API request diagnostics to stdout",
		})
}

func (cmd HelpCommand) displayCommand() error {
	cmdInfo, err := cmd.Actor.CommandInfoByName(Commands, cmd.OptionalArgs.CommandName)
	if err != nil {
		if err, ok := err.(v2actions.ErrorInvalidCommand); ok {
			var found bool
			if cmdInfo, found = cmd.findPlugin(); !found {
				return err
			}
		} else {
			return err
		}
	}

	cmd.UI.DisplayText("NAME:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
		[]string{"CommandDescription"},
		map[string]interface{}{
			"CommandName":        cmdInfo.Name,
			"CommandDescription": cmdInfo.Description,
		})

	cmd.UI.DisplayNewline()
	usageString := strings.Replace(cmdInfo.Usage, "CF_NAME", cmd.Config.BinaryName(), -1)
	cmd.UI.DisplayText("USAGE:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandUsage}}",
		[]string{"CommandUsage"},
		map[string]interface{}{
			"CommandUsage": usageString,
		})

	if cmdInfo.Alias != "" {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("ALIAS:")
		cmd.UI.DisplayText("   {{.Alias}}",
			map[string]interface{}{
				"Alias": cmdInfo.Alias,
			})
	}

	if len(cmdInfo.Flags) != 0 {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("OPTIONS:")
		nameWidth := internal.LongestFlagWidth(cmdInfo.Flags) + 6
		for _, flag := range cmdInfo.Flags {
			var name string
			if flag.Short != "" && flag.Long != "" {
				name = fmt.Sprintf("--%s, -%s", flag.Long, flag.Short)
			} else if flag.Short != "" {
				name = "-" + flag.Short
			} else {
				name = "--" + flag.Long
			}

			cmd.UI.DisplayTextWithKeyTranslations("   {{.Flags}}{{.Spaces}}{{.Description}}",
				[]string{"Description"},
				map[string]interface{}{
					"Flags":       name,
					"Spaces":      strings.Repeat(" ", nameWidth-len(name)),
					"Description": flag.Description,
				})
		}
	}

	if len(cmdInfo.RelatedCommands) > 0 {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("SEE ALSO:")
		cmd.UI.DisplayText("   {{.RelatedCommands}}",
			map[string]interface{}{
				"RelatedCommands": strings.Join(cmdInfo.RelatedCommands, ", "),
			})
	}

	return nil
}

func (cmd HelpCommand) findPlugin() (v2actions.CommandInfo, bool) {
	for _, pluginConfig := range cmd.Config.PluginConfig() {
		for _, command := range pluginConfig.Commands {
			if command.Name == cmd.OptionalArgs.CommandName {
				return internal.ConvertPluginToCommandInfo(command), true
			}
		}
	}

	return v2actions.CommandInfo{}, false
}

func (cmd HelpCommand) getSortedPluginCommands() config.PluginCommands {
	plugins := cmd.Config.PluginConfig()

	sortedPluginNames := sortutils.Alphabetic{}
	for plugin, _ := range plugins {
		sortedPluginNames = append(sortedPluginNames, plugin)
	}
	sort.Sort(sortedPluginNames)

	pluginCommands := config.PluginCommands{}
	for _, pluginName := range sortedPluginNames {
		sortedCommands := plugins[pluginName].Commands
		sort.Sort(sortedCommands)
		pluginCommands = append(pluginCommands, sortedCommands...)
	}

	return pluginCommands
}
<commit_msg>re-add line break between command categories for cf help -a<commit_after>package v2

import (
	"fmt"
	"math"
	"sort"
	"strings"

	"code.cloudfoundry.org/cli/actors/v2actions"
	"code.cloudfoundry.org/cli/cf"
	"code.cloudfoundry.org/cli/commands"
	"code.cloudfoundry.org/cli/commands/flags"
	"code.cloudfoundry.org/cli/commands/v2/internal"
	"code.cloudfoundry.org/cli/utils/config"
	"code.cloudfoundry.org/cli/utils/sortutils"
)

//go:generate counterfeiter . HelpActor

// HelpActor handles the business logic of the help command
type HelpActor interface {
	// CommandInfoByName returns back a help command information for the given
	// command
	CommandInfoByName(interface{}, string) (v2actions.CommandInfo, error)

	// CommandInfos returns a list of all commands
	CommandInfos(interface{}) map[string]v2actions.CommandInfo
}

type HelpCommand struct {
	UI     commands.UI
	Actor  HelpActor
	Config commands.Config

	OptionalArgs flags.CommandName `positional-args:"yes"`
	AllCommands  bool              `short:"a" description:"All available CLI commands"`
	usage        interface{}       `usage:"CF_NAME help [COMMAND]"`
}

func (cmd *HelpCommand) Setup(config commands.Config, ui commands.UI) error {
	cmd.Actor = v2actions.NewActor()
	cmd.Config = config
	cmd.UI = ui

	return nil
}

func (cmd HelpCommand) Execute(args []string) error {
	var err error
	if cmd.OptionalArgs.CommandName == "" {
		cmd.displayFullHelp()
	} else {
		err = cmd.displayCommand()
	}

	return err
}

func (cmd HelpCommand) displayFullHelp() {
	cmd.displayHelpPreamble()
	if cmd.AllCommands {
		cmd.displayAllCommands()
		cmd.displayHelpFooter()
	} else {
		cmd.displayCommonCommands()
	}
}

func (cmd HelpCommand) displayHelpPreamble() {
	cmd.UI.DisplayHelpHeader("NAME:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
		[]string{"CommandDescription"},
		map[string]interface{}{
			"CommandName":        cmd.Config.BinaryName(),
			"CommandDescription": "A command line tool to interact with Cloud Foundry",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("USAGE:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} {{.CommandUsage}}",
		[]string{"CommandUsage"},
		map[string]interface{}{
			"CommandName":  cmd.Config.BinaryName(),
			"CommandUsage": "[global options] command [arguments...] [command options]",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("VERSION:")
	cmd.UI.DisplayText("   {{.Version}}-{{.Time}}", map[string]interface{}{
		"Version": cf.Version,
		"Time":    cf.BuiltOnDate,
	})
	cmd.UI.DisplayNewline()
}

func (cmd HelpCommand) displayCommonCommands() {
	cmdInfo := cmd.Actor.CommandInfos(Commands)

	for _, category := range internal.CommonHelpCategoryList {
		cmd.UI.DisplayHelpHeader(category.CategoryName)
		table := [][]string{}

		for _, row := range category.CommandList {
			finalRow := []string{}

			for _, command := range row {
				separator := ""
				if info, ok := cmdInfo[command]; ok {
					if len(info.Alias) > 0 {
						separator = ","
					}
					finalRow = append(finalRow, fmt.Sprintf("%s%s%s\t", info.Name, separator, info.Alias))
				}
			}

			table = append(table, finalRow)
		}

		cmd.UI.DisplayTable("   ", table)
		cmd.UI.DisplayNewline()
	}

	pluginCommands := cmd.getSortedPluginCommands()
	cmd.UI.DisplayHelpHeader("Commands offered by installed plugins:")

	size := int(math.Ceil(float64(len(pluginCommands)) / 3))
	table := make([][]string, size)
	for i := 0; i < size; i++ {
		table[i] = make([]string, 3)
		for j := 0; j < 3; j++ {
			index := i + j*size
			if index < len(pluginCommands) {
				table[i][j] = pluginCommands[index].Name
			}
		}
	}

	cmd.UI.DisplayTable("   ", table)
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("Global options:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "--help, -h",
			"Description": "Show help",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "-v",
			"Description": "Print API request diagnostics to stdout",
		})
	cmd.UI.DisplayNewline()
	cmd.UI.DisplayText("'cf help -a' lists all commands with short descriptions. See 'cf help <command>' to read about a specific command.")
}

func (cmd HelpCommand) displayAllCommands() {
	pluginCommands := cmd.getSortedPluginCommands()
	cmdInfo := cmd.Actor.CommandInfos(Commands)
	longestCmd := internal.LongestCommandName(cmdInfo, pluginCommands)

	for _, category := range internal.HelpCategoryList {
		cmd.UI.DisplayHelpHeader(category.CategoryName)

		for _, row := range category.CommandList {
			for _, command := range row {
				cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}",
					[]string{"CommandDescription"},
					map[string]interface{}{
						"CommandName":        cmdInfo[command].Name,
						"CommandDescription": cmdInfo[command].Description,
						"Gap":                strings.Repeat(" ", longestCmd+1-len(command)),
					})
			}

			cmd.UI.DisplayNewline()
		}
	}

	cmd.UI.DisplayHelpHeader("INSTALLED PLUGIN COMMANDS:")
	for _, pluginCommand := range pluginCommands {
		cmd.UI.DisplayText("   {{.CommandName}}{{.Gap}}{{.CommandDescription}}", map[string]interface{}{
			"CommandName":        pluginCommand.Name,
			"CommandDescription": pluginCommand.HelpText,
			"Gap":                strings.Repeat(" ", longestCmd+1-len(pluginCommand.Name)),
		})
	}
	cmd.UI.DisplayNewline()
}

func (cmd HelpCommand) displayHelpFooter() {
	cmd.UI.DisplayHelpHeader("ENVIRONMENT VARIABLES:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                     {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_COLOR=false",
			"Description": "Do not colorize output",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_HOME=path/to/dir/",
			"Description": "Override path to default config directory",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}        {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_PLUGIN_HOME=path/to/dir/",
			"Description": "Override path to default plugin config directory",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}              {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_STAGING_TIMEOUT=15",
			"Description": "Max wait time for buildpack staging, in minutes",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}               {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_STARTUP_TIMEOUT=5",
			"Description": "Max wait time for app instance startup, in minutes",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                      {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_TRACE=true",
			"Description": "Print API request diagnostics to stdout",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "CF_TRACE=path/to/trace.log",
			"Description": "Append API request diagnostics to a log file",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}} {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "https_proxy=proxy.example.com:8080",
			"Description": "Enable HTTP proxying for API requests",
		})
	cmd.UI.DisplayNewline()

	cmd.UI.DisplayHelpHeader("GLOBAL OPTIONS:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                         {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "--help, -h",
			"Description": "Show help",
		})
	cmd.UI.DisplayTextWithKeyTranslations("   {{.ENVName}}                                 {{.Description}}",
		[]string{"Description"},
		map[string]interface{}{
			"ENVName":     "-v",
			"Description": "Print API request diagnostics to stdout",
		})
}

func (cmd HelpCommand) displayCommand() error {
	cmdInfo, err := cmd.Actor.CommandInfoByName(Commands, cmd.OptionalArgs.CommandName)
	if err != nil {
		if err, ok := err.(v2actions.ErrorInvalidCommand); ok {
			var found bool
			if cmdInfo, found = cmd.findPlugin(); !found {
				return err
			}
		} else {
			return err
		}
	}

	cmd.UI.DisplayText("NAME:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandName}} - {{.CommandDescription}}",
		[]string{"CommandDescription"},
		map[string]interface{}{
			"CommandName":        cmdInfo.Name,
			"CommandDescription": cmdInfo.Description,
		})

	cmd.UI.DisplayNewline()
	usageString := strings.Replace(cmdInfo.Usage, "CF_NAME", cmd.Config.BinaryName(), -1)
	cmd.UI.DisplayText("USAGE:")
	cmd.UI.DisplayTextWithKeyTranslations("   {{.CommandUsage}}",
		[]string{"CommandUsage"},
		map[string]interface{}{
			"CommandUsage": usageString,
		})

	if cmdInfo.Alias != "" {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("ALIAS:")
		cmd.UI.DisplayText("   {{.Alias}}",
			map[string]interface{}{
				"Alias": cmdInfo.Alias,
			})
	}

	if len(cmdInfo.Flags) != 0 {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("OPTIONS:")
		nameWidth := internal.LongestFlagWidth(cmdInfo.Flags) + 6
		for _, flag := range cmdInfo.Flags {
			var name string
			if flag.Short != "" && flag.Long != "" {
				name = fmt.Sprintf("--%s, -%s", flag.Long, flag.Short)
			} else if flag.Short != "" {
				name = "-" + flag.Short
			} else {
				name = "--" + flag.Long
			}

			cmd.UI.DisplayTextWithKeyTranslations("   {{.Flags}}{{.Spaces}}{{.Description}}",
				[]string{"Description"},
				map[string]interface{}{
					"Flags":       name,
					"Spaces":      strings.Repeat(" ", nameWidth-len(name)),
					"Description": flag.Description,
				})
		}
	}

	if len(cmdInfo.RelatedCommands) > 0 {
		cmd.UI.DisplayNewline()
		cmd.UI.DisplayText("SEE ALSO:")
		cmd.UI.DisplayText("   {{.RelatedCommands}}",
			map[string]interface{}{
				"RelatedCommands": strings.Join(cmdInfo.RelatedCommands, ", "),
			})
	}

	return nil
}

func (cmd HelpCommand) findPlugin() (v2actions.CommandInfo, bool) {
	for _, pluginConfig := range cmd.Config.PluginConfig() {
		for _, command := range pluginConfig.Commands {
			if command.Name == cmd.OptionalArgs.CommandName {
				return internal.ConvertPluginToCommandInfo(command), true
			}
		}
	}

	return v2actions.CommandInfo{}, false
}

func (cmd HelpCommand) getSortedPluginCommands() config.PluginCommands {
	plugins := cmd.Config.PluginConfig()

	sortedPluginNames := sortutils.Alphabetic{}
	for plugin, _ := range plugins {
		sortedPluginNames = append(sortedPluginNames, plugin)
	}
	sort.Sort(sortedPluginNames)

	pluginCommands := config.PluginCommands{}
	for _, pluginName := range sortedPluginNames {
		sortedCommands := plugins[pluginName].Commands
		sort.Sort(sortedCommands)
		pluginCommands = append(pluginCommands, sortedCommands...)
	}

	return pluginCommands
}
<|endoftext|>


<commit_before>package members

import (
	"time"

	"github.com/mitchellh/mapstructure"
	"github.com/rackspace/gophercloud"
)

// ImageMember model
type ImageMember struct {
	CreatedAt time.Time `mapstructure:"-"`
	ImageID   string    `mapstructure:"image_id"`
	MemberID  string    `mapstructure:"member_id"`
	Schema    string
	// Status could be one of pending, accepted, reject
	Status    string
	UpdatedAt time.Time `mapstructure:"-"`
}

// CreateMemberResult result model
type CreateMemberResult struct {
	gophercloud.Result
}

// Extract ImageMember model from request if possible
func (cm CreateMemberResult) Extract() (*ImageMember, error) {
	if cm.Err != nil {
		return nil, cm.Err
	}
	casted := cm.Body.(map[string]interface{})
	var results ImageMember

	if err := mapstructure.Decode(casted, &results); err != nil {
		return nil, err
	}

	if t, ok := casted["created_at"].(string); ok && t != "" {
		createdAt, err := time.Parse(time.RFC3339, t)
		if err != nil {
			return &results, err
		}
		results.CreatedAt = createdAt
	}

	if t, ok := casted["updated_at"].(string); ok && t != "" {
		updatedAt, err := time.Parse(time.RFC3339, t)
		if err != nil {
			return &results, err
		}
		results.UpdatedAt = updatedAt
	}

	return &results, nil
}

// ListMembersResult model
type ListMembersResult struct {
	gophercloud.Result
}

// Extract returns list of image members
func (lm ListMembersResult) Extract() (*[]ImageMember, error) {
	if lm.Err != nil {
		return nil, lm.Err
	}
	casted := lm.Body.(map[string]interface{})

	var results struct {
		ImageMembers []ImageMember `mapstructure:"members"`
	}

	err := mapstructure.Decode(casted, &results)
	return &results.ImageMembers, err
}

// MemberDetailsResult model
type MemberDetailsResult struct {
	gophercloud.Result
}

// Extract returns image member details
func (md MemberDetailsResult) Extract() (*ImageMember, error) {
	if md.Err != nil {
		return nil, md.Err
	}
	casted := md.Body.(map[string]interface{})

	var results ImageMember

	err := mapstructure.Decode(casted, &results)
	return &results, err

}

// MemberDeleteResult model
type MemberDeleteResult struct {
	gophercloud.Result
}

// MemberUpdateResult model
type MemberUpdateResult struct {
	CreateMemberResult
}
<commit_msg>imageservice: Not need to use slice pointers<commit_after>package members

import (
	"time"

	"github.com/mitchellh/mapstructure"
	"github.com/rackspace/gophercloud"
)

// ImageMember model
type ImageMember struct {
	CreatedAt time.Time `mapstructure:"-"`
	ImageID   string    `mapstructure:"image_id"`
	MemberID  string    `mapstructure:"member_id"`
	Schema    string
	// Status could be one of pending, accepted, reject
	Status    string
	UpdatedAt time.Time `mapstructure:"-"`
}

// CreateMemberResult result model
type CreateMemberResult struct {
	gophercloud.Result
}

// Extract ImageMember model from request if possible
func (cm CreateMemberResult) Extract() (*ImageMember, error) {
	if cm.Err != nil {
		return nil, cm.Err
	}
	casted := cm.Body.(map[string]interface{})
	var results ImageMember

	if err := mapstructure.Decode(casted, &results); err != nil {
		return nil, err
	}

	if t, ok := casted["created_at"].(string); ok && t != "" {
		createdAt, err := time.Parse(time.RFC3339, t)
		if err != nil {
			return &results, err
		}
		results.CreatedAt = createdAt
	}

	if t, ok := casted["updated_at"].(string); ok && t != "" {
		updatedAt, err := time.Parse(time.RFC3339, t)
		if err != nil {
			return &results, err
		}
		results.UpdatedAt = updatedAt
	}

	return &results, nil
}

// ListMembersResult model
type ListMembersResult struct {
	gophercloud.Result
}

// Extract returns list of image members
func (lm ListMembersResult) Extract() ([]ImageMember, error) {
	if lm.Err != nil {
		return nil, lm.Err
	}
	casted := lm.Body.(map[string]interface{})

	var results struct {
		ImageMembers []ImageMember `mapstructure:"members"`
	}

	err := mapstructure.Decode(casted, &results)
	return results.ImageMembers, err
}

// MemberDetailsResult model
type MemberDetailsResult struct {
	gophercloud.Result
}

// Extract returns image member details
func (md MemberDetailsResult) Extract() (*ImageMember, error) {
	if md.Err != nil {
		return nil, md.Err
	}
	casted := md.Body.(map[string]interface{})

	var results ImageMember

	err := mapstructure.Decode(casted, &results)
	return &results, err

}

// MemberDeleteResult model
type MemberDeleteResult struct {
	gophercloud.Result
}

// MemberUpdateResult model
type MemberUpdateResult struct {
	CreateMemberResult
}
<|endoftext|>