<commit_before>package trillian_client

import (
	"context"
	"fmt"
	"log"

	"github.com/google/trillian"
	"google.golang.org/grpc"
)

const CHUNK = 10

type LogScanner interface {
	Leaf(leaf *trillian.LogLeaf) error
}

type TrillianClient interface {
	Scan(logID int64, s LogScanner) error
	Close()
}

type trillianClient struct {
	g  *grpc.ClientConn
	tc trillian.TrillianLogClient
}

func New(logAddr string) TrillianClient {
	g, err := grpc.Dial(logAddr, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("Failed to dial Trillian Log: %v", err)
	}

	tc := trillian.NewTrillianLogClient(g)

	return &trillianClient{g, tc}
}

func (t *trillianClient) Scan(logID int64, s LogScanner) error {
	ctx := context.Background()

	rr := &trillian.GetLatestSignedLogRootRequest{LogId: logID}
	lr, err := t.tc.GetLatestSignedLogRoot(ctx, rr)
	if err != nil {
		log.Fatalf("Can't get log root: %v", err)
	}

	ts := lr.SignedLogRoot.TreeSize
	for n := int64(0); n < ts; {
		g := &trillian.GetLeavesByRangeRequest{LogId: logID, StartIndex: n, Count: CHUNK}
		r, err := t.tc.GetLeavesByRange(ctx, g)
		if err != nil {
			fmt.Errorf("Can't get leaf %d: %v", n, err)
		}

		// deal with server skew
		if r.Skew.GetTreeSizeSet() {
			ts = r.Skew.GetTreeSize()
		}

		if n < ts && len(r.Leaves) == 0 {
			fmt.Errorf("No progress at leaf %d", n)
		}

		for m := 0; m < len(r.Leaves) && n < ts; n++ {
			if r.Leaves[m] == nil {
				fmt.Errorf("Can't get leaf %d (no error)", n)
			}
			if r.Leaves[m].LeafIndex != n {
				fmt.Errorf("Got index %d expected %d", r.Leaves[n].LeafIndex, n)
			}
			err := s.Leaf(r.Leaves[m])
			if err != nil {
				return err
			}
			m++
		}
	}
	return nil
}

func (t *trillianClient) Close() {
	t.g.Close()
}
<commit_msg>Actually return errors.<commit_after>package trillian_client

import (
	"context"
	"fmt"
	"log"

	"github.com/google/trillian"
	"google.golang.org/grpc"
)

const CHUNK = 10

type LogScanner interface {
	Leaf(leaf *trillian.LogLeaf) error
}

type TrillianClient interface {
	Scan(logID int64, s LogScanner) error
	Close()
}

type trillianClient struct {
	g  *grpc.ClientConn
	tc trillian.TrillianLogClient
}

func New(logAddr string) TrillianClient {
	g, err := grpc.Dial(logAddr, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("Failed to dial Trillian Log: %v", err)
	}

	tc := trillian.NewTrillianLogClient(g)

	return &trillianClient{g, tc}
}

func (t *trillianClient) Scan(logID int64, s LogScanner) error {
	ctx := context.Background()

	rr := &trillian.GetLatestSignedLogRootRequest{LogId: logID}
	lr, err := t.tc.GetLatestSignedLogRoot(ctx, rr)
	if err != nil {
		log.Fatalf("Can't get log root: %v", err)
	}

	ts := lr.SignedLogRoot.TreeSize
	for n := int64(0); n < ts; {
		g := &trillian.GetLeavesByRangeRequest{LogId: logID, StartIndex: n, Count: CHUNK}
		r, err := t.tc.GetLeavesByRange(ctx, g)
		if err != nil {
			return fmt.Errorf("Can't get leaf %d: %v", n, err)
		}

		// deal with server skew
		if r.Skew.GetTreeSizeSet() {
			ts = r.Skew.GetTreeSize()
		}

		if n < ts && len(r.Leaves) == 0 {
			return fmt.Errorf("No progress at leaf %d", n)
		}

		for m := 0; m < len(r.Leaves) && n < ts; n++ {
			if r.Leaves[m] == nil {
				return fmt.Errorf("Can't get leaf %d (no error)", n)
			}
			if r.Leaves[m].LeafIndex != n {
				return fmt.Errorf("Got index %d expected %d", r.Leaves[n].LeafIndex, n)
			}
			err := s.Leaf(r.Leaves[m])
			if err != nil {
				return err
			}
			m++
		}
	}
	return nil
}

func (t *trillianClient) Close() {
	t.g.Close()
}
<|endoftext|>


<commit_before>package test

import (
	"github.com/eaciit/toolkit"
	//. "github.com/frezadev/hdc/hive"
	//. "github.com/eaciit/hdc/hive"
	. "github.com/RyanCi/hdc/hive"
	//"log"
	"os"
	"testing"
)

var h *Hive
var e error

type Sample7 struct {
	Code        string `tag_name:"code"`
	Description string `tag_name:"description"`
	Total_emp   string `tag_name:"total_emp"`
	Salary      string `tag_name:"salary"`
}

type Students struct {
	Name    string
	Age     int
	Phone   string
	Address string
}

type SportMatch struct {
	Point       string
	HomeTeam    string
	awayTeam    string
	MarkerImage string
	Information string
	Fixture     string
	Capacity    string
	Tv          string
}

func killApp(code int) {
	if h != nil {
		h.Conn.Close()
	}
	os.Exit(code)
}

func fatalCheck(t *testing.T, what string, e error) {
	if e != nil {
		t.Fatalf("%s: %s", what, e.Error())
	}
}

func TestHiveConnect(t *testing.T) {
	h = HiveConfig("192.168.0.223:10000", "default", "hdfs", "", "")
	e := h.Conn.Open()
	e = h.Conn.TestConnection()
	h.Conn.Close()
	fatalCheck(t, "Populate", e)
}

// /* Populate will exec query and immidiately return the value into object
// Populate is suitable for short type query that return limited data,
// Exec is suitable for long type query that return massive amount of data and require time to produce it
// Ideally Populate should call Exec as well but already have predefined function on it receiving process
// */
func TestHivePopulate(t *testing.T) {
	q := "select * from sample_07 limit 5;"

	var result []toolkit.M

	e := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	e = h.Populate(q, &result)
	fatalCheck(t, "Populate", e)

	if len(result) != 5 {
		t.Logf("Error want %d got %d", 5, len(result))
	}

	t.Logf("Result: \n%s", toolkit.JsonString(result))

	h.Conn.Close()
}

func TestHiveExec(t *testing.T) {
	i := 0
	q := "select * from sample_07 limit 5;"

	e := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	e = h.Exec(q, func(x HiveResult) error {
		i++
		t.Logf("Receiving data: %s", toolkit.JsonString(x))
		return nil
	})

	if e != nil {
		t.Fatalf("Error exec query: %s", e.Error())
	}

	if i < 5 {
		t.Fatalf("Error receive result. Expect %d got %d", 5, i)
	}

	h.Conn.Close()
}

func TestHiveExecMulti(t *testing.T) {
	e := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	var ms1, ms2 []HiveResult
	q := "select * from sample_07 limit 5"

	e = h.Exec(q, func(x HiveResult) error {
		ms1 = append(ms1, x)
		return nil
	})

	fatalCheck(t, "HS1 exec", e)

	e = h.Exec(q, func(x HiveResult) error {
		ms2 = append(ms2, x)
		return nil
	})

	fatalCheck(t, "HS2 Exec", e)

	t.Logf("Value of HS1\n%s\n\nValue of HS2\n%s", toolkit.JsonString(ms1), toolkit.JsonString(ms2))

	h.Conn.Close()
}

func TestLoad(t *testing.T) {
	err := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	var Student Students

	retVal, err := h.Load("students", "dd/MM/yyyy", &Student)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
}

//this function works on simple csv and json file
func TestLoadFile(t *testing.T) {
	err := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	var Student Students
	//test csv
	retVal, err := h.LoadFile("/home/developer/contoh.txt", "students", "csv", "dd/MM/yyyy", &Student)

	var SportMatch SportMatch

	//test json
	retValSport, err := h.LoadFile("/home/developer/test json.txt", "SportMatch", "json", "dd/MM/yyyy", &SportMatch)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
	t.Log(retValSport)
}

func TestLoadFileWithWorker(t *testing.T) {
	err := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	var student Students

	totalWorker := 10
	retVal, err := h.LoadFileWithWorker("/home/developer/contoh.txt", "students", "csv", "dd/MM/yyyy", &student, totalWorker)

	if err != nil {
		t.Log(err)
	}

	h.Conn.Close()
	t.Log(retVal)
}
<commit_msg>modify checkdatastructure, remove delimiter as parameter, remove ImportHDFS<commit_after>package test

import (
	"github.com/eaciit/toolkit"
	//. "github.com/frezadev/hdc/hive"
	//. "github.com/eaciit/hdc/hive"
	. "github.com/RyanCi/hdc/hive"
	//"log"
	"os"
	"testing"
)

var h *Hive
var e error

type Sample7 struct {
	Code        string `tag_name:"code"`
	Description string `tag_name:"description"`
	Total_emp   string `tag_name:"total_emp"`
	Salary      string `tag_name:"salary"`
}

type Students struct {
	Name    string
	Age     int
	Phone   string
	Address string
}

type SportMatch struct {
	Point       string
	HomeTeam    string
	AwayTeam    string
	MarkerImage string
	Information string
	Fixture     string
	Capacity    string
	Tv          string
}

func killApp(code int) {
	if h != nil {
		h.Conn.Close()
	}
	os.Exit(code)
}

func fatalCheck(t *testing.T, what string, e error) {
	if e != nil {
		t.Fatalf("%s: %s", what, e.Error())
	}
}

func TestHiveConnect(t *testing.T) {
	h = HiveConfig("192.168.0.223:10000", "default", "hdfs", "", "")
	e := h.Conn.Open()
	e = h.Conn.TestConnection()
	h.Conn.Close()
	fatalCheck(t, "Populate", e)
}

// /* Populate will exec query and immidiately return the value into object
// Populate is suitable for short type query that return limited data,
// Exec is suitable for long type query that return massive amount of data and require time to produce it
// Ideally Populate should call Exec as well but already have predefined function on it receiving process
// */
func TestHivePopulate(t *testing.T) {
	q := "select * from sample_07 limit 5;"

	var result []toolkit.M

	e := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	e = h.Populate(q, &result)
	fatalCheck(t, "Populate", e)

	if len(result) != 5 {
		t.Logf("Error want %d got %d", 5, len(result))
	}

	t.Logf("Result: \n%s", toolkit.JsonString(result))

	h.Conn.Close()
}

func TestHiveExec(t *testing.T) {
	i := 0
	q := "select * from sample_07 limit 5;"

	e := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	e = h.Exec(q, func(x HiveResult) error {
		i++
		t.Logf("Receiving data: %s", toolkit.JsonString(x))
		return nil
	})

	if e != nil {
		t.Fatalf("Error exec query: %s", e.Error())
	}

	if i < 5 {
		t.Fatalf("Error receive result. Expect %d got %d", 5, i)
	}

	h.Conn.Close()
}

func TestHiveExecMulti(t *testing.T) {
	e := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	var ms1, ms2 []HiveResult
	q := "select * from sample_07 limit 5"

	e = h.Exec(q, func(x HiveResult) error {
		ms1 = append(ms1, x)
		return nil
	})

	fatalCheck(t, "HS1 exec", e)

	e = h.Exec(q, func(x HiveResult) error {
		ms2 = append(ms2, x)
		return nil
	})

	fatalCheck(t, "HS2 Exec", e)

	t.Logf("Value of HS1\n%s\n\nValue of HS2\n%s", toolkit.JsonString(ms1), toolkit.JsonString(ms2))

	h.Conn.Close()
}

func TestLoad(t *testing.T) {
	err := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	var Student Students

	retVal, err := h.Load("students", "dd/MM/yyyy", &Student)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
}

//this function works on simple csv and json file
func TestLoadFile(t *testing.T) {
	err := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	var Student Students
	//test csv
	retVal, err := h.LoadFile("/home/developer/contoh.txt", "students", "csv", "dd/MM/yyyy", &Student)

	var SportMatch SportMatch

	//test json
	retValSport, err := h.LoadFile("/home/developer/test json.txt", "SportMatch", "json", "dd/MM/yyyy", &SportMatch)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
	t.Log(retValSport)
}

func TestLoadFileWithWorker(t *testing.T) {
	err := h.Conn.Open()
	fatalCheck(t, "Populate", e)

	var student Students

	totalWorker := 10
	retVal, err := h.LoadFileWithWorker("/home/developer/contoh.txt", "students", "csv", "dd/MM/yyyy", &student, totalWorker)

	if err != nil {
		t.Log(err)
	}

	h.Conn.Close()
	t.Log(retVal)
}
<|endoftext|>


<commit_before>package test

import (
	"github.com/eaciit/toolkit"
	//. "github.com/frezadev/hdc/hive"
	. "github.com/eaciit/hdc/hive"
	//. "github.com/RyanCi/hdc/hive"
	"log"
	"os"
	"testing"
)

var h *Hive
var e error

type Sample7 struct {
	Code        string `tag_name:"code"`
	Description string `tag_name:"description"`
	Total_emp   string `tag_name:"total_emp"`
	Salary      string `tag_name:"salary"`
}

type Students struct {
	Name    string
	Age     int
	Phone   string
	Address string
}

func killApp(code int) {
	if h != nil {
		h.Conn.Close()
	}
	os.Exit(code)
}

func fatalCheck(t *testing.T, what string, e error) {
	if e != nil {
		t.Fatalf("%s: %s", what, e.Error())
	}
}

func TestHiveConnect(t *testing.T) {
	h = HiveConfig("192.168.0.223:10000", "default", "hdfs", "", "")
}

/* Populate will exec query and immidiately return the value into object
Populate is suitable for short type query that return limited data,
Exec is suitable for long type query that return massive amount of data and require time to produce it
Ideally Populate should call Exec as well but already have predefined function on it receiving process
*/
func TestHivePopulate(t *testing.T) {
	q := "select * from sample_07 limit 5;"

	var result []toolkit.M

	h.Conn.Open()

	e := h.Populate(q, &result)
	fatalCheck(t, "Populate", e)

	if len(result) != 5 {
		t.Logf("Error want %d got %d", 5, len(result))
	}

	t.Logf("Result: \n%s", toolkit.JsonString(result))

	h.Conn.Close()
}

func TestHiveExec(t *testing.T) {
	i := 0
	q := "select * from sample_07 limit 5;"

	h.Conn.Open()

	e := h.Exec(q, func(x HiveResult) error {
		i++
		t.Logf("Receiving data: %s", toolkit.JsonString(x))
		return nil
	})

	if e != nil {
		t.Fatalf("Error exec query: %s", e.Error())
	}

	if i < 5 {
		t.Fatalf("Error receive result. Expect %d got %d", 5, i)
	}

	h.Conn.Close()
}

func TestHiveExecMulti(t *testing.T) {
	h.Conn.Open()

	var ms1, ms2 []HiveResult
	q := "select * from sample_07 limit 5"

	e := h.Exec(q, func(x HiveResult) error {
		ms1 = append(ms1, x)
		return nil
	})

	fatalCheck(t, "HS1 exec", e)

	e = h.Exec(q, func(x HiveResult) error {
		ms2 = append(ms2, x)
		return nil
	})

	fatalCheck(t, "HS2 Exec", e)

	t.Logf("Value of HS1\n%s\n\nValue of HS2\n%s", toolkit.JsonString(ms1), toolkit.JsonString(ms2))

	h.Conn.Close()
}

func TestLoad(t *testing.T) {
	h.Conn.Open()

	var Student Students

	retVal, err := h.Load("students", "|", &Student)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
}

//for now, this function works on simple csv file
func TestLoadFile(t *testing.T) {
	h.Conn.Open()

	var Student Students

	retVal, err := h.LoadFile("/home/developer/contoh.txt", "students", "txt", &Student)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
}

func TestLoadFileWithWorker(t *testing.T) {
	h.Conn.Open()

	var student Students

	totalWorker := 10
	retVal, err := h.LoadFileWithWorker("/home/developer/contoh.txt", "students", "txt", &student, totalWorker)

	if err != nil {
		log.Println(err)
	}
	h.Conn.Close()
	log.Println(retVal)
}
<commit_msg>testing<commit_after>package test

import (
	"github.com/eaciit/toolkit"
	//. "github.com/frezadev/hdc/hive"
	. "github.com/eaciit/hdc/hive"
	//. "github.com/RyanCi/hdc/hive"
	"log"
	"os"
	"testing"
)

var h *Hive
var e error

type Sample7 struct {
	Code        string `tag_name:"code"`
	Description string `tag_name:"description"`
	Total_emp   string `tag_name:"total_emp"`
	Salary      string `tag_name:"salary"`
}

type Students struct {
	Name    string
	Age     int
	Phone   string
	Address string
}

func killApp(code int) {
	if h != nil {
		h.Conn.Close()
	}
	os.Exit(code)
}

func fatalCheck(t *testing.T, what string, e error) {
	if e != nil {
		t.Fatalf("%s: %s", what, e.Error())
	}
}

func TestHiveConnect(t *testing.T) {
	h = HiveConfig("192.168.0.223:10000", "default", "hdfs", "", "")
}

/* Populate will exec query and immidiately return the value into object
Populate is suitable for short type query that return limited data,
Exec is suitable for long type query that return massive amount of data and require time to produce it
Ideally Populate should call Exec as well but already have predefined function on it receiving process
*/
func TestHivePopulate(t *testing.T) {
	q := "select * from sample_07 limit 5;"

	var result []toolkit.M

	h.Conn.Open()

	e := h.Populate(q, &result)
	fatalCheck(t, "Populate", e)

	if len(result) != 5 {
		t.Logf("Error want %d got %d", 5, len(result))
	}

	t.Logf("Result: \n%s", toolkit.JsonString(result))

	h.Conn.Close()
}

func TestHiveExec(t *testing.T) {
	i := 0
	q := "select * from sample_07 limit 5;"

	h.Conn.Open()

	e := h.Exec(q, func(x HiveResult) error {
		i++
		t.Logf("Receiving data: %s", toolkit.JsonString(x))
		return nil
	})

	if e != nil {
		t.Fatalf("Error exec query: %s", e.Error())
	}

	if i < 5 {
		t.Fatalf("Error receive result. Expect %d got %d", 5, i)
	}

	h.Conn.Close()
}

func TestHiveExecMulti(t *testing.T) {
	h.Conn.Open()

	var ms1, ms2 []HiveResult
	q := "select * from sample_07 limit 5"

	e := h.Exec(q, func(x HiveResult) error {
		ms1 = append(ms1, x)
		return nil
	})

	fatalCheck(t, "HS1 exec", e)

	e = h.Exec(q, func(x HiveResult) error {
		ms2 = append(ms2, x)
		return nil
	})

	fatalCheck(t, "HS2 Exec", e)

	t.Logf("Value of HS1\n%s\n\nValue of HS2\n%s", toolkit.JsonString(ms1), toolkit.JsonString(ms2))

	h.Conn.Close()
}

func TestLoad(t *testing.T) {
	h.Conn.Open()

	var Student Students

	retVal, err := h.Load("students", "|", &Student)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
}

//for now, this function works on simple csv file
func TestLoadFile(t *testing.T) {
	h.Conn.Open()

	var Student Students

	retVal, err := h.LoadFile("/home/developer/contoh.txt", "students", "txt", &Student)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
}

func TestLoadFileWithWorker(t *testing.T) {
	h.Conn.Open()

	var student Students

	totalWorker := 10
	retVal, err := h.LoadFileWithWorker("/home/developer/contoh.txt", "students", "txt", &student, totalWorker)

	if err != nil {
		t.Log(err)
	}
	h.Conn.Close()
	t.Log(retVal)
}
<|endoftext|>


<commit_before>package s3api

import (
	"context"
	"encoding/xml"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
)

type ListBucketResultV2 struct {
	XMLName               xml.Name      `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult"`
	Name                  string        `xml:"Name"`
	Prefix                string        `xml:"Prefix"`
	MaxKeys               int           `xml:"MaxKeys"`
	Delimiter             string        `xml:"Delimiter,omitempty"`
	IsTruncated           bool          `xml:"IsTruncated"`
	Contents              []ListEntry   `xml:"Contents,omitempty"`
	CommonPrefixes        []PrefixEntry `xml:"CommonPrefixes,omitempty"`
	ContinuationToken     string        `xml:"ContinuationToken,omitempty"`
	NextContinuationToken string        `xml:"NextContinuationToken,omitempty"`
	KeyCount              int           `xml:"KeyCount"`
	StartAfter            string        `xml:"StartAfter,omitempty"`
}

func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {

	// https://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html

	// collect parameters
	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("ListObjectsV2Handler %s", bucket)

	originalPrefix, continuationToken, startAfter, delimiter, _, maxKeys := getListObjectsV2Args(r.URL.Query())

	if maxKeys < 0 {
		s3err.WriteErrorResponse(w, r, s3err.ErrInvalidMaxKeys)
		return
	}
	if delimiter != "" && delimiter != "/" {
		s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)
		return
	}

	marker := continuationToken
	if continuationToken == "" {
		marker = startAfter
	}

	response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)

	if err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}

	if len(response.Contents) == 0 {
		if exists, existErr := s3a.exists(s3a.option.BucketsPath, bucket, true); existErr == nil && !exists {
			s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
			return
		}
	}

	responseV2 := &ListBucketResultV2{
		XMLName:               response.XMLName,
		Name:                  response.Name,
		CommonPrefixes:        response.CommonPrefixes,
		Contents:              response.Contents,
		ContinuationToken:     continuationToken,
		Delimiter:             response.Delimiter,
		IsTruncated:           response.IsTruncated,
		KeyCount:              len(response.Contents) + len(response.CommonPrefixes),
		MaxKeys:               response.MaxKeys,
		NextContinuationToken: response.NextMarker,
		Prefix:                response.Prefix,
		StartAfter:            startAfter,
	}

	writeSuccessResponseXML(w, r, responseV2)
}

func (s3a *S3ApiServer) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {

	// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html

	// collect parameters
	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("ListObjectsV1Handler %s", bucket)

	originalPrefix, marker, delimiter, maxKeys := getListObjectsV1Args(r.URL.Query())

	if maxKeys < 0 {
		s3err.WriteErrorResponse(w, r, s3err.ErrInvalidMaxKeys)
		return
	}
	if delimiter != "" && delimiter != "/" {
		s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)
		return
	}

	response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)

	if err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}

	if len(response.Contents) == 0 {
		if exists, existErr := s3a.exists(s3a.option.BucketsPath, bucket, true); existErr == nil && !exists {
			s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
			return
		}
	}

	writeSuccessResponseXML(w, r, response)
}

func (s3a *S3ApiServer) listFilerEntries(bucket string, originalPrefix string, maxKeys int, marker string, delimiter string) (response ListBucketResult, err error) {
	// convert full path prefix into directory name and prefix for entry name
	reqDir, prefix := filepath.Split(originalPrefix)
	if strings.HasPrefix(reqDir, "/") {
		reqDir = reqDir[1:]
	}
	bucketPrefix := fmt.Sprintf("%s/%s/", s3a.option.BucketsPath, bucket)
	reqDir = fmt.Sprintf("%s%s", bucketPrefix, reqDir)
	if strings.HasSuffix(reqDir, "/") {
		// remove trailing "/"
		reqDir = reqDir[:len(reqDir)-1]
	}

	var contents []ListEntry
	var commonPrefixes []PrefixEntry
	var isTruncated bool
	var doErr error
	var nextMarker string

	// check filer
	err = s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {

		_, isTruncated, nextMarker, doErr = s3a.doListFilerEntries(client, reqDir, prefix, maxKeys, marker, delimiter, func(dir string, entry *filer_pb.Entry) {
			if entry.IsDirectory && entry.Attributes.Mime == "" {
				if delimiter == "/" {
					commonPrefixes = append(commonPrefixes, PrefixEntry{
						Prefix: fmt.Sprintf("%s/%s/", dir, entry.Name)[len(bucketPrefix):],
					})
				}
			} else {
				storageClass := "STANDARD"
				if v, ok := entry.Extended[xhttp.AmzStorageClass]; ok {
					storageClass = string(v)
				}
				entryName := entry.Name
				if entry.IsDirectory {
					entryName += "/"
				}
				contents = append(contents, ListEntry{
					Key:          fmt.Sprintf("%s/%s", dir, entryName)[len(bucketPrefix):],
					LastModified: time.Unix(entry.Attributes.Mtime, 0).UTC(),
					ETag:         "\"" + filer.ETag(entry) + "\"",
					Size:         int64(filer.FileSize(entry)),
					Owner: CanonicalUser{
						ID:          fmt.Sprintf("%x", entry.Attributes.Uid),
						DisplayName: entry.Attributes.UserName,
					},
					StorageClass: StorageClass(storageClass),
				})
			}
		})
		if doErr != nil {
			return doErr
		}

		if !isTruncated {
			nextMarker = ""
		}

		response = ListBucketResult{
			Name:           bucket,
			Prefix:         originalPrefix,
			Marker:         marker,
			NextMarker:     nextMarker,
			MaxKeys:        maxKeys,
			Delimiter:      delimiter,
			IsTruncated:    isTruncated,
			Contents:       contents,
			CommonPrefixes: commonPrefixes,
		}

		return nil
	})

	return
}

func (s3a *S3ApiServer) doListFilerEntries(client filer_pb.SeaweedFilerClient, dir, prefix string, maxKeys int, marker, delimiter string, eachEntryFn func(dir string, entry *filer_pb.Entry)) (counter int, isTruncated bool, nextMarker string, err error) {
	// invariants
	//   prefix and marker should be under dir, marker may contain "/"
	//   maxKeys should be updated for each recursion

	if prefix == "/" && delimiter == "/" {
		return
	}
	if maxKeys <= 0 {
		return
	}

	if strings.Contains(marker, "/") {
		sepIndex := strings.Index(marker, "/")
		subDir, subMarker := marker[0:sepIndex], marker[sepIndex+1:]
		// println("doListFilerEntries dir", dir+"/"+subDir, "subMarker", subMarker, "maxKeys", maxKeys)
		subCounter, subIsTruncated, subNextMarker, subErr := s3a.doListFilerEntries(client, dir+"/"+subDir, "", maxKeys, subMarker, delimiter, eachEntryFn)
		if subErr != nil {
			err = subErr
			return
		}
		counter += subCounter
		isTruncated = isTruncated || subIsTruncated
		maxKeys -= subCounter
		nextMarker = subDir + "/" + subNextMarker
		// finished processing this sub directory
		marker = subDir
	}
	if maxKeys <= 0 {
		return
	}

	// now marker is also a direct child of dir
	request := &filer_pb.ListEntriesRequest{
		Directory:          dir,
		Prefix:             prefix,
		Limit:              uint32(maxKeys + 1),
		StartFromFileName:  marker,
		InclusiveStartFrom: false,
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	stream, listErr := client.ListEntries(ctx, request)
	if listErr != nil {
		err = fmt.Errorf("list entires %+v: %v", request, listErr)
		return
	}

	for {
		resp, recvErr := stream.Recv()
		if recvErr != nil {
			if recvErr == io.EOF {
				break
			} else {
				err = fmt.Errorf("iterating entires %+v: %v", request, recvErr)
				return
			}
		}
		if counter >= maxKeys {
			isTruncated = true
			return
		}
		entry := resp.Entry
		nextMarker = entry.Name
		if entry.IsDirectory {
			// println("ListEntries", dir, "dir:", entry.Name)
			if entry.Name != ".uploads" { // FIXME no need to apply to all directories. this extra also affects maxKeys
				if delimiter != "/" {
					eachEntryFn(dir, entry)
					// println("doListFilerEntries2 dir", dir+"/"+entry.Name, "maxKeys", maxKeys-counter)
					subCounter, subIsTruncated, subNextMarker, subErr := s3a.doListFilerEntries(client, dir+"/"+entry.Name, "", maxKeys-counter, "", delimiter, eachEntryFn)
					if subErr != nil {
						err = fmt.Errorf("doListFilerEntries2: %v", subErr)
						return
					}
					// println("doListFilerEntries2 dir", dir+"/"+entry.Name, "maxKeys", maxKeys-counter, "subCounter", subCounter, "subNextMarker", subNextMarker, "subIsTruncated", subIsTruncated)
					counter += subCounter
					nextMarker = entry.Name + "/" + subNextMarker
					if subIsTruncated {
						isTruncated = true
						return
					}
				} else {
					var isEmpty bool
					if !s3a.option.AllowEmptyFolder {
						if isEmpty, err = s3a.isDirectoryAllEmpty(client, dir, entry.Name); err != nil {
							glog.Errorf("check empty folder %s: %v", dir, err)
						}
					}
					if !isEmpty || entry.Attributes.Mime != "" {
						eachEntryFn(dir, entry)
						counter++
					}
				}
			}
		} else {
			// println("ListEntries", dir, "file:", entry.Name)
			eachEntryFn(dir, entry)
			counter++
		}
	}
	return
}

func getListObjectsV2Args(values url.Values) (prefix, token, startAfter, delimiter string, fetchOwner bool, maxkeys int) {
	prefix = values.Get("prefix")
	token = values.Get("continuation-token")
	startAfter = values.Get("start-after")
	delimiter = values.Get("delimiter")
	if values.Get("max-keys") != "" {
		maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
	} else {
		maxkeys = maxObjectListSizeLimit
	}
	fetchOwner = values.Get("fetch-owner") == "true"
	return
}

func getListObjectsV1Args(values url.Values) (prefix, marker, delimiter string, maxkeys int) {
	prefix = values.Get("prefix")
	marker = values.Get("marker")
	delimiter = values.Get("delimiter")
	if values.Get("max-keys") != "" {
		maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
	} else {
		maxkeys = maxObjectListSizeLimit
	}
	return
}

func (s3a *S3ApiServer) isDirectoryAllEmpty(filerClient filer_pb.SeaweedFilerClient, parentDir, name string) (isEmpty bool, err error) {
	// println("+ isDirectoryAllEmpty", dir, name)
	glog.V(4).Infof("+ isEmpty %s/%s", parentDir, name)
	defer glog.V(4).Infof("- isEmpty %s/%s %v", parentDir, name, isEmpty)
	var fileCounter int
	var subDirs []string
	currentDir := parentDir + "/" + name
	var startFrom string
	var isExhausted bool
	var foundEntry bool
	for fileCounter == 0 && !isExhausted && err == nil {
		err = filer_pb.SeaweedList(filerClient, currentDir, "", func(entry *filer_pb.Entry, isLast bool) error {
			foundEntry = true
			if entry.IsDirectory {
				subDirs = append(subDirs, entry.Name)
			} else {
				fileCounter++
			}
			startFrom = entry.Name
			isExhausted = isExhausted || isLast
			glog.V(4).Infof("    * %s/%s isLast: %t", currentDir, startFrom, isLast)
			return nil
		}, startFrom, false, 8)
		if !foundEntry {
			break
		}
	}

	if err != nil {
		return false, err
	}

	if fileCounter > 0 {
		return false, nil
	}

	for _, subDir := range subDirs {
		isSubEmpty, subErr := s3a.isDirectoryAllEmpty(filerClient, currentDir, subDir)
		if subErr != nil {
			return false, subErr
		}
		if !isSubEmpty {
			return false, nil
		}
	}

	glog.V(1).Infof("deleting empty folder %s", currentDir)
	if err = doDeleteEntry(filerClient, parentDir, name, true, true); err != nil {
		return
	}

	return true, nil
}
<commit_msg>list self dir https://github.com/chrislusf/seaweedfs/issues/3086<commit_after>package s3api

import (
	"context"
	"encoding/xml"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
)

type ListBucketResultV2 struct {
	XMLName               xml.Name      `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult"`
	Name                  string        `xml:"Name"`
	Prefix                string        `xml:"Prefix"`
	MaxKeys               int           `xml:"MaxKeys"`
	Delimiter             string        `xml:"Delimiter,omitempty"`
	IsTruncated           bool          `xml:"IsTruncated"`
	Contents              []ListEntry   `xml:"Contents,omitempty"`
	CommonPrefixes        []PrefixEntry `xml:"CommonPrefixes,omitempty"`
	ContinuationToken     string        `xml:"ContinuationToken,omitempty"`
	NextContinuationToken string        `xml:"NextContinuationToken,omitempty"`
	KeyCount              int           `xml:"KeyCount"`
	StartAfter            string        `xml:"StartAfter,omitempty"`
}

func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {

	// https://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html

	// collect parameters
	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("ListObjectsV2Handler %s", bucket)

	originalPrefix, continuationToken, startAfter, delimiter, _, maxKeys := getListObjectsV2Args(r.URL.Query())

	if maxKeys < 0 {
		s3err.WriteErrorResponse(w, r, s3err.ErrInvalidMaxKeys)
		return
	}
	if delimiter != "" && delimiter != "/" {
		s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)
		return
	}

	marker := continuationToken
	if continuationToken == "" {
		marker = startAfter
	}

	response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)

	if err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}

	if len(response.Contents) == 0 {
		if exists, existErr := s3a.exists(s3a.option.BucketsPath, bucket, true); existErr == nil && !exists {
			s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
			return
		}
	}

	responseV2 := &ListBucketResultV2{
		XMLName:               response.XMLName,
		Name:                  response.Name,
		CommonPrefixes:        response.CommonPrefixes,
		Contents:              response.Contents,
		ContinuationToken:     continuationToken,
		Delimiter:             response.Delimiter,
		IsTruncated:           response.IsTruncated,
		KeyCount:              len(response.Contents) + len(response.CommonPrefixes),
		MaxKeys:               response.MaxKeys,
		NextContinuationToken: response.NextMarker,
		Prefix:                response.Prefix,
		StartAfter:            startAfter,
	}

	writeSuccessResponseXML(w, r, responseV2)
}

func (s3a *S3ApiServer) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {

	// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html

	// collect parameters
	bucket, _ := xhttp.GetBucketAndObject(r)
	glog.V(3).Infof("ListObjectsV1Handler %s", bucket)

	originalPrefix, marker, delimiter, maxKeys := getListObjectsV1Args(r.URL.Query())

	if maxKeys < 0 {
		s3err.WriteErrorResponse(w, r, s3err.ErrInvalidMaxKeys)
		return
	}
	if delimiter != "" && delimiter != "/" {
		s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)
		return
	}

	response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)

	if err != nil {
		s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
		return
	}

	if len(response.Contents) == 0 {
		if exists, existErr := s3a.exists(s3a.option.BucketsPath, bucket, true); existErr == nil && !exists {
			s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
			return
		}
	}

	writeSuccessResponseXML(w, r, response)
}

func (s3a *S3ApiServer) listFilerEntries(bucket string, originalPrefix string, maxKeys int, marker string, delimiter string) (response ListBucketResult, err error) {
	// convert full path prefix into directory name and prefix for entry name
	reqDir, prefix := filepath.Split(originalPrefix)
	if strings.HasPrefix(reqDir, "/") {
		reqDir = reqDir[1:]
	}
	bucketPrefix := fmt.Sprintf("%s/%s/", s3a.option.BucketsPath, bucket)
	reqDir = fmt.Sprintf("%s%s", bucketPrefix, reqDir)
	if strings.HasSuffix(reqDir, "/") {
		reqDir = strings.TrimSuffix(reqDir, "/")
	}

	var contents []ListEntry
	var commonPrefixes []PrefixEntry
	var isTruncated bool
	var doErr error
	var nextMarker string

	// check filer
	err = s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {

		_, isTruncated, nextMarker, doErr = s3a.doListFilerEntries(client, reqDir, prefix, maxKeys, marker, delimiter, false, func(dir string, entry *filer_pb.Entry) {
			if entry.IsDirectory {
				if delimiter == "/" {
					commonPrefixes = append(commonPrefixes, PrefixEntry{
						Prefix: fmt.Sprintf("%s/%s/", dir, entry.Name)[len(bucketPrefix):],
					})
				}
				return
			}
			storageClass := "STANDARD"
			if v, ok := entry.Extended[xhttp.AmzStorageClass]; ok {
				storageClass = string(v)
			}
			contents = append(contents, ListEntry{
				Key:          fmt.Sprintf("%s/%s", dir, entry.Name)[len(bucketPrefix):],
				LastModified: time.Unix(entry.Attributes.Mtime, 0).UTC(),
				ETag:         "\"" + filer.ETag(entry) + "\"",
				Size:         int64(filer.FileSize(entry)),
				Owner: CanonicalUser{
					ID:          fmt.Sprintf("%x", entry.Attributes.Uid),
					DisplayName: entry.Attributes.UserName,
				},
				StorageClass: StorageClass(storageClass),
			})
		})
		if doErr != nil {
			return doErr
		}

		if !isTruncated {
			nextMarker = ""
		}

		if len(contents) == 0 && maxKeys > 0 {
			if strings.HasSuffix(originalPrefix, "/") && prefix == "" {
				reqDir, prefix = filepath.Split(strings.TrimSuffix(reqDir, "/"))
				reqDir = strings.TrimSuffix(reqDir, "/")
			}
			_, _, _, doErr = s3a.doListFilerEntries(client, reqDir, prefix, 1, prefix, delimiter, true, func(dir string, entry *filer_pb.Entry) {
				if entry.IsDirectory && entry.Attributes.Mime != "" && entry.Name == prefix {
					storageClass := "STANDARD"
					if v, ok := entry.Extended[xhttp.AmzStorageClass]; ok {
						storageClass = string(v)
					}
					contents = append(contents, ListEntry{
						Key:          fmt.Sprintf("%s/%s", dir, entry.Name+"/")[len(bucketPrefix):],
						LastModified: time.Unix(entry.Attributes.Mtime, 0).UTC(),
						ETag:         "\"" + filer.ETag(entry) + "\"",
						Size:         int64(filer.FileSize(entry)),
						Owner: CanonicalUser{
							ID:          fmt.Sprintf("%x", entry.Attributes.Uid),
							DisplayName: entry.Attributes.UserName,
						},
						StorageClass: StorageClass(storageClass),
					})
				}
			})
		}

		response = ListBucketResult{
			Name:           bucket,
			Prefix:         originalPrefix,
			Marker:         marker,
			NextMarker:     nextMarker,
			MaxKeys:        maxKeys,
			Delimiter:      delimiter,
			IsTruncated:    isTruncated,
			Contents:       contents,
			CommonPrefixes: commonPrefixes,
		}

		return nil
	})

	return
}

func (s3a *S3ApiServer) doListFilerEntries(client filer_pb.SeaweedFilerClient, dir, prefix string, maxKeys int, marker, delimiter string, inclusiveStartFrom bool, eachEntryFn func(dir string, entry *filer_pb.Entry)) (counter int, isTruncated bool, nextMarker string, err error) {
	// invariants
	//   prefix and marker should be under dir, marker may contain "/"
	//   maxKeys should be updated for each recursion

	if prefix == "/" && delimiter == "/" {
		return
	}
	if maxKeys <= 0 {
		return
	}

	if strings.Contains(marker, "/") {
		sepIndex := strings.Index(marker, "/")
		subDir, subMarker := marker[0:sepIndex], marker[sepIndex+1:]
		glog.V(4).Infoln("doListFilerEntries dir", dir+"/"+subDir, "subMarker", subMarker, "maxKeys", maxKeys)
		subCounter, subIsTruncated, subNextMarker, subErr := s3a.doListFilerEntries(client, dir+"/"+subDir, "", maxKeys, subMarker, delimiter, false, eachEntryFn)
		if subErr != nil {
			err = subErr
			return
		}
		counter += subCounter
		isTruncated = isTruncated || subIsTruncated
		maxKeys -= subCounter
		nextMarker = subDir + "/" + subNextMarker
		// finished processing this sub directory
		marker = subDir
	}
	if maxKeys <= 0 {
		return
	}

	// now marker is also a direct child of dir
	request := &filer_pb.ListEntriesRequest{
		Directory:          dir,
		Prefix:             prefix,
		Limit:              uint32(maxKeys + 1),
		StartFromFileName:  marker,
		InclusiveStartFrom: inclusiveStartFrom,
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	stream, listErr := client.ListEntries(ctx, request)
	if listErr != nil {
		err = fmt.Errorf("list entires %+v: %v", request, listErr)
		return
	}

	for {
		resp, recvErr := stream.Recv()
		if recvErr != nil {
			if recvErr == io.EOF {
				break
			} else {
				err = fmt.Errorf("iterating entires %+v: %v", request, recvErr)
				return
			}
		}
		if counter >= maxKeys {
			isTruncated = true
			return
		}
		entry := resp.Entry
		nextMarker = entry.Name
		if entry.IsDirectory {
			// println("ListEntries", dir, "dir:", entry.Name)
			if entry.Name == ".uploads" { // FIXME no need to apply to all directories. this extra also affects maxKeys
				continue
			}
			if delimiter != "/" {
				eachEntryFn(dir, entry)
				// println("doListFilerEntries2 dir", dir+"/"+entry.Name, "maxKeys", maxKeys-counter)
				subCounter, subIsTruncated, subNextMarker, subErr := s3a.doListFilerEntries(client, dir+"/"+entry.Name, "", maxKeys-counter, "", delimiter, false, eachEntryFn)
				if subErr != nil {
					err = fmt.Errorf("doListFilerEntries2: %v", subErr)
					return
				}
				// println("doListFilerEntries2 dir", dir+"/"+entry.Name, "maxKeys", maxKeys-counter, "subCounter", subCounter, "subNextMarker", subNextMarker, "subIsTruncated", subIsTruncated)
				counter += subCounter
				nextMarker = entry.Name + "/" + subNextMarker
				if subIsTruncated {
					isTruncated = true
					return
				}
			} else {
				var isEmpty bool
				if !s3a.option.AllowEmptyFolder {
					if isEmpty, err = s3a.isDirectoryAllEmpty(client, dir, entry.Name); err != nil {
						glog.Errorf("check empty folder %s: %v", dir, err)
					}
				}
				if !isEmpty {
					eachEntryFn(dir, entry)
					counter++
				}
			}
		} else {
			// println("ListEntries", dir, "file:", entry.Name)
			eachEntryFn(dir, entry)
			counter++
		}
	}
	return
}

func getListObjectsV2Args(values url.Values) (prefix, token, startAfter, delimiter string, fetchOwner bool, maxkeys int) {
	prefix = values.Get("prefix")
	token = values.Get("continuation-token")
	startAfter = values.Get("start-after")
	delimiter = values.Get("delimiter")
	if values.Get("max-keys") != "" {
		maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
	} else {
		maxkeys = maxObjectListSizeLimit
	}
	fetchOwner = values.Get("fetch-owner") == "true"
	return
}

func getListObjectsV1Args(values url.Values) (prefix, marker, delimiter string, maxkeys int) {
	prefix = values.Get("prefix")
	marker = values.Get("marker")
	delimiter = values.Get("delimiter")
	if values.Get("max-keys") != "" {
		maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
	} else {
		maxkeys = maxObjectListSizeLimit
	}
	return
}

func (s3a *S3ApiServer) isDirectoryAllEmpty(filerClient filer_pb.SeaweedFilerClient, parentDir, name string) (isEmpty bool, err error) {
	// println("+ isDirectoryAllEmpty", dir, name)
	glog.V(4).Infof("+ isEmpty %s/%s", parentDir, name)
	defer glog.V(4).Infof("- isEmpty %s/%s %v", parentDir, name, isEmpty)
	var fileCounter int
	var subDirs []string
	currentDir := parentDir + "/" + name
	var startFrom string
	var isExhausted bool
	var foundEntry bool
	for fileCounter == 0 && !isExhausted && err == nil {
		err = filer_pb.SeaweedList(filerClient, currentDir, "", func(entry *filer_pb.Entry, isLast bool) error {
			foundEntry = true
			if entry.IsDirectory {
				subDirs = append(subDirs, entry.Name)
			} else {
				fileCounter++
			}
			startFrom = entry.Name
			isExhausted = isExhausted || isLast
			glog.V(4).Infof("    * %s/%s isLast: %t", currentDir, startFrom, isLast)
			return nil
		}, startFrom, false, 8)
		if !foundEntry {
			break
		}
	}

	if err != nil {
		return false, err
	}

	if fileCounter > 0 {
		return false, nil
	}

	for _, subDir := range subDirs {
		isSubEmpty, subErr := s3a.isDirectoryAllEmpty(filerClient, currentDir, subDir)
		if subErr != nil {
			return false, subErr
		}
		if !isSubEmpty {
			return false, nil
		}
	}

	glog.V(1).Infof("deleting empty folder %s", currentDir)
	if err = doDeleteEntry(filerClient, parentDir, name, true, true); err != nil {
		return
	}

	return true, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"strconv"

	"github.com/MJKWoolnough/gopherjs/overlay"
	"github.com/MJKWoolnough/gopherjs/tabs"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"honnef.co/go/js/dom"
)

func maps(c dom.Element) {
	xjs.RemoveChildren(c)
	mapsDiv := xjs.CreateElement("div")
	defer c.AppendChild(mapsDiv)
	list, err := MapList()
	if err != nil {
		xjs.SetInnerText(mapsDiv, err.Error())
		return
	}

	newButton := xjs.CreateElement("input").(*dom.HTMLInputElement)
	newButton.Type = "button"
	newButton.Value = "New Map"
	newButton.AddEventListener("click", false, newMap(c))

	mapsDiv.AppendChild(newButton)

	for _, m := range list {
		sd := xjs.CreateElement("div")
		xjs.SetInnerText(sd, m.Name)
		sd.AddEventListener("click", false, viewMap(m))
		mapsDiv.AppendChild(sd)
	}
	c.AppendChild(mapsDiv)
}

func newMap(c dom.Element) func(dom.Event) {
	return func(dom.Event) {
		f := xjs.CreateElement("div")
		o := overlay.New(f)
		f.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "New Map"))
		f.AppendChild(tabs.MakeTabs([]tabs.Tab{
			{"Create", createMap(o)},
			{"Upload/Download", uploadMap(o)},
			{"Generate", generate},
		}))
		o.OnClose(func() {
			maps(c)
		})
		c.AppendChild(o)
	}
}

var gameModes = [...]string{"Survival", "Creative", "Adventure", "Hardcore", "Spectator"}

func createMap(o overlay.Overlay) func(dom.Element) {
	c := xjs.CreateElement("div")
	nameLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	nameLabel.For = "name"
	xjs.SetInnerText(nameLabel, "Level Name")

	name := xjs.CreateElement("input").(*dom.HTMLInputElement)
	name.Type = "text"
	name.SetID("name")

	gameModeLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	gameModeLabel.For = "gameMode"
	xjs.SetInnerText(gameModeLabel, "Game Mode")

	gameMode := xjs.CreateElement("select").(*dom.HTMLSelectElement)
	for k, v := range gameModes {
		o := xjs.CreateElement("option").(*dom.HTMLOptionElement)
		o.Value = strconv.Itoa(k)
		xjs.SetInnerText(o, v)
		gameMode.AppendChild(o)
	}

	seedLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	seedLabel.For = "seed"
	xjs.SetInnerText(seedLabel, "Level Seed")

	seed := xjs.CreateElement("input").(*dom.HTMLInputElement)
	seed.Type = "text"
	seed.SetID("seed")
	seed.Value = ""

	structuresLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	structuresLabel.For = "structures"
	xjs.SetInnerText(structuresLabel, "Generate Structures")

	structures := xjs.CreateElement("input").(*dom.HTMLInputElement)
	structures.Type = "checkbox"
	structures.Checked = true
	structures.SetID("structures")

	cheatsLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	cheatsLabel.For = "cheats"
	xjs.SetInnerText(cheatsLabel, "Allow Cheats")

	cheats := xjs.CreateElement("input").(*dom.HTMLInputElement)
	cheats.Type = "checkbox"
	cheats.Checked = false
	cheats.SetID("cheats")

	c.AppendChild(nameLabel)
	c.AppendChild(name)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(gameModeLabel)
	c.AppendChild(gameMode)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(seedLabel)
	c.AppendChild(seed)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(structuresLabel)
	c.AppendChild(structures)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(cheatsLabel)
	c.AppendChild(cheats)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(xjs.CreateElement("br"))

	dataParser := func(mode int) func() (DefaultMap, error) {
		return func() (DefaultMap, error) {
			data := DefaultMap{
				Mode: mode,
			}
			var err error
			data.Name = name.Value
			si := gameMode.SelectedIndex
			if si < 0 || si >= len(gameModes) {
				return data, ErrInvalidGameMode
			}
			if seed.Value == "" {
				seed.Value = "0"
			}
			data.Seed, err = strconv.ParseInt(seed.Value, 10, 64)
			if err != nil {
				return data, err
			}
			data.Structures = structures.Checked
			data.Cheats = cheats.Checked
			return data, nil
		}
	}

	c.AppendChild(tabs.MakeTabs([]tabs.Tab{
		{"Default", createMapMode(0, o, dataParser(0))},
		{"Super Flat", createSuperFlatMap(o, dataParser(1))},
		{"Large Biomes", createMapMode(2, o, dataParser(2))},
		{"Amplified", createMapMode(3, o, dataParser(3))},
		{"Customised", createCustomisedMap(o, dataParser(4))},
	}))
	return func(d dom.Element) {
		d.AppendChild(c)
	}
}

var worldTypes = [...]string{
	"The standard minecraft map generation.",
	"A simple generator allowing customised levels of blocks.",
	"The standard minecraft map generation, but tweaked to allow for much larger biomes.",
	"The standard minecraft map generation, but tweaked to stretch the land upwards.",
	"A completely customiseable generator.",
}

func createMapMode(mode int, o overlay.Overlay, dataParser func() (DefaultMap, error)) func(dom.Element) {
	submit := xjs.CreateElement("input").(*dom.HTMLInputElement)
	submit.Type = "button"
	submit.Value = "Create Map"
	submit.AddEventListener("click", false, func(dom.Event) {
		data, err := dataParser()
		if err != nil {
			return
		}
		go func() {
			err = CreateDefaultMap(data)
			if err != nil {
				dom.GetWindow().Alert(err.Error())
			}
			o.Close()
		}()
	})
	return func(c dom.Element) {
		d := xjs.CreateElement("div")
		xjs.SetPreText(d, worldTypes[mode])
		c.AppendChild(d)
		c.AppendChild(xjs.CreateElement("br"))
		c.AppendChild(submit)
	}
}

func createSuperFlatMap(o overlay.Overlay, dataParser func() (DefaultMap, error)) func(dom.Element) {
	d := xjs.CreateElement("div")
	return func(c dom.Element) {
		c.AppendChild(d)
	}
}

func createCustomisedMap(o overlay.Overlay, dataParser func() (DefaultMap, error)) func(dom.Element) {
	d := xjs.CreateElement("div")
	return func(c dom.Element) {
		c.AppendChild(d)
	}
	// Sea Level - 0-255
	// Caves, Strongholds, Villages, Mineshafts, Temples, Ocean Monuments, Ravines
	// Dungeons + Count 1-100
	// Water Lakes + Rarity 1-100
	// Lava Lakes + Rarity 1-100
	// Lava Oceans
	// Biome - All/Choose
	// Biome Size 1-8
	// River Size 1-5
	// Ores -> Dirt/Gravel/Granite/Diorite/Andesite/Coal Ore/Iron Ore/Gold Ore/Redstone Ore/Diamond Ore/Lapis Lazuli Ore ->
	//           Spawn Size - 1-50
	//           Spawn Tries - 0-40
	//           Min-Height - 0-255
	//           Max-Height - 0-255
	// Advanced ->
	//           Main Noise Scale X - 1-5000
	//           Main Noise Scale Y - 1-5000
	//           Main Noise Scale Z - 1-5000
	//           Depth Noise Scale X - 1-2000
	//           Depth Noise Scale Y - 1-2000
	//           Depth Noise Scale Z - 1-2000
	//           Depth Base Size - 1-25
	//           Coordinate Scale - 1-6000
	//           Height Scale - 1-6000
	//           Height Stretch - 0.01-50
	//           Upper Limit Scale - 1-5000
	//           Lower Limit Scale - 1-5000
	//           Biome Depth Weight - 1-20
	//           Biome Depth Offset - 1-20
	//           Biome Scale Weight - 1-20
	//           Biome Scale Offset - 1-20

}

func uploadMap(o overlay.Overlay) func(dom.Element) {
	return func(c dom.Element) {
	}
}

func viewMap(m Map) func(dom.Event) {
	return func(dom.Event) {
		d := xjs.CreateElement("div")
		od := overlay.New(d)
		d.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "Map Details"))

		nameLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
		nameLabel.For = "name"
		xjs.SetInnerText(nameLabel, "Name")
		name := xjs.CreateElement("input").(*dom.HTMLInputElement)
		xjs.SetInnerText(nameLabel, "Name")
		name.Value = m.Name
		name.Type = "text"

		d.AppendChild(nameLabel)
		d.AppendChild(name)

		dom.GetWindow().Document().DocumentElement().AppendChild(od)
	}
}

// Errors
var ErrInvalidGameMode = errors.New("invalid game mode")
<commit_msg>Added client-side server select for map<commit_after>package main

import (
	"errors"
	"strconv"

	"github.com/MJKWoolnough/gopherjs/overlay"
	"github.com/MJKWoolnough/gopherjs/tabs"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"honnef.co/go/js/dom"
)

func maps(c dom.Element) {
	xjs.RemoveChildren(c)
	mapsDiv := xjs.CreateElement("div")
	defer c.AppendChild(mapsDiv)
	list, err := MapList()
	if err != nil {
		xjs.SetInnerText(mapsDiv, err.Error())
		return
	}

	newButton := xjs.CreateElement("input").(*dom.HTMLInputElement)
	newButton.Type = "button"
	newButton.Value = "New Map"
	newButton.AddEventListener("click", false, newMap(c))

	mapsDiv.AppendChild(newButton)

	for _, m := range list {
		sd := xjs.CreateElement("div")
		xjs.SetInnerText(sd, m.Name)
		sd.AddEventListener("click", false, viewMap(m))
		mapsDiv.AppendChild(sd)
	}
	c.AppendChild(mapsDiv)
}

func newMap(c dom.Element) func(dom.Event) {
	return func(dom.Event) {
		f := xjs.CreateElement("div")
		o := overlay.New(f)
		f.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "New Map"))
		f.AppendChild(tabs.MakeTabs([]tabs.Tab{
			{"Create", createMap(o)},
			{"Upload/Download", uploadMap(o)},
			{"Generate", generate},
		}))
		o.OnClose(func() {
			maps(c)
		})
		c.AppendChild(o)
	}
}

var gameModes = [...]string{"Survival", "Creative", "Adventure", "Hardcore", "Spectator"}

func createMap(o overlay.Overlay) func(dom.Element) {
	c := xjs.CreateElement("div")
	nameLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	nameLabel.For = "name"
	xjs.SetInnerText(nameLabel, "Level Name")

	name := xjs.CreateElement("input").(*dom.HTMLInputElement)
	name.Type = "text"
	name.SetID("name")

	gameModeLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	gameModeLabel.For = "gameMode"
	xjs.SetInnerText(gameModeLabel, "Game Mode")

	gameMode := xjs.CreateElement("select").(*dom.HTMLSelectElement)
	for k, v := range gameModes {
		o := xjs.CreateElement("option").(*dom.HTMLOptionElement)
		o.Value = strconv.Itoa(k)
		xjs.SetInnerText(o, v)
		gameMode.AppendChild(o)
	}

	seedLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	seedLabel.For = "seed"
	xjs.SetInnerText(seedLabel, "Level Seed")

	seed := xjs.CreateElement("input").(*dom.HTMLInputElement)
	seed.Type = "text"
	seed.SetID("seed")
	seed.Value = ""

	structuresLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	structuresLabel.For = "structures"
	xjs.SetInnerText(structuresLabel, "Generate Structures")

	structures := xjs.CreateElement("input").(*dom.HTMLInputElement)
	structures.Type = "checkbox"
	structures.Checked = true
	structures.SetID("structures")

	cheatsLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
	cheatsLabel.For = "cheats"
	xjs.SetInnerText(cheatsLabel, "Allow Cheats")

	cheats := xjs.CreateElement("input").(*dom.HTMLInputElement)
	cheats.Type = "checkbox"
	cheats.Checked = false
	cheats.SetID("cheats")

	c.AppendChild(nameLabel)
	c.AppendChild(name)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(gameModeLabel)
	c.AppendChild(gameMode)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(seedLabel)
	c.AppendChild(seed)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(structuresLabel)
	c.AppendChild(structures)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(cheatsLabel)
	c.AppendChild(cheats)
	c.AppendChild(xjs.CreateElement("br"))
	c.AppendChild(xjs.CreateElement("br"))

	dataParser := func(mode int) func() (DefaultMap, error) {
		return func() (DefaultMap, error) {
			data := DefaultMap{
				Mode: mode,
			}
			var err error
			data.Name = name.Value
			si := gameMode.SelectedIndex
			if si < 0 || si >= len(gameModes) {
				return data, ErrInvalidGameMode
			}
			if seed.Value == "" {
				seed.Value = "0"
			}
			data.Seed, err = strconv.ParseInt(seed.Value, 10, 64)
			if err != nil {
				return data, err
			}
			data.Structures = structures.Checked
			data.Cheats = cheats.Checked
			return data, nil
		}
	}

	c.AppendChild(tabs.MakeTabs([]tabs.Tab{
		{"Default", createMapMode(0, o, dataParser(0))},
		{"Super Flat", createSuperFlatMap(o, dataParser(1))},
		{"Large Biomes", createMapMode(2, o, dataParser(2))},
		{"Amplified", createMapMode(3, o, dataParser(3))},
		{"Customised", createCustomisedMap(o, dataParser(4))},
	}))
	return func(d dom.Element) {
		d.AppendChild(c)
	}
}

var worldTypes = [...]string{
	"The standard minecraft map generation.",
	"A simple generator allowing customised levels of blocks.",
	"The standard minecraft map generation, but tweaked to allow for much larger biomes.",
	"The standard minecraft map generation, but tweaked to stretch the land upwards.",
	"A completely customiseable generator.",
}

func createMapMode(mode int, o overlay.Overlay, dataParser func() (DefaultMap, error)) func(dom.Element) {
	submit := xjs.CreateElement("input").(*dom.HTMLInputElement)
	submit.Type = "button"
	submit.Value = "Create Map"
	submit.AddEventListener("click", false, func(dom.Event) {
		data, err := dataParser()
		if err != nil {
			return
		}
		go func() {
			err = CreateDefaultMap(data)
			if err != nil {
				dom.GetWindow().Alert(err.Error())
			}
			o.Close()
		}()
	})
	return func(c dom.Element) {
		d := xjs.CreateElement("div")
		xjs.SetPreText(d, worldTypes[mode])
		c.AppendChild(d)
		c.AppendChild(xjs.CreateElement("br"))
		c.AppendChild(submit)
	}
}

func createSuperFlatMap(o overlay.Overlay, dataParser func() (DefaultMap, error)) func(dom.Element) {
	d := xjs.CreateElement("div")
	return func(c dom.Element) {
		c.AppendChild(d)
	}
}

func createCustomisedMap(o overlay.Overlay, dataParser func() (DefaultMap, error)) func(dom.Element) {
	d := xjs.CreateElement("div")
	return func(c dom.Element) {
		c.AppendChild(d)
	}
	// Sea Level - 0-255
	// Caves, Strongholds, Villages, Mineshafts, Temples, Ocean Monuments, Ravines
	// Dungeons + Count 1-100
	// Water Lakes + Rarity 1-100
	// Lava Lakes + Rarity 1-100
	// Lava Oceans
	// Biome - All/Choose
	// Biome Size 1-8
	// River Size 1-5
	// Ores -> Dirt/Gravel/Granite/Diorite/Andesite/Coal Ore/Iron Ore/Gold Ore/Redstone Ore/Diamond Ore/Lapis Lazuli Ore ->
	//           Spawn Size - 1-50
	//           Spawn Tries - 0-40
	//           Min-Height - 0-255
	//           Max-Height - 0-255
	// Advanced ->
	//           Main Noise Scale X - 1-5000
	//           Main Noise Scale Y - 1-5000
	//           Main Noise Scale Z - 1-5000
	//           Depth Noise Scale X - 1-2000
	//           Depth Noise Scale Y - 1-2000
	//           Depth Noise Scale Z - 1-2000
	//           Depth Base Size - 1-25
	//           Coordinate Scale - 1-6000
	//           Height Scale - 1-6000
	//           Height Stretch - 0.01-50
	//           Upper Limit Scale - 1-5000
	//           Lower Limit Scale - 1-5000
	//           Biome Depth Weight - 1-20
	//           Biome Depth Offset - 1-20
	//           Biome Scale Weight - 1-20
	//           Biome Scale Offset - 1-20

}

func uploadMap(o overlay.Overlay) func(dom.Element) {
	return func(c dom.Element) {
	}
}

func viewMap(m Map) func(dom.Event) {
	return func(dom.Event) {
		servers, err := ServerList()
		if err != nil {
			return
		}
		d := xjs.CreateElement("div")
		od := overlay.New(d)
		d.AppendChild(xjs.SetInnerText(xjs.CreateElement("h1"), "Map Details"))

		nameLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
		nameLabel.For = "name"
		xjs.SetInnerText(nameLabel, "Name")
		name := xjs.CreateElement("input").(*dom.HTMLInputElement)
		xjs.SetInnerText(nameLabel, "Name")
		name.SetID("name")
		name.Value = m.Name
		name.Type = "text"

		serverLabel := xjs.CreateElement("label").(*dom.HTMLLabelElement)
		serverLabel.For = "server"
		xjs.SetInnerText(serverLabel, "Server")
		serverEditable := true
		var (
			selServer Server
			server    dom.Element
		)
		if m.Server != -1 {
			for _, s := range servers {
				if s.ID == m.Server {
					selServer = s
					serverEditable = !s.IsRunning()
					break
				}
			}
		}
		if serverEditable {
			sel := xjs.CreateElement("select").(*dom.HTMLSelectElement)
			sel.SetID("server")
			for _, s := range servers {
				o := xjs.CreateElement("option").(*dom.HTMLOptionElement)
				o.Value = strconv.Itoa(s.ID)
				xjs.SetInnerText(o, s.Name)
				if s.ID == m.Server {
					o.Selected = true
				}
				sel.AppendChild(o)
			}
			server = sel
		} else {
			server.AppendChild(xjs.SetInnerText(xjs.CreateElement("div"), selServer.Name))
		}

		d.AppendChild(nameLabel)
		d.AppendChild(name)
		d.AppendChild(xjs.CreateElement("br"))
		d.AppendChild(serverLabel)
		d.AppendChild(server)

		dom.GetWindow().Document().DocumentElement().AppendChild(od)
	}
}

// Errors
var ErrInvalidGameMode = errors.New("invalid game mode")
<|endoftext|>


<commit_before>package xmmsclient

import (
	"bytes"
	"encoding/binary"
	"errors"
	"math"
)

type listConsumer func(value XmmsValue)

func deserializeInt(buffer *bytes.Buffer) (value XmmsInt, err error) {
	err = binary.Read(buffer, binary.BigEndian, &value)
	return
}

func deserializeFloat(buffer *bytes.Buffer) (value XmmsFloat, err error) {
	var mantissaInt int32
	var exponent int32
	var mantissa float64

	err = binary.Read(buffer, binary.BigEndian, &mantissaInt)
	if err != nil {
		return
	}

	err = binary.Read(buffer, binary.BigEndian, &exponent)
	if err != nil {
		return
	}

	if mantissaInt > 0 {
		mantissa = float64(mantissaInt) / float64(math.MaxInt32)
	} else {
		mantissa = float64(mantissaInt) / float64(math.Abs(math.MinInt32))
	}

	value = XmmsFloat(math.Ldexp(mantissa, int(exponent)))

	return
}

func deserializeRawString(buffer *bytes.Buffer) (value string, err error) {
	var length uint32
	err = binary.Read(buffer, binary.BigEndian, &length)
	if err != nil {
		return
	}
	var data = make([]byte, length)
	err = binary.Read(buffer, binary.BigEndian, &data)
	if err != nil {
		return
	}
	value = string(data[:length-1])

	return
}

func deserializeString(buffer *bytes.Buffer) (value XmmsString, err error) {
	data, err := deserializeRawString(buffer)
	if err == nil {
		value = XmmsString(data)
	}
	return
}

func deserializeError(buffer *bytes.Buffer) (value XmmsError, err error) {
	data, err := deserializeRawString(buffer)
	if err == nil {
		value = XmmsError(data)
	}
	return
}

func deserializeAnyList(buffer *bytes.Buffer, consumer listConsumer) error {
	var restrict uint32
	err := binary.Read(buffer, binary.BigEndian, &restrict) // TODO: respect restrict
	if err != nil {
		return err
	}
	var length uint32
	err = binary.Read(buffer, binary.BigEndian, &length)
	if err != nil {
		return err
	}
	if restrict != TypeNone {
		for i := uint32(0); i < length; i++ {
			entry, err := deserializeXmmsValueOfType(restrict, buffer)
			if err != nil {
				return err
			}
			consumer(entry)
		}
	} else {
		for i := uint32(0); i < length; i++ {
			entry, err := deserializeXmmsValue(buffer)
			if err != nil {
				return err
			}
			consumer(entry)
		}
	}
	return nil
}

func deserializeList(buffer *bytes.Buffer) (value XmmsList, err error) {
	list := XmmsList{}
	err = deserializeAnyList(buffer, func(value XmmsValue) {
		list = append(list, value)
	})
	value = list
	return
}

func deserializeDict(buffer *bytes.Buffer) (value XmmsDict, err error) {
	var length uint32
	err = binary.Read(buffer, binary.BigEndian, &length)
	if err != nil {
		return
	}
	var dict = XmmsDict{}
	for i := uint32(0); i < length; i++ {
		var entry XmmsValue
		var key string

		key, err = deserializeRawString(buffer)
		if err != nil {
			return
		}

		entry, err = deserializeXmmsValue(buffer)
		if err != nil {
			return
		}

		dict[string(key)] = entry
	}
	value = dict
	return
}

func deserializeColl(buffer *bytes.Buffer) (result XmmsColl, err error) {
	err = binary.Read(buffer, binary.BigEndian, &result.Type)
	if err != nil {
		return
	}

	result.Attributes, err = deserializeDict(buffer)
	if err != nil {
		return
	}

	err = deserializeAnyList(buffer, func(raw XmmsValue) {
		if value, ok := raw.(XmmsInt); ok {
			result.IdList = append(result.IdList, int(value))
		}
	})
	if err != nil {
		return
	}

	err = deserializeAnyList(buffer, func(raw XmmsValue) {
		if value, ok := raw.(XmmsColl); ok {
			result.Operands = append(result.Operands, value)
		}
	})

	return
}

func deserializeXmmsValueOfType(valueType uint32, buffer *bytes.Buffer) (result XmmsValue, err error) {
	switch valueType {
	case TypeInt64:
		result, err = deserializeInt(buffer)
	case TypeFloat:
		result, err = deserializeFloat(buffer)
	case TypeError:
		result, err = deserializeError(buffer)
	case TypeString:
		result, err = deserializeString(buffer)
	case TypeList:
		result, err = deserializeList(buffer)
	case TypeDict:
		result, err = deserializeDict(buffer)
	case TypeColl:
		result, err = deserializeColl(buffer)
	}
	return
}

func deserializeXmmsValue(buffer *bytes.Buffer) (XmmsValue, error) {
	var valueType uint32
	err := binary.Read(buffer, binary.BigEndian, &valueType)
	if err != nil {
		return nil, err
	}
	return deserializeXmmsValueOfType(valueType, buffer)
}

func tryDeserialize(buffer *bytes.Buffer) (XmmsValue, error) {
	value, err := deserializeXmmsValue(buffer)
	if err != nil {
		return nil, err
	}

	errorMessage, ok := value.(XmmsError)
	if ok {
		return nil, errors.New(string(errorMessage))
	}

	return value, nil
}
<commit_msg>Add support for deserializing typed lists.<commit_after>package xmmsclient

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math"
)

type listConsumer func(value XmmsValue)

func deserializeInt(buffer *bytes.Buffer) (value XmmsInt, err error) {
	err = binary.Read(buffer, binary.BigEndian, &value)
	return
}

func deserializeFloat(buffer *bytes.Buffer) (value XmmsFloat, err error) {
	var mantissaInt int32
	var exponent int32
	var mantissa float64

	err = binary.Read(buffer, binary.BigEndian, &mantissaInt)
	if err != nil {
		return
	}

	err = binary.Read(buffer, binary.BigEndian, &exponent)
	if err != nil {
		return
	}

	if mantissaInt > 0 {
		mantissa = float64(mantissaInt) / float64(math.MaxInt32)
	} else {
		mantissa = float64(mantissaInt) / float64(math.Abs(math.MinInt32))
	}

	value = XmmsFloat(math.Ldexp(mantissa, int(exponent)))

	return
}

func deserializeRawString(buffer *bytes.Buffer) (value string, err error) {
	var length uint32
	err = binary.Read(buffer, binary.BigEndian, &length)
	if err != nil {
		return
	}
	var data = make([]byte, length)
	err = binary.Read(buffer, binary.BigEndian, &data)
	if err != nil {
		return
	}
	value = string(data[:length-1])

	return
}

func deserializeString(buffer *bytes.Buffer) (value XmmsString, err error) {
	data, err := deserializeRawString(buffer)
	if err == nil {
		value = XmmsString(data)
	}
	return
}

func deserializeError(buffer *bytes.Buffer) (value XmmsError, err error) {
	data, err := deserializeRawString(buffer)
	if err == nil {
		value = XmmsError(data)
	}
	return
}

func deserializeAnyList(buffer *bytes.Buffer, consumer listConsumer) error {
	var restrict uint32
	err := binary.Read(buffer, binary.BigEndian, &restrict) // TODO: respect restrict
	if err != nil {
		return err
	}
	var length uint32
	err = binary.Read(buffer, binary.BigEndian, &length)
	if err != nil {
		return err
	}
	if restrict != TypeNone {
		for i := uint32(0); i < length; i++ {
			entry, err := deserializeXmmsValueOfType(restrict, buffer)
			if err != nil {
				return err
			}
			consumer(entry)
		}
	} else {
		for i := uint32(0); i < length; i++ {
			entry, err := deserializeXmmsValue(buffer)
			if err != nil {
				return err
			}
			consumer(entry)
		}
	}
	return nil
}

func deserializeList(buffer *bytes.Buffer) (value XmmsList, err error) {
	list := XmmsList{}
	err = deserializeAnyList(buffer, func(value XmmsValue) {
		list = append(list, value)
	})
	value = list
	return
}

func deserializeDict(buffer *bytes.Buffer) (value XmmsDict, err error) {
	var length uint32
	err = binary.Read(buffer, binary.BigEndian, &length)
	if err != nil {
		return
	}
	var dict = XmmsDict{}
	for i := uint32(0); i < length; i++ {
		var entry XmmsValue
		var key string

		key, err = deserializeRawString(buffer)
		if err != nil {
			return
		}

		entry, err = deserializeXmmsValue(buffer)
		if err != nil {
			return
		}

		dict[string(key)] = entry
	}
	value = dict
	return
}

func deserializeColl(buffer *bytes.Buffer) (result XmmsColl, err error) {
	err = binary.Read(buffer, binary.BigEndian, &result.Type)
	if err != nil {
		return
	}

	result.Attributes, err = deserializeDict(buffer)
	if err != nil {
		return
	}

	err = deserializeAnyList(buffer, func(raw XmmsValue) {
		if value, ok := raw.(XmmsInt); ok {
			result.IdList = append(result.IdList, int(value))
		}
	})
	if err != nil {
		return
	}

	err = deserializeAnyList(buffer, func(raw XmmsValue) {
		if value, ok := raw.(XmmsColl); ok {
			result.Operands = append(result.Operands, value)
		}
	})

	return
}

func deserializeXmmsValueOfType(valueType uint32, buffer *bytes.Buffer) (result XmmsValue, err error) {
	switch valueType {
	case TypeInt64:
		result, err = deserializeInt(buffer)
	case TypeFloat:
		result, err = deserializeFloat(buffer)
	case TypeError:
		result, err = deserializeError(buffer)
	case TypeString:
		result, err = deserializeString(buffer)
	case TypeList:
		result, err = deserializeList(buffer)
	case TypeDict:
		result, err = deserializeDict(buffer)
	case TypeColl:
		result, err = deserializeColl(buffer)
	}
	return
}

func deserializeXmmsValue(buffer *bytes.Buffer) (XmmsValue, error) {
	var valueType uint32
	err := binary.Read(buffer, binary.BigEndian, &valueType)
	if err != nil {
		return nil, err
	}
	return deserializeXmmsValueOfType(valueType, buffer)
}

func tryDeserialize(buffer *bytes.Buffer) (XmmsValue, error) {
	value, err := deserializeXmmsValue(buffer)
	if err != nil {
		return nil, err
	}

	errorMessage, ok := value.(XmmsError)
	if ok {
		return nil, errors.New(string(errorMessage))
	}

	return value, nil
}

func tryDeserializeList(buffer *bytes.Buffer, consumer listConsumer) error {
	var valueType uint32

	err := binary.Read(buffer, binary.BigEndian, &valueType)
	if err != nil {
		return err
	}

	switch valueType {
	case TypeError:
		value, err := deserializeError(buffer)
		if err != nil {
			return err
		}
		return errors.New(string(value))
	case TypeList:
		return deserializeAnyList(buffer, consumer)
	default:
		return errors.New(fmt.Sprintf("Trying to parse non-list as list (%v)", valueType))
	}
}

func tryDeserializeIntList(buffer *bytes.Buffer) ([]int, error) {
	var list []int
	err := tryDeserializeList(buffer, func(raw XmmsValue) {
		if value, ok := raw.(XmmsInt); ok {
			list = append(list, int(value))
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}

func tryDeserializeStringList(buffer *bytes.Buffer) ([]string, error) {
	var list []string
	err := tryDeserializeList(buffer, func(raw XmmsValue) {
		if value, ok := raw.(XmmsString); ok {
			list = append(list, string(value))
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}

func tryDeserializeDictList(buffer *bytes.Buffer) ([]XmmsDict, error) {
	var list []XmmsDict
	err := tryDeserializeList(buffer, func(raw XmmsValue) {
		if value, ok := raw.(XmmsDict); ok {
			list = append(list, value)
		}
	})
	if err != nil {
		return nil, err
	}
	return list, nil
}
<|endoftext|>


<commit_before>package pongo2

import (
	"github.com/astaxie/beego"
	p2 "github.com/flosch/pongo2"
)

var xsrfTemplate = p2.Must(p2.FromString(`<input type="hidden" name="_xsrf" value="{{ _xsrf }}">`))

type tagXSRFTokenNode struct{}

func (node *tagXSRFTokenNode) Execute(ctx *p2.ExecutionContext) (string, error) {
	if !beego.EnableXSRF {
		return "", nil
	}

	xsrftoken := ctx.Public["_xsrf"]
	return xsrfTemplate.Execute(p2.Context{"_xsrf": xsrftoken})
}

// tagXSRFParser implements a {% xsrftoken %} tag that inserts <input type="hidden" name="_xsrf" value="{{ _xsrf }}">
// just like Django's {% csrftoken %}. Note that we follow Beego's convention by using "XSRF" and not "CSRF".
func tagXSRFParser(doc *p2.Parser, start *p2.Token, arguments *p2.Parser) (p2.INodeTag, error) {
	return &tagXSRFTokenNode{}, nil
}

func init() {
	p2.RegisterTag("xsrftoken", tagXSRFParser)
}
<commit_msg>Makes the xsrftoken-tag compatible to an APIchange<commit_after>package pongo2

import (
	"bytes"

	"github.com/astaxie/beego"
	p2 "github.com/flosch/pongo2"
)

var xsrfTemplate = p2.Must(p2.FromString(`<input type="hidden" name="_xsrf" value="{{ _xsrf }}">`))

type tagXSRFTokenNode struct{}

func (node *tagXSRFTokenNode) Execute(ctx *p2.ExecutionContext, buffer *bytes.Buffer) error {
	if !beego.EnableXSRF {
		return nil
	}

	xsrftoken := ctx.Public["_xsrf"]
	return xsrfTemplate.ExecuteWriter(p2.Context{"_xsrf": xsrftoken}, buffer)
}

// tagXSRFParser implements a {% xsrftoken %} tag that inserts <input type="hidden" name="_xsrf" value="{{ _xsrf }}">
// just like Django's {% csrftoken %}. Note that we follow Beego's convention by using "XSRF" and not "CSRF".
func tagXSRFParser(doc *p2.Parser, start *p2.Token, arguments *p2.Parser) (p2.INodeTag, error) {
	return &tagXSRFTokenNode{}, nil
}

func init() {
	p2.RegisterTag("xsrftoken", tagXSRFParser)
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package statushistorypruner_test

import (
	"time"

	"github.com/juju/errors"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/state"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker"
	"github.com/juju/juju/worker/statushistorypruner"
)

type mockTimer struct {
	period time.Duration
	c      chan time.Time
}

func (t *mockTimer) Reset(d time.Duration) bool {
	t.period = d
	return true
}

func (t *mockTimer) CountDown() <-chan time.Time {
	return t.c
}

func (t *mockTimer) fire() error {
	select {
	case t.c <- time.Time{}:
	case <-time.After(coretesting.LongWait):
		return errors.New("timed out waiting for pruner to run")
	}
	return nil
}

func newMockTimer(d time.Duration) worker.PeriodicTimer {
	return &mockTimer{period: d,
		c: make(chan time.Time),
	}
}

var _ = gc.Suite(&statusHistoryPrunerSuite{})

type statusHistoryPrunerSuite struct {
	coretesting.BaseSuite
}

func (s *statusHistoryPrunerSuite) TestWorker(c *gc.C) {
	var passedMaxLogs int
	fakePruner := func(_ *state.State, maxLogs int) error {
		passedMaxLogs = maxLogs
		return nil
	}
	params := statushistorypruner.HistoryPrunerParams{
		MaxLogsPerState: 3,
		PruneInterval:   coretesting.ShortWait,
	}
	fakeTimer := newMockTimer(coretesting.LongWait)

	fakeTimerFunc := func(d time.Duration) worker.PeriodicTimer {
		// construction of timer should be with 0 because we intend it to
		// run once before waiting.
		c.Assert(d, gc.Equals, 0*time.Nanosecond)
		return fakeTimer
	}
	pruner := statushistorypruner.NewPruneWorker(
		&state.State{},
		&params,
		fakeTimerFunc,
		fakePruner,
	)
	s.AddCleanup(func(*gc.C) {
		pruner.Kill()
		c.Assert(pruner.Wait(), jc.ErrorIsNil)
	})
	err := fakeTimer.(*mockTimer).fire()
	c.Check(err, jc.ErrorIsNil)
	c.Assert(passedMaxLogs, gc.Equals, 3)
	// Reset will have been called with the actual PruneInterval
	c.Assert(fakeTimer.(*mockTimer).period, gc.Equals, coretesting.ShortWait)
}
<commit_msg>Removed race in history pruner tests.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package statushistorypruner_test

import (
	"time"

	"github.com/juju/errors"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/state"
	coretesting "github.com/juju/juju/testing"
	"github.com/juju/juju/worker"
	"github.com/juju/juju/worker/statushistorypruner"
)

type mockTimer struct {
	period chan time.Duration
	c      chan time.Time
}

func (t *mockTimer) Reset(d time.Duration) bool {
	select {
	case t.period <- d:
	case <-time.After(coretesting.LongWait):
		panic("timed out waiting for timer to reset")
	}
	return true
}

func (t *mockTimer) CountDown() <-chan time.Time {
	return t.c
}

func (t *mockTimer) fire() error {
	select {
	case t.c <- time.Time{}:
	case <-time.After(coretesting.LongWait):
		return errors.New("timed out waiting for pruner to run")
	}
	return nil
}

func newMockTimer(d time.Duration) worker.PeriodicTimer {
	return &mockTimer{period: make(chan time.Duration, 1),
		c: make(chan time.Time),
	}
}

var _ = gc.Suite(&statusHistoryPrunerSuite{})

type statusHistoryPrunerSuite struct {
	coretesting.BaseSuite
}

func (s *statusHistoryPrunerSuite) TestWorker(c *gc.C) {
	var passedMaxLogs chan int
	passedMaxLogs = make(chan int, 1)
	fakePruner := func(_ *state.State, maxLogs int) error {
		passedMaxLogs <- maxLogs
		return nil
	}
	params := statushistorypruner.HistoryPrunerParams{
		MaxLogsPerState: 3,
		PruneInterval:   coretesting.ShortWait,
	}
	fakeTimer := newMockTimer(coretesting.LongWait)

	fakeTimerFunc := func(d time.Duration) worker.PeriodicTimer {
		// construction of timer should be with 0 because we intend it to
		// run once before waiting.
		c.Assert(d, gc.Equals, 0*time.Nanosecond)
		return fakeTimer
	}
	pruner := statushistorypruner.NewPruneWorker(
		&state.State{},
		&params,
		fakeTimerFunc,
		fakePruner,
	)
	s.AddCleanup(func(*gc.C) {
		pruner.Kill()
		c.Assert(pruner.Wait(), jc.ErrorIsNil)
	})
	err := fakeTimer.(*mockTimer).fire()
	c.Check(err, jc.ErrorIsNil)
	var passedLogs int
	select {
	case passedLogs = <-passedMaxLogs:
	case <-time.After(coretesting.LongWait):
		c.Fatal("timed out waiting for passed logs to pruner")
	}
	c.Assert(passedLogs, gc.Equals, 3)
	// Reset will have been called with the actual PruneInterval
	var period time.Duration
	select {
	case period = <-fakeTimer.(*mockTimer).period:
	case <-time.After(coretesting.LongWait):
		c.Fatal("timed out waiting for period reset by pruner")
	}
	c.Assert(period, gc.Equals, coretesting.ShortWait)
}
<|endoftext|>


<commit_before>//
// Copyright 2015 flannel 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 flannel

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sync"

	"github.com/coreos/flannel/backend"
	"github.com/coreos/flannel/network"
	"github.com/coreos/flannel/pkg/ip"
	"github.com/coreos/flannel/subnet/kube"
	"golang.org/x/net/context"
	log "k8s.io/klog"

	// Backends need to be imported for their init() to get executed and them to register
	_ "github.com/coreos/flannel/backend/vxlan"
)

const (
	subnetFile = "/run/flannel/subnet.env"
)

func flannel(ctx context.Context, flannelIface *net.Interface, flannelConf, kubeConfigFile string) error {
	extIface, err := LookupExtIface(flannelIface)
	if err != nil {
		return err
	}

	sm, err := kube.NewSubnetManager("", flannelConf, kubeConfigFile, "flannel.alpha.coreos.com")
	if err != nil {
		return err
	}

	config, err := sm.GetNetworkConfig(ctx)
	if err != nil {
		return err
	}

	// Create a backend manager then use it to create the backend and register the network with it.
	bm := backend.NewManager(ctx, sm, extIface)

	be, err := bm.GetBackend(config.BackendType)
	if err != nil {
		return err
	}

	bn, err := be.RegisterNetwork(ctx, sync.WaitGroup{}, config)
	if err != nil {
		return err
	}

	go network.SetupAndEnsureIPTables(network.MasqRules(config.Network, bn.Lease()), 60)
	go network.SetupAndEnsureIPTables(network.ForwardRules(config.Network.String()), 50)

	if err := WriteSubnetFile(subnetFile, config.Network, true, bn); err != nil {
		// Continue, even though it failed.
		log.Warningf("Failed to write subnet file: %s", err)
	} else {
		log.Infof("Wrote subnet file to %s", subnetFile)
	}

	// Start "Running" the backend network. This will block until the context is done so run in another goroutine.
	log.Info("Running backend.")
	bn.Run(ctx)
	return nil
}

func LookupExtIface(iface *net.Interface) (*backend.ExternalInterface, error) {
	var ifaceAddr net.IP
	var err error

	if iface == nil {
		log.Info("Determining IP address of default interface")
		if iface, err = ip.GetDefaultGatewayIface(); err != nil {
			return nil, fmt.Errorf("failed to get default interface: %s", err)
		}
	} else {
		log.Info("Determining IP address of specified interface: ", iface.Name)
	}

	ifaceAddr, err = ip.GetIfaceIP4Addr(iface)
	if err != nil {
		return nil, fmt.Errorf("failed to find IPv4 address for interface %s", iface.Name)
	}

	log.Infof("Using interface with name %s and address %s", iface.Name, ifaceAddr)

	if iface.MTU == 0 {
		return nil, fmt.Errorf("failed to determine MTU for %s interface", ifaceAddr)
	}

	return &backend.ExternalInterface{
		Iface:     iface,
		IfaceAddr: ifaceAddr,
		ExtAddr:   ifaceAddr,
	}, nil
}

func WriteSubnetFile(path string, nw ip.IP4Net, ipMasq bool, bn backend.Network) error {
	dir, name := filepath.Split(path)
	os.MkdirAll(dir, 0755)

	tempFile := filepath.Join(dir, "."+name)
	f, err := os.Create(tempFile)
	if err != nil {
		return err
	}

	// Write out the first usable IP by incrementing
	// sn.IP by one
	sn := bn.Lease().Subnet
	sn.IP++

	fmt.Fprintf(f, "FLANNEL_NETWORK=%s\n", nw)
	fmt.Fprintf(f, "FLANNEL_SUBNET=%s\n", sn)
	fmt.Fprintf(f, "FLANNEL_MTU=%d\n", bn.MTU())
	_, err = fmt.Fprintf(f, "FLANNEL_IPMASQ=%v\n", ipMasq)
	f.Close()
	if err != nil {
		return err
	}

	// rename(2) the temporary file to the desired location so that it becomes
	// atomically visible with the contents
	return os.Rename(tempFile, path)
	//TODO - is this safe? What if it's not on the same FS?
}
<commit_msg>Enable extension and ipsec flannel backends<commit_after>//
// Copyright 2015 flannel 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 flannel

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"sync"

	"github.com/coreos/flannel/backend"
	"github.com/coreos/flannel/network"
	"github.com/coreos/flannel/pkg/ip"
	"github.com/coreos/flannel/subnet/kube"
	"golang.org/x/net/context"
	log "k8s.io/klog"

	// Backends need to be imported for their init() to get executed and them to register
	_ "github.com/coreos/flannel/backend/extension"
	_ "github.com/coreos/flannel/backend/ipsec"
	_ "github.com/coreos/flannel/backend/vxlan"
)

const (
	subnetFile = "/run/flannel/subnet.env"
)

func flannel(ctx context.Context, flannelIface *net.Interface, flannelConf, kubeConfigFile string) error {
	extIface, err := LookupExtIface(flannelIface)
	if err != nil {
		return err
	}

	sm, err := kube.NewSubnetManager("", flannelConf, kubeConfigFile, "flannel.alpha.coreos.com")
	if err != nil {
		return err
	}

	config, err := sm.GetNetworkConfig(ctx)
	if err != nil {
		return err
	}

	// Create a backend manager then use it to create the backend and register the network with it.
	bm := backend.NewManager(ctx, sm, extIface)

	be, err := bm.GetBackend(config.BackendType)
	if err != nil {
		return err
	}

	bn, err := be.RegisterNetwork(ctx, sync.WaitGroup{}, config)
	if err != nil {
		return err
	}

	go network.SetupAndEnsureIPTables(network.MasqRules(config.Network, bn.Lease()), 60)
	go network.SetupAndEnsureIPTables(network.ForwardRules(config.Network.String()), 50)

	if err := WriteSubnetFile(subnetFile, config.Network, true, bn); err != nil {
		// Continue, even though it failed.
		log.Warningf("Failed to write subnet file: %s", err)
	} else {
		log.Infof("Wrote subnet file to %s", subnetFile)
	}

	// Start "Running" the backend network. This will block until the context is done so run in another goroutine.
	log.Info("Running backend.")
	bn.Run(ctx)
	return nil
}

func LookupExtIface(iface *net.Interface) (*backend.ExternalInterface, error) {
	var ifaceAddr net.IP
	var err error

	if iface == nil {
		log.Info("Determining IP address of default interface")
		if iface, err = ip.GetDefaultGatewayIface(); err != nil {
			return nil, fmt.Errorf("failed to get default interface: %s", err)
		}
	} else {
		log.Info("Determining IP address of specified interface: ", iface.Name)
	}

	ifaceAddr, err = ip.GetIfaceIP4Addr(iface)
	if err != nil {
		return nil, fmt.Errorf("failed to find IPv4 address for interface %s", iface.Name)
	}

	log.Infof("Using interface with name %s and address %s", iface.Name, ifaceAddr)

	if iface.MTU == 0 {
		return nil, fmt.Errorf("failed to determine MTU for %s interface", ifaceAddr)
	}

	return &backend.ExternalInterface{
		Iface:     iface,
		IfaceAddr: ifaceAddr,
		ExtAddr:   ifaceAddr,
	}, nil
}

func WriteSubnetFile(path string, nw ip.IP4Net, ipMasq bool, bn backend.Network) error {
	dir, name := filepath.Split(path)
	os.MkdirAll(dir, 0755)

	tempFile := filepath.Join(dir, "."+name)
	f, err := os.Create(tempFile)
	if err != nil {
		return err
	}

	// Write out the first usable IP by incrementing
	// sn.IP by one
	sn := bn.Lease().Subnet
	sn.IP++

	fmt.Fprintf(f, "FLANNEL_NETWORK=%s\n", nw)
	fmt.Fprintf(f, "FLANNEL_SUBNET=%s\n", sn)
	fmt.Fprintf(f, "FLANNEL_MTU=%d\n", bn.MTU())
	_, err = fmt.Fprintf(f, "FLANNEL_IPMASQ=%v\n", ipMasq)
	f.Close()
	if err != nil {
		return err
	}

	// rename(2) the temporary file to the desired location so that it becomes
	// atomically visible with the contents
	return os.Rename(tempFile, path)
	//TODO - is this safe? What if it's not on the same FS?
}
<|endoftext|>