<commit_before>package docker

import (
	"bytes"
	"fmt"
	"github.com/mitchellh/packer/packer"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
)

type Communicator struct {
	ContainerId  string
	HostDir      string
	ContainerDir string
}

func (c *Communicator) Start(remote *packer.RemoteCmd) error {
	// Create a temporary file to store the output. Because of a bug in
	// Docker, sometimes all the output doesn't properly show up. This
	// file will capture ALL of the output, and we'll read that.
	//
	// https://github.com/dotcloud/docker/issues/2625
	outputFile, err := ioutil.TempFile(c.HostDir, "cmd")
	if err != nil {
		return err
	}
	outputFile.Close()
	defer os.Remove(outputFile.Name())

	// This file will store the exit code of the command once it is complete.
	exitCodePath := outputFile.Name() + "-exit"

	// Modify the remote command so that all the output of the commands
	// go to a single file and so that the exit code is redirected to
	// a single file. This lets us determine both when the command
	// is truly complete (because the file will have data), what the
	// exit status is (because Docker loses it because of the pty, not
	// Docker's fault), and get the output (Docker bug).
	remoteCmd := fmt.Sprintf("(%s) >%s 2>&1; echo $? >%s",
		remote.Command,
		filepath.Join(c.ContainerDir, filepath.Base(outputFile.Name())),
		filepath.Join(c.ContainerDir, filepath.Base(exitCodePath)))

	cmd := exec.Command("docker", "attach", c.ContainerId)
	stdin_w, err := cmd.StdinPipe()
	if err != nil {
		return err
	}

	log.Printf("Executing in container %s: %#v", c.ContainerId, remoteCmd)
	if err := cmd.Start(); err != nil {
		return err
	}

	go func() {
		defer stdin_w.Close()

		// This sleep needs to be here because of the issue linked to below.
		// Basically, without it, Docker will hang on reading stdin forever,
		// and won't see what we write, for some reason.
		//
		// https://github.com/dotcloud/docker/issues/2628
		time.Sleep(2 * time.Second)

		stdin_w.Write([]byte(remoteCmd + "\n"))
	}()

	err = cmd.Wait()
	if exitErr, ok := err.(*exec.ExitError); ok {
		exitStatus := 1

		// There is no process-independent way to get the REAL
		// exit status so we just try to go deeper.
		if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
			exitStatus = status.ExitStatus()
		}

		// Say that we ended, since if Docker itself failed, then
		// the command must've not run, or so we assume
		remote.SetExited(exitStatus)
		return nil
	}

	// Wait for the exit code to appear in our file...
	log.Println("Waiting for exit code to appear for remote command...")
	for {
		fi, err := os.Stat(exitCodePath)
		if err == nil && fi.Size() > 0 {
			break
		}

		time.Sleep(1 * time.Second)
	}

	// Read the exit code
	exitRaw, err := ioutil.ReadFile(exitCodePath)
	if err != nil {
		return err
	}

	exitStatus, err := strconv.ParseInt(string(bytes.TrimSpace(exitRaw)), 10, 0)
	if err != nil {
		return err
	}
	log.Printf("Executed command exit status: %d", exitStatus)

	// Read the output
	f, err := os.Open(outputFile.Name())
	if err != nil {
		return err
	}
	defer f.Close()

	if remote.Stdout != nil {
		io.Copy(remote.Stdout, f)
	} else {
		output, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}

		log.Printf("Command output: %s", string(output))
	}

	// Finally, we're done
	remote.SetExited(int(exitStatus))

	return nil
}

func (c *Communicator) Upload(dst string, src io.Reader) error {
	// Create a temporary file to store the upload
	tempfile, err := ioutil.TempFile(c.HostDir, "upload")
	if err != nil {
		return err
	}
	defer os.Remove(tempfile.Name())

	// Copy the contents to the temporary file
	_, err = io.Copy(tempfile, src)
	tempfile.Close()
	if err != nil {
		return err
	}

	// TODO(mitchellh): Copy the file into place
	cmd := &packer.RemoteCmd{
		Command: fmt.Sprintf("cp %s/%s %s", c.ContainerDir,
			filepath.Base(tempfile.Name()), dst),
	}

	if err := c.Start(cmd); err != nil {
		return err
	}

	// Wait for the copy to complete
	cmd.Wait()
	if cmd.ExitStatus != 0 {
		return fmt.Errorf("Upload failed with non-zero exit status: %d", cmd.ExitStatus)
	}

	return nil
}

func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
	return nil
}

func (c *Communicator) Download(src string, dst io.Writer) error {
	return nil
}
<commit_msg>builder/docker: remove the exit code file when we're done<commit_after>package docker

import (
	"bytes"
	"fmt"
	"github.com/mitchellh/packer/packer"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
)

type Communicator struct {
	ContainerId  string
	HostDir      string
	ContainerDir string
}

func (c *Communicator) Start(remote *packer.RemoteCmd) error {
	// Create a temporary file to store the output. Because of a bug in
	// Docker, sometimes all the output doesn't properly show up. This
	// file will capture ALL of the output, and we'll read that.
	//
	// https://github.com/dotcloud/docker/issues/2625
	outputFile, err := ioutil.TempFile(c.HostDir, "cmd")
	if err != nil {
		return err
	}
	outputFile.Close()
	defer os.Remove(outputFile.Name())

	// This file will store the exit code of the command once it is complete.
	exitCodePath := outputFile.Name() + "-exit"
	defer os.Remove(exitCodePath)

	// Modify the remote command so that all the output of the commands
	// go to a single file and so that the exit code is redirected to
	// a single file. This lets us determine both when the command
	// is truly complete (because the file will have data), what the
	// exit status is (because Docker loses it because of the pty, not
	// Docker's fault), and get the output (Docker bug).
	remoteCmd := fmt.Sprintf("(%s) >%s 2>&1; echo $? >%s",
		remote.Command,
		filepath.Join(c.ContainerDir, filepath.Base(outputFile.Name())),
		filepath.Join(c.ContainerDir, filepath.Base(exitCodePath)))

	cmd := exec.Command("docker", "attach", c.ContainerId)
	stdin_w, err := cmd.StdinPipe()
	if err != nil {
		return err
	}

	log.Printf("Executing in container %s: %#v", c.ContainerId, remoteCmd)
	if err := cmd.Start(); err != nil {
		return err
	}

	go func() {
		defer stdin_w.Close()

		// This sleep needs to be here because of the issue linked to below.
		// Basically, without it, Docker will hang on reading stdin forever,
		// and won't see what we write, for some reason.
		//
		// https://github.com/dotcloud/docker/issues/2628
		time.Sleep(2 * time.Second)

		stdin_w.Write([]byte(remoteCmd + "\n"))
	}()

	err = cmd.Wait()
	if exitErr, ok := err.(*exec.ExitError); ok {
		exitStatus := 1

		// There is no process-independent way to get the REAL
		// exit status so we just try to go deeper.
		if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
			exitStatus = status.ExitStatus()
		}

		// Say that we ended, since if Docker itself failed, then
		// the command must've not run, or so we assume
		remote.SetExited(exitStatus)
		return nil
	}

	// Wait for the exit code to appear in our file...
	log.Println("Waiting for exit code to appear for remote command...")
	for {
		fi, err := os.Stat(exitCodePath)
		if err == nil && fi.Size() > 0 {
			break
		}

		time.Sleep(1 * time.Second)
	}

	// Read the exit code
	exitRaw, err := ioutil.ReadFile(exitCodePath)
	if err != nil {
		return err
	}

	exitStatus, err := strconv.ParseInt(string(bytes.TrimSpace(exitRaw)), 10, 0)
	if err != nil {
		return err
	}
	log.Printf("Executed command exit status: %d", exitStatus)

	// Read the output
	f, err := os.Open(outputFile.Name())
	if err != nil {
		return err
	}
	defer f.Close()

	if remote.Stdout != nil {
		io.Copy(remote.Stdout, f)
	} else {
		output, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}

		log.Printf("Command output: %s", string(output))
	}

	// Finally, we're done
	remote.SetExited(int(exitStatus))

	return nil
}

func (c *Communicator) Upload(dst string, src io.Reader) error {
	// Create a temporary file to store the upload
	tempfile, err := ioutil.TempFile(c.HostDir, "upload")
	if err != nil {
		return err
	}
	defer os.Remove(tempfile.Name())

	// Copy the contents to the temporary file
	_, err = io.Copy(tempfile, src)
	tempfile.Close()
	if err != nil {
		return err
	}

	// TODO(mitchellh): Copy the file into place
	cmd := &packer.RemoteCmd{
		Command: fmt.Sprintf("cp %s/%s %s", c.ContainerDir,
			filepath.Base(tempfile.Name()), dst),
	}

	if err := c.Start(cmd); err != nil {
		return err
	}

	// Wait for the copy to complete
	cmd.Wait()
	if cmd.ExitStatus != 0 {
		return fmt.Errorf("Upload failed with non-zero exit status: %d", cmd.ExitStatus)
	}

	return nil
}

func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
	return nil
}

func (c *Communicator) Download(src string, dst io.Writer) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"

	"github.com/github/git-lfs/test"

	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

type TestObject struct {
	Oid  string
	Size int64
}

type ServerTest struct {
	Name string
	F    func(oidsExist, oidsMissing []TestObject) error
}

var (
	RootCmd = &cobra.Command{
		Use:   "git-lfs-test-server-api [--url=<apiurl> | --clone=<cloneurl>] [<oid-exists-file> <oid-missing-file>]",
		Short: "Test a Git LFS API server for compliance",
		Run:   testServerApi,
	}
	apiUrl   string
	cloneUrl string

	tests []ServerTest
)

func main() {
	RootCmd.Execute()
}

func testServerApi(cmd *cobra.Command, args []string) {

	if (len(apiUrl) == 0 && len(cloneUrl) == 0) ||
		(len(apiUrl) != 0 && len(cloneUrl) != 0) {
		exit("Must supply either --url or --clone (and not both)")
	}

	if len(args) != 0 && len(args) != 2 {
		exit("Must supply either no file arguments or both the exists AND missing file")
	}

	// Force loading of config before we alter it
	lfs.Config.AllGitConfig()

	// Configure the endpoint manually
	var endp lfs.Endpoint
	if len(cloneUrl) > 0 {
		endp = lfs.NewEndpointFromCloneURL(cloneUrl)
	} else {
		endp = lfs.NewEndpoint(apiUrl)
	}
	lfs.Config.SetManualEndpoint(endp)

	var oidsExist, oidsMissing []TestObject
	if len(args) >= 2 {
		fmt.Printf("Reading test data from files (no server content changes)\n")
		oidsExist = readTestOids(args[0])
		oidsMissing = readTestOids(args[1])
	} else {
		var err error
		oidsExist, oidsMissing, err = buildTestData()
		if err != nil {
			exit("Failed to set up test data, aborting")
		}
	}

	runTests(oidsExist, oidsMissing)
}

func readTestOids(filename string) []TestObject {
	f, err := os.OpenFile(filename, os.O_RDONLY, 0644)
	if err != nil {
		exit("Error opening file %s", filename)
	}
	defer f.Close()

	var ret []TestObject
	rdr := bufio.NewReader(f)
	line, err := rdr.ReadString('\n')
	for err == nil {
		fields := strings.Fields(strings.TrimSpace(line))
		if len(fields) == 2 {
			sz, _ := strconv.ParseInt(fields[1], 10, 64)
			ret = append(ret, TestObject{Oid: fields[0], Size: sz})
		}

		line, err = rdr.ReadString('\n')
	}

	return ret
}

type testDataCallback struct{}

func (*testDataCallback) Fatalf(format string, args ...interface{}) {
	exit(format, args...)
}
func (*testDataCallback) Errorf(format string, args ...interface{}) {
	fmt.Printf(format, args...)
}

func buildTestData() (oidsExist, oidsMissing []TestObject, err error) {
	const oidCount = 50
	oidsExist = make([]TestObject, 0, oidCount)
	oidsMissing = make([]TestObject, 0, oidCount)

	// Build test data for existing files & upload
	// Use test repo for this to simplify the process of making sure data matches oid
	// We're not performing a real test at this point (although an upload fail will break it)
	var callback testDataCallback
	repo := test.NewRepo(&callback)
	repo.Pushd()
	defer repo.Cleanup()
	// just one commit
	commit := test.CommitInput{CommitterName: "A N Other", CommitterEmail: "noone@somewhere.com"}
	var totalSize int64
	for i := 0; i < oidCount; i++ {
		filename := fmt.Sprintf("file%d.dat", i)
		sz := int64(rand.Intn(200)) + 50
		commit.Files = append(commit.Files, &test.FileInput{Filename: filename, Size: sz})
		totalSize += sz
	}
	outputs := repo.AddCommits([]*test.CommitInput{&commit})

	// now upload
	uploadQueue := lfs.NewUploadQueue(len(oidsExist), totalSize, false)
	for _, f := range outputs[0].Files {
		oidsExist = append(oidsExist, TestObject{Oid: f.Oid, Size: f.Size})

		u, err := lfs.NewUploadable(f.Oid, "Test file")
		if err != nil {
			return nil, nil, err
		}
		uploadQueue.Add(u)
	}
	uploadQueue.Wait()

	for _, err := range uploadQueue.Errors() {
		if lfs.IsFatalError(err) {
			exit("Fatal error setting up test data: %s", err)
		}
	}

	// Generate SHAs for missing files, random but repeatable
	// No actual file content needed for these
	rand.Seed(int64(oidCount))
	runningSha := sha256.New()
	for i := 0; i < oidCount; i++ {
		runningSha.Write([]byte{byte(rand.Intn(256))})
		oid := hex.EncodeToString(runningSha.Sum(nil))
		sz := int64(rand.Intn(200)) + 50
		oidsMissing = append(oidsMissing, TestObject{Oid: oid, Size: sz})
	}
	return oidsExist, oidsMissing, nil
}

func runTests(oidsExist, oidsMissing []TestObject) {

	fmt.Printf("Running %d tests...\n", len(tests))
	for _, t := range tests {
		runTest(t, oidsExist, oidsMissing)
	}

}

func runTest(t ServerTest, oidsExist, oidsMissing []TestObject) error {
	const linelen = 70
	line := t.Name
	if len(line) > linelen {
		line = line[:linelen]
	} else if len(line) < linelen {
		line = fmt.Sprintf("%s%s", line, strings.Repeat(" ", linelen-len(line)))
	}
	fmt.Printf("%s...\r", line)

	err := t.F(oidsExist, oidsMissing)
	if err != nil {
		fmt.Printf("%s FAILED\n", line)
		fmt.Println(err.Error())
	} else {
		fmt.Printf("%s OK\n", line)
	}
	return err
}

// Exit prints a formatted message and exits.
func exit(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	os.Exit(2)
}

func init() {
	RootCmd.Flags().StringVarP(&apiUrl, "url", "u", "", "URL of the API (must supply this or --clone)")
	RootCmd.Flags().StringVarP(&cloneUrl, "clone", "c", "", "Clone URL from which to find API (must supply this or --url)")
}
<commit_msg>Report construction of test data<commit_after>package main

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"

	"github.com/github/git-lfs/test"

	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

type TestObject struct {
	Oid  string
	Size int64
}

type ServerTest struct {
	Name string
	F    func(oidsExist, oidsMissing []TestObject) error
}

var (
	RootCmd = &cobra.Command{
		Use:   "git-lfs-test-server-api [--url=<apiurl> | --clone=<cloneurl>] [<oid-exists-file> <oid-missing-file>]",
		Short: "Test a Git LFS API server for compliance",
		Run:   testServerApi,
	}
	apiUrl   string
	cloneUrl string

	tests []ServerTest
)

func main() {
	RootCmd.Execute()
}

func testServerApi(cmd *cobra.Command, args []string) {

	if (len(apiUrl) == 0 && len(cloneUrl) == 0) ||
		(len(apiUrl) != 0 && len(cloneUrl) != 0) {
		exit("Must supply either --url or --clone (and not both)")
	}

	if len(args) != 0 && len(args) != 2 {
		exit("Must supply either no file arguments or both the exists AND missing file")
	}

	// Force loading of config before we alter it
	lfs.Config.AllGitConfig()

	// Configure the endpoint manually
	var endp lfs.Endpoint
	if len(cloneUrl) > 0 {
		endp = lfs.NewEndpointFromCloneURL(cloneUrl)
	} else {
		endp = lfs.NewEndpoint(apiUrl)
	}
	lfs.Config.SetManualEndpoint(endp)

	var oidsExist, oidsMissing []TestObject
	if len(args) >= 2 {
		fmt.Printf("Reading test data from files (no server content changes)\n")
		oidsExist = readTestOids(args[0])
		oidsMissing = readTestOids(args[1])
	} else {
		fmt.Printf("Creating test data (will upload to server)\n")
		var err error
		oidsExist, oidsMissing, err = buildTestData()
		if err != nil {
			exit("Failed to set up test data, aborting")
		}
	}

	runTests(oidsExist, oidsMissing)
}

func readTestOids(filename string) []TestObject {
	f, err := os.OpenFile(filename, os.O_RDONLY, 0644)
	if err != nil {
		exit("Error opening file %s", filename)
	}
	defer f.Close()

	var ret []TestObject
	rdr := bufio.NewReader(f)
	line, err := rdr.ReadString('\n')
	for err == nil {
		fields := strings.Fields(strings.TrimSpace(line))
		if len(fields) == 2 {
			sz, _ := strconv.ParseInt(fields[1], 10, 64)
			ret = append(ret, TestObject{Oid: fields[0], Size: sz})
		}

		line, err = rdr.ReadString('\n')
	}

	return ret
}

type testDataCallback struct{}

func (*testDataCallback) Fatalf(format string, args ...interface{}) {
	exit(format, args...)
}
func (*testDataCallback) Errorf(format string, args ...interface{}) {
	fmt.Printf(format, args...)
}

func buildTestData() (oidsExist, oidsMissing []TestObject, err error) {
	const oidCount = 50
	oidsExist = make([]TestObject, 0, oidCount)
	oidsMissing = make([]TestObject, 0, oidCount)

	// Build test data for existing files & upload
	// Use test repo for this to simplify the process of making sure data matches oid
	// We're not performing a real test at this point (although an upload fail will break it)
	var callback testDataCallback
	repo := test.NewRepo(&callback)
	repo.Pushd()
	defer repo.Cleanup()
	// just one commit
	commit := test.CommitInput{CommitterName: "A N Other", CommitterEmail: "noone@somewhere.com"}
	var totalSize int64
	for i := 0; i < oidCount; i++ {
		filename := fmt.Sprintf("file%d.dat", i)
		sz := int64(rand.Intn(200)) + 50
		commit.Files = append(commit.Files, &test.FileInput{Filename: filename, Size: sz})
		totalSize += sz
	}
	outputs := repo.AddCommits([]*test.CommitInput{&commit})

	// now upload
	uploadQueue := lfs.NewUploadQueue(len(oidsExist), totalSize, false)
	for _, f := range outputs[0].Files {
		oidsExist = append(oidsExist, TestObject{Oid: f.Oid, Size: f.Size})

		u, err := lfs.NewUploadable(f.Oid, "Test file")
		if err != nil {
			return nil, nil, err
		}
		uploadQueue.Add(u)
	}
	uploadQueue.Wait()

	for _, err := range uploadQueue.Errors() {
		if lfs.IsFatalError(err) {
			exit("Fatal error setting up test data: %s", err)
		}
	}

	// Generate SHAs for missing files, random but repeatable
	// No actual file content needed for these
	rand.Seed(int64(oidCount))
	runningSha := sha256.New()
	for i := 0; i < oidCount; i++ {
		runningSha.Write([]byte{byte(rand.Intn(256))})
		oid := hex.EncodeToString(runningSha.Sum(nil))
		sz := int64(rand.Intn(200)) + 50
		oidsMissing = append(oidsMissing, TestObject{Oid: oid, Size: sz})
	}
	return oidsExist, oidsMissing, nil
}

func runTests(oidsExist, oidsMissing []TestObject) {

	fmt.Printf("Running %d tests...\n", len(tests))
	for _, t := range tests {
		runTest(t, oidsExist, oidsMissing)
	}

}

func runTest(t ServerTest, oidsExist, oidsMissing []TestObject) error {
	const linelen = 70
	line := t.Name
	if len(line) > linelen {
		line = line[:linelen]
	} else if len(line) < linelen {
		line = fmt.Sprintf("%s%s", line, strings.Repeat(" ", linelen-len(line)))
	}
	fmt.Printf("%s...\r", line)

	err := t.F(oidsExist, oidsMissing)
	if err != nil {
		fmt.Printf("%s FAILED\n", line)
		fmt.Println(err.Error())
	} else {
		fmt.Printf("%s OK\n", line)
	}
	return err
}

// Exit prints a formatted message and exits.
func exit(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	os.Exit(2)
}

func init() {
	RootCmd.Flags().StringVarP(&apiUrl, "url", "u", "", "URL of the API (must supply this or --clone)")
	RootCmd.Flags().StringVarP(&cloneUrl, "clone", "c", "", "Clone URL from which to find API (must supply this or --url)")
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package binloginfo_test

import (
	"net"
	"os"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/ngaut/log"
	. "github.com/pingcap/check"
	"github.com/pingcap/tidb"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/ddl"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/sessionctx"
	"github.com/pingcap/tidb/sessionctx/binloginfo"
	"github.com/pingcap/tidb/store/tikv"
	"github.com/pingcap/tidb/util/codec"
	"github.com/pingcap/tidb/util/testkit"
	"github.com/pingcap/tidb/util/types"
	"github.com/pingcap/tipb/go-binlog"
	goctx "golang.org/x/net/context"
	"google.golang.org/grpc"
)

func TestT(t *testing.T) {
	CustomVerboseFlag = true
	TestingT(t)
}

type mockBinlogPump struct {
	mu struct {
		sync.Mutex
		payloads [][]byte
	}
}

func (p *mockBinlogPump) WriteBinlog(ctx goctx.Context, req *binlog.WriteBinlogReq) (*binlog.WriteBinlogResp, error) {
	p.mu.Lock()
	p.mu.payloads = append(p.mu.payloads, req.Payload)
	p.mu.Unlock()
	return &binlog.WriteBinlogResp{}, nil
}

// PullBinlogs implements PumpServer interface.
func (p *mockBinlogPump) PullBinlogs(req *binlog.PullBinlogReq, srv binlog.Pump_PullBinlogsServer) error {
	return nil
}

var _ = Suite(&testBinlogSuite{})

type testBinlogSuite struct {
	store    kv.Storage
	unixFile string
	serv     *grpc.Server
	pump     *mockBinlogPump
	tk       *testkit.TestKit
	ddl      ddl.DDL
}

func (s *testBinlogSuite) SetUpSuite(c *C) {
	logLevel := os.Getenv("log_level")
	log.SetLevelByString(logLevel)
	store, err := tikv.NewMockTikvStore("")
	c.Assert(err, IsNil)
	s.store = store
	tidb.SetSchemaLease(0)
	s.unixFile = "/tmp/mock-binlog-pump" + strconv.FormatInt(time.Now().UnixNano(), 10)
	l, err := net.Listen("unix", s.unixFile)
	c.Assert(err, IsNil)
	s.serv = grpc.NewServer()
	s.pump = new(mockBinlogPump)
	binlog.RegisterPumpServer(s.serv, s.pump)
	go s.serv.Serve(l)
	opt := grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
		return net.DialTimeout("unix", addr, timeout)
	})
	clientCon, err := grpc.Dial(s.unixFile, opt, grpc.WithInsecure())
	c.Assert(err, IsNil)
	c.Assert(clientCon, NotNil)
	binloginfo.PumpClient = binlog.NewPumpClient(clientCon)
	s.tk = testkit.NewTestKit(c, s.store)
	_, err = tidb.BootstrapSession(store)
	c.Assert(err, IsNil)
	s.tk.MustExec("use test")
	domain := sessionctx.GetDomain(s.tk.Se.(context.Context))
	s.ddl = domain.DDL()
}

func (s *testBinlogSuite) TearDownSuite(c *C) {
	s.ddl.Stop()
	binloginfo.PumpClient = nil
	s.serv.Stop()
	os.Remove(s.unixFile)
	s.store.Close()
}

func (s *testBinlogSuite) TestBinlog(c *C) {
	tk := s.tk
	pump := s.pump
	tk.MustExec("drop table if exists local_binlog")
	ddlQuery := "create table local_binlog (id int primary key, name varchar(10))"
	tk.MustExec(ddlQuery)
	var matched bool // got matched pre DDL and commit DDL
	for i := 0; i < 10; i++ {
		preDDL, commitDDL := getLatestDDLBinlog(c, pump, ddlQuery)
		if preDDL != nil && commitDDL != nil {
			if preDDL.DdlJobId == commitDDL.DdlJobId {
				c.Assert(commitDDL.StartTs, Equals, preDDL.StartTs)
				c.Assert(commitDDL.CommitTs, Greater, commitDDL.StartTs)
				matched = true
				break
			}
		}
		time.Sleep(time.Millisecond * 10)
	}
	c.Assert(matched, IsTrue)

	tk.MustExec("insert local_binlog values (1, 'abc'), (2, 'cde')")
	prewriteVal := getLatestBinlogPrewriteValue(c, pump)
	c.Assert(prewriteVal.SchemaVersion, Greater, int64(0))
	c.Assert(prewriteVal.Mutations[0].TableId, Greater, int64(0))
	expected := [][]types.Datum{
		{types.NewIntDatum(1), types.NewStringDatum("abc")},
		{types.NewIntDatum(2), types.NewStringDatum("cde")},
	}
	gotRows := mutationRowsToRows(c, prewriteVal.Mutations[0].InsertedRows, 0, 2)
	c.Assert(gotRows, DeepEquals, expected)

	tk.MustExec("update local_binlog set name = 'xyz' where id = 2")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	oldRow := [][]types.Datum{
		{types.NewIntDatum(2), types.NewStringDatum("cde")},
	}
	newRow := [][]types.Datum{
		{types.NewIntDatum(2), types.NewStringDatum("xyz")},
	}
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].UpdatedRows, 1, 3)
	c.Assert(gotRows, DeepEquals, oldRow)

	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].UpdatedRows, 5, 7)
	c.Assert(gotRows, DeepEquals, newRow)

	tk.MustExec("delete from local_binlog where id = 1")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].DeletedRows, 1, 3)
	expected = [][]types.Datum{
		{types.NewIntDatum(1), types.NewStringDatum("abc")},
	}
	c.Assert(gotRows, DeepEquals, expected)

	// Test table primary key is not integer.
	tk.MustExec("create table local_binlog2 (name varchar(64) primary key, age int)")
	tk.MustExec("insert local_binlog2 values ('abc', 16), ('def', 18)")
	tk.MustExec("delete from local_binlog2 where name = 'def'")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	c.Assert(prewriteVal.Mutations[0].Sequence[0], Equals, binlog.MutationType_DeleteRow)

	expected = [][]types.Datum{
		{types.NewStringDatum("def"), types.NewIntDatum(18)},
	}
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].DeletedRows, 1, 3)
	c.Assert(gotRows, DeepEquals, expected)

	// Test Table don't have primary key.
	tk.MustExec("create table local_binlog3 (c1 int, c2 int)")
	tk.MustExec("insert local_binlog3 values (1, 2), (1, 3), (2, 3)")
	tk.MustExec("update local_binlog3 set c1 = 3 where c1 = 2")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].UpdatedRows, 5, 7)
	expected = [][]types.Datum{
		{types.NewIntDatum(3), types.NewIntDatum(3)},
	}
	c.Assert(gotRows, DeepEquals, expected)

	tk.MustExec("delete from local_binlog3 where c1 = 3 and c2 = 3")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	c.Assert(prewriteVal.Mutations[0].Sequence[0], Equals, binlog.MutationType_DeleteRow)
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].DeletedRows, 1, 3)
	expected = [][]types.Datum{
		{types.NewIntDatum(3), types.NewIntDatum(3)},
	}
	c.Assert(gotRows, DeepEquals, expected)

	// Test Mutation Sequence.
	tk.MustExec("create table local_binlog4 (c1 int primary key, c2 int)")
	tk.MustExec("insert local_binlog4 values (1, 1), (2, 2), (3, 2)")
	tk.MustExec("begin")
	tk.MustExec("delete from local_binlog4 where c1 = 1")
	tk.MustExec("insert local_binlog4 values (1, 1)")
	tk.MustExec("update local_binlog4 set c2 = 3 where c1 = 3")
	tk.MustExec("commit")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	c.Assert(prewriteVal.Mutations[0].Sequence, DeepEquals, []binlog.MutationType{
		binlog.MutationType_DeleteRow,
		binlog.MutationType_Insert,
		binlog.MutationType_Update,
	})

	checkBinlogCount(c, pump)

	pump.mu.Lock()
	originBinlogLen := len(pump.mu.payloads)
	pump.mu.Unlock()
	tk.MustExec("set @@global.autocommit = 0")
	tk.MustExec("set @@global.autocommit = 1")
	pump.mu.Lock()
	newBinlogLen := len(pump.mu.payloads)
	pump.mu.Unlock()
	c.Assert(newBinlogLen, Equals, originBinlogLen)
}

func getLatestBinlogPrewriteValue(c *C, pump *mockBinlogPump) *binlog.PrewriteValue {
	var bin *binlog.Binlog
	pump.mu.Lock()
	for i := len(pump.mu.payloads) - 1; i >= 0; i-- {
		payload := pump.mu.payloads[i]
		bin = new(binlog.Binlog)
		bin.Unmarshal(payload)
		if bin.Tp == binlog.BinlogType_Prewrite {
			break
		}
	}
	pump.mu.Unlock()
	c.Assert(bin, NotNil)
	preVal := new(binlog.PrewriteValue)
	preVal.Unmarshal(bin.PrewriteValue)
	return preVal
}

func getLatestDDLBinlog(c *C, pump *mockBinlogPump, ddlQuery string) (preDDL, commitDDL *binlog.Binlog) {
	pump.mu.Lock()
	for i := len(pump.mu.payloads) - 1; i >= 0; i-- {
		payload := pump.mu.payloads[i]
		bin := new(binlog.Binlog)
		bin.Unmarshal(payload)
		if bin.Tp == binlog.BinlogType_Commit && bin.DdlJobId > 0 {
			commitDDL = bin
		}
		if bin.Tp == binlog.BinlogType_Prewrite && bin.DdlJobId != 0 {
			preDDL = bin
		}
		if preDDL != nil && commitDDL != nil {
			break
		}
	}
	pump.mu.Unlock()
	c.Assert(preDDL.DdlJobId, Greater, int64(0))
	c.Assert(preDDL.StartTs, Greater, int64(0))
	c.Assert(preDDL.CommitTs, Equals, int64(0))
	c.Assert(string(preDDL.DdlQuery), Equals, ddlQuery)
	return
}

func checkBinlogCount(c *C, pump *mockBinlogPump) {
	var bin *binlog.Binlog
	prewriteCount := 0
	ddlCount := 0
	pump.mu.Lock()
	length := len(pump.mu.payloads)
	for i := length - 1; i >= 0; i-- {
		payload := pump.mu.payloads[i]
		bin = new(binlog.Binlog)
		bin.Unmarshal(payload)
		if bin.Tp == binlog.BinlogType_Prewrite {
			if bin.DdlJobId != 0 {
				ddlCount++
			} else {
				prewriteCount++
			}
		}
	}
	pump.mu.Unlock()
	c.Assert(ddlCount, Greater, 0)
	match := false
	for i := 0; i < 10; i++ {
		pump.mu.Lock()
		length = len(pump.mu.payloads)
		pump.mu.Unlock()
		if (prewriteCount+ddlCount)*2 == length {
			match = true
			break
		}
		time.Sleep(time.Millisecond * 10)
	}
	c.Assert(match, IsTrue)
}

func mutationRowsToRows(c *C, mutationRows [][]byte, firstColumn, secondColumn int) [][]types.Datum {
	var rows [][]types.Datum
	for _, mutationRow := range mutationRows {
		datums, err := codec.Decode(mutationRow, 5)
		c.Assert(err, IsNil)
		for i := range datums {
			if datums[i].Kind() == types.KindBytes {
				datums[i].SetBytesAsString(datums[i].GetBytes())
			}
		}
		row := []types.Datum{datums[firstColumn], datums[secondColumn]}
		rows = append(rows, row)
	}
	return rows
}
<commit_msg>binloginfo: skip test for parallel issue (#3425)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package binloginfo_test

import (
	"net"
	"os"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/ngaut/log"
	. "github.com/pingcap/check"
	"github.com/pingcap/tidb"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/ddl"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/sessionctx"
	"github.com/pingcap/tidb/sessionctx/binloginfo"
	"github.com/pingcap/tidb/store/tikv"
	"github.com/pingcap/tidb/util/codec"
	"github.com/pingcap/tidb/util/testkit"
	"github.com/pingcap/tidb/util/types"
	"github.com/pingcap/tipb/go-binlog"
	goctx "golang.org/x/net/context"
	"google.golang.org/grpc"
)

func TestT(t *testing.T) {
	CustomVerboseFlag = true
	TestingT(t)
}

type mockBinlogPump struct {
	mu struct {
		sync.Mutex
		payloads [][]byte
	}
}

func (p *mockBinlogPump) WriteBinlog(ctx goctx.Context, req *binlog.WriteBinlogReq) (*binlog.WriteBinlogResp, error) {
	p.mu.Lock()
	p.mu.payloads = append(p.mu.payloads, req.Payload)
	p.mu.Unlock()
	return &binlog.WriteBinlogResp{}, nil
}

// PullBinlogs implements PumpServer interface.
func (p *mockBinlogPump) PullBinlogs(req *binlog.PullBinlogReq, srv binlog.Pump_PullBinlogsServer) error {
	return nil
}

var _ = Suite(&testBinlogSuite{})

type testBinlogSuite struct {
	store    kv.Storage
	unixFile string
	serv     *grpc.Server
	pump     *mockBinlogPump
	tk       *testkit.TestKit
	ddl      ddl.DDL
}

func (s *testBinlogSuite) SetUpSuite(c *C) {
	logLevel := os.Getenv("log_level")
	log.SetLevelByString(logLevel)
	store, err := tikv.NewMockTikvStore("")
	c.Assert(err, IsNil)
	s.store = store
	tidb.SetSchemaLease(0)
	s.unixFile = "/tmp/mock-binlog-pump" + strconv.FormatInt(time.Now().UnixNano(), 10)
	l, err := net.Listen("unix", s.unixFile)
	c.Assert(err, IsNil)
	s.serv = grpc.NewServer()
	s.pump = new(mockBinlogPump)
	binlog.RegisterPumpServer(s.serv, s.pump)
	go s.serv.Serve(l)
	opt := grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
		return net.DialTimeout("unix", addr, timeout)
	})
	clientCon, err := grpc.Dial(s.unixFile, opt, grpc.WithInsecure())
	c.Assert(err, IsNil)
	c.Assert(clientCon, NotNil)
	binloginfo.PumpClient = binlog.NewPumpClient(clientCon)
	s.tk = testkit.NewTestKit(c, s.store)
	_, err = tidb.BootstrapSession(store)
	c.Assert(err, IsNil)
	s.tk.MustExec("use test")
	domain := sessionctx.GetDomain(s.tk.Se.(context.Context))
	s.ddl = domain.DDL()
}

func (s *testBinlogSuite) TearDownSuite(c *C) {
	s.ddl.Stop()
	binloginfo.PumpClient = nil
	s.serv.Stop()
	os.Remove(s.unixFile)
	s.store.Close()
}

func (s *testBinlogSuite) TestBinlog(c *C) {
	// TODO: find a way to avoid this parallel test issue and remove skip.
	c.Skip("Some other package may run tests in parallel, makes the test fail.")
	tk := s.tk
	pump := s.pump
	tk.MustExec("drop table if exists local_binlog")
	ddlQuery := "create table local_binlog (id int primary key, name varchar(10))"
	tk.MustExec(ddlQuery)
	var matched bool // got matched pre DDL and commit DDL
	for i := 0; i < 10; i++ {
		preDDL, commitDDL := getLatestDDLBinlog(c, pump, ddlQuery)
		if preDDL != nil && commitDDL != nil {
			if preDDL.DdlJobId == commitDDL.DdlJobId {
				c.Assert(commitDDL.StartTs, Equals, preDDL.StartTs)
				c.Assert(commitDDL.CommitTs, Greater, commitDDL.StartTs)
				matched = true
				break
			}
		}
		time.Sleep(time.Millisecond * 10)
	}
	c.Assert(matched, IsTrue)

	tk.MustExec("insert local_binlog values (1, 'abc'), (2, 'cde')")
	prewriteVal := getLatestBinlogPrewriteValue(c, pump)
	c.Assert(prewriteVal.SchemaVersion, Greater, int64(0))
	c.Assert(prewriteVal.Mutations[0].TableId, Greater, int64(0))
	expected := [][]types.Datum{
		{types.NewIntDatum(1), types.NewStringDatum("abc")},
		{types.NewIntDatum(2), types.NewStringDatum("cde")},
	}
	gotRows := mutationRowsToRows(c, prewriteVal.Mutations[0].InsertedRows, 0, 2)
	c.Assert(gotRows, DeepEquals, expected)

	tk.MustExec("update local_binlog set name = 'xyz' where id = 2")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	oldRow := [][]types.Datum{
		{types.NewIntDatum(2), types.NewStringDatum("cde")},
	}
	newRow := [][]types.Datum{
		{types.NewIntDatum(2), types.NewStringDatum("xyz")},
	}
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].UpdatedRows, 1, 3)
	c.Assert(gotRows, DeepEquals, oldRow)

	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].UpdatedRows, 5, 7)
	c.Assert(gotRows, DeepEquals, newRow)

	tk.MustExec("delete from local_binlog where id = 1")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].DeletedRows, 1, 3)
	expected = [][]types.Datum{
		{types.NewIntDatum(1), types.NewStringDatum("abc")},
	}
	c.Assert(gotRows, DeepEquals, expected)

	// Test table primary key is not integer.
	tk.MustExec("create table local_binlog2 (name varchar(64) primary key, age int)")
	tk.MustExec("insert local_binlog2 values ('abc', 16), ('def', 18)")
	tk.MustExec("delete from local_binlog2 where name = 'def'")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	c.Assert(prewriteVal.Mutations[0].Sequence[0], Equals, binlog.MutationType_DeleteRow)

	expected = [][]types.Datum{
		{types.NewStringDatum("def"), types.NewIntDatum(18)},
	}
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].DeletedRows, 1, 3)
	c.Assert(gotRows, DeepEquals, expected)

	// Test Table don't have primary key.
	tk.MustExec("create table local_binlog3 (c1 int, c2 int)")
	tk.MustExec("insert local_binlog3 values (1, 2), (1, 3), (2, 3)")
	tk.MustExec("update local_binlog3 set c1 = 3 where c1 = 2")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].UpdatedRows, 5, 7)
	expected = [][]types.Datum{
		{types.NewIntDatum(3), types.NewIntDatum(3)},
	}
	c.Assert(gotRows, DeepEquals, expected)

	tk.MustExec("delete from local_binlog3 where c1 = 3 and c2 = 3")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	c.Assert(prewriteVal.Mutations[0].Sequence[0], Equals, binlog.MutationType_DeleteRow)
	gotRows = mutationRowsToRows(c, prewriteVal.Mutations[0].DeletedRows, 1, 3)
	expected = [][]types.Datum{
		{types.NewIntDatum(3), types.NewIntDatum(3)},
	}
	c.Assert(gotRows, DeepEquals, expected)

	// Test Mutation Sequence.
	tk.MustExec("create table local_binlog4 (c1 int primary key, c2 int)")
	tk.MustExec("insert local_binlog4 values (1, 1), (2, 2), (3, 2)")
	tk.MustExec("begin")
	tk.MustExec("delete from local_binlog4 where c1 = 1")
	tk.MustExec("insert local_binlog4 values (1, 1)")
	tk.MustExec("update local_binlog4 set c2 = 3 where c1 = 3")
	tk.MustExec("commit")
	prewriteVal = getLatestBinlogPrewriteValue(c, pump)
	c.Assert(prewriteVal.Mutations[0].Sequence, DeepEquals, []binlog.MutationType{
		binlog.MutationType_DeleteRow,
		binlog.MutationType_Insert,
		binlog.MutationType_Update,
	})

	checkBinlogCount(c, pump)

	pump.mu.Lock()
	originBinlogLen := len(pump.mu.payloads)
	pump.mu.Unlock()
	tk.MustExec("set @@global.autocommit = 0")
	tk.MustExec("set @@global.autocommit = 1")
	pump.mu.Lock()
	newBinlogLen := len(pump.mu.payloads)
	pump.mu.Unlock()
	c.Assert(newBinlogLen, Equals, originBinlogLen)
}

func getLatestBinlogPrewriteValue(c *C, pump *mockBinlogPump) *binlog.PrewriteValue {
	var bin *binlog.Binlog
	pump.mu.Lock()
	for i := len(pump.mu.payloads) - 1; i >= 0; i-- {
		payload := pump.mu.payloads[i]
		bin = new(binlog.Binlog)
		bin.Unmarshal(payload)
		if bin.Tp == binlog.BinlogType_Prewrite {
			break
		}
	}
	pump.mu.Unlock()
	c.Assert(bin, NotNil)
	preVal := new(binlog.PrewriteValue)
	preVal.Unmarshal(bin.PrewriteValue)
	return preVal
}

func getLatestDDLBinlog(c *C, pump *mockBinlogPump, ddlQuery string) (preDDL, commitDDL *binlog.Binlog) {
	pump.mu.Lock()
	for i := len(pump.mu.payloads) - 1; i >= 0; i-- {
		payload := pump.mu.payloads[i]
		bin := new(binlog.Binlog)
		bin.Unmarshal(payload)
		if bin.Tp == binlog.BinlogType_Commit && bin.DdlJobId > 0 {
			commitDDL = bin
		}
		if bin.Tp == binlog.BinlogType_Prewrite && bin.DdlJobId != 0 {
			preDDL = bin
		}
		if preDDL != nil && commitDDL != nil {
			break
		}
	}
	pump.mu.Unlock()
	c.Assert(preDDL.DdlJobId, Greater, int64(0))
	c.Assert(preDDL.StartTs, Greater, int64(0))
	c.Assert(preDDL.CommitTs, Equals, int64(0))
	c.Assert(string(preDDL.DdlQuery), Equals, ddlQuery)
	return
}

func checkBinlogCount(c *C, pump *mockBinlogPump) {
	var bin *binlog.Binlog
	prewriteCount := 0
	ddlCount := 0
	pump.mu.Lock()
	length := len(pump.mu.payloads)
	for i := length - 1; i >= 0; i-- {
		payload := pump.mu.payloads[i]
		bin = new(binlog.Binlog)
		bin.Unmarshal(payload)
		if bin.Tp == binlog.BinlogType_Prewrite {
			if bin.DdlJobId != 0 {
				ddlCount++
			} else {
				prewriteCount++
			}
		}
	}
	pump.mu.Unlock()
	c.Assert(ddlCount, Greater, 0)
	match := false
	for i := 0; i < 10; i++ {
		pump.mu.Lock()
		length = len(pump.mu.payloads)
		pump.mu.Unlock()
		if (prewriteCount+ddlCount)*2 == length {
			match = true
			break
		}
		time.Sleep(time.Millisecond * 10)
	}
	c.Assert(match, IsTrue)
}

func mutationRowsToRows(c *C, mutationRows [][]byte, firstColumn, secondColumn int) [][]types.Datum {
	var rows [][]types.Datum
	for _, mutationRow := range mutationRows {
		datums, err := codec.Decode(mutationRow, 5)
		c.Assert(err, IsNil)
		for i := range datums {
			if datums[i].Kind() == types.KindBytes {
				datums[i].SetBytesAsString(datums[i].GetBytes())
			}
		}
		row := []types.Datum{datums[firstColumn], datums[secondColumn]}
		rows = append(rows, row)
	}
	return rows
}
<|endoftext|>


<commit_before>package flotilla

import (
	"fmt"
	"io"
	"reflect"

	"github.com/simulatedsimian/flotilla/dock"
	"github.com/tarm/serial"
)

// Event is a wrapper around a dock.Event that contains an additional Dock index
type Event struct {
	dock.Event
	dockIndex int
}

type Client struct {
	ports           []io.ReadWriteCloser
	docks           []*dock.Dock
	connecteModules map[ModuleAddress]Updateable
	modules         []Updateable
	eventChan       chan Event
}

func structMembersToInterfaces(moduleStructPtr interface{}) (res []interface{}) {

	typeof := reflect.TypeOf(moduleStructPtr)

	if typeof.Kind() != reflect.Ptr && typeof.Elem().Kind() != reflect.Struct {
		panic("modules supplied to Client.AquireModules not a struct pointer")
	}

	fields := typeof.Elem().NumField()
	for i := 0; i < fields; i++ {
		iface := reflect.ValueOf(moduleStructPtr).Elem().Field(i).Addr().Interface()
		res = append(res, iface)
	}
	return
}

func (c *Client) AquireModules(moduleStructPtr interface{}) {

	modules := structMembersToInterfaces(moduleStructPtr)

	for _, m := range modules {
		module := reflect.ValueOf(m).Elem().FieldByName("Module")
		if module.IsValid() {
			if mod, ok := module.Addr().Interface().(*Module); ok {
				mod.client = c
				mod.address = ModuleAddress{-1, -1}
				if u, ok := module.Addr().Interface().(Updateable); ok {
					c.modules = append(c.modules, u)
				}
			}
		}
	}
}

func (c *Client) Run() error {
	for {
		err := c.processEvent()
		if err != nil {
			return err
		}
	}
}

func (c *Client) processEvent() error {
	ev := <-c.eventChan
	if ev.EventType == dock.EventError {
		return ev.Error
	}

	addr := ModuleAddress{dock: ev.dockIndex, channel: ev.Channel}

	if m, ok := c.connecteModules[addr]; ok {
		m.Update(ev)
		if !m.Connected() {
			delete(c.connecteModules, addr)
		}
		return nil
	}

	if ev.EventType == dock.EventConnected {
		for _, m := range c.modules {
			if !m.Connected() {
				m.Update(ev)
				if m.Connected() {
					c.connecteModules[addr] = m
					break
				}
			}
		}
	}
	return nil
}

func (c *Client) Close() {
	for _, p := range c.ports {
		p.Close()
	}
	c.docks = nil
}

func makeClient() *Client {
	client := Client{}
	client.eventChan = make(chan Event, 100)
	client.connecteModules = make(map[ModuleAddress]Updateable)
	return &client
}

func ConnectToDock(serialport string) (*Client, error) {
	return ConnectToDocks(serialport)
}

func ConnectToDocks(serialports ...string) (*Client, error) {
	if len(serialports) == 0 {
		return nil, fmt.Errorf("ConnectToDocks: No Serial Ports supplied")
	}

	ports := []io.ReadWriteCloser{}

	for i, s := range serialports {
		serialcfg := serial.Config{Name: s, Baud: 115200}
		port, err := serial.OpenPort(&serialcfg)
		if err != nil {
			for _, p := range ports {
				p.Close()
			}
			return nil, fmt.Errorf("Failed to connect to Dock %d (%s): %v", i, s, err)
		}
		ports = append(ports, port)
	}

	return ConnectToDocksRaw(ports...)
}

func ConnectToDocksRaw(ports ...io.ReadWriteCloser) (*Client, error) {
	client := makeClient()

	for _, port := range ports {
		client.ports = append(client.ports, port)
		client.docks = append(client.docks, dock.ConnectDock(port))
	}

	// create a go routine for each dock that reads the event and gives it to the
	// common client event chan along with source dock index
	for i, d := range client.docks {
		go func(d *dock.Dock, dockIndex int) {
			ev := <-d.Events
			client.eventChan <- Event{ev, dockIndex}
		}(d, i)
	}

	return client, nil
}

func FindDocks() (*Client, error) {
	return nil, fmt.Errorf("Find Docks Not Implemented Yet")
}
<commit_msg>got events working<commit_after>package flotilla

import (
	"fmt"
	"io"
	"reflect"

	"github.com/simulatedsimian/flotilla/dock"
	"github.com/tarm/serial"
)

// Event is a wrapper around a dock.Event that contains an additional Dock index
type Event struct {
	dock.Event
	dockIndex int
}

type Client struct {
	ports           []io.ReadWriteCloser
	docks           []*dock.Dock
	connecteModules map[ModuleAddress]Updateable
	modules         []Updateable
	eventChan       chan Event
}

func structMembersToInterfaces(moduleStructPtr interface{}) (res []interface{}) {

	typeof := reflect.TypeOf(moduleStructPtr)

	if typeof.Kind() != reflect.Ptr && typeof.Elem().Kind() != reflect.Struct {
		panic("modules supplied to Client.AquireModules not a struct pointer")
	}

	fields := typeof.Elem().NumField()
	for i := 0; i < fields; i++ {
		iface := reflect.ValueOf(moduleStructPtr).Elem().Field(i).Addr().Interface()
		res = append(res, iface)
	}
	return
}

func (c *Client) AquireModules(moduleStructPtr interface{}) {

	modules := structMembersToInterfaces(moduleStructPtr)

	for _, m := range modules {
		module := reflect.ValueOf(m).Elem().FieldByName("Module")
		if module.IsValid() {
			if mod, ok := module.Addr().Interface().(*Module); ok {
				mod.client = c
				mod.address = ModuleAddress{-1, -1}
				if u, ok := reflect.ValueOf(m).Interface().(Updateable); ok {
					mod.moduleType = u.Type()
					c.modules = append(c.modules, u)
				}
			}
		}
	}
}

func (c *Client) Run() error {
	for {
		err := c.processEvent()
		if err != nil {
			return err
		}
	}
}

func (c *Client) processEvent() error {
	ev := <-c.eventChan
	if ev.EventType == dock.EventError {
		return ev.Error
	}

	addr := ModuleAddress{dock: ev.dockIndex, channel: ev.Channel}

	if m, ok := c.connecteModules[addr]; ok {
		m.Update(ev)
		if !m.Connected() {
			delete(c.connecteModules, addr)
		}
		return nil
	}

	if ev.EventType == dock.EventConnected {
		for _, m := range c.modules {
			if !m.Connected() {
				m.Update(ev)
				if m.Connected() {
					c.connecteModules[addr] = m
					break
				}
			}
		}
	}
	return nil
}

func (c *Client) Close() {
	for _, p := range c.ports {
		p.Close()
	}
	c.docks = nil
}

func makeClient() *Client {
	client := Client{}
	client.eventChan = make(chan Event, 100)
	client.connecteModules = make(map[ModuleAddress]Updateable)
	return &client
}

func ConnectToDock(serialport string) (*Client, error) {
	return ConnectToDocks(serialport)
}

func ConnectToDocks(serialports ...string) (*Client, error) {
	if len(serialports) == 0 {
		return nil, fmt.Errorf("ConnectToDocks: No Serial Ports supplied")
	}

	ports := []io.ReadWriteCloser{}

	for i, s := range serialports {
		serialcfg := serial.Config{Name: s, Baud: 115200}
		port, err := serial.OpenPort(&serialcfg)
		if err != nil {
			for _, p := range ports {
				p.Close()
			}
			return nil, fmt.Errorf("Failed to connect to Dock %d (%s): %v", i, s, err)
		}
		ports = append(ports, port)
	}

	return ConnectToDocksRaw(ports...)
}

func ConnectToDocksRaw(ports ...io.ReadWriteCloser) (*Client, error) {
	client := makeClient()

	for _, port := range ports {
		client.ports = append(client.ports, port)
		client.docks = append(client.docks, dock.ConnectDock(port))
	}

	// create a go routine for each dock that reads the event and gives it to the
	// common client event chan along with source dock index
	for i, d := range client.docks {
		go func(d *dock.Dock, dockIndex int) {
			ev := <-d.Events
			client.eventChan <- Event{ev, dockIndex}
		}(d, i)
	}

	return client, nil
}

func FindDocks() (*Client, error) {
	return nil, fmt.Errorf("Find Docks Not Implemented Yet")
}
<|endoftext|>


<commit_before>// Copyright 2021 The LUCI 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 resultdb

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"

	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/sync/parallel"
	"go.chromium.org/luci/gae/service/info"
	"go.chromium.org/luci/grpc/prpc"
	rdbPb "go.chromium.org/luci/resultdb/proto/v1"
	"go.chromium.org/luci/server/auth"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"

	"go.chromium.org/luci/buildbucket/appengine/model"
	pb "go.chromium.org/luci/buildbucket/proto"
	"go.chromium.org/luci/buildbucket/protoutil"
)

var mockRecorderClientKey = "used in tests only for setting the mock recorder client"

// CreateInvocations creates resultdb invocations for each build.
// build.Proto.Infra.Resultdb must not be nil.
//
// cfgs is the builder config map with the struct of Bucket ID -> Builder name -> *pb.Builder.
//
// Note: it will mutate the value of build.Proto.Infra.Resultdb.Invocation and build.ResultDBUpdateToken.
func CreateInvocations(ctx context.Context, builds []*model.Build, cfgs map[string]map[string]*pb.Builder, host string) error {
	bbHost := info.AppID(ctx) + ".appspot.com"

	err := parallel.WorkPool(64, func(ch chan<- func() error) {
		for _, b := range builds {
			b := b
			proj := b.Proto.Builder.Project
			cfg := cfgs[protoutil.FormatBucketID(proj, b.Proto.Builder.Bucket)][b.Proto.Builder.Builder]
			if !cfg.GetResultdb().GetEnable() {
				continue
			}
			realm := b.Realm()
			if realm == "" {
				logging.Warningf(ctx, fmt.Sprintf("the builder %q has resultDB enabled while the build %d doesn't have realm", b.Proto.Builder.Builder, b.Proto.Id))
				continue
			}
			ch <- func() error {
				// TODO(crbug/1042991): After build scheduling flow also dedups number not just the id,
				// we can combine build id invocation and number invocation into a Batch.

				// Use per-project credential to create invocation.
				recorderClient, err := newRecorderClient(ctx, host, proj)
				if err != nil {
					return errors.Annotate(err, "failed to create resultDB recorder client for project: %s", proj).Err()
				}

				// Make a call to create build id invocation.
				invID := fmt.Sprintf("build-%d", b.Proto.Id)
				reqForBldID := &rdbPb.CreateInvocationRequest{
					InvocationId: invID,
					Invocation: &rdbPb.Invocation{
						BigqueryExports:  cfg.Resultdb.BqExports,
						ProducerResource: fmt.Sprintf("//%s/builds/%d", bbHost, b.Proto.Id),
						Realm:            realm,
						HistoryOptions: &rdbPb.HistoryOptions{
							UseInvocationTimestamp: cfg.Resultdb.HistoryOptions.UseInvocationTimestamp,
						},
					},
					RequestId: invID,
				}
				header := metadata.MD{}
				if _, err = recorderClient.CreateInvocation(ctx, reqForBldID, grpc.Header(&header)); err != nil {
					return errors.Annotate(err, "failed to create the invocation for build id: %d", b.Proto.Id).Err()
				}
				token, ok := header["update-token"]
				if !ok {
					return errors.Reason("CreateInvocation response doesn't have update-token header for build id: %d", b.Proto.Id).Err()
				}
				b.ResultDBUpdateToken = token[0]
				b.Proto.Infra.Resultdb.Invocation = fmt.Sprintf("invocations/%s", reqForBldID.InvocationId)

				// Create another invocation for the build number in which it includes the invocation for build id,
				// If the build has the Number field populated.
				if b.Proto.Number > 0 {
					sha256Builder := sha256.Sum256([]byte(protoutil.FormatBuilderID(b.Proto.Builder)))
					_, err = recorderClient.CreateInvocation(ctx, &rdbPb.CreateInvocationRequest{
						InvocationId: fmt.Sprintf("build-%s-%d", hex.EncodeToString(sha256Builder[:]), b.Proto.Number),
						Invocation: &rdbPb.Invocation{
							State:               rdbPb.Invocation_FINALIZING,
							ProducerResource:    reqForBldID.Invocation.ProducerResource,
							Realm:               realm,
							IncludedInvocations: []string{fmt.Sprintf("invocations/%s", reqForBldID.InvocationId)},
						},
						RequestId: fmt.Sprintf("build-%d-%d", b.Proto.Id, b.Proto.Number),
					})
					if err != nil {
						return errors.Annotate(err, "failed to create the invocation for build number: %d (build id: %d)", b.Proto.Number, b.Proto.Id).Err()
					}
				}
				return nil
			}
		}
	})

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

func newRecorderClient(ctx context.Context, host string, project string) (rdbPb.RecorderClient, error) {
	if mockClient, ok := ctx.Value(&mockRecorderClientKey).(*rdbPb.MockRecorderClient); ok {
		return mockClient, nil
	}

	t, err := auth.GetRPCTransport(ctx, auth.AsProject, auth.WithProject(project))
	if err != nil {
		return nil, err
	}
	return rdbPb.NewRecorderPRPCClient(
		&prpc.Client{
			C:    &http.Client{Transport: t},
			Host: host,
		}), nil
}
<commit_msg>[buildbucket] Handle nil history options<commit_after>// Copyright 2021 The LUCI 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 resultdb

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"

	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/sync/parallel"
	"go.chromium.org/luci/gae/service/info"
	"go.chromium.org/luci/grpc/prpc"
	rdbPb "go.chromium.org/luci/resultdb/proto/v1"
	"go.chromium.org/luci/server/auth"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"

	"go.chromium.org/luci/buildbucket/appengine/model"
	pb "go.chromium.org/luci/buildbucket/proto"
	"go.chromium.org/luci/buildbucket/protoutil"
)

var mockRecorderClientKey = "used in tests only for setting the mock recorder client"

// CreateInvocations creates resultdb invocations for each build.
// build.Proto.Infra.Resultdb must not be nil.
//
// cfgs is the builder config map with the struct of Bucket ID -> Builder name -> *pb.Builder.
//
// Note: it will mutate the value of build.Proto.Infra.Resultdb.Invocation and build.ResultDBUpdateToken.
func CreateInvocations(ctx context.Context, builds []*model.Build, cfgs map[string]map[string]*pb.Builder, host string) error {
	bbHost := info.AppID(ctx) + ".appspot.com"

	err := parallel.WorkPool(64, func(ch chan<- func() error) {
		for _, b := range builds {
			b := b
			proj := b.Proto.Builder.Project
			cfg := cfgs[protoutil.FormatBucketID(proj, b.Proto.Builder.Bucket)][b.Proto.Builder.Builder]
			if !cfg.GetResultdb().GetEnable() {
				continue
			}
			realm := b.Realm()
			if realm == "" {
				logging.Warningf(ctx, fmt.Sprintf("the builder %q has resultDB enabled while the build %d doesn't have realm", b.Proto.Builder.Builder, b.Proto.Id))
				continue
			}
			ch <- func() error {
				// TODO(crbug/1042991): After build scheduling flow also dedups number not just the id,
				// we can combine build id invocation and number invocation into a Batch.

				// Use per-project credential to create invocation.
				recorderClient, err := newRecorderClient(ctx, host, proj)
				if err != nil {
					return errors.Annotate(err, "failed to create resultDB recorder client for project: %s", proj).Err()
				}

				// Make a call to create build id invocation.
				invID := fmt.Sprintf("build-%d", b.Proto.Id)
				reqForBldID := &rdbPb.CreateInvocationRequest{
					InvocationId: invID,
					Invocation: &rdbPb.Invocation{
						BigqueryExports:  cfg.Resultdb.BqExports,
						ProducerResource: fmt.Sprintf("//%s/builds/%d", bbHost, b.Proto.Id),
						Realm:            realm,
						HistoryOptions: &rdbPb.HistoryOptions{
							UseInvocationTimestamp: cfg.Resultdb.HistoryOptions.GetUseInvocationTimestamp(),
						},
					},
					RequestId: invID,
				}
				header := metadata.MD{}
				if _, err = recorderClient.CreateInvocation(ctx, reqForBldID, grpc.Header(&header)); err != nil {
					return errors.Annotate(err, "failed to create the invocation for build id: %d", b.Proto.Id).Err()
				}
				token, ok := header["update-token"]
				if !ok {
					return errors.Reason("CreateInvocation response doesn't have update-token header for build id: %d", b.Proto.Id).Err()
				}
				b.ResultDBUpdateToken = token[0]
				b.Proto.Infra.Resultdb.Invocation = fmt.Sprintf("invocations/%s", reqForBldID.InvocationId)

				// Create another invocation for the build number in which it includes the invocation for build id,
				// If the build has the Number field populated.
				if b.Proto.Number > 0 {
					sha256Builder := sha256.Sum256([]byte(protoutil.FormatBuilderID(b.Proto.Builder)))
					_, err = recorderClient.CreateInvocation(ctx, &rdbPb.CreateInvocationRequest{
						InvocationId: fmt.Sprintf("build-%s-%d", hex.EncodeToString(sha256Builder[:]), b.Proto.Number),
						Invocation: &rdbPb.Invocation{
							State:               rdbPb.Invocation_FINALIZING,
							ProducerResource:    reqForBldID.Invocation.ProducerResource,
							Realm:               realm,
							IncludedInvocations: []string{fmt.Sprintf("invocations/%s", reqForBldID.InvocationId)},
						},
						RequestId: fmt.Sprintf("build-%d-%d", b.Proto.Id, b.Proto.Number),
					})
					if err != nil {
						return errors.Annotate(err, "failed to create the invocation for build number: %d (build id: %d)", b.Proto.Number, b.Proto.Id).Err()
					}
				}
				return nil
			}
		}
	})

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

func newRecorderClient(ctx context.Context, host string, project string) (rdbPb.RecorderClient, error) {
	if mockClient, ok := ctx.Value(&mockRecorderClientKey).(*rdbPb.MockRecorderClient); ok {
		return mockClient, nil
	}

	t, err := auth.GetRPCTransport(ctx, auth.AsProject, auth.WithProject(project))
	if err != nil {
		return nil, err
	}
	return rdbPb.NewRecorderPRPCClient(
		&prpc.Client{
			C:    &http.Client{Transport: t},
			Host: host,
		}), nil
}
<|endoftext|>


<commit_before>package main

// #cgo CFLAGS: -Iajtcl/inc -Iajtcl/target/linux
// #cgo LDFLAGS: -Llajtcl -lajtcl
// #include <stdio.h>
// #include <aj_debug.h>
// #include <aj_guid.h>
// #include <aj_creds.h>
// #include "alljoyn.h"
//
// typedef struct _AJ_UnmarshalResult {
//	AJ_Status status;
//	AJ_Message msg;
// } AJ_UnmarshalResult;
//
// AJ_UnmarshalResult AJ_UnmarshalMsgHelper(AJ_BusAttachment* bus, uint32_t timeout) {
// 	AJ_UnmarshalResult res;
// 	res.status = AJ_UnmarshalMsg(bus, &res.msg, timeout);
// 	return res;
// }
//
// AJ_Object Create_AJ_Object(char* path, AJ_InterfaceDescription* interfaces, uint8_t flags, void* context) {
//   AJ_Object obj = {path, interfaces, flags, context};
//   return obj;
// }
//
//
import "C"
import (
	"github.com/godbus/dbus"
	"github.com/godbus/dbus/introspect"
	"log"
	"unsafe"
)

type IntrospectProvider func(dbusService, dbusPath string) (node *introspect.Node, err error)

type AllJoynBridge struct {
	bus                *dbus.Conn
	introspectProvider IntrospectProvider
	services           map[string][]*introspect.Node
}

func NewAllJoynBridge(bus *dbus.Conn, introspectProvider IntrospectProvider) *AllJoynBridge {
	bridge := new(AllJoynBridge)
	bridge.bus = bus
	bridge.services = make(map[string][]*introspect.Node)
	bridge.introspectProvider = introspectProvider

	return bridge
}

func ParseArgumentOrProperty(name, access, _type string) string {
	s := name
	if access == "in" || access == "write" {
		s = s + "<"
	} else if access == "out" || access == "read" {
		s = s + ">"
	} else {
		s = s + "="
	}
	s = s + _type
	return s
}

func ParseArguments(args []introspect.Arg) string {
	argString := ""
	for _, arg := range args {
		argString = argString + " " + ParseArgumentOrProperty(arg.Name, arg.Direction, arg.Type)
	}
	return argString
}

func ParseAllJoynInterfaces(interfaces []introspect.Interface) []C.AJ_InterfaceDescription {
	res := make([]C.AJ_InterfaceDescription, 0)

	for _, iface := range interfaces {
		desc := make([]*C.char, 0)
		desc = append(desc, C.CString(iface.Name))

		for _, method := range iface.Methods {
			methogString := "?" + method.Name
			argString := ParseArguments(method.Args)
			log.Print(methogString + argString)
			desc = append(desc, C.CString(methogString+argString))
		}

		for _, signal := range iface.Signals {
			signalString := "!" + signal.Name
			argString := ParseArguments(signal.Args)
			log.Print(signalString + argString)
			desc = append(desc, C.CString(signalString+argString))
		}

		for _, prop := range iface.Properties {
			propString := "@" + ParseArgumentOrProperty(prop.Name, prop.Access, prop.Type)
			log.Print(propString)
			desc = append(desc, C.CString(propString))
		}

		desc = append(desc, nil)
		log.Print(desc)
		res = append(res, (C.AJ_InterfaceDescription)(&desc[0]))
	}
	return append(res, nil)
}

func ParseAllJoynObject(service *introspect.Node) C.AJ_Object {
	// Because of C struct alignment, we can't initialize inline and had to create accessor function
	obj := C.Create_AJ_Object(C.CString(service.Name), &ParseAllJoynInterfaces(service.Interfaces)[0], C.uint8_t(0), unsafe.Pointer(nil))
	return obj
}

func GetAllJoynObjects(services []*introspect.Node) []C.AJ_Object {
	res := make([]C.AJ_Object, 0)

	for _, service := range services {
		res = append(res, ParseAllJoynObject(service))
	}

	res = append(res, C.Create_AJ_Object(nil, nil, 0, nil))

	return res
}

func PrintObjects(objects []C.AJ_Object) {
	C.AJ_PrintXML(&objects[0])
}

func (a *AllJoynBridge) StartAllJoyn(dbusService string) *dbus.Error {
	objects := GetAllJoynObjects(a.services[dbusService])
	go func() {
		C.AJ_Initialize()
		C.AJ_PrintXML(&objects[0])
		C.AJ_RegisterObjects(&objects[0], nil)
		connected := false
		var status C.AJ_Status = C.AJ_OK
		for {
			var msg C.AJ_Message
			var busAttachment C.AJ_BusAttachment

			if !connected {
				status = C.AJ_StartService(&busAttachment,
					nil,
					60*1000, // TODO: Move connection timeout to config
					C.FALSE,
					25, // TODO: Move port to config
					C.CString(dbusService),
					C.AJ_NAME_REQ_DO_NOT_QUEUE,
					nil)

				if status != C.AJ_OK {
					continue
				}

				log.Printf("StartService returned %d", status)

				connected = true
			}

			var res C.AJ_UnmarshalResult
			res = C.AJ_UnmarshalMsgHelper(&busAttachment,
				5*1000) // TODO: Move unmarshal timeout to config
			status = res.status
			msg = res.msg

			if C.AJ_ERR_TIMEOUT == status {
				continue
			}

			if C.AJ_OK == status {
				log.Printf("Received message: %+v", msg)
				switch msg.msgId {
				case C.AJ_METHOD_ACCEPT_SESSION:
					{
						// uint16_t port;
						// char* joiner;
						// uint32_t sessionId;

						// AJ_UnmarshalArgs(&msg, "qus", &port, &sessionId, &joiner);
						status = C.AJ_BusReplyAcceptSession(&msg, C.TRUE)
						log.Printf("ACCEPT_SESSION: %+v", msg)
					}

					// If it's a message for the app
					// TODO: parse individual service, interace and method IDs and dispatch them to dbus
				case (msg.msgId & 0x01000000):
					log.Printf("Received application alljoyn message: %+v", msg)

				case C.AJ_SIGNAL_SESSION_LOST_WITH_REASON:
					{
						// uint32_t id, reason;
						// AJ_UnmarshalArgs(&msg, "uu", &id, &reason);
						// AJ_AlwaysPrintf(("Session lost. ID = %u, reason = %u", id, reason));
						log.Printf("Session lost: %+v", msg)
					}

				default:
					/* Pass to the built-in handlers. */
					status = C.AJ_BusHandleBusMessage(&msg)
				}
			}

			/* Messages MUST be discarded to free resources. */
			C.AJ_CloseMsg(&msg)

			if status == C.AJ_ERR_READ {
				C.AJ_Disconnect(&busAttachment)
				log.Print("AllJoyn disconnected, retrying")
				connected = false
				C.AJ_Sleep(1000 * 2) // TODO: Move sleep time to const
			}
		}
	}()
	return nil
}

func (a *AllJoynBridge) addService(service string, node *introspect.Node) {
	services, ok := a.services[service]
	if ok {
		a.services[service] = append(services, node)
	} else {
		a.services[service] = []*introspect.Node{node}
	}
}

func (a *AllJoynBridge) AddService(dbusPath, dbusService, allJoynPath, allJoynService string) *dbus.Error {
	node, err := a.introspectProvider(dbusService, dbusPath)

	if err != nil {
		log.Printf("Error getting introspect from [%s, %s]: %s", dbusService, dbusPath, err)
	}

	a.addService(dbusService, node)

	log.Printf("Received introspect: %+v", node)

	return nil
}

// func main() {
// 	bus, err := dbus.SystemBus()
// 	bus.RequestName("com.devicehive.alljoyn",
// 		dbus.NameFlagDoNotQueue)

// 	if err != nil {
// 		log.Panic(err)
// 	}

// 	allJoynBridge := NewAllJoynBridge(bus, func(dbusService, dbusPath string) (*introspect.Node, error) {
// 		return introspect.Call(bus.Object(dbusService, dbus.ObjectPath(dbusPath)))
// 	})

// 	bus.Export(allJoynBridge, "/com/devicehive/alljoyn", "com.devicehive.alljoyn")
// 	select {}
// }
<commit_msg>fix error: cannot find -lajtcl<commit_after>package main

// #cgo CFLAGS: -Iajtcl/inc -Iajtcl/target/linux
// #cgo LDFLAGS: -Lajtcl -lajtcl
// #include <stdio.h>
// #include <aj_debug.h>
// #include <aj_guid.h>
// #include <aj_creds.h>
// #include "alljoyn.h"
//
// typedef struct _AJ_UnmarshalResult {
//	AJ_Status status;
//	AJ_Message msg;
// } AJ_UnmarshalResult;
//
// AJ_UnmarshalResult AJ_UnmarshalMsgHelper(AJ_BusAttachment* bus, uint32_t timeout) {
// 	AJ_UnmarshalResult res;
// 	res.status = AJ_UnmarshalMsg(bus, &res.msg, timeout);
// 	return res;
// }
//
// AJ_Object Create_AJ_Object(char* path, AJ_InterfaceDescription* interfaces, uint8_t flags, void* context) {
//   AJ_Object obj = {path, interfaces, flags, context};
//   return obj;
// }
//
//
import "C"
import (
	"log"
	"unsafe"

	"github.com/godbus/dbus"
	"github.com/godbus/dbus/introspect"
)

type IntrospectProvider func(dbusService, dbusPath string) (node *introspect.Node, err error)

type AllJoynBridge struct {
	bus                *dbus.Conn
	introspectProvider IntrospectProvider
	services           map[string][]*introspect.Node
}

func NewAllJoynBridge(bus *dbus.Conn, introspectProvider IntrospectProvider) *AllJoynBridge {
	bridge := new(AllJoynBridge)
	bridge.bus = bus
	bridge.services = make(map[string][]*introspect.Node)
	bridge.introspectProvider = introspectProvider

	return bridge
}

func ParseArgumentOrProperty(name, access, _type string) string {
	s := name
	if access == "in" || access == "write" {
		s = s + "<"
	} else if access == "out" || access == "read" {
		s = s + ">"
	} else {
		s = s + "="
	}
	s = s + _type
	return s
}

func ParseArguments(args []introspect.Arg) string {
	argString := ""
	for _, arg := range args {
		argString = argString + " " + ParseArgumentOrProperty(arg.Name, arg.Direction, arg.Type)
	}
	return argString
}

func ParseAllJoynInterfaces(interfaces []introspect.Interface) []C.AJ_InterfaceDescription {
	res := make([]C.AJ_InterfaceDescription, 0)

	for _, iface := range interfaces {
		desc := make([]*C.char, 0)
		desc = append(desc, C.CString(iface.Name))

		for _, method := range iface.Methods {
			methogString := "?" + method.Name
			argString := ParseArguments(method.Args)
			log.Print(methogString + argString)
			desc = append(desc, C.CString(methogString+argString))
		}

		for _, signal := range iface.Signals {
			signalString := "!" + signal.Name
			argString := ParseArguments(signal.Args)
			log.Print(signalString + argString)
			desc = append(desc, C.CString(signalString+argString))
		}

		for _, prop := range iface.Properties {
			propString := "@" + ParseArgumentOrProperty(prop.Name, prop.Access, prop.Type)
			log.Print(propString)
			desc = append(desc, C.CString(propString))
		}

		desc = append(desc, nil)
		log.Print(desc)
		res = append(res, (C.AJ_InterfaceDescription)(&desc[0]))
	}
	return append(res, nil)
}

func ParseAllJoynObject(service *introspect.Node) C.AJ_Object {
	// Because of C struct alignment, we can't initialize inline and had to create accessor function
	obj := C.Create_AJ_Object(C.CString(service.Name), &ParseAllJoynInterfaces(service.Interfaces)[0], C.uint8_t(0), unsafe.Pointer(nil))
	return obj
}

func GetAllJoynObjects(services []*introspect.Node) []C.AJ_Object {
	res := make([]C.AJ_Object, 0)

	for _, service := range services {
		res = append(res, ParseAllJoynObject(service))
	}

	res = append(res, C.Create_AJ_Object(nil, nil, 0, nil))

	return res
}

func PrintObjects(objects []C.AJ_Object) {
	C.AJ_PrintXML(&objects[0])
}

func (a *AllJoynBridge) StartAllJoyn(dbusService string) *dbus.Error {
	objects := GetAllJoynObjects(a.services[dbusService])
	go func() {
		C.AJ_Initialize()
		C.AJ_PrintXML(&objects[0])
		C.AJ_RegisterObjects(&objects[0], nil)
		connected := false
		var status C.AJ_Status = C.AJ_OK
		for {
			var msg C.AJ_Message
			var busAttachment C.AJ_BusAttachment

			if !connected {
				status = C.AJ_StartService(&busAttachment,
					nil,
					60*1000, // TODO: Move connection timeout to config
					C.FALSE,
					25, // TODO: Move port to config
					C.CString(dbusService),
					C.AJ_NAME_REQ_DO_NOT_QUEUE,
					nil)

				if status != C.AJ_OK {
					continue
				}

				log.Printf("StartService returned %d", status)

				connected = true
			}

			var res C.AJ_UnmarshalResult
			res = C.AJ_UnmarshalMsgHelper(&busAttachment,
				5*1000) // TODO: Move unmarshal timeout to config
			status = res.status
			msg = res.msg

			if C.AJ_ERR_TIMEOUT == status {
				continue
			}

			if C.AJ_OK == status {
				log.Printf("Received message: %+v", msg)
				switch msg.msgId {
				case C.AJ_METHOD_ACCEPT_SESSION:
					{
						// uint16_t port;
						// char* joiner;
						// uint32_t sessionId;

						// AJ_UnmarshalArgs(&msg, "qus", &port, &sessionId, &joiner);
						status = C.AJ_BusReplyAcceptSession(&msg, C.TRUE)
						log.Printf("ACCEPT_SESSION: %+v", msg)
					}

					// If it's a message for the app
					// TODO: parse individual service, interace and method IDs and dispatch them to dbus
				case (msg.msgId & 0x01000000):
					log.Printf("Received application alljoyn message: %+v", msg)

				case C.AJ_SIGNAL_SESSION_LOST_WITH_REASON:
					{
						// uint32_t id, reason;
						// AJ_UnmarshalArgs(&msg, "uu", &id, &reason);
						// AJ_AlwaysPrintf(("Session lost. ID = %u, reason = %u", id, reason));
						log.Printf("Session lost: %+v", msg)
					}

				default:
					/* Pass to the built-in handlers. */
					status = C.AJ_BusHandleBusMessage(&msg)
				}
			}

			/* Messages MUST be discarded to free resources. */
			C.AJ_CloseMsg(&msg)

			if status == C.AJ_ERR_READ {
				C.AJ_Disconnect(&busAttachment)
				log.Print("AllJoyn disconnected, retrying")
				connected = false
				C.AJ_Sleep(1000 * 2) // TODO: Move sleep time to const
			}
		}
	}()
	return nil
}

func (a *AllJoynBridge) addService(service string, node *introspect.Node) {
	services, ok := a.services[service]
	if ok {
		a.services[service] = append(services, node)
	} else {
		a.services[service] = []*introspect.Node{node}
	}
}

func (a *AllJoynBridge) AddService(dbusPath, dbusService, allJoynPath, allJoynService string) *dbus.Error {
	node, err := a.introspectProvider(dbusService, dbusPath)

	if err != nil {
		log.Printf("Error getting introspect from [%s, %s]: %s", dbusService, dbusPath, err)
	}

	a.addService(dbusService, node)

	log.Printf("Received introspect: %+v", node)

	return nil
}

// func main() {
// 	bus, err := dbus.SystemBus()
// 	bus.RequestName("com.devicehive.alljoyn",
// 		dbus.NameFlagDoNotQueue)

// 	if err != nil {
// 		log.Panic(err)
// 	}

// 	allJoynBridge := NewAllJoynBridge(bus, func(dbusService, dbusPath string) (*introspect.Node, error) {
// 		return introspect.Call(bus.Object(dbusService, dbus.ObjectPath(dbusPath)))
// 	})

// 	bus.Export(allJoynBridge, "/com/devicehive/alljoyn", "com.devicehive.alljoyn")
// 	select {}
// }
<|endoftext|>


<commit_before>// +build integration

package felixcheck_test

import (
	"os"

	"testing"

	"github.com/streadway/amqp"

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

func amqpUrlFromEnv() string {
	url := os.Getenv("AMQP_URL")
	if url == "" {
		url = "amqp://"
	}
	return url
}

func publishMessage(ch *amqp.Channel, exchange, routingKey, text string) {
	ch.Publish(
		exchange,
		routingKey,
		false,
		false,
		amqp.Publishing{
			Headers:         amqp.Table{},
			ContentType:     "application/json",
			ContentEncoding: "",
			Body:            []byte(text),
			DeliveryMode:    amqp.Transient,
			Priority:        0,
		})

}

func TestRabbitMQQueueLenCheck(t *testing.T) {
	t.Parallel()
	amqpUrl := amqpUrlFromEnv()
	queue := "q"
	exchange := "e"
	routingKey := "r"

	conn, _ := amqp.Dial(amqpUrl)
	ch, _ := conn.Channel()
	defer conn.Close()
	defer ch.Close()

	ch.ExchangeDeclare(exchange, "topic", true, false, false, false, nil)
	ch.QueueDelete(queue, false, false, true)
	ch.QueueDeclare(queue, false, false, false, false, nil)
	ch.QueueBind(queue, "#", exchange, false, nil)

	check := NewRabbitMQQueueLenCheck("host", "service", amqpUrl, queue, 2)
	checkResult := check()
	assert.Equal(t, checkResult.State, "ok")
	assert.Equal(t, checkResult.Metric, float32(0))

	publishMessage(ch, exchange, routingKey, "msg1")
	publishMessage(ch, exchange, routingKey, "msg2")

	checkResult = check()
	assert.Equal(t, checkResult.State, "ok")
	assert.Equal(t, checkResult.Metric, float32(2))

	publishMessage(ch, exchange, routingKey, "msg3")
	checkResult = check()
	assert.Equal(t, checkResult.State, "critical")
	assert.Equal(t, checkResult.Metric, float32(3))

}

func TestRabbitMQQueueLenCheckReturnsCriticalWhenCantConnectToRabbitMQ(t *testing.T) {
	t.Parallel()

	check := NewRabbitMQQueueLenCheck("host", "service", amqpUrlFromEnv()+"whatever", "queue", 2)
	checkResult := check()

	assert.Equal(t, checkResult.State, "critical")
}
<commit_msg>Added initial tests for http checks<commit_after>// +build integration

package felixcheck_test

import (
	"fmt"
	"os"
	"testing"

	"net/http"
	"net/http/httptest"

	"github.com/streadway/amqp"

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

func TestHttpCheckerWithHttpServerUp(t *testing.T) {
	t.Parallel()

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	check := NewHttpChecker("host", "service", ts.URL, 200)
	checkResult := check()

	assert.Equal(t, checkResult.State, "ok")
}

func TestHttpCheckerWithServerDown(t *testing.T) {
	t.Parallel()

	check := NewHttpChecker("host", "service", "https://unknownurl/", 200)
	checkResult := check()

	assert.Equal(t, checkResult.State, "critical")
}

func amqpUrlFromEnv() string {
	url := os.Getenv("AMQP_URL")
	if url == "" {
		url = "amqp://"
	}
	return url
}

func publishMessage(ch *amqp.Channel, exchange, routingKey, text string) {
	ch.Publish(
		exchange,
		routingKey,
		false,
		false,
		amqp.Publishing{
			Headers:         amqp.Table{},
			ContentType:     "application/json",
			ContentEncoding: "",
			Body:            []byte(text),
			DeliveryMode:    amqp.Transient,
			Priority:        0,
		})

}

func TestRabbitMQQueueLenCheck(t *testing.T) {
	t.Parallel()
	amqpUrl := amqpUrlFromEnv()
	queue := "q"
	exchange := "e"
	routingKey := "r"

	conn, _ := amqp.Dial(amqpUrl)
	ch, _ := conn.Channel()
	defer conn.Close()
	defer ch.Close()

	ch.ExchangeDeclare(exchange, "topic", true, false, false, false, nil)
	ch.QueueDelete(queue, false, false, true)
	ch.QueueDeclare(queue, false, false, false, false, nil)
	ch.QueueBind(queue, "#", exchange, false, nil)

	check := NewRabbitMQQueueLenCheck("host", "service", amqpUrl, queue, 2)
	checkResult := check()
	assert.Equal(t, checkResult.State, "ok")
	assert.Equal(t, checkResult.Metric, float32(0))

	publishMessage(ch, exchange, routingKey, "msg1")
	publishMessage(ch, exchange, routingKey, "msg2")

	checkResult = check()
	assert.Equal(t, checkResult.State, "ok")
	assert.Equal(t, checkResult.Metric, float32(2))

	publishMessage(ch, exchange, routingKey, "msg3")
	checkResult = check()
	assert.Equal(t, checkResult.State, "critical")
	assert.Equal(t, checkResult.Metric, float32(3))

}

func TestRabbitMQQueueLenCheckReturnsCriticalWhenCantConnectToRabbitMQ(t *testing.T) {
	t.Parallel()

	check := NewRabbitMQQueueLenCheck("host", "service", amqpUrlFromEnv()+"whatever", "queue", 2)
	checkResult := check()

	assert.Equal(t, checkResult.State, "critical")
}
<|endoftext|>


<commit_before>package metadata

import (
	"github.com/StackExchange/slog"
	"github.com/StackExchange/wmi"
	"github.com/bosun-monitor/scollector/opentsdb"
)

func init() {
	metafuncs = append(metafuncs, metaWindowsVersion, metaWindowsIfaces)
}

func metaWindowsVersion() {
	var dst []Win32_OperatingSystem
	q := wmi.CreateQuery(&dst, "")
	err := wmi.Query(q, &dst)
	if err != nil {
		slog.Error(err)
		return
	}
	for _, v := range dst {
		AddMeta("", nil, "version", v.Version, true)
		AddMeta("", nil, "versionCaption", v.Caption, true)
	}
}

type Win32_OperatingSystem struct {
	Caption string
	Version string
}

func metaWindowsIfaces() {
	var dstConfigs []Win32_NetworkAdapterConfiguration
	q := wmi.CreateQuery(&dstConfigs, "")
	err := wmi.Query(q, &dstConfigs)
	if err != nil {
		slog.Error(err)
		return
	}

	mNicConfigs := make(map[string]*Win32_NetworkAdapterConfiguration)
	for i, nic := range dstConfigs {
		mNicConfigs[nic.SettingID] = &dstConfigs[i]
	}

	var dstAdapters []MSFT_NetAdapter
	q = wmi.CreateQuery(&dstAdapters, "WHERE HardwareInterface = True") //Exclude virtual adapters
	err = wmi.QueryNamespace(q, &dstAdapters, "root\\StandardCimv2")
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dstAdapters {
		tag := opentsdb.TagSet{"iface": v.InterfaceName}
		AddMeta("", tag, "description", v.InterfaceDescription, true)
		AddMeta("", tag, "name", v.Name, true)
		AddMeta("", tag, "speed", v.Speed, true)

		nicConfig := mNicConfigs[v.InterfaceGuid]
		if nicConfig != nil {
			AddMeta("", tag, "mac", v.InterfaceGuid, true) // should be nicConfig.MACAddress
			//for _, ip := range nic.IPAddress {
			AddMeta("", tag, "addr", nicConfig.SettingID, true) // should be ip
			//}
		}
	}
}

type MSFT_NetAdapter struct {
	Name                 string //NY-WEB09-PRI-NIC-A
	Speed                uint64 //Bits per Second
	InterfaceDescription string //Intel(R) Gigabit ET Quad Port Server Adapter #2
	InterfaceName        string //Ethernet_10
	InterfaceGuid        string //unique id
}

type Win32_NetworkAdapterConfiguration struct {
	//IPAddress  []string //Both IPv4 and IPv6
	//MACAddress string //00:1B:21:93:00:00
	SettingID string //Matches InterfaceGuid
	Caption   string
}
<commit_msg>cmd/scollector: Fix issue with getting MACAddress<commit_after>package metadata

import (
	"strings"

	"github.com/StackExchange/slog"
	"github.com/StackExchange/wmi"
	"github.com/bosun-monitor/scollector/opentsdb"
)

func init() {
	metafuncs = append(metafuncs, metaWindowsVersion, metaWindowsIfaces)
}

func metaWindowsVersion() {
	var dst []Win32_OperatingSystem
	q := wmi.CreateQuery(&dst, "")
	err := wmi.Query(q, &dst)
	if err != nil {
		slog.Error(err)
		return
	}
	for _, v := range dst {
		AddMeta("", nil, "version", v.Version, true)
		AddMeta("", nil, "versionCaption", v.Caption, true)
	}
}

type Win32_OperatingSystem struct {
	Caption string
	Version string
}

func metaWindowsIfaces() {
	var dstConfigs []Win32_NetworkAdapterConfiguration
	q := wmi.CreateQuery(&dstConfigs, "WHERE MACAddress != null")
	err := wmi.Query(q, &dstConfigs)
	if err != nil {
		slog.Error(err)
		return
	}

	mNicConfigs := make(map[string]*Win32_NetworkAdapterConfiguration)
	for i, nic := range dstConfigs {
		mNicConfigs[nic.SettingID] = &dstConfigs[i]
	}

	var dstAdapters []MSFT_NetAdapter
	q = wmi.CreateQuery(&dstAdapters, "WHERE HardwareInterface = True") //Exclude virtual adapters
	err = wmi.QueryNamespace(q, &dstAdapters, "root\\StandardCimv2")
	if err != nil {
		slog.Error(err)
		return
	}

	for _, v := range dstAdapters {
		tag := opentsdb.TagSet{"iface": v.InterfaceName} //Should be v.Name. See https://github.com/bosun-monitor/scollector/issues/119
		AddMeta("", tag, "description", v.InterfaceDescription, true)
		AddMeta("", tag, "name", v.Name, true)
		AddMeta("", tag, "speed", v.Speed, true)

		nicConfig := mNicConfigs[v.InterfaceGuid]
		if nicConfig != nil {
			AddMeta("", tag, "mac", strings.Replace(nicConfig.MACAddress, ":", "", -1), true)
			//for _, ip := range nic.IPAddress {
			AddMeta("", tag, "addr", nicConfig.SettingID, true) // Should be ip. See https://github.com/StackExchange/wmi/issues/5
			//}
		}
	}
}

type MSFT_NetAdapter struct {
	Name                 string //NY-WEB09-PRI-NIC-A
	Speed                uint64 //Bits per Second
	InterfaceDescription string //Intel(R) Gigabit ET Quad Port Server Adapter #2
	InterfaceName        string //Ethernet_10
	InterfaceGuid        string //unique id
}

type Win32_NetworkAdapterConfiguration struct {
	//IPAddress  []string //Both IPv4 and IPv6
	MACAddress string //00:1B:21:93:00:00
	SettingID  string //Matches InterfaceGuid
	Caption    string
}
<|endoftext|>


<commit_before>package clusterconf_test

import (
	"fmt"
	"time"
)

func (s *clusterConf) TestConfigTTLs() {
	ttlTypes := []struct {
		key string
		fn  func() time.Duration
	}{
		{"dataset_ttl", s.config.DatasetTTL},
		{"bundle_ttl", s.config.BundleTTL},
		{"node_ttl", s.config.NodeTTL},
	}

	for _, ttlType := range ttlTypes {
		s.Equal(time.Minute, ttlType.fn(), ttlType.key)
	}
}

func (s *clusterConf) TestValidate() {
	datasetTTL := s.config.DatasetTTL()
	bundleTTL := s.config.DatasetTTL()
	nodeTTL := s.config.DatasetTTL()
	defer s.viper.Set("dataset_ttl", datasetTTL.String())
	defer s.viper.Set("bundle_ttl", bundleTTL.String())
	defer s.viper.Set("node_ttl", nodeTTL.String())

	ttlTypes := []string{
		"dataset_ttl",
		"bundle_ttl",
		"node_ttl",
	}

	tests := []struct {
		duration string
		valid    bool
	}{
		{"0", false},
		{"1", false},
		{"-1s", false},
		{"1s", true},
		{"1m", true},
		{"1h", true},
	}

	for _, ttlType := range ttlTypes {
		for _, test := range tests {
			desc := fmt.Sprintf("%s : %s", ttlType, test.duration)
			s.viper.Set(ttlType, test.duration)
			err := s.config.Validate()
			if test.valid {
				s.NoError(err, desc)
			} else {
				s.EqualError(err, fmt.Sprintf("invalid %s", ttlType), desc)
			}
		}
	}
}
<commit_msg>Test for clusterconf LoadConfig<commit_after>package clusterconf_test

import (
	"fmt"
	"time"

	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/spf13/pflag"
)

func (s *clusterConf) TestConfigTTLs() {
	ttlTypes := []struct {
		key string
		fn  func() time.Duration
	}{
		{"dataset_ttl", s.config.DatasetTTL},
		{"bundle_ttl", s.config.BundleTTL},
		{"node_ttl", s.config.NodeTTL},
	}

	for _, ttlType := range ttlTypes {
		s.Equal(time.Minute, ttlType.fn(), ttlType.key)
	}
}

func (s *clusterConf) TestValidate() {
	datasetTTL := s.config.DatasetTTL()
	bundleTTL := s.config.DatasetTTL()
	nodeTTL := s.config.DatasetTTL()
	defer s.viper.Set("dataset_ttl", datasetTTL.String())
	defer s.viper.Set("bundle_ttl", bundleTTL.String())
	defer s.viper.Set("node_ttl", nodeTTL.String())

	ttlTypes := []string{
		"dataset_ttl",
		"bundle_ttl",
		"node_ttl",
	}

	tests := []struct {
		duration string
		valid    bool
	}{
		{"0", false},
		{"1", false},
		{"-1s", false},
		{"1s", true},
		{"1m", true},
		{"1h", true},
	}

	for _, ttlType := range ttlTypes {
		for _, test := range tests {
			desc := fmt.Sprintf("%s : %s", ttlType, test.duration)
			s.viper.Set(ttlType, test.duration)
			err := s.config.Validate()
			if test.valid {
				s.NoError(err, desc)
			} else {
				s.EqualError(err, fmt.Sprintf("invalid %s", ttlType), desc)
			}
		}
	}
}

func (s *clusterConf) TestLoadConfig() {
	datasetTTL := s.config.DatasetTTL()
	bundleTTL := s.config.DatasetTTL()
	nodeTTL := s.config.DatasetTTL()
	defer s.viper.Set("dataset_ttl", datasetTTL.String())
	defer s.viper.Set("bundle_ttl", bundleTTL.String())
	defer s.viper.Set("node_ttl", nodeTTL.String())

	v := s.coordinator.NewProviderViper()
	flagset := pflag.NewFlagSet("clusterconfLoadConfig", pflag.PanicOnError)
	// Note these explicit flags can be removed with issue #149
	flagset.DurationP("dataset_ttl", "d", time.Minute, "ttl for dataset usage heartbeats")
	flagset.DurationP("bundle_ttl", "b", time.Minute, "ttl for bundle usage heartbeats")
	flagset.DurationP("node_ttl", "o", time.Minute, "ttl for node heartbeats")
	config := clusterconf.NewConfig(flagset, v)
	s.NoError(flagset.Parse([]string{
		"--dataset_ttl", "123s",
		"--bundle_ttl", "456s",
		"--node_ttl", "789s",
	}))
	if !s.NoError(config.LoadConfig()) {
		return
	}
	s.Equal(123*time.Second, config.DatasetTTL())
	s.Equal(456*time.Second, config.BundleTTL())
	s.Equal(789*time.Second, config.NodeTTL())

}
<|endoftext|>