<commit_before>package grim

import (
	"testing"
	"os"
	"io/ioutil"
	"strings"
	"errors"
	"fmt"
)

// Copyright 2015 MediaMath <http://www.mediamath.com>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var resultPath = "./"

type testBuilder struct {
	workspaceErr    error
	workspaceResult string
	buildScriptErr  error
	buildScriptPath string
	buildErr        error
	buildResult *executeResult
}

func (tb *testBuilder) PrepareWorkspace() (string, error) {
	return tb.workspaceResult, tb.workspaceErr
}
func (tb *testBuilder) FindBuildScript(workspacePath string) (string, error) {
	return tb.buildScriptPath, tb.buildScriptErr
}
func (tb *testBuilder) RunBuildScript(workspacePath, buildScript string, outputChan chan string) (*executeResult, error) {
	return tb.buildResult, tb.buildErr
}

func TestOnBuildStatusFileError(t *testing.T){
	tb := &testBuilder{workspaceErr: errors.New(""), workspaceResult: "@$@$"}
	defer os.RemoveAll(resultPath + "/build.txt")
	grimBuild(tb, resultPath)

	_,err := ioutil.ReadFile(resultPath + "/build.txt")
	if err != nil {
		t.Errorf(fmt.Sprintf("Error in building status file: %v",err))
	}
}

func TestOnPrepareWorkspaceFailure(t *testing.T) {
	tb := &testBuilder{workspaceErr: errors.New(""), workspaceResult: "@$@$"}
	defer os.RemoveAll(resultPath + "/build.txt")
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	if !strings.Contains(string(buildFile), "failed to prepare workspace @$@$") {
		t.Errorf("Failed to log error in preparing workspace")
	}
}

func TestOnBuildScriptFailure(t *testing.T) {
	tb := &testBuilder{buildScriptErr: errors.New("&^&^")}
	defer os.RemoveAll(resultPath + "/build.txt")
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	buildText := string(buildFile)

	if !strings.Contains(buildText, "workspace created") {
		t.Errorf("Failed to log sucessful workspace creation")
	}

	if !strings.Contains(buildText, "&^&^") {
		t.Errorf("Failed to log error in FindBuildScript")
	}
}

func TestOnRunBuildScriptError(t *testing.T) {
	tb := &testBuilder{buildScriptPath: "!@#", buildErr: errors.New("^%$")}//buildResult: &executeResult{ExitCode: 0}}
	defer os.RemoveAll(resultPath + "/build.txt")
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	buildText := string(buildFile)

	if !strings.Contains(buildText, "workspace created") {
		t.Errorf("Failed to log successful workspace creation")
	}

	if !strings.Contains(buildText, "build script found !@#") {
		t.Errorf("Failed to log successful build start")
	}
	if !strings.Contains(buildText, "build started ...") {
		t.Errorf("Failed to log build start")
	}

	if !strings.Contains(buildText, "build error ^%$") {
		t.Errorf("Failed to log build error")
	}
}

func TestOnRunBuildScriptSuccess(t *testing.T) {
	tb := &testBuilder{buildScriptPath: "!@#", buildResult: &executeResult{ExitCode: 0}}
	defer os.RemoveAll(resultPath + "/build.txt")
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	buildText := string(buildFile)

	if !strings.Contains(buildText, "workspace created") {
		t.Errorf("Failed to log successful workspace creation")
	}

	if !strings.Contains(buildText, "build script found !@#") {
		t.Errorf("Failed to log successful build start")
	}
	if !strings.Contains(buildText, "build started ...") {
		t.Errorf("Failed to log build start")
	}

	if !strings.Contains(buildText, "build success") {
		t.Errorf("Failed to log build success")
	}
}

func TestOnRunBuildScriptFailure(t *testing.T) {
	tb := &testBuilder{buildScriptPath: "!@#", buildResult: &executeResult{ExitCode: 123123123}}
	defer os.RemoveAll(resultPath + "/build.txt")
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	buildText := string(buildFile)

	if !strings.Contains(buildText, "workspace created") {
		t.Errorf("Failed to log successful workspace creation")
	}

	if !strings.Contains(buildText, "build script found !@#") {
		t.Errorf("Failed to log successful build start")
	}
	if !strings.Contains(buildText, "build started ...") {
		t.Errorf("Failed to log build start")
	}

	if !strings.Contains(buildText, "build failed") || !strings.Contains(buildText,"123123123"){
		t.Errorf("Failed to log build failure")
	}
}

<commit_msg>build tests added<commit_after>package grim

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

// Copyright 2015 MediaMath <http://www.mediamath.com>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
type testBuilder struct {
	workspaceErr    error
	workspaceResult string
	buildScriptErr  error
	buildScriptPath string
	buildErr        error
	buildResult     *executeResult
}

func (tb *testBuilder) PrepareWorkspace() (string, error) {
	return tb.workspaceResult, tb.workspaceErr
}
func (tb *testBuilder) FindBuildScript(workspacePath string) (string, error) {
	return tb.buildScriptPath, tb.buildScriptErr
}
func (tb *testBuilder) RunBuildScript(workspacePath, buildScript string, outputChan chan string) (*executeResult, error) {
	return tb.buildResult, tb.buildErr
}

func TestOnBuildStatusFileError(t *testing.T) {
	resultPath, _ := ioutil.TempDir("", "build-status-file-error")
	defer os.RemoveAll(resultPath)

	tb := &testBuilder{workspaceErr: errors.New(""), workspaceResult: "@$@$"}
	grimBuild(tb, resultPath)

	_, err := ioutil.ReadFile(resultPath + "/build.txt")
	if err != nil {
		t.Errorf(fmt.Sprintf("Error in building status file: %v", err))
	}
}

func TestOnPrepareWorkspaceFailure(t *testing.T) {
	resultPath, _ := ioutil.TempDir("", "prepare-workspace-failure")
	defer os.RemoveAll(resultPath)

	tb := &testBuilder{workspaceErr: errors.New(""), workspaceResult: "@$@$"}
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	if !strings.Contains(string(buildFile), "failed to prepare workspace @$@$") {
		t.Errorf("Failed to log error in preparing workspace")
	}
}

func TestOnBuildScriptFailure(t *testing.T) {
	resultPath, _ := ioutil.TempDir("", "builds-script-failure")
	defer os.RemoveAll(resultPath)

	tb := &testBuilder{buildScriptErr: errors.New("&^&^")}
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	buildText := string(buildFile)

	if !strings.Contains(buildText, "workspace created") {
		t.Errorf("Failed to log sucessful workspace creation")
	}

	if !strings.Contains(buildText, "&^&^") {
		t.Errorf("Failed to log error in FindBuildScript")
	}
}

func TestOnRunBuildScriptError(t *testing.T) {
	resultPath, _ := ioutil.TempDir("", "builds-script-error")
	defer os.RemoveAll(resultPath)

	tb := &testBuilder{buildScriptPath: "!@#", buildErr: errors.New("^%$")} //buildResult: &executeResult{ExitCode: 0}}
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	buildText := string(buildFile)

	if !strings.Contains(buildText, "workspace created") {
		t.Errorf("Failed to log successful workspace creation")
	}

	if !strings.Contains(buildText, "build script found !@#") {
		t.Errorf("Failed to log successful build start")
	}
	if !strings.Contains(buildText, "build started ...") {
		t.Errorf("Failed to log build start")
	}

	if !strings.Contains(buildText, "build error ^%$") {
		t.Errorf("Failed to log build error")
	}
}

func TestOnRunBuildScriptSuccess(t *testing.T) {
	resultPath, _ := ioutil.TempDir("", "build-script-success")
	defer os.RemoveAll(resultPath)

	tb := &testBuilder{buildScriptPath: "!@#", buildResult: &executeResult{ExitCode: 0}}
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	buildText := string(buildFile)

	if !strings.Contains(buildText, "workspace created") {
		t.Errorf("Failed to log successful workspace creation")
	}

	if !strings.Contains(buildText, "build script found !@#") {
		t.Errorf("Failed to log successful build start")
	}
	if !strings.Contains(buildText, "build started ...") {
		t.Errorf("Failed to log build start")
	}

	if !strings.Contains(buildText, "build success") {
		t.Errorf("Failed to log build success")
	}
}

func TestOnRunBuildScriptFailure(t *testing.T) {
	resultPath, _ := ioutil.TempDir("", "build-script-error")
	defer os.RemoveAll(resultPath)

	tb := &testBuilder{buildScriptPath: "!@#", buildResult: &executeResult{ExitCode: 123123123}}
	grimBuild(tb, resultPath)

	buildFile, _ := ioutil.ReadFile(resultPath + "/build.txt")
	buildText := string(buildFile)

	if !strings.Contains(buildText, "workspace created") {
		t.Errorf("Failed to log successful workspace creation")
	}

	if !strings.Contains(buildText, "build script found !@#") {
		t.Errorf("Failed to log successful build start")
	}
	if !strings.Contains(buildText, "build started ...") {
		t.Errorf("Failed to log build start")
	}

	if !strings.Contains(buildText, "build failed") || !strings.Contains(buildText, "123123123") {
		t.Errorf("Failed to log build failure")
	}
}
<|endoftext|>


<commit_before>package wellington

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"

	"github.com/fatih/color"
	"github.com/wellington/go-libsass"
)

func init() {
	testch = make(chan struct{})
	close(testch)
	color.NoColor = true
}

func TestCompileStdin_imports(t *testing.T) {

	in := bytes.NewBufferString(`@import "compass";
@import "compass/utilities/sprite/base";

`)
	ctx := libsass.NewContext()
	InitializeContext(ctx)
	ctx.Imports.Init()

	var buf bytes.Buffer
	err := ctx.Compile(in, &buf)
	if err != nil {
		t.Fatal(err)
	}

	out := buf.String()

	if e := ``; e != out {
		t.Fatalf("mismatch expected:\n%s\nwas:\n%s\n", e, out)
	}

}

func TestNewBuild(t *testing.T) {

	b := NewBuild([]string{"test/sass/error.scss"}, &BuildArgs{}, nil, false)
	if b == nil {
		t.Fatal("build is nil")
	}

	err := b.Build()
	if err != ErrPartialMap {
		t.Errorf("got: %s wanted: %s", err, ErrPartialMap)
	}
	b.Close()
}

func TestNewBuild_two(t *testing.T) {
	tdir, _ := ioutil.TempDir("", "testnewbuild_two")
	bb := NewBuild([]string{"test/sass/file.scss"},
		&BuildArgs{BuildDir: tdir}, NewPartialMap(), false)

	err := bb.Build()
	if err != nil {
		t.Fatal(err)
	}

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

}

func TestNewBuild_dir(t *testing.T) {
	tdir, _ := ioutil.TempDir("", "testnewbuild_two")
	bb := NewBuild([]string{"test/sass"},
		&BuildArgs{BuildDir: tdir}, NewPartialMap(), false)

	err := bb.Build()
	if err == nil {
		t.Fatal("expected error")
	}

}

func ExampleNewBuild() {
	b := NewBuild([]string{"test/sass/file.scss"},
		&BuildArgs{}, NewPartialMap(), false)

	err := b.Build()
	if err != nil {
		log.Fatal(err)
	}

	// Output:
	// div {
	//   color: black; }
}

func TestBuild_error(t *testing.T) {

	_, w, _ := os.Pipe()

	err := loadAndBuild("test/sass/error.scss", &BuildArgs{},
		NewPartialMap(), w, "")

	if err == nil {
		t.Fatal("no error thrown")
	}

	e := `Invalid CSS after "div {": expected "}", was ""`
	if !strings.HasSuffix(err.Error(), e) {
		t.Fatalf("Error contains invalid text:\n%s", err)
	}
}

func TestBuild_args(t *testing.T) {
	r, w, _ := os.Pipe()

	bArgs := &BuildArgs{
		Includes: "test",
	}

	err := loadAndBuild("test/sass/file.scss", bArgs,
		NewPartialMap(), w, "")
	if err != nil {
		t.Fatal(err)
	}

	bs, err := ioutil.ReadAll(r)
	if err != nil {
		t.Fatal(err)
	}

	e := `div {
  color: black; }
`
	if e != string(bs) {
		t.Errorf("got:\n%s\nwanted:\n%s", string(bs), e)
	}
}

func TestBuild_comply(t *testing.T) {
	r, w, _ := os.Pipe()

	err := loadAndBuild("test/compass/top.scss",
		&BuildArgs{
			Includes: "test",
		},
		NewPartialMap(), w, "")

	bs, err := ioutil.ReadAll(r)
	if err != nil {
		t.Fatal(err)
	}

	e := `one {
  color: red; }

two {
  color: blue; }

three {
  color: purple; }
`

	if e != string(bs) {
		t.Errorf("got:\n%s\nwanted:\n%s", string(bs), e)
	}
}

func TestUpdateFileOutputType(t *testing.T) {
	s := "file.scss"
	ren := updateFileOutputType(s)
	if e := "file.css"; e != ren {
		t.Errorf("got: %s wanted: %s", ren, e)
	}
}
<commit_msg>test number of files on disk<commit_after>package wellington

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/fatih/color"
	"github.com/wellington/go-libsass"
)

func init() {
	testch = make(chan struct{})
	close(testch)
	color.NoColor = true
}

func TestCompileStdin_imports(t *testing.T) {

	in := bytes.NewBufferString(`@import "compass";
@import "compass/utilities/sprite/base";

`)
	ctx := libsass.NewContext()
	InitializeContext(ctx)
	ctx.Imports.Init()

	var buf bytes.Buffer
	err := ctx.Compile(in, &buf)
	if err != nil {
		t.Fatal(err)
	}

	out := buf.String()

	if e := ``; e != out {
		t.Fatalf("mismatch expected:\n%s\nwas:\n%s\n", e, out)
	}

}

func TestNewBuild(t *testing.T) {

	b := NewBuild([]string{"test/sass/error.scss"}, &BuildArgs{}, nil, false)
	if b == nil {
		t.Fatal("build is nil")
	}

	err := b.Build()
	if err != ErrPartialMap {
		t.Errorf("got: %s wanted: %s", err, ErrPartialMap)
	}
	b.Close()
}

func TestNewBuild_two(t *testing.T) {
	tdir, _ := ioutil.TempDir("", "testnewbuild_two")
	bb := NewBuild([]string{"test/sass/file.scss"},
		&BuildArgs{BuildDir: tdir}, NewPartialMap(), false)

	err := bb.Build()
	if err != nil {
		t.Fatal(err)
	}

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

	matches, err := filepath.Glob(filepath.Join(tdir, "test", "*.css"))
	if err != nil {
		t.Fatal(err)
	}
	if e := 0; len(matches) != e {
		t.Errorf("got: %d wanted: %d", len(matches), e)
	}

	matches, err = filepath.Glob(filepath.Join(tdir, "test", "sass", "*.css"))
	if err != nil {
		t.Fatal(err)
	}
	if e := 1; len(matches) != e {
		t.Errorf("got: %d wanted: %d", len(matches), e)
	}

}

func TestNewBuild_dir(t *testing.T) {
	tdir, _ := ioutil.TempDir("", "testnewbuild_two")
	bb := NewBuild([]string{"test/sass"},
		&BuildArgs{BuildDir: tdir}, NewPartialMap(), false)

	err := bb.Build()
	if err == nil {
		t.Fatal("expected error")
	}

	matches, err := filepath.Glob(filepath.Join(tdir, "test", "*.css"))
	if err != nil {
		t.Fatal(err)
	}
	if e := 0; len(matches) != e {
		t.Errorf("got: %d wanted: %d", len(matches), e)
	}

	matches, err = filepath.Glob(filepath.Join(tdir, "test", "sass", "*.css"))
	if err != nil {
		t.Fatal(err)
	}
	if e := 6; len(matches) != e {
		t.Errorf("got: %d wanted: %d", len(matches), e)
	}
}

func ExampleNewBuild() {
	b := NewBuild([]string{"test/sass/file.scss"},
		&BuildArgs{}, NewPartialMap(), false)

	err := b.Build()
	if err != nil {
		log.Fatal(err)
	}

	// Output:
	// div {
	//   color: black; }
}

func TestBuild_error(t *testing.T) {

	_, w, _ := os.Pipe()

	err := loadAndBuild("test/sass/error.scss", &BuildArgs{},
		NewPartialMap(), w, "")

	if err == nil {
		t.Fatal("no error thrown")
	}

	e := `Invalid CSS after "div {": expected "}", was ""`
	if !strings.HasSuffix(err.Error(), e) {
		t.Fatalf("Error contains invalid text:\n%s", err)
	}
}

func TestBuild_args(t *testing.T) {
	r, w, _ := os.Pipe()

	bArgs := &BuildArgs{
		Includes: "test",
	}

	err := loadAndBuild("test/sass/file.scss", bArgs,
		NewPartialMap(), w, "")
	if err != nil {
		t.Fatal(err)
	}

	bs, err := ioutil.ReadAll(r)
	if err != nil {
		t.Fatal(err)
	}

	e := `div {
  color: black; }
`
	if e != string(bs) {
		t.Errorf("got:\n%s\nwanted:\n%s", string(bs), e)
	}
}

func TestBuild_comply(t *testing.T) {
	r, w, _ := os.Pipe()

	err := loadAndBuild("test/compass/top.scss",
		&BuildArgs{
			Includes: "test",
		},
		NewPartialMap(), w, "")

	bs, err := ioutil.ReadAll(r)
	if err != nil {
		t.Fatal(err)
	}

	e := `one {
  color: red; }

two {
  color: blue; }

three {
  color: purple; }
`

	if e != string(bs) {
		t.Errorf("got:\n%s\nwanted:\n%s", string(bs), e)
	}
}

func TestUpdateFileOutputType(t *testing.T) {
	s := "file.scss"
	ren := updateFileOutputType(s)
	if e := "file.css"; e != ren {
		t.Errorf("got: %s wanted: %s", ren, e)
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Nan Deng
 *
 * 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 (
	"net"
	"testing"
	//"runtime"
	"fmt"
	"time"
)

func buildBullies(startPort, N int, t *testing.T) []*Bully {
	ret := make([]*Bully, 0, N)
	for i := 0; i < N; i++ {
		port := startPort + i
		addrStr := fmt.Sprintf("127.0.0.1:%v", port)
		ln, err := net.Listen("tcp", addrStr)
		if err != nil {
			t.Errorf("%v\n", err)
		}
		bully := NewBully(ln, nil)
		ret = append(ret, bully)
	}
	return ret
}

func printCandies(bullies []*Bully) {
	for _, alice := range bullies {
		fmt.Printf("[LISTCANDYLIST] %v:\n", alice.localhost())
		aliceCandy := alice.CandidateList()
		for _, c := range aliceCandy {
			fmt.Printf("\t%v\n", c.Addr)
		}
	}
}

func buildConnections(bullies []*Bully, t *testing.T) {
	for _, alice := range bullies {
		fmt.Println("++++++++++++++++")
		for _, bob := range bullies {
			fmt.Printf("[TEST] Ask %v to add %v\n", alice.localhost(), bob.localhost())
			err := alice.AddCandidate(bob.localhost().String(), nil, 3*time.Second)
			if err != nil {
				t.Errorf("%v\n", err)
			}
			fmt.Printf("[TEST] %v has added%v\n", alice.localhost(), bob.localhost())
			printCandies(bullies)
			fmt.Println("#############")
		}
		fmt.Println("++++++++++++++++")
	}
}

func testSameViewOnBullies(bullies []*Bully, t *testing.T) {
	n := len(bullies)
	for _, alice := range bullies {
		for _, bob := range bullies {

			bobCandy := bob.CandidateList()
			aliceCandy := alice.CandidateList()
			if len(bobCandy) != len(aliceCandy) {
				t.Errorf("Not same number of candidates!")
			}

			if len(bobCandy) != n {
				t.Errorf("Should have %v candidates; Now have %v", n, len(bobCandy))
			}

			for _, bc := range bobCandy {
				found := false
				for _, ac := range aliceCandy {
					if bc.Id.Cmp(ac.Id) == 0 {
						found = true
						break
					}
				}
				if !found {
					t.Errorf("Bob's candidate %v cannot be found in Alice's", bc.Id)
				}
			}

		}
	}
}

func testSameLeader(bullies []*Bully, t *testing.T) {
	fmt.Println("------------test leader---------")
	for _, alice := range bullies {
		fmt.Println("&&&&&&&&&&&&&&&&&&&&&")
		for _, bob := range bullies {
			aliceLeader, err := alice.Leader()
			if err != nil {
				t.Errorf("%v\n", err)
			}
			bobLeader, err := bob.Leader()
			if err != nil {
				t.Errorf("%v\n", err)
			}

			if aliceLeader.Id.Cmp(bobLeader.Id) != 0 {
				t.Errorf("%v thinks its leader is %v;\n\tbut %v thinks its leader is %v\n",
					alice.MyId(), aliceLeader.Id, bob.MyId(), bobLeader.Id)
			}
		}
		fmt.Println("&&&&&&&&&&&&&&&&&&&&&")
	}
}

func cleanBullies(bullies []*Bully) {
	for _, bully := range bullies {
		bully.Finalize()
	}
}

func TestSingleBullyElect(t *testing.T) {
	fmt.Println("-------------------1------------------")
	bullies := buildBullies(8088, 1, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

func TestDoubleBullyElect(t *testing.T) {
	fmt.Println("-------------------2------------------")
	bullies := buildBullies(8088, 2, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

func TestTripleBullyElect(t *testing.T) {
	fmt.Println("-------------------3------------------")
	bullies := buildBullies(8088, 3, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

func Test4BullyElect(t *testing.T) {
	fmt.Println("-------------------4------------------")
	bullies := buildBullies(8088, 4, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	fmt.Println("-------------------*------------------")
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

<commit_msg>[Mod] tested 5-bully case. No error on my machine. need to remove debug messages<commit_after>/*
 * Copyright 2013 Nan Deng
 *
 * 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 (
	"net"
	"testing"
	//"runtime"
	"fmt"
	"time"
)

func buildBullies(startPort, N int, t *testing.T) []*Bully {
	ret := make([]*Bully, 0, N)
	for i := 0; i < N; i++ {
		port := startPort + i
		addrStr := fmt.Sprintf("127.0.0.1:%v", port)
		ln, err := net.Listen("tcp", addrStr)
		if err != nil {
			t.Errorf("%v\n", err)
		}
		bully := NewBully(ln, nil)
		ret = append(ret, bully)
	}
	return ret
}

func printCandies(bullies []*Bully) {
	for _, alice := range bullies {
		fmt.Printf("[LISTCANDYLIST] %v:\n", alice.localhost())
		aliceCandy := alice.CandidateList()
		for _, c := range aliceCandy {
			fmt.Printf("\t%v\n", c.Addr)
		}
	}
}

func buildConnections(bullies []*Bully, t *testing.T) {
	for _, alice := range bullies {
		fmt.Println("++++++++++++++++")
		for _, bob := range bullies {
			fmt.Printf("[TEST] Ask %v to add %v\n", alice.localhost(), bob.localhost())
			err := alice.AddCandidate(bob.localhost().String(), nil, 3*time.Second)
			if err != nil {
				t.Errorf("%v\n", err)
			}
			fmt.Printf("[TEST] %v has added%v\n", alice.localhost(), bob.localhost())
			printCandies(bullies)
			fmt.Println("#############")
		}
		fmt.Println("++++++++++++++++")
	}
}

func testSameViewOnBullies(bullies []*Bully, t *testing.T) {
	n := len(bullies)
	for _, alice := range bullies {
		for _, bob := range bullies {

			bobCandy := bob.CandidateList()
			aliceCandy := alice.CandidateList()
			if len(bobCandy) != len(aliceCandy) {
				t.Errorf("Not same number of candidates!")
			}

			if len(bobCandy) != n {
				t.Errorf("Should have %v candidates; Now have %v", n, len(bobCandy))
			}

			for _, bc := range bobCandy {
				found := false
				for _, ac := range aliceCandy {
					if bc.Id.Cmp(ac.Id) == 0 {
						found = true
						break
					}
				}
				if !found {
					t.Errorf("Bob's candidate %v cannot be found in Alice's", bc.Id)
				}
			}

		}
	}
}

func testSameLeader(bullies []*Bully, t *testing.T) {
	fmt.Println("------------test leader---------")
	for _, alice := range bullies {
		fmt.Println("&&&&&&&&&&&&&&&&&&&&&")
		for _, bob := range bullies {
			aliceLeader, err := alice.Leader()
			if err != nil {
				t.Errorf("%v\n", err)
			}
			bobLeader, err := bob.Leader()
			if err != nil {
				t.Errorf("%v\n", err)
			}

			if aliceLeader.Id.Cmp(bobLeader.Id) != 0 {
				t.Errorf("%v thinks its leader is %v;\n\tbut %v thinks its leader is %v\n",
					alice.MyId(), aliceLeader.Id, bob.MyId(), bobLeader.Id)
			}
		}
		fmt.Println("&&&&&&&&&&&&&&&&&&&&&")
	}
}

func cleanBullies(bullies []*Bully) {
	for _, bully := range bullies {
		bully.Finalize()
	}
}

func TestSingleBullyElect(t *testing.T) {
	fmt.Println("-------------------1------------------")
	bullies := buildBullies(8088, 1, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

func TestDoubleBullyElect(t *testing.T) {
	fmt.Println("-------------------2------------------")
	bullies := buildBullies(8088, 2, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

func TestTripleBullyElect(t *testing.T) {
	fmt.Println("-------------------3------------------")
	bullies := buildBullies(8088, 3, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

func Test4BullyElect(t *testing.T) {
	fmt.Println("-------------------4------------------")
	bullies := buildBullies(8088, 4, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	fmt.Println("-------------------*------------------")
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

func Test5BullyElect(t *testing.T) {
	fmt.Println("-------------------5------------------")
	bullies := buildBullies(8088, 5, t)
	for _, b := range bullies {
		fmt.Printf("bully: %v on addr %v\n", b.MyId(), b.MyAddr())
	}
	buildConnections(bullies, t)
	testSameViewOnBullies(bullies, t)
	testSameLeader(bullies, t)
	fmt.Println("-------------------*------------------")
	cleanBullies(bullies)
	fmt.Println("-------------------#------------------")
}

<|endoftext|>


<commit_before>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-ego/ego/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package file

// package gt

import (
	"errors"
	"fmt"
	"hash"
	"io"
	"log"
	"os"
	"path"
	"strings"

	"crypto/sha1"
	"crypto/sha256"
	"io/ioutil"
	"path/filepath"
)

// Exist checks whether a file or directory exists.
// It returns false when the file or directory does not exist.
func Exist(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil || os.IsExist(err)
}

// PathExists path is exist
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

// SearchFile Search a file in paths.
func SearchFile(filename string, paths ...string) (fullpath string, err error) {
	for _, path := range paths {
		if fullpath = filepath.Join(path, filename); Exist(fullpath) {
			return
		}
	}
	err = errors.New(fullpath + " not found in paths")
	return
}

// Size returns file size in bytes and possible error.
func Size(file string) (int64, error) {
	f, err := os.Stat(file)
	if err != nil {
		return 0, err
	}
	return f.Size(), nil
}

// Sha open file return sha
func Sha(filePath string, args ...string) (sha string, err error) {
	file, fsErr := os.Open(filePath)
	if fsErr != nil {
		return "", fsErr
	}
	defer file.Close()

	if len(args) > 0 {
		sha, err = IoSha(file, args[0])
		return
	}

	sha, err = IoSha(file)
	return
}

// IoSha file sha
func IoSha(fileIO *os.File, args ...string) (string, error) {
	var h hash.Hash

	if len(args) > 0 {
		h = sha256.New()
	} else {
		h = sha1.New()
	}

	_, err := io.Copy(h, fileIO)
	if err != nil {
		return "", err
	}

	sha := fmt.Sprintf("%x", h.Sum(nil))

	return sha, nil
}

// Copy copies file from source to target path.
func Copy(src, dest string) error {
	// Gather file information to set back later.
	si, err := os.Lstat(src)
	if err != nil {
		return err
	}

	// Handle symbolic link.
	if si.Mode()&os.ModeSymlink != 0 {
		target, err := os.Readlink(src)
		if err != nil {
			return err
		}
		// NOTE: os.Chmod and os.Chtimes don't recoganize symbolic link,
		// which will lead "no such file or directory" error.
		return os.Symlink(target, dest)
	}

	sr, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sr.Close()

	dw, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer dw.Close()

	if _, err = io.Copy(dw, sr); err != nil {
		return err
	}

	// Set back file information.
	if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
		return err
	}
	return os.Chmod(dest, si.Mode())
}

// CopyFile copies file from source to target path.
func CopyFile(src, dst string) (w int64, err error) {
	srcFile, err := os.Open(src)
	if err != nil {
		return
	}
	defer srcFile.Close()

	// if Exist(dst) != true {
	if !Exist(dst) {
		Writefile("", dst)
	}

	dstFile, err := os.Create(dst)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer dstFile.Close()
	return io.Copy(dstFile, srcFile)
}

// OpenCopy open and copy file
func OpenCopy(srcName, dstName string) (int64, error) {
	src, err := os.Open(srcName)
	if err != nil {
		return 0, err
	}
	defer src.Close()

	dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		fmt.Println(err)
		return 0, err
	}
	defer dst.Close()
	return io.Copy(dst, src)
}

// Readfile read file and return string
func Readfile(userFile string) (string, error) {
	// userFile := fname
	fin, err := os.Open(userFile)
	if err != nil {
		log.Println("os.Open: ", userFile, err)
		return "", err
	}
	defer fin.Close()

	var str string
	buf := make([]byte, 1024)
	for {
		n, _ := fin.Read(buf)
		if 0 == n {
			break
		}
		// os.Stdout.Write(buf[:n])
		strBuf := string(buf[:n])
		str += strBuf
	}

	return str, nil
}

// WriteFile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it
// and its upper level paths.
func WriteFile(fileName string, data []byte) error {
	os.MkdirAll(path.Dir(fileName), os.ModePerm)
	return ioutil.WriteFile(fileName, data, 0655)
}

// Writefile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it
// and its upper level paths.
func Writefile(fileName, writeStr string) {
	os.MkdirAll(path.Dir(fileName), os.ModePerm)

	fout, err := os.Create(fileName)
	if err != nil {
		log.Println("write file "+fileName, err)
		return
	}
	defer fout.Close()

	fout.WriteString(writeStr)
}

// AppendToFile append file
func AppendToFile(fileName, content string) error {
	// 以只写的模式，打开文件
	f, err := os.OpenFile(fileName, os.O_WRONLY, 0644)
	if err != nil {
		log.Println("file create failed. err: " + err.Error())
		return err
	}

	n, _ := f.Seek(0, os.SEEK_END)
	_, err = f.WriteAt([]byte(content), n)

	defer f.Close()
	return err
}

// ListFile file list
func ListFile(dirPth string, suffix string) (files []string, err error) {
	files = make([]string, 0, 10)
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}
	PthSep := string(os.PathSeparator)
	suffix = strings.ToUpper(suffix)
	for _, fi := range dir {
		if fi.IsDir() {
			continue
		}
		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, dirPth+PthSep+fi.Name())
		}
	}
	return files, nil
}

// ListDir dir list
func ListDir(dirPth string, suffix string) (files []string, err error) {
	files = make([]string, 0, 10)
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}
	PthSep := string(os.PathSeparator)
	suffix = strings.ToUpper(suffix)
	for _, fi := range dir {
		if !fi.IsDir() {
			continue
		}
		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, dirPth+PthSep+fi.Name())
		}
	}
	return files, nil
}

// WalkFile file walk
func WalkFile(dirPth, suffix string) (files []string, err error) {
	files = make([]string, 0, 30)
	suffix = strings.ToUpper(suffix)
	err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error {
		if fi.IsDir() {
			return nil
		}

		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, filename)
		}
		return nil
	})
	return files, err
}

// WalkDir dir walk
func WalkDir(dirPth, suffix string) (files []string, err error) {
	files = make([]string, 0, 30)
	suffix = strings.ToUpper(suffix)
	err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error {

		if !fi.IsDir() {
			return nil
		}

		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, filename)
		}
		return nil
	})
	return files, err
}
<commit_msg>optimize and simplify file api<commit_after>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-ego/ego/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package file

// package gt

import (
	"errors"
	"fmt"
	"hash"
	"io"
	"log"
	"os"
	"path"
	"strings"

	"crypto/sha1"
	"crypto/sha256"
	"io/ioutil"
	"path/filepath"
)

// Exist checks whether a file or directory exists.
// It returns false when the file or directory does not exist.
func Exist(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil || os.IsExist(err)
}

// PathExists path is exist
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

// Search Search a file in paths.
func Search(filename string, paths ...string) (fullpath string, err error) {
	for _, path := range paths {
		if fullpath = filepath.Join(path, filename); Exist(fullpath) {
			return
		}
	}
	err = errors.New(fullpath + " not found in paths")
	return
}

// Size returns file size in bytes and possible error.
func Size(file string) (int64, error) {
	f, err := os.Stat(file)
	if err != nil {
		return 0, err
	}
	return f.Size(), nil
}

// Sha open file return sha
func Sha(filePath string, args ...string) (sha string, err error) {
	file, fsErr := os.Open(filePath)
	if fsErr != nil {
		return "", fsErr
	}
	defer file.Close()

	if len(args) > 0 {
		sha, err = IoSha(file, args[0])
		return
	}

	sha, err = IoSha(file)
	return
}

// IoSha file sha
func IoSha(fileIO *os.File, args ...string) (string, error) {
	var h hash.Hash

	if len(args) > 0 {
		h = sha256.New()
	} else {
		h = sha1.New()
	}

	_, err := io.Copy(h, fileIO)
	if err != nil {
		return "", err
	}

	sha := fmt.Sprintf("%x", h.Sum(nil))

	return sha, nil
}

// Copy copies file from source to target path.
func Copy(src, dest string) error {
	// Gather file information to set back later.
	si, err := os.Lstat(src)
	if err != nil {
		return err
	}

	// Handle symbolic link.
	if si.Mode()&os.ModeSymlink != 0 {
		target, err := os.Readlink(src)
		if err != nil {
			return err
		}
		// NOTE: os.Chmod and os.Chtimes don't recoganize symbolic link,
		// which will lead "no such file or directory" error.
		return os.Symlink(target, dest)
	}

	sr, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sr.Close()

	dw, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer dw.Close()

	if _, err = io.Copy(dw, sr); err != nil {
		return err
	}

	// Set back file information.
	if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
		return err
	}
	return os.Chmod(dest, si.Mode())
}

// CopyFile copies file from source to target path.
func CopyFile(src, dst string) (w int64, err error) {
	srcFile, err := os.Open(src)
	if err != nil {
		return
	}
	defer srcFile.Close()

	// if Exist(dst) != true {
	if !Exist(dst) {
		Write("", dst)
	}

	dstFile, err := os.Create(dst)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer dstFile.Close()
	return io.Copy(dstFile, srcFile)
}

// OpenCopy open and copy file
func OpenCopy(srcName, dstName string) (int64, error) {
	src, err := os.Open(srcName)
	if err != nil {
		return 0, err
	}
	defer src.Close()

	dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		fmt.Println(err)
		return 0, err
	}
	defer dst.Close()
	return io.Copy(dst, src)
}

// Read read file and return string
func Read(userFile string) (string, error) {
	// userFile := fname
	fin, err := os.Open(userFile)
	if err != nil {
		log.Println("os.Open: ", userFile, err)
		return "", err
	}
	defer fin.Close()

	var str string
	buf := make([]byte, 1024)
	for {
		n, _ := fin.Read(buf)
		if 0 == n {
			break
		}
		// os.Stdout.Write(buf[:n])
		strBuf := string(buf[:n])
		str += strBuf
	}

	return str, nil
}

// WriteFile writes data to a file named by filename.
// If the file does not exist, WriteFile creates it
// and its upper level paths.
func WriteFile(fileName string, data []byte) error {
	os.MkdirAll(path.Dir(fileName), os.ModePerm)
	return ioutil.WriteFile(fileName, data, 0655)
}

// Write writes data to a file named by filename.
// If the file does not exist, WriteFile creates it
// and its upper level paths.
func Write(fileName, writeStr string) {
	os.MkdirAll(path.Dir(fileName), os.ModePerm)

	fout, err := os.Create(fileName)
	if err != nil {
		log.Println("write file "+fileName, err)
		return
	}
	defer fout.Close()

	fout.WriteString(writeStr)
}

// AppendTo append to file
func AppendTo(fileName, content string) error {
	// 以只写的模式，打开文件
	f, err := os.OpenFile(fileName, os.O_WRONLY, 0644)
	if err != nil {
		log.Println("file create failed. err: " + err.Error())
		return err
	}

	n, _ := f.Seek(0, os.SEEK_END)
	_, err = f.WriteAt([]byte(content), n)

	defer f.Close()
	return err
}

// List file list
func List(dirPth string, suffix string) (files []string, err error) {
	files = make([]string, 0, 10)
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}
	PthSep := string(os.PathSeparator)
	suffix = strings.ToUpper(suffix)
	for _, fi := range dir {
		if fi.IsDir() {
			continue
		}
		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, dirPth+PthSep+fi.Name())
		}
	}
	return files, nil
}

// ListDir dir list
func ListDir(dirPth string, suffix string) (files []string, err error) {
	files = make([]string, 0, 10)
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, err
	}
	PthSep := string(os.PathSeparator)
	suffix = strings.ToUpper(suffix)
	for _, fi := range dir {
		if !fi.IsDir() {
			continue
		}
		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, dirPth+PthSep+fi.Name())
		}
	}
	return files, nil
}

// Walk file walk
func Walk(dirPth, suffix string) (files []string, err error) {
	files = make([]string, 0, 30)
	suffix = strings.ToUpper(suffix)
	err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error {
		if fi.IsDir() {
			return nil
		}

		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, filename)
		}
		return nil
	})
	return files, err
}

// WalkDir dir walk
func WalkDir(dirPth, suffix string) (files []string, err error) {
	files = make([]string, 0, 30)
	suffix = strings.ToUpper(suffix)
	err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error {

		if !fi.IsDir() {
			return nil
		}

		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
			files = append(files, filename)
		}
		return nil
	})
	return files, err
}
<|endoftext|>


<commit_before>package file

import (
	"os"
	"path/filepath"
	"strings"
	"time"
)

func New(name string) (*File, error) {
	name = filepath.ToSlash(name)
	isDir := strings.HasSuffix(name, "/")
	name, err := filepath.Abs(name)

	if err != nil {
		return nil, err
	}

	if !isDir {
		fi, _ := os.Stat(name)
		isDir = fi != nil && fi.IsDir()
	}
	if isDir {
		name = filepath.Join(name, `log.txt`)
	}

	dir, name := filepath.Split(name)
	fi, _ := os.Stat(name)

	if fi == nil {
		os.MkdirAll(dir, os.ModePerm)
	}

	ext := filepath.Ext(name)
	if len(ext) == 0 {
		ext = `.txt`
	} else {
		name = name[:len(name)-len(ext)]
	}
	prefix := strings.SplitN(name, `-`, 2)[0]

	f := &File{nil, dir, prefix, ext, name}
	t := time.Now()
	f.Rotate(t, t)
	return f, err
}

var layout = "20060102150405.00000"

type File struct {
	fd                     *os.File
	dir, prefix, ext, Name string
}

func (f *File) Rotate(begin, now time.Time) {
	f.Close()
	for {
		name := filepath.Join(
			f.dir, f.prefix+`-`+now.Format(layout)+f.ext,
		)
		fi, _ := os.Stat(name)
		if fi == nil && name != f.Name {
			f.Name = name
			break
		}
		now.Add(time.Now().Sub(now))
	}
	f.fd, _ = os.OpenFile(f.Name, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	return
}

func (f *File) Write(b []byte) (n int, err error) {
	if f.fd == nil {
		return 0, os.ErrNotExist
	}
	return f.fd.Write(b)
}

func (f *File) Close() (err error) {
	if f.fd == nil {
		return nil
	}
	err = f.fd.Sync()
	if err == nil {
		err = f.fd.Close()
	} else {
		f.fd.Close()
	}
	f.fd = nil
	return
}
<commit_msg>fix filemode to 0664<commit_after>package file

import (
	"os"
	"path/filepath"
	"strings"
	"time"
)

func New(name string) (*File, error) {
	name = filepath.ToSlash(name)
	isDir := strings.HasSuffix(name, "/")
	name, err := filepath.Abs(name)

	if err != nil {
		return nil, err
	}

	if !isDir {
		fi, _ := os.Stat(name)
		isDir = fi != nil && fi.IsDir()
	}
	if isDir {
		name = filepath.Join(name, `log.txt`)
	}

	dir, name := filepath.Split(name)
	fi, _ := os.Stat(name)

	if fi == nil {
		os.MkdirAll(dir, os.ModePerm)
	}

	ext := filepath.Ext(name)
	if len(ext) == 0 {
		ext = `.txt`
	} else {
		name = name[:len(name)-len(ext)]
	}
	prefix := strings.SplitN(name, `-`, 2)[0]

	f := &File{nil, dir, prefix, ext, name}
	t := time.Now()
	f.Rotate(t, t)
	return f, err
}

var layout = "20060102150405.00000"

type File struct {
	fd                     *os.File
	dir, prefix, ext, Name string
}

func (f *File) Rotate(begin, now time.Time) {
	f.Close()
	for {
		name := filepath.Join(
			f.dir, f.prefix+`-`+now.Format(layout)+f.ext,
		)
		fi, _ := os.Stat(name)
		if fi == nil && name != f.Name {
			f.Name = name
			break
		}
		now.Add(time.Now().Sub(now))
	}
	f.fd, _ = os.OpenFile(f.Name, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0664)
	return
}

func (f *File) Write(b []byte) (n int, err error) {
	if f.fd == nil {
		return 0, os.ErrNotExist
	}
	return f.fd.Write(b)
}

func (f *File) Close() (err error) {
	if f.fd == nil {
		return nil
	}
	err = f.fd.Sync()
	if err == nil {
		err = f.fd.Close()
	} else {
		f.fd.Close()
	}
	f.fd = nil
	return
}
<|endoftext|>


<commit_before>package captcha

import (
	"strings"
	"bytes"
	"errors"
	"net/http"
	"io/ioutil"
	"mime/multipart"
	"fmt"
	)

const (
	inputUrl  = "http://2captcha.com/in.php"
	responseUrl = "http://2captcha.com/res.php"
	OK             = "OK"
	notReady       = "CAPCHA_NOT_READY"
	reportedOK     = "OK_REPORT_RECORDED"
)

type Captcha struct{
	key string
}

func New(key string) (*Captcha, error){
	if key == ""{
		return nil, errors.New("key should not be empty")
	}
	return &Captcha{
		key: key,
	}, nil
}

//upload one base64 imagem to twocaptcha to be resolverd
//return captcha id or one error
func (captcha *Captcha) UploadBase64Image(base64 string) (string, error){
	if base64 == ""{
		return "", errors.New("base64 should be not empty")
	}
	bf, contentType, err := captcha.createForm(base64)
	if err != nil {
		return "", errors.New("failed to create form")
	}

	req, err := http.NewRequest("POST", inputUrl, bf)
	if err != nil {
		return "", errors.New("failed to create request/post")
	}
	req.Header.Set("Content-Type", contentType)
	body, err := perfomRequest(req)
	if err != nil {
		return "", err
	}
	_, err = getCaptchaID(body)
	if err != nil{
		return "", err
	}
	return body, nil
}

func getCaptchaID(body string) (string, error){
	if strings.Contains(body, "OK|"){
		return strings.Split(body,"|")[1], nil
	}
	return "", errors.New(body)
}

func perfomRequest(request *http.Request) (string, error){
	client := &http.Client{}
	resp, err := client.Do(request)
	defer resp.Body.Close()

	if err != nil {
		return "", err
	}

	if resp.StatusCode != http.StatusOK {
		return "", errors.New("response status code different 200")
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

type formCreator struct {
	err error
}

func (captcha *Captcha) createForm(image string) (*bytes.Buffer, string, error) {
	var buffer bytes.Buffer
	writer := multipart.NewWriter(&buffer)
	defer writer.Close()

	formCreator := &formCreator{}
	formCreator.createFormField("key", captcha.key, writer)
	formCreator.createFormField("body", image, writer)
	formCreator.createFormField("method", "base64", writer)
	if formCreator.err != nil {
		return nil, "", formCreator.err
	}

	return &buffer, writer.FormDataContentType(), nil
}

func (fc *formCreator) createFormField(fieldName string, fieldValue string, writer *multipart.Writer) {
	if fc.err != nil {
		return
	}
	fw, err := writer.CreateFormField(fieldName)
	if err != nil {
		fc.err = errors.New(fmt.Sprintf("failed to create field %s ", fieldName))
		return
	}
	if _, err := fw.Write([]byte(fieldValue)); err != nil {
		fc.err = errors.New(fmt.Sprintf("failed to set %s value", fieldName))
		return
	}
}

// polling 2captcha response page until captcha is ready.
// initSleep represent 2captcha average time to solve captcha, don't make senses polling
// response before average time
func PollingCaptchaResponse(captchaId string, initSleep int, pollingTime int) (string, error){
	return "", nil
}

<commit_msg>formating code<commit_after>package captcha

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"strings"
)

const (
	inputUrl    = "http://2captcha.com/in.php"
	responseUrl = "http://2captcha.com/res.php"
	OK          = "OK"
	notReady    = "CAPCHA_NOT_READY"
	reportedOK  = "OK_REPORT_RECORDED"
)

type Captcha struct {
	key string
}

func New(key string) (*Captcha, error) {
	if key == "" {
		return nil, errors.New("key should not be empty")
	}
	return &Captcha{
		key: key,
	}, nil
}

//upload one base64 imagem to twocaptcha to be resolverd
//return captcha id or one error
func (captcha *Captcha) UploadBase64Image(base64 string) (string, error) {
	if base64 == "" {
		return "", errors.New("base64 should be not empty")
	}
	bf, contentType, err := captcha.createForm(base64)
	if err != nil {
		return "", errors.New("failed to create form")
	}

	req, err := http.NewRequest("POST", inputUrl, bf)
	if err != nil {
		return "", errors.New("failed to create request/post")
	}
	req.Header.Set("Content-Type", contentType)
	body, err := perfomRequest(req)
	if err != nil {
		return "", err
	}
	_, err = getCaptchaID(body)
	if err != nil {
		return "", err
	}
	return body, nil
}

func getCaptchaID(body string) (string, error) {
	if strings.Contains(body, "OK|") {
		return strings.Split(body, "|")[1], nil
	}
	return "", errors.New(body)
}

func perfomRequest(request *http.Request) (string, error) {
	client := &http.Client{}
	resp, err := client.Do(request)
	defer resp.Body.Close()

	if err != nil {
		return "", err
	}

	if resp.StatusCode != http.StatusOK {
		return "", errors.New("response status code different 200")
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

type formCreator struct {
	err error
}

func (captcha *Captcha) createForm(image string) (*bytes.Buffer, string, error) {
	var buffer bytes.Buffer
	writer := multipart.NewWriter(&buffer)
	defer writer.Close()

	formCreator := &formCreator{}
	formCreator.createFormField("key", captcha.key, writer)
	formCreator.createFormField("body", image, writer)
	formCreator.createFormField("method", "base64", writer)
	if formCreator.err != nil {
		return nil, "", formCreator.err
	}

	return &buffer, writer.FormDataContentType(), nil
}

func (fc *formCreator) createFormField(fieldName string, fieldValue string, writer *multipart.Writer) {
	if fc.err != nil {
		return
	}
	fw, err := writer.CreateFormField(fieldName)
	if err != nil {
		fc.err = errors.New(fmt.Sprintf("failed to create field %s ", fieldName))
		return
	}
	if _, err := fw.Write([]byte(fieldValue)); err != nil {
		fc.err = errors.New(fmt.Sprintf("failed to set %s value", fieldName))
		return
	}
}

// polling 2captcha response page until captcha is ready.
// initSleep represent 2captcha average time to solve captcha, don't make senses polling
// response before average time
func PollingCaptchaResponse(captchaId string, initSleep int, pollingTime int) (string, error) {
	return "", nil
}
<|endoftext|>


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

package main

import (
	"bytes"
	"expvar"
	"flag"
	"fmt"
	"http"
	"io"
	"log"
	"os"
	"strconv"
)

// hello world, the web server
var helloRequests = expvar.NewInt("hello-requests")

func HelloServer(w http.ResponseWriter, req *http.Request) {
	helloRequests.Add(1)
	io.WriteString(w, "hello, world!\n")
}

// Simple counter server. POSTing to it will set the value.
type Counter struct {
	n int
}

// This makes Counter satisfy the expvar.Var interface, so we can export
// it directly.
func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) }

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		ctr.n++
	case "POST":
		buf := new(bytes.Buffer)
		io.Copy(buf, req.Body)
		body := buf.String()
		if n, err := strconv.Atoi(body); err != nil {
			fmt.Fprintf(w, "bad POST: %v\nbody: [%v]\n", err, body)
		} else {
			ctr.n = n
			fmt.Fprint(w, "counter reset\n")
		}
	}
	fmt.Fprintf(w, "counter = %d\n", ctr.n)
}

// simple flag server
var booleanflag = flag.Bool("boolean", true, "another flag for testing")

func FlagServer(w http.ResponseWriter, req *http.Request) {
	w.Header.Set("Content-Type", "text/plain; charset=utf-8")
	fmt.Fprint(w, "Flags:\n")
	flag.VisitAll(func(f *flag.Flag) {
		if f.Value.String() != f.DefValue {
			fmt.Fprintf(w, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue)
		} else {
			fmt.Fprintf(w, "%s = %s\n", f.Name, f.Value.String())
		}
	})
}

// simple argument server
func ArgServer(w http.ResponseWriter, req *http.Request) {
	for _, s := range os.Args {
		fmt.Fprint(w, s, " ")
	}
}

// a channel (just for the fun of it)
type Chan chan int

func ChanCreate() Chan {
	c := make(Chan)
	go func(c Chan) {
		for x := 0; ; x++ {
			c <- x
		}
	}(c)
	return c
}

func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, fmt.Sprintf("channel send #%d\n", <-ch))
}

// exec a program, redirecting output
func DateServer(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
	r, w, err := os.Pipe()
	if err != nil {
		fmt.Fprintf(rw, "pipe: %s\n", err)
		return
	}

	p, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, w, w}})
	defer r.Close()
	w.Close()
	if err != nil {
		fmt.Fprintf(rw, "fork/exec: %s\n", err)
		return
	}
	defer p.Release()
	io.Copy(rw, r)
	wait, err := p.Wait(0)
	if err != nil {
		fmt.Fprintf(rw, "wait: %s\n", err)
		return
	}
	if !wait.Exited() || wait.ExitStatus() != 0 {
		fmt.Fprintf(rw, "date: %v\n", wait)
		return
	}
}

func Logger(w http.ResponseWriter, req *http.Request) {
	log.Print(req.URL.Raw)
	w.WriteHeader(404)
	w.Write([]byte("oops"))
}

var webroot = flag.String("root", "/home/rsc", "web root directory")

func main() {
	flag.Parse()

	// The counter is published as a variable directly.
	ctr := new(Counter)
	http.Handle("/counter", ctr)
	expvar.Publish("counter", ctr)

	http.Handle("/", http.HandlerFunc(Logger))
	http.Handle("/go/", http.FileServer(*webroot, "/go/"))
	http.Handle("/flags", http.HandlerFunc(FlagServer))
	http.Handle("/args", http.HandlerFunc(ArgServer))
	http.Handle("/go/hello", http.HandlerFunc(HelloServer))
	http.Handle("/chan", ChanCreate())
	http.Handle("/date", http.HandlerFunc(DateServer))
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Panicln("ListenAndServe:", err)
	}
}
<commit_msg>http: make triv.go example compile again<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"expvar"
	"flag"
	"fmt"
	"http"
	"io"
	"log"
	"os"
	"strconv"
)

// hello world, the web server
var helloRequests = expvar.NewInt("hello-requests")

func HelloServer(w http.ResponseWriter, req *http.Request) {
	helloRequests.Add(1)
	io.WriteString(w, "hello, world!\n")
}

// Simple counter server. POSTing to it will set the value.
type Counter struct {
	n int
}

// This makes Counter satisfy the expvar.Var interface, so we can export
// it directly.
func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) }

func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		ctr.n++
	case "POST":
		buf := new(bytes.Buffer)
		io.Copy(buf, req.Body)
		body := buf.String()
		if n, err := strconv.Atoi(body); err != nil {
			fmt.Fprintf(w, "bad POST: %v\nbody: [%v]\n", err, body)
		} else {
			ctr.n = n
			fmt.Fprint(w, "counter reset\n")
		}
	}
	fmt.Fprintf(w, "counter = %d\n", ctr.n)
}

// simple flag server
var booleanflag = flag.Bool("boolean", true, "another flag for testing")

func FlagServer(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	fmt.Fprint(w, "Flags:\n")
	flag.VisitAll(func(f *flag.Flag) {
		if f.Value.String() != f.DefValue {
			fmt.Fprintf(w, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue)
		} else {
			fmt.Fprintf(w, "%s = %s\n", f.Name, f.Value.String())
		}
	})
}

// simple argument server
func ArgServer(w http.ResponseWriter, req *http.Request) {
	for _, s := range os.Args {
		fmt.Fprint(w, s, " ")
	}
}

// a channel (just for the fun of it)
type Chan chan int

func ChanCreate() Chan {
	c := make(Chan)
	go func(c Chan) {
		for x := 0; ; x++ {
			c <- x
		}
	}(c)
	return c
}

func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, fmt.Sprintf("channel send #%d\n", <-ch))
}

// exec a program, redirecting output
func DateServer(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
	r, w, err := os.Pipe()
	if err != nil {
		fmt.Fprintf(rw, "pipe: %s\n", err)
		return
	}

	p, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, w, w}})
	defer r.Close()
	w.Close()
	if err != nil {
		fmt.Fprintf(rw, "fork/exec: %s\n", err)
		return
	}
	defer p.Release()
	io.Copy(rw, r)
	wait, err := p.Wait(0)
	if err != nil {
		fmt.Fprintf(rw, "wait: %s\n", err)
		return
	}
	if !wait.Exited() || wait.ExitStatus() != 0 {
		fmt.Fprintf(rw, "date: %v\n", wait)
		return
	}
}

func Logger(w http.ResponseWriter, req *http.Request) {
	log.Print(req.URL.Raw)
	w.WriteHeader(404)
	w.Write([]byte("oops"))
}

var webroot = flag.String("root", "/home/rsc", "web root directory")

func main() {
	flag.Parse()

	// The counter is published as a variable directly.
	ctr := new(Counter)
	http.Handle("/counter", ctr)
	expvar.Publish("counter", ctr)

	http.Handle("/", http.HandlerFunc(Logger))
	http.Handle("/go/", http.FileServer(*webroot, "/go/"))
	http.Handle("/flags", http.HandlerFunc(FlagServer))
	http.Handle("/args", http.HandlerFunc(ArgServer))
	http.Handle("/go/hello", http.HandlerFunc(HelloServer))
	http.Handle("/chan", ChanCreate())
	http.Handle("/date", http.HandlerFunc(DateServer))
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Panicln("ListenAndServe:", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/hex"
	"encoding/json"
	"fmt"
	MQTT "git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
	"github.com/PandoCloud/pando-cloud/pkg/protocol"
	"github.com/PandoCloud/pando-cloud/pkg/tlv"
	"log"
	"os"
	"time"
)

const (
	commonCmdGetStatus = uint16(65528)
)

// device register args
type DeviceRegisterArgs struct {
	ProductKey string `json:"product_key"  binding:"required"`
	DeviceCode string `json:"device_code"  binding:"required"`
	Version    string `json:"version"  binding:"required"`
}

// device authentication args
type DeviceAuthArgs struct {
	DeviceId     int64  `json:"device_id" binding:"required"`
	DeviceSecret string `json:"device_secret" binding:"required"`
	Protocol     string `json:"protocol" binding:"required"`
}

// common response fields
type Common struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// device register response data field
type DeviceRegisterData struct {
	DeviceId         int64  `json:"device_id"`
	DeviceSecret     string `json:"device_secret"`
	DeviceKey        string `json:"device_key"`
	DeviceIdentifier string `json:"device_identifier"`
}

// device register response
type DeviceRegisterResponse struct {
	Common
	Data DeviceRegisterData `json:"data"`
}

// device auth response data field
type DeviceAuthData struct {
	AccessToken string `json:"access_token"`
	AccessAddr  string `json:"access_addr"`
}

// device auth response
type DeviceAuthResponse struct {
	Common
	Data DeviceAuthData `json:"data"`
}

type Device struct {
	// API URL
	Url string

	// basic info
	ProductKey string
	DeviceCode string
	Version    string

	// private things
	id      int64
	secrect string
	token   []byte
	access  string
}

func NewDevice(url string, productkey string, code string, version string) *Device {

	return &Device{
		Url:        url,
		ProductKey: productkey,
		DeviceCode: code,
		Version:    version,
	}
}

func (d *Device) DoRegister() error {
	args := DeviceRegisterArgs{
		ProductKey: d.ProductKey,
		DeviceCode: d.DeviceCode,
		Version:    d.Version,
	}
	regUrl := fmt.Sprintf("%v%v", d.Url, "/v1/devices/registration")
	request, err := json.Marshal(args)
	if err != nil {
		return err
	}
	jsonresp, err := SendHttpRequest(regUrl, string(request), "POST", nil)
	if err != nil {
		return err
	}
	response := DeviceRegisterResponse{}
	err = json.Unmarshal(jsonresp, &response)
	if err != nil {
		return err
	}
	err = CheckHttpsCode(response)
	if err != nil {
		return err
	}

	d.id = response.Data.DeviceId
	d.secrect = response.Data.DeviceSecret

	return nil
}

func (d *Device) DoLogin() error {
	args := DeviceAuthArgs{
		DeviceId:     d.id,
		DeviceSecret: d.secrect,
		Protocol:     "mqtt",
	}
	regUrl := fmt.Sprintf("%v%v", d.Url, "/v1/devices/authentication")
	request, err := json.Marshal(args)
	if err != nil {
		return err
	}
	jsonresp, err := SendHttpRequest(regUrl, string(request), "POST", nil)
	if err != nil {
		return err
	}
	response := DeviceAuthResponse{}
	err = json.Unmarshal(jsonresp, &response)
	if err != nil {
		return err
	}
	err = CheckHttpsCode(response)
	if err != nil {
		return err
	}
	// ecode hex
	htoken, err := hex.DecodeString(response.Data.AccessToken)
	if err != nil {
		return err
	}
	d.token = htoken
	d.access = response.Data.AccessAddr

	return nil
}

func (d *Device) reportStatus(client *MQTT.Client) {

	payloadHead := protocol.DataHead{
		Flag:      0,
		Timestamp: uint64(time.Now().Unix() * 1000),
	}
	param := []interface{}{uint8(1)}
	params, err := tlv.MakeTLVs(param)
	if err != nil {
		fmt.Println(err)
		return
	}
	sub := protocol.SubData{
		Head: protocol.SubDataHead{
			SubDeviceid: uint16(1),
			PropertyNum: uint16(1),
			ParamsCount: uint16(len(params)),
		},
		Params: params,
	}

	status := protocol.Data{
		Head:    payloadHead,
		SubData: []protocol.SubData{},
	}

	status.SubData = append(status.SubData, sub)

	payload, err := status.Marshal()
	if err != nil {
		fmt.Println(err)
		return
	}

	client.Publish("d", 1, false, payload)

}

func (d *Device) statusHandler(client *MQTT.Client, msg MQTT.Message) {
	status := protocol.Data{}

	err := status.UnMarshal(msg.Payload())
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println("device receiving status set : ")

	for _, one := range status.SubData {
		fmt.Println("subdeviceid : ", one.Head.SubDeviceid)
		fmt.Println("no : ", one.Head.PropertyNum)
		fmt.Println("params : ", one.Params)
	}
}

func (d *Device) commandHandler(client *MQTT.Client, msg MQTT.Message) {
	cmd := protocol.Command{}

	err := cmd.UnMarshal(msg.Payload())
	if err != nil {
		fmt.Println(err)
		return
	}

	switch cmd.Head.No {
	case commonCmdGetStatus:
		d.reportStatus(client)
	default:
		fmt.Println("unsuported command : %v", cmd.Head.No)
	}
}

func (d *Device) messageHandler(client *MQTT.Client, msg MQTT.Message) {
	fmt.Printf("TOPIC: %s\n", msg.Topic())
	fmt.Printf("MSG: %x\n", msg.Payload())
	msgtype := msg.Topic()
	fmt.Println(msgtype)

	switch msgtype {
	case "c":
		d.commandHandler(client, msg)
	case "s":
		d.statusHandler(client, msg)
	default:
		fmt.Println("unsuported message type :", msgtype)
	}
}

func (d *Device) DoAccess() error {
	logger := log.New(os.Stdout, "", log.LstdFlags)
	MQTT.ERROR = logger
	MQTT.CRITICAL = logger
	MQTT.WARN = logger
	MQTT.DEBUG = logger

	//create a ClientOptions struct setting the broker address, clientid, turn
	//off trace output and set the default message handler
	opts := MQTT.NewClientOptions().AddBroker("tls://" + d.access)
	clientid := fmt.Sprintf("%x", d.id)
	opts.SetClientID(clientid)
	opts.SetUsername(clientid) // clientid as username
	opts.SetPassword(hex.EncodeToString(d.token))
	opts.SetKeepAlive(30 * time.Second)
	opts.SetDefaultPublishHandler(d.messageHandler)
	opts.SetTLSConfig(&tls.Config{Certificates: nil, InsecureSkipVerify: true})

	//create and start a client using the above ClientOptions
	c := MQTT.NewClient(opts)
	if token := c.Connect(); token.Wait() && token.Error() != nil {
		return token.Error()
	}

	// we just pause here to wait for messages
	<-make(chan int)

	defer c.Disconnect(250)

	return nil
}
<commit_msg>fix device status report topic mismatch bug.<commit_after>package main

import (
	"crypto/tls"
	"encoding/hex"
	"encoding/json"
	"fmt"
	MQTT "git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
	"github.com/PandoCloud/pando-cloud/pkg/protocol"
	"github.com/PandoCloud/pando-cloud/pkg/tlv"
	"log"
	"os"
	"time"
)

const (
	commonCmdGetStatus = uint16(65528)
)

// device register args
type DeviceRegisterArgs struct {
	ProductKey string `json:"product_key"  binding:"required"`
	DeviceCode string `json:"device_code"  binding:"required"`
	Version    string `json:"version"  binding:"required"`
}

// device authentication args
type DeviceAuthArgs struct {
	DeviceId     int64  `json:"device_id" binding:"required"`
	DeviceSecret string `json:"device_secret" binding:"required"`
	Protocol     string `json:"protocol" binding:"required"`
}

// common response fields
type Common struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// device register response data field
type DeviceRegisterData struct {
	DeviceId         int64  `json:"device_id"`
	DeviceSecret     string `json:"device_secret"`
	DeviceKey        string `json:"device_key"`
	DeviceIdentifier string `json:"device_identifier"`
}

// device register response
type DeviceRegisterResponse struct {
	Common
	Data DeviceRegisterData `json:"data"`
}

// device auth response data field
type DeviceAuthData struct {
	AccessToken string `json:"access_token"`
	AccessAddr  string `json:"access_addr"`
}

// device auth response
type DeviceAuthResponse struct {
	Common
	Data DeviceAuthData `json:"data"`
}

type Device struct {
	// API URL
	Url string

	// basic info
	ProductKey string
	DeviceCode string
	Version    string

	// private things
	id      int64
	secrect string
	token   []byte
	access  string
}

func NewDevice(url string, productkey string, code string, version string) *Device {

	return &Device{
		Url:        url,
		ProductKey: productkey,
		DeviceCode: code,
		Version:    version,
	}
}

func (d *Device) DoRegister() error {
	args := DeviceRegisterArgs{
		ProductKey: d.ProductKey,
		DeviceCode: d.DeviceCode,
		Version:    d.Version,
	}
	regUrl := fmt.Sprintf("%v%v", d.Url, "/v1/devices/registration")
	request, err := json.Marshal(args)
	if err != nil {
		return err
	}
	jsonresp, err := SendHttpRequest(regUrl, string(request), "POST", nil)
	if err != nil {
		return err
	}
	response := DeviceRegisterResponse{}
	err = json.Unmarshal(jsonresp, &response)
	if err != nil {
		return err
	}
	err = CheckHttpsCode(response)
	if err != nil {
		return err
	}

	d.id = response.Data.DeviceId
	d.secrect = response.Data.DeviceSecret

	return nil
}

func (d *Device) DoLogin() error {
	args := DeviceAuthArgs{
		DeviceId:     d.id,
		DeviceSecret: d.secrect,
		Protocol:     "mqtt",
	}
	regUrl := fmt.Sprintf("%v%v", d.Url, "/v1/devices/authentication")
	request, err := json.Marshal(args)
	if err != nil {
		return err
	}
	jsonresp, err := SendHttpRequest(regUrl, string(request), "POST", nil)
	if err != nil {
		return err
	}
	response := DeviceAuthResponse{}
	err = json.Unmarshal(jsonresp, &response)
	if err != nil {
		return err
	}
	err = CheckHttpsCode(response)
	if err != nil {
		return err
	}
	// ecode hex
	htoken, err := hex.DecodeString(response.Data.AccessToken)
	if err != nil {
		return err
	}
	d.token = htoken
	d.access = response.Data.AccessAddr

	return nil
}

func (d *Device) reportStatus(client *MQTT.Client) {

	payloadHead := protocol.DataHead{
		Flag:      0,
		Timestamp: uint64(time.Now().Unix() * 1000),
	}
	param := []interface{}{uint8(1)}
	params, err := tlv.MakeTLVs(param)
	if err != nil {
		fmt.Println(err)
		return
	}
	sub := protocol.SubData{
		Head: protocol.SubDataHead{
			SubDeviceid: uint16(1),
			PropertyNum: uint16(1),
			ParamsCount: uint16(len(params)),
		},
		Params: params,
	}

	status := protocol.Data{
		Head:    payloadHead,
		SubData: []protocol.SubData{},
	}

	status.SubData = append(status.SubData, sub)

	payload, err := status.Marshal()
	if err != nil {
		fmt.Println(err)
		return
	}

	client.Publish("s", 1, false, payload)

}

func (d *Device) statusHandler(client *MQTT.Client, msg MQTT.Message) {
	status := protocol.Data{}

	err := status.UnMarshal(msg.Payload())
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println("device receiving status set : ")

	for _, one := range status.SubData {
		fmt.Println("subdeviceid : ", one.Head.SubDeviceid)
		fmt.Println("no : ", one.Head.PropertyNum)
		fmt.Println("params : ", one.Params)
	}
}

func (d *Device) commandHandler(client *MQTT.Client, msg MQTT.Message) {
	cmd := protocol.Command{}

	err := cmd.UnMarshal(msg.Payload())
	if err != nil {
		fmt.Println(err)
		return
	}

	switch cmd.Head.No {
	case commonCmdGetStatus:
		d.reportStatus(client)
	default:
		fmt.Println("unsuported command : %v", cmd.Head.No)
	}
}

func (d *Device) messageHandler(client *MQTT.Client, msg MQTT.Message) {
	fmt.Printf("TOPIC: %s\n", msg.Topic())
	fmt.Printf("MSG: %x\n", msg.Payload())
	msgtype := msg.Topic()
	fmt.Println(msgtype)

	switch msgtype {
	case "c":
		d.commandHandler(client, msg)
	case "s":
		d.statusHandler(client, msg)
	default:
		fmt.Println("unsuported message type :", msgtype)
	}
}

func (d *Device) DoAccess() error {
	logger := log.New(os.Stdout, "", log.LstdFlags)
	MQTT.ERROR = logger
	MQTT.CRITICAL = logger
	MQTT.WARN = logger
	MQTT.DEBUG = logger

	//create a ClientOptions struct setting the broker address, clientid, turn
	//off trace output and set the default message handler
	opts := MQTT.NewClientOptions().AddBroker("tls://" + d.access)
	clientid := fmt.Sprintf("%x", d.id)
	opts.SetClientID(clientid)
	opts.SetUsername(clientid) // clientid as username
	opts.SetPassword(hex.EncodeToString(d.token))
	opts.SetKeepAlive(30 * time.Second)
	opts.SetDefaultPublishHandler(d.messageHandler)
	opts.SetTLSConfig(&tls.Config{Certificates: nil, InsecureSkipVerify: true})

	//create and start a client using the above ClientOptions
	c := MQTT.NewClient(opts)
	if token := c.Connect(); token.Wait() && token.Error() != nil {
		return token.Error()
	}

	// we just pause here to wait for messages
	<-make(chan int)

	defer c.Disconnect(250)

	return nil
}
<|endoftext|>


<commit_before>package highbatch

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"github.com/BurntSushi/toml"
	"github.com/robfig/cron"
	"io"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
	"fmt"
)

type Spec struct {
	Id          string   `json:"id"`
	Key         string   `json:"key"`
	Name        string   `json:"name"`
	Description string   `json:"description"`
	Cmd         string   `json:"cmd"`
	Schedule    string   `json:"schedule"`
	Route       []string `json:"route"`
	Chain       []string `json:"chain"`
	Error       string   `json:"error"`
	OnErrorStop string   `json:"onErrorStop"`
	Group       string   `json:"group"`
	Assets      []string `json:"assets"`

	Machine []string `json:"machine"`
	Tags    []string `json:"tags"`

	Hostname    string `json:"hostname"`
	Started     string `json:"started"`
	Completed   string `json:"completed"`
	Duration    string `json:"duration"`
	ExitCode    int    `json:"exitCode"`
	Output      string `json:"output"`
	DurationInt string `json:"durationInt"`
}

func StartArranger() {
	Ld("in startArranger")
	for {
		c := cron.New()

		specs := taskFileSerch()

		for i := range specs {
			spec := specs[i]
			Li(fmt.Sprintf("%v", spec))
			if spec.Schedule != "" {
				c.AddFunc(spec.Schedule, func() {
					sendWorker(spec)
				})
			}
		}
		c.Start()
		time.Sleep(10 * 60 * time.Second)
		c.Stop()
		c = nil
	}
}

func taskFileSerch() (specs []Spec) {
	Ld("in taskFileSerch")
	root := "tasks"

	if err := filepath.Walk(root,
		func(path string, info os.FileInfo, err error) error {

			isMatch, err := regexp.MatchString("\\.toml$", path)
			if err != nil {
				return err
			}

			if info.IsDir() || !isMatch {
				return nil
			}

			spec := parseSpec(path)
			specs = append(specs, spec)

			return nil

		}); err != nil {
		Le(err)
	}
	return
}

func findAssets(task string) (assets []string) {
	Ld("in findAssets")
	if err := filepath.Walk(task,
		func(path string, info os.FileInfo, err error) error {
			isMatch, err := regexp.MatchString("\\.toml$", path)
			if err != nil {
				return err
			}

			if info.IsDir() {
				return nil
			}

			if !isMatch {
				file := strings.Split(path, string(os.PathSeparator))[2]
				assets = append(assets, file)
				return nil
			}

			return nil
		}); err != nil {
		Le(err)
	}
	return assets
}

func copyAsset(path string, copyTo string) error {
	copyToParent := strings.Join([]string{"public", "tasks"}, string(os.PathSeparator))

	if _, err := os.Stat(copyToParent); err != nil {
		if err := os.Mkdir(copyToParent, 0777); err != nil {
			return err
		}
	}

	src, err := os.Open(path)
	defer src.Close()
	if err != nil {
		Le(err)
		return err
	}

	dst, err := os.Create(copyToParent + copyTo)
	if err != nil {
		Le(err)
		return err
	}

	if _, err := io.Copy(dst, src); err != nil {
		Le(err)
		return err
	}
	return nil
}

func parseSpec(path string) (s Spec) {
	Ld("in parseSpec")

	if _, err := toml.DecodeFile(path, &s); err != nil {
		Le(err)
	}

	Li(fmt.Sprint(path))

	name := strings.Split(path, string(os.PathSeparator))[1]
	key := md5.Sum([]byte(name))
	s.Key = hex.EncodeToString(key[:])
	s.Name = name
	s.Assets = findAssets(strings.Join([]string{"tasks", name}, string(os.PathSeparator)))

	return s
}

func sendWorker(spec Spec) {
	Ld("in sendWorkder")
	spec.Started = fmt.Sprint(time.Now().Format("20060102150405"), rand.Intn(9))
	client := &http.Client{}
	for i := range spec.Machine {

		spec.Hostname = spec.Machine[i]
		spec.Id = spec.Started + "_" + spec.Hostname + "_" + spec.Key

		if err := writeDB(spec); err != nil {
			Le(err)
		}

		m, _ := json.Marshal(spec)

		req, _ := http.NewRequest(
			"POST",
			"http://"+spec.Hostname+":8081/worker",
			bytes.NewBuffer(m),
		)
		req.Header.Set("Content-Type", "application/json; charset=utf-8")
		if _, err := client.Do(req); err != nil {
			Le(err)
		}
	}
}
<commit_msg>Workerに指示を出せなかった時のエラー対応<commit_after>package highbatch

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"github.com/BurntSushi/toml"
	"github.com/robfig/cron"
	"io"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
	"fmt"
	"errors"
)

type Spec struct {
	Id          string   `json:"id"`
	Key         string   `json:"key"`
	Name        string   `json:"name"`
	Description string   `json:"description"`
	Cmd         string   `json:"cmd"`
	Schedule    string   `json:"schedule"`
	Route       []string `json:"route"`
	Chain       []string `json:"chain"`
	Error       string   `json:"error"`
	OnErrorStop string   `json:"onErrorStop"`
	Group       string   `json:"group"`
	Assets      []string `json:"assets"`

	Machine []string `json:"machine"`
	Tags    []string `json:"tags"`

	Hostname    string `json:"hostname"`
	Started     string `json:"started"`
	Completed   string `json:"completed"`
	Duration    string `json:"duration"`
	ExitCode    int    `json:"exitCode"`
	Output      string `json:"output"`
	DurationInt string `json:"durationInt"`
}

func StartArranger() {
	Ld("in startArranger")
	for {
		c := cron.New()

		specs := taskFileSerch()

		for i := range specs {
			spec := specs[i]
			Li(fmt.Sprintf("%v", spec))
			if spec.Schedule != "" {
				c.AddFunc(spec.Schedule, func() {
					if spec, err := sendWorker(spec); err != nil {
						spec.ExitCode = 99
						spec.Output = err.Error()
						spec.Hostname = "unknown"
						spec.Completed = fmt.Sprint(time.Now().Format("20060102150405"), rand.Intn(9))

						go write(spec)
					}
				})
			}
		}
		c.Start()
		time.Sleep(10 * 60 * time.Second)
		c.Stop()
		c = nil
	}
}

func taskFileSerch() (specs []Spec) {
	Ld("in taskFileSerch")
	root := "tasks"

	if err := filepath.Walk(root,
		func(path string, info os.FileInfo, err error) error {

			isMatch, err := regexp.MatchString("\\.toml$", path)
			if err != nil {
				return err
			}

			if info.IsDir() || !isMatch {
				return nil
			}

			spec := parseSpec(path)
			specs = append(specs, spec)

			return nil

		}); err != nil {
		Le(err)
	}
	return
}

func findAssets(task string) (assets []string) {
	Ld("in findAssets")
	if err := filepath.Walk(task,
		func(path string, info os.FileInfo, err error) error {
			isMatch, err := regexp.MatchString("\\.toml$", path)
			if err != nil {
				return err
			}

			if info.IsDir() {
				return nil
			}

			if !isMatch {
				file := strings.Split(path, string(os.PathSeparator))[2]
				assets = append(assets, file)
				return nil
			}

			return nil
		}); err != nil {
		Le(err)
	}
	return assets
}

func copyAsset(path string, copyTo string) error {
	copyToParent := strings.Join([]string{"public", "tasks"}, string(os.PathSeparator))

	if _, err := os.Stat(copyToParent); err != nil {
		if err := os.Mkdir(copyToParent, 0777); err != nil {
			return err
		}
	}

	src, err := os.Open(path)
	defer src.Close()
	if err != nil {
		Le(err)
		return err
	}

	dst, err := os.Create(copyToParent + copyTo)
	if err != nil {
		Le(err)
		return err
	}

	if _, err := io.Copy(dst, src); err != nil {
		Le(err)
		return err
	}
	return nil
}

func parseSpec(path string) (s Spec) {
	Ld("in parseSpec")

	if _, err := toml.DecodeFile(path, &s); err != nil {
		Le(err)
	}

	Li(fmt.Sprint(path))

	name := strings.Split(path, string(os.PathSeparator))[1]
	key := md5.Sum([]byte(name))
	s.Key = hex.EncodeToString(key[:])
	s.Name = name
	s.Assets = findAssets(strings.Join([]string{"tasks", name}, string(os.PathSeparator)))

	return s
}

func sendWorker(spec Spec) (Spec, error) {
	Ld("in sendWorkder")
	spec.Started = fmt.Sprint(time.Now().Format("20060102150405"), rand.Intn(9))
	client := &http.Client{}
	for i := range spec.Machine {

		spec.Hostname = spec.Machine[i]
		spec.Id = spec.Started + "_" + spec.Hostname + "_" + spec.Key

		if err := writeDB(spec); err != nil {
			return spec, err
		}

		m, _ := json.Marshal(spec)

		req, err := http.NewRequest(
			"POST",
			"http://"+spec.Hostname+":8081/worker",
			bytes.NewBuffer(m),
		)
		if err != nil {
			return spec, err
		}

		req.Header.Set("Content-Type", "application/json; charset=utf-8")
		resp, err := client.Do(req)
		// defer resp.Body.Close()
		if err != nil {
			return spec, err
		}

		if resp.StatusCode != 200 {
			err := errors.New("HTTP Status code error")
			return spec, err
		}
	}
	return spec, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The HAProxy Ingress Controller 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 annotations

import (
	"fmt"
	"sort"
	"strconv"

	hatypes "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/types"
)

// MapBuilder ...
type MapBuilder struct {
	logger      types.Logger
	annPrefix   string
	annDefaults map[string]string
}

// Mapper ...
type Mapper struct {
	MapBuilder
	maps map[string][]*Map
}

// Map ...
//
// TODO rename URI to Hostpath -- currently this is a little mess.
// Fix also testCase data in order to represent a hostname+path.
// Hostname is the domain name. Path is the declared starting path on ingress
// Together they populate a map_beg() converter in order to match HAProxy's
// `base` sample fetch method.
//
type Map struct {
	Source *Source
	Link   hatypes.PathLink
	Value  string
}

// Source ...
type Source struct {
	Namespace string
	Name      string
	Type      string
}

// BackendConfig ...
type BackendConfig struct {
	Paths  hatypes.BackendPaths
	Config map[string]*ConfigValue
}

// ConfigValue ...
type ConfigValue struct {
	Source *Source
	Value  string
}

// NewMapBuilder ...
func NewMapBuilder(logger types.Logger, annPrefix string, annDefaults map[string]string) *MapBuilder {
	return &MapBuilder{
		logger:      logger,
		annPrefix:   annPrefix,
		annDefaults: annDefaults,
	}
}

// NewMapper ...
func (b *MapBuilder) NewMapper() *Mapper {
	return &Mapper{
		MapBuilder: *b,
		maps:       map[string][]*Map{},
	}
}

func (c *Mapper) addAnnotation(source *Source, link hatypes.PathLink, key, value string) (conflict bool) {
	conflict = false
	annMaps, found := c.maps[key]
	if link.IsEmpty() {
		// empty means default value
		panic("path link cannot be empty")
	}
	if found {
		for _, annMap := range annMaps {
			if annMap.Link == link {
				return annMap.Value != value
			}
		}
	}
	var realValue string
	var ok bool
	validator, found := validators[key]
	if found {
		if realValue, ok = validator(validate{logger: c.logger, source: source, key: key, value: value}); !ok {
			return
		}
	} else {
		realValue = value
	}
	annMaps = append(annMaps, &Map{
		Source: source,
		Link:   link,
		Value:  realValue,
	})
	c.maps[key] = annMaps
	return
}

// AddAnnotations ...
func (c *Mapper) AddAnnotations(source *Source, link hatypes.PathLink, ann map[string]string) (conflicts []string) {
	conflicts = make([]string, 0, len(ann))
	for key, value := range ann {
		if conflict := c.addAnnotation(source, link, key, value); conflict {
			conflicts = append(conflicts, key)
		}
	}
	return conflicts
}

// GetStrMap ...
func (c *Mapper) GetStrMap(key string) ([]*Map, bool) {
	annMaps, found := c.maps[key]
	if found && len(annMaps) > 0 {
		return annMaps, true
	}
	value, found := c.annDefaults[key]
	if found {
		return []*Map{{Value: value}}, true
	}
	return nil, false
}

// Get ...
func (c *Mapper) Get(key string) *ConfigValue {
	annMaps, found := c.GetStrMap(key)
	if !found {
		return &ConfigValue{}
	}
	value := &ConfigValue{
		Source: annMaps[0].Source,
		Value:  annMaps[0].Value,
	}
	if len(annMaps) > 1 {
		sources := make([]*Source, 0, len(annMaps))
		for _, annMap := range annMaps {
			if value.Value != annMap.Value {
				sources = append(sources, annMap.Source)
			}
		}
		if len(sources) > 0 {
			c.logger.Warn(
				"annotation '%s' from %s overrides the same annotation with distinct value from %s",
				c.annPrefix+key, value.Source, sources)
		}
	}
	return value
}

// ConfigOverwrite ...
type ConfigOverwrite func(path *hatypes.BackendPath, values map[string]*ConfigValue) map[string]*ConfigValue

// GetBackendConfig builds a generic BackendConfig using
// annotation maps registered previously as its data source
//
// An annotation map is a `map[<uri>]<value>` collected on
// ingress/service parsing phase. A HAProxy backend need a group
// of annotation keys - ie a group of maps - grouped by URI in
// order to create and apply ACLs.
//
// The rule of thumb on the final BackendConfig array is:
//
//   1. Every backend path must be declared, so a HAProxy method can
//      just `if len(BackendConfig) > 1 then need-acl`;
//   2. Added annotation means declared annotation (ingress, service
//      or default) so the config reader `Get<Type>FromMap()`` can
//      distinguish between `undeclared` and `declared empty`.
//
func (c *Mapper) GetBackendConfig(backend *hatypes.Backend, keys []string, overwrite ConfigOverwrite) []*BackendConfig {
	// all backend paths need to be declared, filling up previously with default values
	rawConfig := make(map[hatypes.PathLink]map[string]*ConfigValue, len(backend.Paths))
	for _, path := range backend.Paths {
		kv := make(map[string]*ConfigValue, len(keys))
		for _, key := range keys {
			if value, found := c.annDefaults[key]; found {
				kv[key] = &ConfigValue{
					Value: value,
				}
			}
		}
		rawConfig[path.Link] = kv
	}
	// populate rawConfig with declared annotations, grouping annotation maps by URI
	for _, key := range keys {
		if maps, found := c.GetStrMap(key); found {
			for _, m := range maps {
				// skip default value
				if !m.Link.IsEmpty() {
					if cfg, found := rawConfig[m.Link]; found {
						cfg[key] = &ConfigValue{
							Source: m.Source,
							Value:  m.Value,
						}
					} else {
						panic(fmt.Sprintf("backend '%s/%s' is missing hostname/path '%+v'", backend.Namespace, backend.Name, m.Link))
					}
				}
			}
		}
	}
	// iterate the URIs and create the BackendConfig array
	// most configs should have just one item with default kv
	config := make([]*BackendConfig, 0, 1)
	for link, kv := range rawConfig {
		path := backend.FindBackendPath(link)
		realKV := kv
		if overwrite != nil {
			realKV = overwrite(path, kv)
			if realKV == nil {
				realKV = map[string]*ConfigValue{}
			}
		}
		if cfg := findConfig(config, realKV); cfg != nil {
			cfg.Paths.Add(path)
		} else {
			config = append(config, &BackendConfig{
				Paths:  hatypes.NewBackendPaths(path),
				Config: realKV,
			})
		}
	}
	// rawConfig is a map which by definition does not have explicit order.
	// sort in order to the same input generates the same output
	sort.SliceStable(config, func(i, j int) bool {
		l1 := config[i].Paths.Items[0].Link
		l2 := config[j].Paths.Items[0].Link
		return l1.Less(l2, false)
	})
	return config
}

func findConfig(config []*BackendConfig, kv map[string]*ConfigValue) *BackendConfig {
	for _, cfg := range config {
		if cfg.ConfigEquals(kv) {
			return cfg
		}
	}
	return nil
}

// ConfigEquals ...
func (b *BackendConfig) ConfigEquals(other map[string]*ConfigValue) bool {
	if len(b.Config) != len(other) {
		return false
	}
	for key, value := range b.Config {
		if otherValue, found := other[key]; !found {
			return false
		} else if value.Value != otherValue.Value {
			return false
		}
	}
	return true
}

// Get ...
func (b *BackendConfig) Get(key string) *ConfigValue {
	if configValue, found := b.Config[key]; found && configValue != nil {
		return configValue
	}
	return &ConfigValue{}
}

// String ...
func (b *BackendConfig) String() string {
	return fmt.Sprintf("%+v", *b)
}

// String ...
func (cv *ConfigValue) String() string {
	return cv.Value
}

// Bool ...
func (cv *ConfigValue) Bool() bool {
	value, _ := strconv.ParseBool(cv.Value)
	return value
}

// Int ...
func (cv *ConfigValue) Int() int {
	value, _ := strconv.Atoi(cv.Value)
	return value
}

// Int64 ...
func (cv *ConfigValue) Int64() int64 {
	value, _ := strconv.ParseInt(cv.Value, 10, 0)
	return value
}

// FullName ...
func (s *Source) FullName() string {
	return s.Namespace + "/" + s.Name
}

// String ...
func (m *Map) String() string {
	return fmt.Sprintf("%+v", *m)
}

// String ...
func (s *Source) String() string {
	return s.Type + " '" + s.FullName() + "'"
}
<commit_msg>add per path config getter in the ann mapper<commit_after>/*
Copyright 2019 The HAProxy Ingress Controller 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 annotations

import (
	"fmt"
	"sort"
	"strconv"

	hatypes "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/types"
)

// MapBuilder ...
type MapBuilder struct {
	logger      types.Logger
	annPrefix   string
	annDefaults map[string]string
}

// Mapper ...
type Mapper struct {
	MapBuilder
	maps    map[string][]*Map
	configs map[hatypes.PathLink]*AnnConfig
}

// AnnConfig ...
type AnnConfig struct {
	mapper *Mapper
	keys   map[string]*ConfigValue
}

// Map ...
type Map struct {
	Source *Source
	Link   hatypes.PathLink
	Value  string
}

// Source ...
type Source struct {
	Namespace string
	Name      string
	Type      string
}

// BackendConfig ...
type BackendConfig struct {
	Paths  hatypes.BackendPaths
	Config map[string]*ConfigValue
}

// ConfigValue ...
type ConfigValue struct {
	Source *Source
	Value  string
}

// NewMapBuilder ...
func NewMapBuilder(logger types.Logger, annPrefix string, annDefaults map[string]string) *MapBuilder {
	return &MapBuilder{
		logger:      logger,
		annPrefix:   annPrefix,
		annDefaults: annDefaults,
	}
}

// NewMapper ...
func (b *MapBuilder) NewMapper() *Mapper {
	return &Mapper{
		MapBuilder: *b,
		//
		maps:    map[string][]*Map{},
		configs: map[hatypes.PathLink]*AnnConfig{},
	}
}

func newAnnConfig(mapper *Mapper) *AnnConfig {
	return &AnnConfig{
		mapper: mapper,
		keys:   map[string]*ConfigValue{},
	}
}

// Add a new annotation to the current mapper.
// Return the conflict state: true if a conflict was found, false if the annotation was assigned or at least handled
func (c *Mapper) addAnnotation(source *Source, link hatypes.PathLink, key, value string) bool {
	if link.IsEmpty() {
		// empty means default value, cannot register as an annotation
		panic("path link cannot be empty")
	}
	// check overlap
	config, configfound := c.configs[link]
	if !configfound {
		config = newAnnConfig(c)
		c.configs[link] = config
	}
	if cfg, found := config.keys[key]; found {
		return cfg.Value != value
	}
	// validate (bool; int; ...) and normalize (int "01" => "1"; ...)
	realValue := value
	if validator, found := validators[key]; found {
		var ok bool
		if realValue, ok = validator(validate{logger: c.logger, source: source, key: key, value: value}); !ok {
			return false
		}
	}
	// update internal fields
	config.keys[key] = &ConfigValue{
		Source: source,
		Value:  realValue,
	}
	annMaps, _ := c.maps[key]
	annMaps = append(annMaps, &Map{
		Source: source,
		Link:   link,
		Value:  realValue,
	})
	c.maps[key] = annMaps
	return false
}

// AddAnnotations ...
func (c *Mapper) AddAnnotations(source *Source, link hatypes.PathLink, ann map[string]string) (conflicts []string) {
	conflicts = make([]string, 0, len(ann))
	for key, value := range ann {
		if conflict := c.addAnnotation(source, link, key, value); conflict {
			conflicts = append(conflicts, key)
		}
	}
	return conflicts
}

// GetStrMap ...
func (c *Mapper) GetStrMap(key string) ([]*Map, bool) {
	annMaps, found := c.maps[key]
	if found && len(annMaps) > 0 {
		return annMaps, true
	}
	value, found := c.annDefaults[key]
	if found {
		return []*Map{{Value: value}}, true
	}
	return nil, false
}

// GetConfig ...
func (c *Mapper) GetConfig(link hatypes.PathLink) *AnnConfig {
	if config, found := c.configs[link]; found {
		return config
	}
	config := newAnnConfig(c)
	c.configs[link] = config
	return config
}

// Get ...
func (c *Mapper) Get(key string) *ConfigValue {
	annMaps, found := c.GetStrMap(key)
	if !found {
		return &ConfigValue{}
	}
	value := &ConfigValue{
		Source: annMaps[0].Source,
		Value:  annMaps[0].Value,
	}
	if len(annMaps) > 1 {
		sources := make([]*Source, 0, len(annMaps))
		for _, annMap := range annMaps {
			if value.Value != annMap.Value {
				sources = append(sources, annMap.Source)
			}
		}
		if len(sources) > 0 {
			c.logger.Warn(
				"annotation '%s' from %s overrides the same annotation with distinct value from %s",
				c.annPrefix+key, value.Source, sources)
		}
	}
	return value
}

// ConfigOverwrite ...
type ConfigOverwrite func(path *hatypes.BackendPath, values map[string]*ConfigValue) map[string]*ConfigValue

// GetBackendConfig builds a generic BackendConfig using
// annotation maps registered previously as its data source
//
// An annotation map is a `map[<uri>]<value>` collected on
// ingress/service parsing phase. A HAProxy backend need a group
// of annotation keys - ie a group of maps - grouped by URI in
// order to create and apply ACLs.
//
// The rule of thumb on the final BackendConfig array is:
//
//   1. Every backend path must be declared, so a HAProxy method can
//      just `if len(BackendConfig) > 1 then need-acl`;
//   2. Added annotation means declared annotation (ingress, service
//      or default) so the config reader `Get<Type>FromMap()`` can
//      distinguish between `undeclared` and `declared empty`.
//
func (c *Mapper) GetBackendConfig(backend *hatypes.Backend, keys []string, overwrite ConfigOverwrite) []*BackendConfig {
	// all backend paths need to be declared, filling up previously with default values
	rawConfig := make(map[hatypes.PathLink]map[string]*ConfigValue, len(backend.Paths))
	for _, path := range backend.Paths {
		kv := make(map[string]*ConfigValue, len(keys))
		for _, key := range keys {
			if value, found := c.annDefaults[key]; found {
				kv[key] = &ConfigValue{
					Value: value,
				}
			}
		}
		rawConfig[path.Link] = kv
	}
	// populate rawConfig with declared annotations, grouping annotation maps by URI
	for _, key := range keys {
		if maps, found := c.GetStrMap(key); found {
			for _, m := range maps {
				// skip default value
				if !m.Link.IsEmpty() {
					if cfg, found := rawConfig[m.Link]; found {
						cfg[key] = &ConfigValue{
							Source: m.Source,
							Value:  m.Value,
						}
					} else {
						panic(fmt.Sprintf("backend '%s/%s' is missing hostname/path '%+v'", backend.Namespace, backend.Name, m.Link))
					}
				}
			}
		}
	}
	// iterate the URIs and create the BackendConfig array
	// most configs should have just one item with default kv
	config := make([]*BackendConfig, 0, 1)
	for link, kv := range rawConfig {
		path := backend.FindBackendPath(link)
		realKV := kv
		if overwrite != nil {
			realKV = overwrite(path, kv)
			if realKV == nil {
				realKV = map[string]*ConfigValue{}
			}
		}
		if cfg := findConfig(config, realKV); cfg != nil {
			cfg.Paths.Add(path)
		} else {
			config = append(config, &BackendConfig{
				Paths:  hatypes.NewBackendPaths(path),
				Config: realKV,
			})
		}
	}
	// rawConfig is a map which by definition does not have explicit order.
	// sort in order to the same input generates the same output
	sort.SliceStable(config, func(i, j int) bool {
		l1 := config[i].Paths.Items[0].Link
		l2 := config[j].Paths.Items[0].Link
		return l1.Less(l2, false)
	})
	return config
}

func findConfig(config []*BackendConfig, kv map[string]*ConfigValue) *BackendConfig {
	for _, cfg := range config {
		if cfg.ConfigEquals(kv) {
			return cfg
		}
	}
	return nil
}

// Get ...
func (c *AnnConfig) Get(key string) *ConfigValue {
	if value, found := c.keys[key]; found {
		return value
	}
	if value, found := c.mapper.annDefaults[key]; found {
		return &ConfigValue{Value: value}
	}
	return &ConfigValue{}
}

// ConfigEquals ...
func (b *BackendConfig) ConfigEquals(other map[string]*ConfigValue) bool {
	if len(b.Config) != len(other) {
		return false
	}
	for key, value := range b.Config {
		if otherValue, found := other[key]; !found {
			return false
		} else if value.Value != otherValue.Value {
			return false
		}
	}
	return true
}

// Get ...
func (b *BackendConfig) Get(key string) *ConfigValue {
	if configValue, found := b.Config[key]; found && configValue != nil {
		return configValue
	}
	return &ConfigValue{}
}

// String ...
func (b *BackendConfig) String() string {
	return fmt.Sprintf("%+v", *b)
}

// String ...
func (cv *ConfigValue) String() string {
	return cv.Value
}

// Bool ...
func (cv *ConfigValue) Bool() bool {
	value, _ := strconv.ParseBool(cv.Value)
	return value
}

// Int ...
func (cv *ConfigValue) Int() int {
	value, _ := strconv.Atoi(cv.Value)
	return value
}

// Int64 ...
func (cv *ConfigValue) Int64() int64 {
	value, _ := strconv.ParseInt(cv.Value, 10, 0)
	return value
}

// FullName ...
func (s *Source) FullName() string {
	return s.Namespace + "/" + s.Name
}

// String ...
func (m *Map) String() string {
	return fmt.Sprintf("%+v", *m)
}

// String ...
func (s *Source) String() string {
	return s.Type + " '" + s.FullName() + "'"
}
<|endoftext|>