<commit_before>package main

import (
	"flag"
	// "fmt"
	zk "github.com/bretthoerner/gozk"
	"log"
	"time"
	"os/exec"
	"os"
	// "strings"
	"syscall"
)

var (
	zkServers = flag.String(
		"zk",
		"localhost:2181",
		"the comma separated ZK connection string")

	zkTimeout = flag.Int(
		"zk-timeout",
		5,
		"the ZK connection timeout (in seconds)")

	lock = flag.String(
		"lock",
		"",
		"path of lock to hold before running cmd")

	noblock = flag.Bool(
		"noblock",
		false,
		"instead of waiting for the lock, exit immediately")

	register = flag.String(
		"register",
		"",
		"path of ephemeral node to register")

	data = flag.String(
		"data",
		"",
		"string data to register in ephemeral node")
)

func main() {
	flag.Parse()

	// if len(flag.Args()) < 1 {
	//	log.Fatalln("Command required")
	// }

	if (len(flag.Args())) == 0 {
		log.Fatalln("Must provide a command to run.")
	}

	if len(*lock) == 0 && len(*register) == 0 {
		log.Fatalln("At least one of --lock or --register is required.")
	}

	if len(*register) > 0 && len(*data) == 0 {
		log.Fatalln("Must provide --data to to set with --register.")
	}

	if len(*data) > 0 && len(*register) == 0 {
		log.Fatalln("Must provide --register path where --data will be set.")
	}

	timeoutDuration := time.Duration(*zkTimeout) * time.Second

	conn, session, err := zk.Dial(*zkServers, timeoutDuration)
	if err != nil {
		log.Fatalf("Can't connect: %v", err)
	}
	defer conn.Close()

	// wait for connection.
	event := <-session
	if event.State != zk.STATE_CONNECTED {
		log.Fatalf("Can't connect: %v", event)
	} else {
		log.Println("Connected to zk.")
	}

	if len(*lock) > 0 {
		for {
			// ensure we have the lock before proceeding
			_, err = conn.Create(*lock, "", zk.EPHEMERAL, zk.WorldACL(zk.PERM_ALL))
			if err != nil {
				if *noblock {
					log.Fatalf("Couldn't obtain lock: %v", *lock)
				} else {
					log.Printf("Couldn't obtain lock: %v", *lock)
				}

				if !zk.IsError(err, zk.ZNODEEXISTS) {
					log.Fatalf("Unknown error: %v", err)
				}

				_, _, watch, err := conn.GetW(*lock)
				if err != nil {
					log.Fatalf("Unknown error: %v", err)
				}

				log.Println("Waiting on lock watch.")

				event := <-watch
				if !event.Ok() {
					log.Fatalf("Error while waiting on lock: %v", event.String())
				} else {
					log.Println("Lock changed, retrying obtain.")
				}
			} else {
				log.Printf("Created lock: %v", *lock)
				break
			}
		}
	}

	// register ourselves if necessary
	if len(*register) > 0 {
		path, err := conn.Create(*register, *data, zk.SEQUENCE|zk.EPHEMERAL, zk.WorldACL(zk.PERM_ALL))
		if err != nil {
			log.Fatalf("Error while registering: %v", err)
		} else {
			log.Printf("Registered at: %v", path)
			log.Printf("Registered data: %v", *data)
		}
	}

	// run subprocess
	cmd := exec.Command(flag.Args()[0], flag.Args()[1:]...)
	cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr

	err = cmd.Start()
	if err != nil {
		log.Fatalf("Error running subprocess: %v", err)
	} else {
		log.Println("Running command.")
	}

	pid := cmd.Process.Pid

	// watch for any ZK disconnects
	go func() {
		for {
			event := <-session
			if !event.Ok() {
				syscall.Kill(pid, 9)
				log.Fatalf("Problem with ZK event: %v", event)
			}
		}
  }()

	err = cmd.Wait()
	if err != nil {
		log.Fatalf("Error from command: %v", err)
	}
}
<commit_msg>gofmt<commit_after>package main

import (
	"flag"
	// "fmt"
	zk "github.com/bretthoerner/gozk"
	"log"
	"os"
	"os/exec"
	"time"
	// "strings"
	"syscall"
)

var (
	zkServers = flag.String(
		"zk",
		"localhost:2181",
		"the comma separated ZK connection string")

	zkTimeout = flag.Int(
		"zk-timeout",
		5,
		"the ZK connection timeout (in seconds)")

	lock = flag.String(
		"lock",
		"",
		"path of lock to hold before running cmd")

	noblock = flag.Bool(
		"noblock",
		false,
		"instead of waiting for the lock, exit immediately")

	register = flag.String(
		"register",
		"",
		"path of ephemeral node to register")

	data = flag.String(
		"data",
		"",
		"string data to register in ephemeral node")
)

func main() {
	flag.Parse()

	// if len(flag.Args()) < 1 {
	//	log.Fatalln("Command required")
	// }

	if (len(flag.Args())) == 0 {
		log.Fatalln("Must provide a command to run.")
	}

	if len(*lock) == 0 && len(*register) == 0 {
		log.Fatalln("At least one of --lock or --register is required.")
	}

	if len(*register) > 0 && len(*data) == 0 {
		log.Fatalln("Must provide --data to to set with --register.")
	}

	if len(*data) > 0 && len(*register) == 0 {
		log.Fatalln("Must provide --register path where --data will be set.")
	}

	timeoutDuration := time.Duration(*zkTimeout) * time.Second

	conn, session, err := zk.Dial(*zkServers, timeoutDuration)
	if err != nil {
		log.Fatalf("Can't connect: %v", err)
	}
	defer conn.Close()

	// wait for connection.
	event := <-session
	if event.State != zk.STATE_CONNECTED {
		log.Fatalf("Can't connect: %v", event)
	} else {
		log.Println("Connected to zk.")
	}

	if len(*lock) > 0 {
		for {
			// ensure we have the lock before proceeding
			_, err = conn.Create(*lock, "", zk.EPHEMERAL, zk.WorldACL(zk.PERM_ALL))
			if err != nil {
				if *noblock {
					log.Fatalf("Couldn't obtain lock: %v", *lock)
				} else {
					log.Printf("Couldn't obtain lock: %v", *lock)
				}

				if !zk.IsError(err, zk.ZNODEEXISTS) {
					log.Fatalf("Unknown error: %v", err)
				}

				_, _, watch, err := conn.GetW(*lock)
				if err != nil {
					log.Fatalf("Unknown error: %v", err)
				}

				log.Println("Waiting on lock watch.")

				event := <-watch
				if !event.Ok() {
					log.Fatalf("Error while waiting on lock: %v", event.String())
				} else {
					log.Println("Lock changed, retrying obtain.")
				}
			} else {
				log.Printf("Created lock: %v", *lock)
				break
			}
		}
	}

	// register ourselves if necessary
	if len(*register) > 0 {
		path, err := conn.Create(*register, *data, zk.SEQUENCE|zk.EPHEMERAL, zk.WorldACL(zk.PERM_ALL))
		if err != nil {
			log.Fatalf("Error while registering: %v", err)
		} else {
			log.Printf("Registered at: %v", path)
			log.Printf("Registered data: %v", *data)
		}
	}

	// run subprocess
	cmd := exec.Command(flag.Args()[0], flag.Args()[1:]...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = cmd.Start()
	if err != nil {
		log.Fatalf("Error running subprocess: %v", err)
	} else {
		log.Println("Running command.")
	}

	pid := cmd.Process.Pid

	// watch for any ZK disconnects
	go func() {
		for {
			event := <-session
			if !event.Ok() {
				syscall.Kill(pid, 9)
				log.Fatalf("Problem with ZK event: %v", event)
			}
		}
	}()

	err = cmd.Wait()
	if err != nil {
		log.Fatalf("Error from command: %v", err)
	}
}
<|endoftext|>


<commit_before>package dns

// A structure for handling zone data

import (
	"github.com/petar/GoLLRB/llrb"
)

type Zone struct {
	Name       string // Name of the zone
	*llrb.Tree        // Zone data
}

type ZoneData struct {
	Name       string                 // Domain name for this node
	RR         map[uint16][]RR        // Map ...
	Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures
	Glue       bool                   // True if the A and AAAA record are glue
	// nsec3, next
}
<commit_msg>more stuff<commit_after>package dns

// A structure for handling zone data

import (
	"github.com/petar/GoLLRB/llrb"
)

type Zone struct {
	Name       string // Name of the zone
	*llrb.Tree        // Zone data
}

type ZoneData struct {
	Name       string                 // Domain name for this node
	RR         map[uint16][]RR        // Map ...
	Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures
	Glue       bool                   // True if the A and AAAA record are glue
}

func lessZone(a, b interface{}) bool { return a.(string) < b.(string) }

// New ...
func New(name string) *Zone {
	z := new(Zone)
	z.Name = name
	z.Tree = llrb.New(lessZone)
	return z
}

func (z *Zone) Insert(r RR) {
	zd := z.Tree.Get(r.Header().Name)
}

func (z *Zone) Remove(r RR) {

}
<|endoftext|>


<commit_before>// Copyright 2015 Alex Browne.  All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

// Package zoom is a blazing-fast datastore and querying engine for
// Go built on Redis. It supports models of any arbitrary struct
// type and provides basic querying functionality. It also supports
// atomic transactions, lua scripts, and running Redis commands
// directly if needed.
package zoom

// Init starts the Zoom library and creates a connection pool. It accepts
// a Configuration struct as an argument. Any zero values in the configuration
// will fallback to their default values. Init should be called once during
// application startup.
func Init(config *Configuration) {
	config = parseConfig(config)
	initPool(config.Network, config.Address, config.Database, config.Password)
	if err := initScripts(); err != nil {
		panic(err)
	}
}

// Close closes the connection pool and shuts down the Zoom library.
// It should be run when application exits, e.g. using defer.
func Close() error {
	return pool.Close()
}
<commit_msg>Make Init return an error<commit_after>// Copyright 2015 Alex Browne.  All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

// Package zoom is a blazing-fast datastore and querying engine for
// Go built on Redis. It supports models of any arbitrary struct
// type and provides basic querying functionality. It also supports
// atomic transactions, lua scripts, and running Redis commands
// directly if needed.
package zoom

// Init starts the Zoom library and creates a connection pool. It accepts
// a Configuration struct as an argument. Any zero values in the configuration
// will fallback to their default values. Init should be called once during
// application startup.
func Init(config *Configuration) error {
	config = parseConfig(config)
	initPool(config.Network, config.Address, config.Database, config.Password)
	if err := initScripts(); err != nil {
		return err
	}
	return nil
}

// Close closes the connection pool and shuts down the Zoom library.
// It should be run when application exits, e.g. using defer.
func Close() error {
	return pool.Close()
}
<|endoftext|>


<commit_before>// Common vars and constants, shared by many parts of the bagman library.
package constants

import (
	"regexp"
)

// The tar files that make up multipart bags include a suffix
// that follows this pattern. For example, after stripping off
// the .tar suffix, you'll have a name like "my_bag.b04.of12"
var MultipartSuffix = regexp.MustCompile("\\.b\\d+\\.of\\d+$")

const (
	APTrustNamespace        = "urn:mace:aptrust.org"
	ReceiveBucketPrefix     = "aptrust.receiving."
	ReceiveTestBucketPrefix = "aptrust.receiving.test."
	RestoreBucketPrefix     = "aptrust.restore."
	S3DateFormat            = "2006-01-02T15:04:05.000Z"
	// All S3 urls begin with this.
	S3UriPrefix             = "https://s3.amazonaws.com/"
)

// Status enumerations match values defined in
// https://github.com/APTrust/fluctus/blob/develop/config/application.rb
const (
	StatusStarted              = "Started"
	StatusPending              = "Pending"
	StatusSuccess              = "Success"
	StatusFailed               = "Failed"
	StatusCancelled            = "Cancelled"
)

var StatusTypes []string = []string{
	StatusStarted,
	StatusPending,
	StatusSuccess,
	StatusFailed,
	StatusCancelled,
}

// Stage enumerations match values defined in
// https://github.com/APTrust/fluctus/blob/develop/config/application.rb
const (
	StageRequested           = "Requested"
	StageReceive             = "Receive"
	StageFetch               = "Fetch"
	StageUnpack              = "Unpack"
	StageValidate            = "Validate"
	StageStore               = "Store"
	StageRecord              = "Record"
	StageCleanup             = "Cleanup"
	StageResolve             = "Resolve"
)

var StageTypes []string = []string{
	StageRequested,
	StageReceive,
	StageFetch,
	StageUnpack,
	StageValidate,
	StageStore,
	StageRecord,
	StageCleanup,
	StageResolve,
}

// Action enumerations match values defined in
// https://github.com/APTrust/fluctus/blob/develop/config/application.rb

const (
	ActionIngest                 = "Ingest"
	ActionFixityCheck            = "Fixity Check"
	ActionRestore                = "Restore"
	ActionDelete                 = "Delete"
)

var ActionTypes []string = []string{
	ActionIngest,
	ActionFixityCheck,
	ActionRestore,
	ActionDelete,
}

const (
	AlgMd5                      = "md5"
	AlgSha256                   = "sha256"
)

const (
	IdTypeStorageURL                 = "url"
	IdTypeBagAndPath                 = "uuid"
)

// List of valid APTrust IntellectualObject AccessRights.
var AccessRights []string = []string{
	"consortia",
	"institution",
	"restricted",
}

// List of valid Premis Event types.
var EventTypes []string = []string{
	"ingest",
	"validation",
	"fixity_generation",
	"fixity_check",
	"identifier_assignment",
	"quarentine",
	"delete_action",
	"replication",
}

const (
	AWSVirginia = "us-east-1"
	AWSOregon = "us-west-2"
)

// GenericFile types. GenericFile.IngestFileType
const (
	PAYLOAD_FILE     = "payload_file"
	PAYLOAD_MANIFEST = "payload_manifest"
	TAG_MANIFEST     = "tag_manifest"
	TAG_FILE         = "tag_file"
)
<commit_msg>Added constants.ChecksumAlgorithms<commit_after>// Common vars and constants, shared by many parts of the bagman library.
package constants

import (
	"regexp"
)

// The tar files that make up multipart bags include a suffix
// that follows this pattern. For example, after stripping off
// the .tar suffix, you'll have a name like "my_bag.b04.of12"
var MultipartSuffix = regexp.MustCompile("\\.b\\d+\\.of\\d+$")

const (
	APTrustNamespace        = "urn:mace:aptrust.org"
	ReceiveBucketPrefix     = "aptrust.receiving."
	ReceiveTestBucketPrefix = "aptrust.receiving.test."
	RestoreBucketPrefix     = "aptrust.restore."
	S3DateFormat            = "2006-01-02T15:04:05.000Z"
	// All S3 urls begin with this.
	S3UriPrefix             = "https://s3.amazonaws.com/"
)

// Status enumerations match values defined in
// https://github.com/APTrust/fluctus/blob/develop/config/application.rb
const (
	StatusStarted              = "Started"
	StatusPending              = "Pending"
	StatusSuccess              = "Success"
	StatusFailed               = "Failed"
	StatusCancelled            = "Cancelled"
)

var StatusTypes []string = []string{
	StatusStarted,
	StatusPending,
	StatusSuccess,
	StatusFailed,
	StatusCancelled,
}

// Stage enumerations match values defined in
// https://github.com/APTrust/fluctus/blob/develop/config/application.rb
const (
	StageRequested           = "Requested"
	StageReceive             = "Receive"
	StageFetch               = "Fetch"
	StageUnpack              = "Unpack"
	StageValidate            = "Validate"
	StageStore               = "Store"
	StageRecord              = "Record"
	StageCleanup             = "Cleanup"
	StageResolve             = "Resolve"
)

var StageTypes []string = []string{
	StageRequested,
	StageReceive,
	StageFetch,
	StageUnpack,
	StageValidate,
	StageStore,
	StageRecord,
	StageCleanup,
	StageResolve,
}

// Action enumerations match values defined in
// https://github.com/APTrust/fluctus/blob/develop/config/application.rb

const (
	ActionIngest                 = "Ingest"
	ActionFixityCheck            = "Fixity Check"
	ActionRestore                = "Restore"
	ActionDelete                 = "Delete"
)

var ActionTypes []string = []string{
	ActionIngest,
	ActionFixityCheck,
	ActionRestore,
	ActionDelete,
}

const (
	AlgMd5                      = "md5"
	AlgSha256                   = "sha256"
)

var ChecksumAlgorithms = []string{ AlgMd5, AlgSha256 }

const (
	IdTypeStorageURL                 = "url"
	IdTypeBagAndPath                 = "uuid"
)

// List of valid APTrust IntellectualObject AccessRights.
var AccessRights []string = []string{
	"consortia",
	"institution",
	"restricted",
}

// List of valid Premis Event types.
var EventTypes []string = []string{
	"ingest",
	"validation",
	"fixity_generation",
	"fixity_check",
	"identifier_assignment",
	"quarentine",
	"delete_action",
	"replication",
}

const (
	AWSVirginia = "us-east-1"
	AWSOregon = "us-west-2"
)

// GenericFile types. GenericFile.IngestFileType
const (
	PAYLOAD_FILE     = "payload_file"
	PAYLOAD_MANIFEST = "payload_manifest"
	TAG_MANIFEST     = "tag_manifest"
	TAG_FILE         = "tag_file"
)
<|endoftext|>


<commit_before>package consumer

import (
	"container/list"
	"encoding/json"
	"flume-log-sdk/config"
	"flume-log-sdk/consumer/client"
	"flume-log-sdk/consumer/pool"
	"flume-log-sdk/rpc/flume"
	"fmt"
	"github.com/momotech/GoRedis/libs/stdlog"
	"log"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

type counter struct {
	lastSuccValue int64

	currSuccValue int64

	lastFailValue int64

	currFailValue int64
}

//生成对象池用于缓存 thriftEvent对象
var objpool *sync.Pool

func init() {
	objpool = &sync.Pool{}
	objpool.New = func() interface{} {
		//创建生成thriftevent的
		return client.NewFlumeEvent()
	}
}

// 用于向flume中作为sink 通过thrift客户端写入日志
type SourceServer struct {
	flumeClientPool *list.List
	isStop          bool
	monitorCount    counter
	business        string
	batchSize       int
	buffChannel     chan *flume.ThriftFlumeEvent
	sourceLog       stdlog.Logger
}

func newSourceServer(business string, flumePool *list.List, sourceLog stdlog.Logger) (server *SourceServer) {
	batchSize := 300
	sendbuff := 500
	buffChannel := make(chan *flume.ThriftFlumeEvent, sendbuff)
	sourceServer := &SourceServer{
		business:        business,
		flumeClientPool: flumePool,
		batchSize:       batchSize,
		buffChannel:     buffChannel,
		sourceLog:       sourceLog}
	return sourceServer
}

func (self *SourceServer) monitor() (succ, fail int64, bufferSize int) {
	currSucc := self.monitorCount.currSuccValue
	currFail := self.monitorCount.currFailValue
	succ = (currSucc - self.monitorCount.lastSuccValue)
	fail = (currFail - self.monitorCount.lastFailValue)
	self.monitorCount.lastSuccValue = currSucc
	self.monitorCount.lastFailValue = currFail

	//自己的Buffer大小
	bufferSize = len(self.buffChannel)
	return
}

//启动pop
func (self *SourceServer) start() {

	self.isStop = false

	//创建chan ,buffer 为10
	sendbuff := make(chan []*flume.ThriftFlumeEvent, self.batchSize)
	//启动20个go程从channel获取
	for i := 0; i < 10; i++ {
		go func(ch chan []*flume.ThriftFlumeEvent) {
			for !self.isStop {
				events := <-ch
				self.innerSend(events)
			}
		}(sendbuff)
	}

	go func() {
		//批量收集数据
		pack := make([]*flume.ThriftFlumeEvent, 0, self.batchSize)
		for !self.isStop {
			event := <-self.buffChannel
			//如果总数大于batchsize则提交
			if len(pack) < self.batchSize {
				//批量提交
				pack = append(pack, event)
				continue
			}
			sendbuff <- pack[:len(pack)]
			// pack = make([]*flume.ThriftFlumeEvent, 0, self.batchSize)
		}

		close(sendbuff)
	}()

	self.sourceLog.Printf("LOG_SOURCE|SOURCE SERVER [%s]|STARTED\n", self.business)
}

func (self *SourceServer) innerSend(events []*flume.ThriftFlumeEvent) {

	for i := 0; i < 3; i++ {

		pool := self.getFlumeClientPool()
		if nil == pool {
			continue
		}
		flumeclient, err := pool.Get(5 * time.Second)
		if nil != err || nil == flumeclient {
			self.sourceLog.Printf("LOG_SOURCE|GET FLUMECLIENT|FAIL|%s|%s|TRY:%d\n", self.business, err, i)
			continue
		}

		err = flumeclient.AppendBatch(events)
		defer func() {
			if err := recover(); nil != err {
				//回收这个坏的连接
				pool.ReleaseBroken(flumeclient)
			} else {
				pool.Release(flumeclient)
			}
		}()

		if nil != err {
			atomic.AddInt64(&self.monitorCount.currFailValue, int64(len(events)))
			self.sourceLog.Printf("LOG_SOURCE|SEND FLUME|FAIL|%s|%s|TRY:%d\n", self.business, err.Error(), i)

		} else {
			atomic.AddInt64(&self.monitorCount.currSuccValue, int64(1*self.batchSize))
			if rand.Int()%10000 == 0 {
				self.sourceLog.Printf("trace|send 2 flume succ|%s|%d\n", flumeclient.HostPort(), len(events))
			}
			break
		}

	}

	//归还event对线到池子中
	defer func() {
		for _, v := range events {
			objpool.Put(*v)
		}
	}()
}

//解析出decodecommand
func decodeCommand(resp []byte) (string, *flume.ThriftFlumeEvent) {
	var cmd config.Command
	err := json.Unmarshal(resp, &cmd)
	if nil != err {
		log.Printf("command unmarshal fail ! %T | error:%s\n", resp, err.Error())
		return "", nil
	}
	//
	momoid := cmd.Params["momoid"].(string)

	businessName := cmd.Params["businessName"].(string)

	action := cmd.Params["type"].(string)

	bodyContent := cmd.Params["body"]

	//将businessName 加入到body中
	bodyMap := bodyContent.(map[string]interface{})
	bodyMap["business_type"] = businessName

	body, err := json.Marshal(bodyContent)
	if nil != err {
		log.Printf("marshal log body fail %s", err.Error())
		return businessName, nil
	}

	//拼Body
	flumeBody := fmt.Sprintf("%s\t%s\t%s", momoid, action, string(body))
	obj := objpool.Get()
	event := client.EventFillUp(obj, businessName, action, []byte(flumeBody))
	// event := client.NewFlumeEvent(businessName, action, []byte(flumeBody))
	return businessName, event
}

func (self *SourceServer) stop() {
	self.isStop = true
	time.Sleep(5 * time.Second)

	//遍历所有的flumeclientlink，将当前Business从该链表中移除
	for v := self.flumeClientPool.Back(); nil != v; v = v.Prev() {
		v.Value.(*pool.FlumePoolLink).DetachBusiness(self.business)
	}
	close(self.buffChannel)
	self.sourceLog.Printf("LOG_SOURCE|SOURCE SERVER|[%s]|STOPPED\n", self.business)
}

func (self *SourceServer) getFlumeClientPool() *pool.FlumeClientPool {

	//采用轮训算法
	e := self.flumeClientPool.Back()
	if nil == e {
		return nil
	}
	self.flumeClientPool.MoveToFront(e)
	return e.Value.(*pool.FlumePoolLink).FlumePool

}
<commit_msg>增大sendbuffer和batchSize 	modified:   consumer/log_source.go<commit_after>package consumer

import (
	"container/list"
	"encoding/json"
	"flume-log-sdk/config"
	"flume-log-sdk/consumer/client"
	"flume-log-sdk/consumer/pool"
	"flume-log-sdk/rpc/flume"
	"fmt"
	"github.com/momotech/GoRedis/libs/stdlog"
	"log"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

type counter struct {
	lastSuccValue int64

	currSuccValue int64

	lastFailValue int64

	currFailValue int64
}

//生成对象池用于缓存 thriftEvent对象
var objpool *sync.Pool

func init() {
	objpool = &sync.Pool{}
	objpool.New = func() interface{} {
		//创建生成thriftevent的
		return client.NewFlumeEvent()
	}
}

// 用于向flume中作为sink 通过thrift客户端写入日志
type SourceServer struct {
	flumeClientPool *list.List
	isStop          bool
	monitorCount    counter
	business        string
	batchSize       int
	buffChannel     chan *flume.ThriftFlumeEvent
	sourceLog       stdlog.Logger
}

func newSourceServer(business string, flumePool *list.List, sourceLog stdlog.Logger) (server *SourceServer) {
	batchSize := 2000
	sendbuff := 100000
	buffChannel := make(chan *flume.ThriftFlumeEvent, sendbuff)
	sourceServer := &SourceServer{
		business:        business,
		flumeClientPool: flumePool,
		batchSize:       batchSize,
		buffChannel:     buffChannel,
		sourceLog:       sourceLog}
	return sourceServer
}

func (self *SourceServer) monitor() (succ, fail int64, bufferSize int) {
	currSucc := self.monitorCount.currSuccValue
	currFail := self.monitorCount.currFailValue
	succ = (currSucc - self.monitorCount.lastSuccValue)
	fail = (currFail - self.monitorCount.lastFailValue)
	self.monitorCount.lastSuccValue = currSucc
	self.monitorCount.lastFailValue = currFail

	//自己的Buffer大小
	bufferSize = len(self.buffChannel)
	return
}

//启动pop
func (self *SourceServer) start() {

	self.isStop = false

	//创建chan ,buffer 为10
	sendbuff := make(chan []*flume.ThriftFlumeEvent, self.batchSize)
	//启动20个go程从channel获取
	for i := 0; i < 10; i++ {
		go func(ch chan []*flume.ThriftFlumeEvent) {
			for !self.isStop {
				events := <-ch
				self.innerSend(events)
			}
		}(sendbuff)
	}

	go func() {
		//批量收集数据
		pack := make([]*flume.ThriftFlumeEvent, 0, self.batchSize)
		for !self.isStop {
			event := <-self.buffChannel
			//如果总数大于batchsize则提交
			if len(pack) < self.batchSize {
				//批量提交
				pack = append(pack, event)
				continue
			}
			sendbuff <- pack[:len(pack)]
			// pack = make([]*flume.ThriftFlumeEvent, 0, self.batchSize)
		}

		close(sendbuff)
	}()

	self.sourceLog.Printf("LOG_SOURCE|SOURCE SERVER [%s]|STARTED\n", self.business)
}

func (self *SourceServer) innerSend(events []*flume.ThriftFlumeEvent) {

	for i := 0; i < 3; i++ {

		pool := self.getFlumeClientPool()
		if nil == pool {
			continue
		}
		flumeclient, err := pool.Get(5 * time.Second)
		if nil != err || nil == flumeclient {
			self.sourceLog.Printf("LOG_SOURCE|GET FLUMECLIENT|FAIL|%s|%s|TRY:%d\n", self.business, err, i)
			continue
		}

		err = flumeclient.AppendBatch(events)
		defer func() {
			if err := recover(); nil != err {
				//回收这个坏的连接
				pool.ReleaseBroken(flumeclient)
			} else {
				pool.Release(flumeclient)
			}
		}()

		if nil != err {
			atomic.AddInt64(&self.monitorCount.currFailValue, int64(len(events)))
			self.sourceLog.Printf("LOG_SOURCE|SEND FLUME|FAIL|%s|%s|TRY:%d\n", self.business, err.Error(), i)

		} else {
			atomic.AddInt64(&self.monitorCount.currSuccValue, int64(1*self.batchSize))
			if rand.Int()%10000 == 0 {
				self.sourceLog.Printf("trace|send 2 flume succ|%s|%d\n", flumeclient.HostPort(), len(events))
			}
			break
		}

	}

	//归还event对线到池子中
	defer func() {
		for _, v := range events {
			objpool.Put(*v)
		}
	}()
}

//解析出decodecommand
func decodeCommand(resp []byte) (string, *flume.ThriftFlumeEvent) {
	var cmd config.Command
	err := json.Unmarshal(resp, &cmd)
	if nil != err {
		log.Printf("command unmarshal fail ! %T | error:%s\n", resp, err.Error())
		return "", nil
	}
	//
	momoid := cmd.Params["momoid"].(string)

	businessName := cmd.Params["businessName"].(string)

	action := cmd.Params["type"].(string)

	bodyContent := cmd.Params["body"]

	//将businessName 加入到body中
	bodyMap := bodyContent.(map[string]interface{})
	bodyMap["business_type"] = businessName

	body, err := json.Marshal(bodyContent)
	if nil != err {
		log.Printf("marshal log body fail %s", err.Error())
		return businessName, nil
	}

	//拼Body
	flumeBody := fmt.Sprintf("%s\t%s\t%s", momoid, action, string(body))
	obj := objpool.Get()
	event := client.EventFillUp(obj, businessName, action, []byte(flumeBody))
	// event := client.NewFlumeEvent(businessName, action, []byte(flumeBody))
	return businessName, event
}

func (self *SourceServer) stop() {
	self.isStop = true
	time.Sleep(5 * time.Second)

	//遍历所有的flumeclientlink，将当前Business从该链表中移除
	for v := self.flumeClientPool.Back(); nil != v; v = v.Prev() {
		v.Value.(*pool.FlumePoolLink).DetachBusiness(self.business)
	}
	close(self.buffChannel)
	self.sourceLog.Printf("LOG_SOURCE|SOURCE SERVER|[%s]|STOPPED\n", self.business)
}

func (self *SourceServer) getFlumeClientPool() *pool.FlumeClientPool {

	//采用轮训算法
	e := self.flumeClientPool.Back()
	if nil == e {
		return nil
	}
	self.flumeClientPool.MoveToFront(e)
	return e.Value.(*pool.FlumePoolLink).FlumePool

}
<|endoftext|>


<commit_before>// Package nuimo provides an interaction layer for Senic Nuimo devices. It allows to receive user inputs and can write out led pictographs to the LED display.
package nuimo

import (
	"encoding/binary"
	"fmt"
	"strings"
	"time"

	"github.com/currantlabs/ble"
	"github.com/currantlabs/ble/examples/lib/gatt"
	"github.com/currantlabs/ble/linux/hci"
	"github.com/currantlabs/ble/linux/hci/cmd"
	"github.com/mgutz/logxi/v1"
)

const SERVICE_BATTERY_STATUS = "180F"
const SERVICE_DEVICE_INFO = "180A"
const SERVICE_LED_MATRIX = "F29B1523CB1940F3BE5C7241ECB82FD1"
const SERVICE_USER_INPUT = "F29B1525CB1940F3BE5C7241ECB82FD2"

const CHAR_BATTERY_LEVEL = "2A19"
const CHAR_DEVICE_INFO = "2A29"
const CHAR_LED_MATRIX = "F29B1524CB1940F3BE5C7241ECB82FD1"
const CHAR_INPUT_FLY = "F29B1526CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_SWIPE = "F29B1527CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_ROTATE = "F29B1528CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_CLICK = "F29B1529CB1940F3BE5C7241ECB82FD2"

const DIR_LEFT = 0
const DIR_RIGHT = 1
const DIR_UP = 2
const DIR_BACKWARDS = 2
const DIR_DOWN = 3
const DIR_TOWARDS = 3
const DIR_UPDOWN = 4

const CLICK_DOWN = 1
const CLICK_UP = 0

var logger = log.New("nuimo")

type Nuimo struct {
	client ble.Client
	events chan Event
	led    *ble.Characteristic
	bttry  *ble.Characteristic
}

type Event struct {
	Key   string
	Value int64
	Raw   []byte
}

// Connect tried to find nearby devices and connects to them. It tries to reconnect when a timeout interval is passed as first argument.
func Connect(params ...int) (*Nuimo, error) {

	ch := make(chan Event, 100)
	n := &Nuimo{events: ch}
	err := n.reconnect()

	if err != nil {
		logger.Fatal("%s", err)
	}

	if len(params) == 1 && params[0] > 0 {
		go n.keepConnected(params[0])
	}

	return n, err
}

func discoverDevice() (ble.Client, error) {
	logger.Info("Discover")
	filter := func(a ble.Advertisement) bool {
		return strings.ToUpper(a.LocalName()) == "NUIMO"
	}

	// Set connection parameters. Only supported on Linux platform.
	d := gatt.DefaultDevice()
	if h, ok := d.(*hci.HCI); ok {
		if err := h.Option(hci.OptConnParams(
			cmd.LECreateConnection{
				LEScanInterval:        0x0004,    // 0x0004 - 0x4000; N * 0.625 msec
				LEScanWindow:          0x0004,    // 0x0004 - 0x4000; N * 0.625 msec
				InitiatorFilterPolicy: 0x00,      // White list is not used
				PeerAddressType:       0x00,      // Public Device Address
				PeerAddress:           [6]byte{}, //
				OwnAddressType:        0x00,      // Public Device Address
				ConnIntervalMin:       0x0006,    // 0x0006 - 0x0C80; N * 1.25 msec
				ConnIntervalMax:       0x0006,    // 0x0006 - 0x0C80; N * 1.25 msec
				ConnLatency:           0x0000,    // 0x0000 - 0x01F3; N * 1.25 msec
				SupervisionTimeout:    0x0048,    // 0x000A - 0x0C80; N * 10 msec
				MinimumCELength:       0x0000,    // 0x0000 - 0xFFFF; N * 0.625 msec
				MaximumCELength:       0x0000,    // 0x0000 - 0xFFFF; N * 0.625 msec
			})); err != nil {
			logger.Fatal("can't set advertising param: %s", err)
		}
	}
	return gatt.Discover(gatt.FilterFunc(filter))
}

func (n *Nuimo) reconnect() error {
	logger.Info("Reconnect")
	if n.client != nil {
		n.client.ClearSubscriptions()
		n.client.CancelConnection()
	}
	client, err := discoverDevice()
	if err != nil {
		return err
	}
	n.client = client
	return n.discoverServices()
}

func (n *Nuimo) keepConnected(refresh int) {

	for {
		c := make(chan uint8, 1)
		go func() {
			logger.Info("Reading batterie")
			data, err := n.client.ReadCharacteristic(n.bttry)
			if err != nil {
				logger.Error("Error", err)
				// this will cause a reconnect
				return
			}
			c <- uint8(data[0])

		}()
		select {
		case data := <-c:
			logger.Info("Batterie level", data)
		case <-time.After(30 * time.Second):
			n.reconnect()
		}
		close(c)
		time.Sleep(time.Duration(refresh) * time.Second)
	}

}

// Events provides access to the events channel which contains the user interaction and battery level events
func (n *Nuimo) Events() <-chan Event {
	return n.events
}

// Display sends the passed byte atrix into the LED display of the Nuimo
func (n *Nuimo) Display(matrix []byte, brightness uint8, timeout uint8) {

	displayMatrix := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

	for c, dots := range matrix {
		if c > 10 {
			break
		}
		displayMatrix[c] = dots
	}

	displayMatrix[11] = brightness
	displayMatrix[12] = timeout

	n.client.WriteCharacteristic(n.led, displayMatrix, true)
}

// DisplayMatrix transforms a matrix consisting of 0s and 1s into a byte matrix
func DisplayMatrix(dots ...byte) []byte {
	bytes := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	var b uint8
	var i uint8
	dotCount := uint8(len(dots))

	for b = 0; b < 11; b++ {
		for i = 0; i < 8; i++ {
			dot := (b * 8) + i
			if dot < dotCount && dots[dot] > 0 {
				bytes[b] |= byte(1) << i
			}
		}

	}

	return bytes
}

//  TODO: make sure we only subscribe to the services we need
func (n *Nuimo) discoverServices() error {
	p, err := n.client.DiscoverProfile(true)
	if err != nil {
		return fmt.Errorf("can't discover services: %s\n", err)
	}

	for _, s := range p.Services {

		switch {
		case s.UUID.Equal(ble.MustParse(SERVICE_DEVICE_INFO)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_DEVICE_INFO)):
					logger.Info("Info subscribed")
					n.client.Subscribe(c, false, n.info)
				default:
					logger.Warn("Unknown device char", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_BATTERY_STATUS)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_BATTERY_LEVEL)):
					logger.Info("Battery subscribed")
					n.bttry = c
					n.client.Subscribe(c, false, n.battery)

				default:
					logger.Warn("Unknown battery char", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_USER_INPUT)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_CLICK)):
					n.client.Subscribe(c, false, n.click)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_ROTATE)):
					n.client.Subscribe(c, false, n.rotate)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_SWIPE)):
					n.client.Subscribe(c, false, n.swipe)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_FLY)):
					n.client.Subscribe(c, false, n.fly)
				default:
					logger.Warn("Unknown input characteristik", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_LED_MATRIX)):
			for _, c := range s.Characteristics {
				logger.Info("Led found")
				n.led = c
			}
		default:
			logger.Warn("Unknown service %s", "uuid", s.UUID.String())
		}
	}
	return nil
}

// Disconnect closes the connection and drops all subscriptions
func (n *Nuimo) Disconnect() error {
	logger.Warn("Nuimo connection closed")
	close(n.events)
	return n.client.CancelConnection()
}

func (n *Nuimo) battery(req []byte) {
	uval, _ := binary.Uvarint(req)
	level := int64(uval)
	n.send(Event{Key: "battery", Raw: req, Value: level})
}
func (n *Nuimo) info(req []byte) {
	logger.Info("Info: " + string(req))
}

func (n *Nuimo) click(req []byte) {
	uval, _ := binary.Uvarint(req)
	dir := int64(uval)
	switch dir {
	case CLICK_DOWN:
		n.send(Event{Key: "press", Raw: req})
	case CLICK_UP:
		n.send(Event{Key: "release", Raw: req})
	}
}

func (n *Nuimo) rotate(req []byte) {
	uval := binary.LittleEndian.Uint16(req)
	val := int64(int16(uval))
	n.send(Event{Key: "rotate", Raw: req, Value: val})
}
func (n *Nuimo) swipe(req []byte) {
	uval, _ := binary.Uvarint(req)
	dir := int64(uval)
	n.send(Event{Key: "swipe", Raw: req, Value: dir})

	switch dir {
	case DIR_LEFT:
		n.send(Event{Key: "swipe_left", Raw: req})
	case DIR_RIGHT:
		n.send(Event{Key: "swipe_right", Raw: req})
	case DIR_UP:
		n.send(Event{Key: "swipe_up", Raw: req})
	case DIR_DOWN:
		n.send(Event{Key: "swipe_down", Raw: req})
	}
}
func (n *Nuimo) fly(req []byte) {
	uval, _ := binary.Uvarint(req[0:1])
	dir := int(uval)
	uval, _ = binary.Uvarint(req[2:])
	distance := int64(uval)

	switch dir {
	case DIR_LEFT:
		n.send(Event{Key: "fly_left", Raw: req, Value: distance})
	case DIR_RIGHT:
		n.send(Event{Key: "fly_right", Raw: req, Value: distance})
	case DIR_BACKWARDS:
		n.send(Event{Key: "fly_backwards", Raw: req, Value: distance})
	case DIR_TOWARDS:
		n.send(Event{Key: "fly_towards", Raw: req, Value: distance})
	case DIR_UPDOWN:
		n.send(Event{Key: "fly_updown", Raw: req, Value: distance})
	}
}
func (n *Nuimo) unknown(req []byte) {
	n.send(Event{Key: "unknown", Raw: req})
}

// make sure missing event sinks don't block the client
func (n *Nuimo) send(e Event) {
	go func() { n.events <- e }()
}
<commit_msg>Send battery event during refreshs<commit_after>// Package nuimo provides an interaction layer for Senic Nuimo devices. It allows to receive user inputs and can write out led pictographs to the LED display.
package nuimo

import (
	"encoding/binary"
	"fmt"
	"strings"
	"time"

	"github.com/currantlabs/ble"
	"github.com/currantlabs/ble/examples/lib/gatt"
	"github.com/currantlabs/ble/linux/hci"
	"github.com/currantlabs/ble/linux/hci/cmd"
	"github.com/mgutz/logxi/v1"
)

const SERVICE_BATTERY_STATUS = "180F"
const SERVICE_DEVICE_INFO = "180A"
const SERVICE_LED_MATRIX = "F29B1523CB1940F3BE5C7241ECB82FD1"
const SERVICE_USER_INPUT = "F29B1525CB1940F3BE5C7241ECB82FD2"

const CHAR_BATTERY_LEVEL = "2A19"
const CHAR_DEVICE_INFO = "2A29"
const CHAR_LED_MATRIX = "F29B1524CB1940F3BE5C7241ECB82FD1"
const CHAR_INPUT_FLY = "F29B1526CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_SWIPE = "F29B1527CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_ROTATE = "F29B1528CB1940F3BE5C7241ECB82FD2"
const CHAR_INPUT_CLICK = "F29B1529CB1940F3BE5C7241ECB82FD2"

const DIR_LEFT = 0
const DIR_RIGHT = 1
const DIR_UP = 2
const DIR_BACKWARDS = 2
const DIR_DOWN = 3
const DIR_TOWARDS = 3
const DIR_UPDOWN = 4

const CLICK_DOWN = 1
const CLICK_UP = 0

var logger = log.New("nuimo")

type Nuimo struct {
	client ble.Client
	events chan Event
	led    *ble.Characteristic
	bttry  *ble.Characteristic
}

type Event struct {
	Key   string
	Value int64
	Raw   []byte
}

// Connect tried to find nearby devices and connects to them. It tries to reconnect when a timeout interval is passed as first argument.
func Connect(params ...int) (*Nuimo, error) {

	ch := make(chan Event, 100)
	n := &Nuimo{events: ch}
	err := n.reconnect()

	if err != nil {
		logger.Fatal("%s", err)
	}

	if len(params) == 1 && params[0] > 0 {
		go n.keepConnected(params[0])
	}

	return n, err
}

func discoverDevice() (ble.Client, error) {
	logger.Info("Discover")
	filter := func(a ble.Advertisement) bool {
		return strings.ToUpper(a.LocalName()) == "NUIMO"
	}

	// Set connection parameters. Only supported on Linux platform.
	d := gatt.DefaultDevice()
	if h, ok := d.(*hci.HCI); ok {
		if err := h.Option(hci.OptConnParams(
			cmd.LECreateConnection{
				LEScanInterval:        0x0004,    // 0x0004 - 0x4000; N * 0.625 msec
				LEScanWindow:          0x0004,    // 0x0004 - 0x4000; N * 0.625 msec
				InitiatorFilterPolicy: 0x00,      // White list is not used
				PeerAddressType:       0x00,      // Public Device Address
				PeerAddress:           [6]byte{}, //
				OwnAddressType:        0x00,      // Public Device Address
				ConnIntervalMin:       0x0006,    // 0x0006 - 0x0C80; N * 1.25 msec
				ConnIntervalMax:       0x0006,    // 0x0006 - 0x0C80; N * 1.25 msec
				ConnLatency:           0x0000,    // 0x0000 - 0x01F3; N * 1.25 msec
				SupervisionTimeout:    0x0048,    // 0x000A - 0x0C80; N * 10 msec
				MinimumCELength:       0x0000,    // 0x0000 - 0xFFFF; N * 0.625 msec
				MaximumCELength:       0x0000,    // 0x0000 - 0xFFFF; N * 0.625 msec
			})); err != nil {
			logger.Fatal("can't set advertising param: %s", err)
		}
	}
	return gatt.Discover(gatt.FilterFunc(filter))
}

func (n *Nuimo) reconnect() error {
	logger.Info("Reconnect")
	if n.client != nil {
		n.client.ClearSubscriptions()
		n.client.CancelConnection()
	}
	client, err := discoverDevice()
	if err != nil {
		return err
	}
	n.client = client
	return n.discoverServices()
}

func (n *Nuimo) keepConnected(refresh int) {

	for {
		c := make(chan []byte, 1)
		go func() {
			logger.Info("Reading batterie")
			data, err := n.client.ReadCharacteristic(n.bttry)
			if err != nil {
				logger.Error("Error", err)
				// this will cause a reconnect
				return
			}
			c <- data

		}()
		select {
		case data := <-c:
			n.battery(data)
		case <-time.After(30 * time.Second):
			n.reconnect()
		}
		close(c)
		time.Sleep(time.Duration(refresh) * time.Second)
	}

}

// Events provides access to the events channel which contains the user interaction and battery level events
func (n *Nuimo) Events() <-chan Event {
	return n.events
}

// Display sends the passed byte atrix into the LED display of the Nuimo
func (n *Nuimo) Display(matrix []byte, brightness uint8, timeout uint8) {

	displayMatrix := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

	for c, dots := range matrix {
		if c > 10 {
			break
		}
		displayMatrix[c] = dots
	}

	displayMatrix[11] = brightness
	displayMatrix[12] = timeout

	n.client.WriteCharacteristic(n.led, displayMatrix, true)
}

// DisplayMatrix transforms a matrix consisting of 0s and 1s into a byte matrix
func DisplayMatrix(dots ...byte) []byte {
	bytes := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	var b uint8
	var i uint8
	dotCount := uint8(len(dots))

	for b = 0; b < 11; b++ {
		for i = 0; i < 8; i++ {
			dot := (b * 8) + i
			if dot < dotCount && dots[dot] > 0 {
				bytes[b] |= byte(1) << i
			}
		}

	}

	return bytes
}

//  TODO: make sure we only subscribe to the services we need
func (n *Nuimo) discoverServices() error {
	p, err := n.client.DiscoverProfile(true)
	if err != nil {
		return fmt.Errorf("can't discover services: %s\n", err)
	}

	for _, s := range p.Services {

		switch {
		case s.UUID.Equal(ble.MustParse(SERVICE_DEVICE_INFO)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_DEVICE_INFO)):
					logger.Info("Info subscribed")
					n.client.Subscribe(c, false, n.info)
				default:
					logger.Warn("Unknown device char", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_BATTERY_STATUS)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_BATTERY_LEVEL)):
					logger.Info("Battery subscribed")
					n.bttry = c
					n.client.Subscribe(c, false, n.battery)

				default:
					logger.Warn("Unknown battery char", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_USER_INPUT)):
			for _, c := range s.Characteristics {
				switch {
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_CLICK)):
					n.client.Subscribe(c, false, n.click)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_ROTATE)):
					n.client.Subscribe(c, false, n.rotate)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_SWIPE)):
					n.client.Subscribe(c, false, n.swipe)
				case c.UUID.Equal(ble.MustParse(CHAR_INPUT_FLY)):
					n.client.Subscribe(c, false, n.fly)
				default:
					logger.Warn("Unknown input characteristik", "uuid", c.UUID.String())
					n.client.Subscribe(c, false, n.unknown)
				}
			}
		case s.UUID.Equal(ble.MustParse(SERVICE_LED_MATRIX)):
			for _, c := range s.Characteristics {
				logger.Info("Led found")
				n.led = c
			}
		default:
			logger.Warn("Unknown service %s", "uuid", s.UUID.String())
		}
	}
	return nil
}

// Disconnect closes the connection and drops all subscriptions
func (n *Nuimo) Disconnect() error {
	logger.Warn("Nuimo connection closed")
	close(n.events)
	return n.client.CancelConnection()
}

func (n *Nuimo) battery(req []byte) {
	uval, _ := binary.Uvarint(req)
	level := int64(uval)
	n.send(Event{Key: "battery", Raw: req, Value: level})
}
func (n *Nuimo) info(req []byte) {
	logger.Info("Info: " + string(req))
}

func (n *Nuimo) click(req []byte) {
	uval, _ := binary.Uvarint(req)
	dir := int64(uval)
	switch dir {
	case CLICK_DOWN:
		n.send(Event{Key: "press", Raw: req})
	case CLICK_UP:
		n.send(Event{Key: "release", Raw: req})
	}
}

func (n *Nuimo) rotate(req []byte) {
	uval := binary.LittleEndian.Uint16(req)
	val := int64(int16(uval))
	n.send(Event{Key: "rotate", Raw: req, Value: val})
}
func (n *Nuimo) swipe(req []byte) {
	uval, _ := binary.Uvarint(req)
	dir := int64(uval)
	n.send(Event{Key: "swipe", Raw: req, Value: dir})

	switch dir {
	case DIR_LEFT:
		n.send(Event{Key: "swipe_left", Raw: req})
	case DIR_RIGHT:
		n.send(Event{Key: "swipe_right", Raw: req})
	case DIR_UP:
		n.send(Event{Key: "swipe_up", Raw: req})
	case DIR_DOWN:
		n.send(Event{Key: "swipe_down", Raw: req})
	}
}
func (n *Nuimo) fly(req []byte) {
	uval, _ := binary.Uvarint(req[0:1])
	dir := int(uval)
	uval, _ = binary.Uvarint(req[2:])
	distance := int64(uval)

	switch dir {
	case DIR_LEFT:
		n.send(Event{Key: "fly_left", Raw: req, Value: distance})
	case DIR_RIGHT:
		n.send(Event{Key: "fly_right", Raw: req, Value: distance})
	case DIR_BACKWARDS:
		n.send(Event{Key: "fly_backwards", Raw: req, Value: distance})
	case DIR_TOWARDS:
		n.send(Event{Key: "fly_towards", Raw: req, Value: distance})
	case DIR_UPDOWN:
		n.send(Event{Key: "fly_updown", Raw: req, Value: distance})
	}
}
func (n *Nuimo) unknown(req []byte) {
	n.send(Event{Key: "unknown", Raw: req})
}

// make sure missing event sinks don't block the client
func (n *Nuimo) send(e Event) {
	go func() { n.events <- e }()
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"regexp"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/kinesisvideo"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccAWSKinesisVideoStream_basic(t *testing.T) {
	var stream kinesisvideo.StreamInfo

	resourceName := "aws_kinesis_video_stream.default"
	rInt1 := acctest.RandInt()
	rInt2 := acctest.RandInt()

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckKinesisVideoStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccKinesisVideoStreamConfig(rInt1),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("terraform-kinesis-video-stream-test-%d", rInt1)),
					testAccMatchResourceAttrRegionalARN(resourceName, "arn", "kinesisvideo", regexp.MustCompile(fmt.Sprintf("stream/terraform-kinesis-video-stream-test-%d/.+", rInt1))),
					resource.TestCheckResourceAttrSet(resourceName, "creation_time"),
					resource.TestCheckResourceAttrSet(resourceName, "version"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccKinesisVideoStreamConfig(rInt2),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("terraform-kinesis-video-stream-test-%d", rInt2)),
					testAccMatchResourceAttrRegionalARN(resourceName, "arn", "kinesisvideo", regexp.MustCompile(fmt.Sprintf("stream/terraform-kinesis-video-stream-test-%d/.+", rInt2))),
				),
			},
		},
	})
}

func TestAccAWSKinesisVideoStream_options(t *testing.T) {
	var stream kinesisvideo.StreamInfo

	resourceName := "aws_kinesis_video_stream.default"
	kmsResourceName := "aws_kms_key.default"
	rInt := acctest.RandInt()
	rName1 := acctest.RandString(8)
	rName2 := acctest.RandString(8)

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckKinesisVideoStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccKinesisVideoStreamConfig_Options(rInt, rName1, "video/h264"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					testAccMatchResourceAttrRegionalARN(resourceName, "arn", "kinesisvideo", regexp.MustCompile(fmt.Sprintf("stream/terraform-kinesis-video-stream-test-%d/.+", rInt))),
					resource.TestCheckResourceAttr(resourceName, "data_retention_in_hours", "1"),
					resource.TestCheckResourceAttr(resourceName, "media_type", "video/h264"),
					resource.TestCheckResourceAttr(resourceName, "device_name", fmt.Sprintf("kinesis-video-device-name-%s", rName1)),
					resource.TestCheckResourceAttrPair(
						resourceName, "kms_key_id",
						kmsResourceName, "id"),
				),
			},
			{
				Config: testAccKinesisVideoStreamConfig_Options(rInt, rName2, "video/h120"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "media_type", "video/h120"),
					resource.TestCheckResourceAttr(resourceName, "device_name", fmt.Sprintf("kinesis-video-device-name-%s", rName2)),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSKinesisVideoStream_Tags(t *testing.T) {
	var stream kinesisvideo.StreamInfo

	resourceName := "aws_kinesis_video_stream.default"
	rInt := acctest.RandInt()

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckKinesisVideoStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccKinesisVideoStreamConfig_Tags1(rInt, "key1", "value1"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"),
				),
			},
			{
				Config: testAccKinesisVideoStreamConfig_Tags2(rInt, "key1", "value1", "key2", "value2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "2"),
					resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccKinesisVideoStreamConfig_Tags1(rInt, "key2", "value2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
				),
			},
		},
	})
}

func TestAccAWSKinesisVideoStream_disappears(t *testing.T) {
	var stream kinesisvideo.StreamInfo

	resourceName := "aws_kinesis_video_stream.default"
	rInt := acctest.RandInt()

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckKinesisVideoStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccKinesisVideoStreamConfig(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					testAccCheckKinesisVideoStreamDisappears(resourceName),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckKinesisVideoStreamDisappears(resourceName string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.Meta().(*AWSClient).kinesisvideoconn

		rs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return fmt.Errorf("Not found: %s", resourceName)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No resource ID is set")
		}

		input := &kinesisvideo.DeleteStreamInput{
			StreamARN:      aws.String(rs.Primary.ID),
			CurrentVersion: aws.String(rs.Primary.Attributes["version"]),
		}

		if _, err := conn.DeleteStream(input); err != nil {
			return err
		}

		stateConf := &resource.StateChangeConf{
			Pending:    []string{kinesisvideo.StatusDeleting},
			Target:     []string{"DELETED"},
			Refresh:    kinesisVideoStreamStateRefresh(conn, rs.Primary.ID),
			Timeout:    15 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		if _, err := stateConf.WaitForState(); err != nil {
			return err
		}

		return nil
	}
}

func testAccCheckKinesisVideoStreamExists(n string, stream *kinesisvideo.StreamInfo) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Kinesis ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).kinesisvideoconn
		describeOpts := &kinesisvideo.DescribeStreamInput{
			StreamARN: aws.String(rs.Primary.ID),
		}
		resp, err := conn.DescribeStream(describeOpts)
		if err != nil {
			return err
		}

		*stream = *resp.StreamInfo

		return nil
	}
}

func testAccCheckKinesisVideoStreamDestroy(s *terraform.State) error {
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_kinesis_video_stream" {
			continue
		}
		conn := testAccProvider.Meta().(*AWSClient).kinesisvideoconn
		describeOpts := &kinesisvideo.DescribeStreamInput{
			StreamARN: aws.String(rs.Primary.ID),
		}
		resp, err := conn.DescribeStream(describeOpts)
		if err == nil {
			if resp.StreamInfo != nil && aws.StringValue(resp.StreamInfo.Status) != "DELETING" {
				return fmt.Errorf("Error Kinesis Video Stream still exists")
			}
		}

		return nil

	}

	return nil
}

func testAccKinesisVideoStreamConfig(rInt int) string {
	return fmt.Sprintf(`
resource "aws_kinesis_video_stream" "default" {
  name = "terraform-kinesis-video-stream-test-%d"
}
`, rInt)
}

func testAccKinesisVideoStreamConfig_Options(rInt int, rName, mediaType string) string {
	return fmt.Sprintf(`
resource "aws_kms_key" "default" {
  description             = "KMS key 1"
  deletion_window_in_days = 7
}

resource "aws_kinesis_video_stream" "default" {
  name = "terraform-kinesis-video-stream-test-%[1]d"

  data_retention_in_hours = 1
  device_name             = "kinesis-video-device-name-%[2]s"
  kms_key_id              = aws_kms_key.default.id
  media_type              = "%[3]s"
}
`, rInt, rName, mediaType)
}

func testAccKinesisVideoStreamConfig_Tags1(rInt int, tagKey1, tagValue1 string) string {
	return fmt.Sprintf(`
resource "aws_kinesis_video_stream" "default" {
  name = "terraform-kinesis-video-stream-test-%d"

  tags = {
    %[2]q = %[3]q
  }
}
`, rInt, tagKey1, tagValue1)
}

func testAccKinesisVideoStreamConfig_Tags2(rInt int, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
	return fmt.Sprintf(`
resource "aws_kinesis_video_stream" "default" {
  name = "terraform-kinesis-video-stream-test-%d"

  tags = {
    %[2]q = %[3]q
    %[4]q = %[5]q
  }
}
`, rInt, tagKey1, tagValue1, tagKey2, tagValue2)
}
<commit_msg>tests/provider: Add precheck (KinesisVideo)<commit_after>package aws

import (
	"fmt"
	"regexp"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/kinesisvideo"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccAWSKinesisVideoStream_basic(t *testing.T) {
	var stream kinesisvideo.StreamInfo

	resourceName := "aws_kinesis_video_stream.default"
	rInt1 := acctest.RandInt()
	rInt2 := acctest.RandInt()

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(kinesisvideo.EndpointsID, t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckKinesisVideoStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccKinesisVideoStreamConfig(rInt1),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("terraform-kinesis-video-stream-test-%d", rInt1)),
					testAccMatchResourceAttrRegionalARN(resourceName, "arn", "kinesisvideo", regexp.MustCompile(fmt.Sprintf("stream/terraform-kinesis-video-stream-test-%d/.+", rInt1))),
					resource.TestCheckResourceAttrSet(resourceName, "creation_time"),
					resource.TestCheckResourceAttrSet(resourceName, "version"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccKinesisVideoStreamConfig(rInt2),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("terraform-kinesis-video-stream-test-%d", rInt2)),
					testAccMatchResourceAttrRegionalARN(resourceName, "arn", "kinesisvideo", regexp.MustCompile(fmt.Sprintf("stream/terraform-kinesis-video-stream-test-%d/.+", rInt2))),
				),
			},
		},
	})
}

func TestAccAWSKinesisVideoStream_options(t *testing.T) {
	var stream kinesisvideo.StreamInfo

	resourceName := "aws_kinesis_video_stream.default"
	kmsResourceName := "aws_kms_key.default"
	rInt := acctest.RandInt()
	rName1 := acctest.RandString(8)
	rName2 := acctest.RandString(8)

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(kinesisvideo.EndpointsID, t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckKinesisVideoStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccKinesisVideoStreamConfig_Options(rInt, rName1, "video/h264"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					testAccMatchResourceAttrRegionalARN(resourceName, "arn", "kinesisvideo", regexp.MustCompile(fmt.Sprintf("stream/terraform-kinesis-video-stream-test-%d/.+", rInt))),
					resource.TestCheckResourceAttr(resourceName, "data_retention_in_hours", "1"),
					resource.TestCheckResourceAttr(resourceName, "media_type", "video/h264"),
					resource.TestCheckResourceAttr(resourceName, "device_name", fmt.Sprintf("kinesis-video-device-name-%s", rName1)),
					resource.TestCheckResourceAttrPair(
						resourceName, "kms_key_id",
						kmsResourceName, "id"),
				),
			},
			{
				Config: testAccKinesisVideoStreamConfig_Options(rInt, rName2, "video/h120"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "media_type", "video/h120"),
					resource.TestCheckResourceAttr(resourceName, "device_name", fmt.Sprintf("kinesis-video-device-name-%s", rName2)),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSKinesisVideoStream_Tags(t *testing.T) {
	var stream kinesisvideo.StreamInfo

	resourceName := "aws_kinesis_video_stream.default"
	rInt := acctest.RandInt()

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(kinesisvideo.EndpointsID, t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckKinesisVideoStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccKinesisVideoStreamConfig_Tags1(rInt, "key1", "value1"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"),
				),
			},
			{
				Config: testAccKinesisVideoStreamConfig_Tags2(rInt, "key1", "value1", "key2", "value2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "2"),
					resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccKinesisVideoStreamConfig_Tags1(rInt, "key2", "value2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
				),
			},
		},
	})
}

func TestAccAWSKinesisVideoStream_disappears(t *testing.T) {
	var stream kinesisvideo.StreamInfo

	resourceName := "aws_kinesis_video_stream.default"
	rInt := acctest.RandInt()

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(kinesisvideo.EndpointsID, t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckKinesisVideoStreamDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccKinesisVideoStreamConfig(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckKinesisVideoStreamExists(resourceName, &stream),
					testAccCheckKinesisVideoStreamDisappears(resourceName),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func testAccCheckKinesisVideoStreamDisappears(resourceName string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		conn := testAccProvider.Meta().(*AWSClient).kinesisvideoconn

		rs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return fmt.Errorf("Not found: %s", resourceName)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No resource ID is set")
		}

		input := &kinesisvideo.DeleteStreamInput{
			StreamARN:      aws.String(rs.Primary.ID),
			CurrentVersion: aws.String(rs.Primary.Attributes["version"]),
		}

		if _, err := conn.DeleteStream(input); err != nil {
			return err
		}

		stateConf := &resource.StateChangeConf{
			Pending:    []string{kinesisvideo.StatusDeleting},
			Target:     []string{"DELETED"},
			Refresh:    kinesisVideoStreamStateRefresh(conn, rs.Primary.ID),
			Timeout:    15 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		if _, err := stateConf.WaitForState(); err != nil {
			return err
		}

		return nil
	}
}

func testAccCheckKinesisVideoStreamExists(n string, stream *kinesisvideo.StreamInfo) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No Kinesis ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).kinesisvideoconn
		describeOpts := &kinesisvideo.DescribeStreamInput{
			StreamARN: aws.String(rs.Primary.ID),
		}
		resp, err := conn.DescribeStream(describeOpts)
		if err != nil {
			return err
		}

		*stream = *resp.StreamInfo

		return nil
	}
}

func testAccCheckKinesisVideoStreamDestroy(s *terraform.State) error {
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_kinesis_video_stream" {
			continue
		}
		conn := testAccProvider.Meta().(*AWSClient).kinesisvideoconn
		describeOpts := &kinesisvideo.DescribeStreamInput{
			StreamARN: aws.String(rs.Primary.ID),
		}
		resp, err := conn.DescribeStream(describeOpts)
		if err == nil {
			if resp.StreamInfo != nil && aws.StringValue(resp.StreamInfo.Status) != "DELETING" {
				return fmt.Errorf("Error Kinesis Video Stream still exists")
			}
		}

		return nil

	}

	return nil
}

func testAccKinesisVideoStreamConfig(rInt int) string {
	return fmt.Sprintf(`
resource "aws_kinesis_video_stream" "default" {
  name = "terraform-kinesis-video-stream-test-%d"
}
`, rInt)
}

func testAccKinesisVideoStreamConfig_Options(rInt int, rName, mediaType string) string {
	return fmt.Sprintf(`
resource "aws_kms_key" "default" {
  description             = "KMS key 1"
  deletion_window_in_days = 7
}

resource "aws_kinesis_video_stream" "default" {
  name = "terraform-kinesis-video-stream-test-%[1]d"

  data_retention_in_hours = 1
  device_name             = "kinesis-video-device-name-%[2]s"
  kms_key_id              = aws_kms_key.default.id
  media_type              = "%[3]s"
}
`, rInt, rName, mediaType)
}

func testAccKinesisVideoStreamConfig_Tags1(rInt int, tagKey1, tagValue1 string) string {
	return fmt.Sprintf(`
resource "aws_kinesis_video_stream" "default" {
  name = "terraform-kinesis-video-stream-test-%d"

  tags = {
    %[2]q = %[3]q
  }
}
`, rInt, tagKey1, tagValue1)
}

func testAccKinesisVideoStreamConfig_Tags2(rInt int, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
	return fmt.Sprintf(`
resource "aws_kinesis_video_stream" "default" {
  name = "terraform-kinesis-video-stream-test-%d"

  tags = {
    %[2]q = %[3]q
    %[4]q = %[5]q
  }
}
`, rInt, tagKey1, tagValue1, tagKey2, tagValue2)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package command

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/vmware/photon-controller-cli/Godeps/_workspace/src/github.com/vmware/photon-controller-go-sdk/photon"
)

// Get limitsList from -limits/-l string flag
func parseLimitsListFromFlag(limits string) ([]photon.QuotaLineItem, error) {
	var limitsList []photon.QuotaLineItem
	if len(limits) != 0 {
		limitsListOri := regexp.MustCompile(`\s*,\s*`).Split(limits, -1)
		for i := 0; i < len(limitsListOri); i++ {
			limit := strings.Fields(limitsListOri[i])
			if len(limit) != 3 {
				return limitsList, fmt.Errorf("Error parsing limits, should be: <key> <value> <unit>, <key> <value> <unit>...")
			}

			key := limit[0]
			value, err := strconv.ParseFloat(limit[1], 64)
			if err != nil {
				return limitsList, fmt.Errorf("Error: %s. Please provide float as value", err.Error())
			}
			unit := limit[2]

			limitsList = append(limitsList, photon.QuotaLineItem{Key: key, Value: value, Unit: unit})
		}
	}
	return limitsList, nil
}

// Get affinitiesList from -affinities/-a string flag
func parseAffinitiesListFromFlag(affinities string) ([]photon.LocalitySpec, error) {
	var affinitiesList []photon.LocalitySpec
	if len(affinities) != 0 {
		affinitiesListOri := regexp.MustCompile(`\s*,\s*`).Split(affinities, -1)
		for i := 0; i < len(affinitiesListOri); i++ {
			affinity := regexp.MustCompile(`\s*:\s*`).Split(affinitiesListOri[i], -1)
			if len(affinity) != 2 {
				return affinitiesList, fmt.Errorf("Error parsing affinities, should be: <kind> <id>, <kind> <id>...")
			}

			kind := affinity[0]
			id := affinity[1]

			affinitiesList = append(affinitiesList, photon.LocalitySpec{Kind: kind, ID: id})
		}
	}
	return affinitiesList, nil
}

// Get disksList from -disks/-d string flag
func parseDisksListFromFlag(disks string) ([]photon.AttachedDisk, error) {
	var disksList []photon.AttachedDisk
	if len(disks) != 0 {
		disksListOri := regexp.MustCompile(`\s*,\s*`).Split(disks, -1)
		for i := 0; i < len(disksListOri); i++ {
			disk := strings.Fields(disksListOri[i])
			if len(disk) != 3 {
				return disksList, fmt.Errorf("Error parsing disks, should be: <name> <flavor> <boot=true/capacity>...")
			}

			name := disk[0]
			flavor := disk[1]
			if disk[2] == "boot=true" {
				disksList = append(disksList, photon.AttachedDisk{Name: name, Flavor: flavor, Kind: "ephemeral-disk", BootDisk: true})
			} else {
				capacity, err := strconv.Atoi(disk[2])
				if err != nil {
					return disksList, err
				}
				disksList = append(disksList, photon.AttachedDisk{Name: name, Flavor: flavor, Kind: "ephemeral-disk", BootDisk: false, CapacityGB: capacity})
			}
		}
	}
	return disksList, nil
}

// Get environment Map from -environment/-e string flag
func parseMapFromFlag(cmdFlag string) (map[string]string, error) {
	newMap := make(map[string]string)
	if len(cmdFlag) != 0 {
		entries := regexp.MustCompile(`\s*,\s*`).Split(cmdFlag, -1)
		for i := 0; i < len(entries); i++ {
			entry := regexp.MustCompile(`\s*:\s*`).Split(entries[i], -1)
			if len(entry) != 2 {
				return newMap, fmt.Errorf("Error parsing the command flag, should be: <key>:<value>, <key>:<value>...")
			}

			key := entry[0]
			value := entry[1]

			newMap[key] = value
		}
	}
	return newMap, nil
}
<commit_msg>vm: affinities: fix parsing error message<commit_after>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package command

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/vmware/photon-controller-cli/Godeps/_workspace/src/github.com/vmware/photon-controller-go-sdk/photon"
)

// Get limitsList from -limits/-l string flag
func parseLimitsListFromFlag(limits string) ([]photon.QuotaLineItem, error) {
	var limitsList []photon.QuotaLineItem
	if len(limits) != 0 {
		limitsListOri := regexp.MustCompile(`\s*,\s*`).Split(limits, -1)
		for i := 0; i < len(limitsListOri); i++ {
			limit := strings.Fields(limitsListOri[i])
			if len(limit) != 3 {
				return limitsList, fmt.Errorf("Error parsing limits, should be: <key> <value> <unit>, <key> <value> <unit>...")
			}

			key := limit[0]
			value, err := strconv.ParseFloat(limit[1], 64)
			if err != nil {
				return limitsList, fmt.Errorf("Error: %s. Please provide float as value", err.Error())
			}
			unit := limit[2]

			limitsList = append(limitsList, photon.QuotaLineItem{Key: key, Value: value, Unit: unit})
		}
	}
	return limitsList, nil
}

// Get affinitiesList from -affinities/-a string flag
func parseAffinitiesListFromFlag(affinities string) ([]photon.LocalitySpec, error) {
	var affinitiesList []photon.LocalitySpec
	if len(affinities) != 0 {
		affinitiesListOri := regexp.MustCompile(`\s*,\s*`).Split(affinities, -1)
		for i := 0; i < len(affinitiesListOri); i++ {
			affinity := regexp.MustCompile(`\s*:\s*`).Split(affinitiesListOri[i], -1)
			if len(affinity) != 2 {
				return affinitiesList, fmt.Errorf("Error parsing affinities, should be: <kind>:<id>, <kind>:<id>...")
			}

			kind := affinity[0]
			id := affinity[1]

			affinitiesList = append(affinitiesList, photon.LocalitySpec{Kind: kind, ID: id})
		}
	}
	return affinitiesList, nil
}

// Get disksList from -disks/-d string flag
func parseDisksListFromFlag(disks string) ([]photon.AttachedDisk, error) {
	var disksList []photon.AttachedDisk
	if len(disks) != 0 {
		disksListOri := regexp.MustCompile(`\s*,\s*`).Split(disks, -1)
		for i := 0; i < len(disksListOri); i++ {
			disk := strings.Fields(disksListOri[i])
			if len(disk) != 3 {
				return disksList, fmt.Errorf("Error parsing disks, should be: <name> <flavor> <boot=true/capacity>...")
			}

			name := disk[0]
			flavor := disk[1]
			if disk[2] == "boot=true" {
				disksList = append(disksList, photon.AttachedDisk{Name: name, Flavor: flavor, Kind: "ephemeral-disk", BootDisk: true})
			} else {
				capacity, err := strconv.Atoi(disk[2])
				if err != nil {
					return disksList, err
				}
				disksList = append(disksList, photon.AttachedDisk{Name: name, Flavor: flavor, Kind: "ephemeral-disk", BootDisk: false, CapacityGB: capacity})
			}
		}
	}
	return disksList, nil
}

// Get environment Map from -environment/-e string flag
func parseMapFromFlag(cmdFlag string) (map[string]string, error) {
	newMap := make(map[string]string)
	if len(cmdFlag) != 0 {
		entries := regexp.MustCompile(`\s*,\s*`).Split(cmdFlag, -1)
		for i := 0; i < len(entries); i++ {
			entry := regexp.MustCompile(`\s*:\s*`).Split(entries[i], -1)
			if len(entry) != 2 {
				return newMap, fmt.Errorf("Error parsing the command flag, should be: <key>:<value>, <key>:<value>...")
			}

			key := entry[0]
			value := entry[1]

			newMap[key] = value
		}
	}
	return newMap, nil
}
<|endoftext|>


<commit_before>// vim: tabstop=2 shiftwidth=2

package main

import (
	"io"
	//"io/ioutil"
	"log"
	"os"
)

const (
	version string = "0.1a"
	date_format string = "2006-01-02"
	key_validity_days int = 60
	max_frag_length = 10230
	maxChainLength = 10
	maxCopies = 5
	base64_line_wrap = 40
	headerBytes = 512
	headersBytes = headerBytes * maxChainLength
	encHeadBytes = headersBytes - headerBytes
	bodyBytes = 10240
	messageBytes = headersBytes + bodyBytes
)

var (
	Trace	*log.Logger
	Info	*log.Logger
	Warn	*log.Logger
	Error	*log.Logger
)

func logInit(
	traceHandle io.Writer,
	infoHandle io.Writer,
	warnHandle io.Writer,
	errorHandle io.Writer) {

	Trace = log.New(traceHandle,
		"Trace: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Info = log.New(infoHandle,
		"Info: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Warn = log.New(warnHandle,
		"Warn: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Error = log.New(errorHandle,
		"Error: ",
		log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {
	logInit(os.Stdout, os.Stdout, os.Stdout, os.Stderr)
	flags()
	if flag_client {
		mixprep()
	} else {
		//server()
		mailRead()
		poolRead()
	}
}
<commit_msg>Add a sleep loop around core server functions.<commit_after>// vim: tabstop=2 shiftwidth=2

package main

import (
	"io"
	//"io/ioutil"
	"log"
	"os"
	"time"
)

const (
	version string = "0.1a"
	date_format string = "2006-01-02"
	key_validity_days int = 60
	max_frag_length = 10230
	maxChainLength = 10
	maxCopies = 5
	base64_line_wrap = 40
	headerBytes = 512
	headersBytes = headerBytes * maxChainLength
	encHeadBytes = headersBytes - headerBytes
	bodyBytes = 10240
	messageBytes = headersBytes + bodyBytes
)

var (
	Trace	*log.Logger
	Info	*log.Logger
	Warn	*log.Logger
	Error	*log.Logger
)

func logInit(
	traceHandle io.Writer,
	infoHandle io.Writer,
	warnHandle io.Writer,
	errorHandle io.Writer) {

	Trace = log.New(traceHandle,
		"Trace: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Info = log.New(infoHandle,
		"Info: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Warn = log.New(warnHandle,
		"Warn: ",
		log.Ldate|log.Ltime|log.Lshortfile)

	Error = log.New(errorHandle,
		"Error: ",
		log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {
	logInit(os.Stdout, os.Stdout, os.Stdout, os.Stderr)
	flags()
	if flag_client {
		mixprep()
	} else {
		Info.Println("Starting YAMN server")
		for {
			mailRead()
			poolRead()
			time.Sleep(60 * time.Second)
		}
	}
}
<|endoftext|>


<commit_before>package context_manager

import (
	"archive/tar"
	"context"
	"crypto/sha256"
	"fmt"
	"io"
	"os"
	"path/filepath"

	uuid "github.com/satori/go.uuid"

	"github.com/werf/logboek"
	"github.com/werf/werf/pkg/util"
	"github.com/werf/werf/pkg/werf"
)

func GetContextTmpDir() string {
	return filepath.Join(werf.GetServiceDir(), "tmp", "context")
}

func ContextAddFileChecksum(ctx context.Context, contextAddFile []string, projectDir string) (string, error) {
	logboek.Context(ctx).Debug().LogF("-- ContextAddFileChecksum %q %q\n", projectDir, contextAddFile)

	h := sha256.New()

	for _, addFile := range contextAddFile {
		h.Write([]byte(addFile))

		path := filepath.Join(projectDir, addFile)
		if _, err := os.Stat(path); os.IsNotExist(err) {
			continue
		} else if err != nil {
			return "", fmt.Errorf("error accessing %q: %s", path, err)
		}

		if f, err := os.Open(path); err != nil {
			return "", fmt.Errorf("unable to open %q: %s", path, err)
		} else {
			defer f.Close()
			if _, err := io.Copy(h, f); err != nil {
				return "", fmt.Errorf("error reading %q: %s", path, err)
			}
		}
	}

	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

type contextAddFileDescriptor struct {
	AddFile           string
	PathInsideContext string
}

func ApplyContextAddFileToArchive(ctx context.Context, originalArchivePath string, contextPath string, contextAddFile []string, projectDir string) (string, error) {
	path := filepath.Join(GetContextTmpDir(), uuid.NewV4().String())
	if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
		return "", fmt.Errorf("unable to create dir %q: %s", filepath.Dir(path), err)
	}

	logboek.Context(ctx).Default().LogF("Will copy %q archive to %q\n", originalArchivePath, path)

	source, err := os.Open(originalArchivePath)
	if err != nil {
		return "", fmt.Errorf("unable to open %q: %s", originalArchivePath, err)
	}
	defer source.Close()

	destination, err := os.Create(path)
	if err != nil {
		return "", fmt.Errorf("unable to create %q: %s", path, err)
	}
	defer destination.Close()

	tr := tar.NewReader(source)
	tw := tar.NewWriter(destination)
	defer tw.Close()

	var contextAddFileDescriptors []*contextAddFileDescriptor
	for _, addFile := range contextAddFile {
		var destFilePath string
		if contextPath != "" {
			if !util.IsSubpathOfBasePath(contextPath, addFile) {
				return "", fmt.Errorf("specified contextAddFile %q is out of context %q", addFile, contextPath)
			}
			destFilePath = util.GetRelativeToBaseFilepath(contextPath, addFile)
		} else {
			destFilePath = addFile
		}

		contextAddFileDescriptors = append(contextAddFileDescriptors, &contextAddFileDescriptor{
			AddFile:           addFile,
			PathInsideContext: destFilePath,
		})
	}

CopyArchive:
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			return "", fmt.Errorf("error reading archive %q: %s", originalArchivePath, err)
		}

		for _, addFileDesc := range contextAddFileDescriptors {
			if hdr.Name == filepath.ToSlash(addFileDesc.PathInsideContext) {
				logboek.Context(ctx).Default().LogF("Matched file %q for replacement in the archive %q by contextAddFile=%q directive\n", hdr.Name, path, addFileDesc.AddFile)
				continue CopyArchive
			}
		}

		tw.WriteHeader(hdr)

		if _, err := io.Copy(tw, tr); err != nil {
			return "", fmt.Errorf("error copying %q from %q archive to %q: %s", hdr.Name, originalArchivePath, path, err)
		}

		logboek.Context(ctx).Default().LogF("Copied %s from %q archive to %q\n", hdr.Name, originalArchivePath, path)
	}

	for _, addFileDesc := range contextAddFileDescriptors {
		sourceFilePath := filepath.Join(projectDir, addFileDesc.AddFile)
		tarEntryName := filepath.ToSlash(addFileDesc.PathInsideContext)
		if err := copyFileIntoTar(sourceFilePath, tarEntryName, tw); err != nil {
			return "", fmt.Errorf("unable to copy %q from workinto archive %q: %s", sourceFilePath, path, err)
		}
		logboek.Context(ctx).Default().LogF("Copied file %q in the archive %q with %q file from working directory (contextAddFile=%s directive)\n", tarEntryName, path, sourceFilePath, addFileDesc.AddFile)
	}

	return path, nil
}

func copyFileIntoTar(sourceFilePath string, tarEntryName string, tw *tar.Writer) error {
	sourceFileStat, err := os.Lstat(sourceFilePath)
	if err != nil {
		return fmt.Errorf("error accessing %q stat: %s", sourceFilePath, err)
	}

	isSymlink := sourceFileStat.Mode()&os.ModeSymlink != 0
	if isSymlink {
		linkname, err := os.Readlink(sourceFilePath)
		if err != nil {
			return fmt.Errorf("cannot read symlink %q: %s", sourceFilePath, err)
		}

		if err := tw.WriteHeader(&tar.Header{
			Format:     tar.FormatGNU,
			Typeflag:   tar.TypeSymlink,
			Name:       tarEntryName,
			Linkname:   linkname,
			Mode:       int64(sourceFileStat.Mode()),
			Size:       sourceFileStat.Size(),
			ModTime:    sourceFileStat.ModTime(),
			AccessTime: sourceFileStat.ModTime(),
			ChangeTime: sourceFileStat.ModTime(),
		}); err != nil {
			return fmt.Errorf("unable to write tar symlink header for file %s: %s", tarEntryName, err)
		}
	} else if sourceFileStat.Mode().IsRegular() {
		if err := tw.WriteHeader(&tar.Header{
			Format:     tar.FormatGNU,
			Name:       tarEntryName,
			Mode:       int64(sourceFileStat.Mode()),
			Size:       sourceFileStat.Size(),
			ModTime:    sourceFileStat.ModTime(),
			AccessTime: sourceFileStat.ModTime(),
			ChangeTime: sourceFileStat.ModTime(),
		}); err != nil {
			return fmt.Errorf("unable to write tar header for file %q: %s", tarEntryName, err)
		}

		f, err := os.Open(sourceFilePath)
		if err != nil {
			return fmt.Errorf("unable to open file %q: %s", sourceFilePath, err)
		}

		if _, err := io.Copy(tw, f); err != nil {
			return fmt.Errorf("unable to write data to tar archive from file %q: %s", sourceFilePath, err)
		}

		if err := f.Close(); err != nil {
			return fmt.Errorf("error closing file %q: %s", sourceFilePath, err)
		}
	}

	return nil
}
<commit_msg>Fix defer in forloop<commit_after>package context_manager

import (
	"archive/tar"
	"context"
	"crypto/sha256"
	"fmt"
	"io"
	"os"
	"path/filepath"

	uuid "github.com/satori/go.uuid"

	"github.com/werf/logboek"
	"github.com/werf/werf/pkg/util"
	"github.com/werf/werf/pkg/werf"
)

func GetContextTmpDir() string {
	return filepath.Join(werf.GetServiceDir(), "tmp", "context")
}

func ContextAddFileChecksum(ctx context.Context, contextAddFile []string, projectDir string) (string, error) {
	logboek.Context(ctx).Debug().LogF("-- ContextAddFileChecksum %q %q\n", projectDir, contextAddFile)

	h := sha256.New()

	for _, addFile := range contextAddFile {
		h.Write([]byte(addFile))

		path := filepath.Join(projectDir, addFile)
		if _, err := os.Stat(path); os.IsNotExist(err) {
			continue
		} else if err != nil {
			return "", fmt.Errorf("error accessing %q: %s", path, err)
		}

		if err := func() error {
			f, err := os.Open(path)
			if err != nil {
				return fmt.Errorf("unable to open %q: %s", path, err)
			}
			defer f.Close()

			if _, err := io.Copy(h, f); err != nil {
				return fmt.Errorf("error reading %q: %s", path, err)
			}

			return nil
		}(); err != nil {
			return "", err
		}
	}

	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

type contextAddFileDescriptor struct {
	AddFile           string
	PathInsideContext string
}

func ApplyContextAddFileToArchive(ctx context.Context, originalArchivePath string, contextPath string, contextAddFile []string, projectDir string) (string, error) {
	path := filepath.Join(GetContextTmpDir(), uuid.NewV4().String())
	if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
		return "", fmt.Errorf("unable to create dir %q: %s", filepath.Dir(path), err)
	}

	logboek.Context(ctx).Default().LogF("Will copy %q archive to %q\n", originalArchivePath, path)

	source, err := os.Open(originalArchivePath)
	if err != nil {
		return "", fmt.Errorf("unable to open %q: %s", originalArchivePath, err)
	}
	defer source.Close()

	destination, err := os.Create(path)
	if err != nil {
		return "", fmt.Errorf("unable to create %q: %s", path, err)
	}
	defer destination.Close()

	tr := tar.NewReader(source)
	tw := tar.NewWriter(destination)
	defer tw.Close()

	var contextAddFileDescriptors []*contextAddFileDescriptor
	for _, addFile := range contextAddFile {
		var destFilePath string
		if contextPath != "" {
			if !util.IsSubpathOfBasePath(contextPath, addFile) {
				return "", fmt.Errorf("specified contextAddFile %q is out of context %q", addFile, contextPath)
			}
			destFilePath = util.GetRelativeToBaseFilepath(contextPath, addFile)
		} else {
			destFilePath = addFile
		}

		contextAddFileDescriptors = append(contextAddFileDescriptors, &contextAddFileDescriptor{
			AddFile:           addFile,
			PathInsideContext: destFilePath,
		})
	}

CopyArchive:
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			return "", fmt.Errorf("error reading archive %q: %s", originalArchivePath, err)
		}

		for _, addFileDesc := range contextAddFileDescriptors {
			if hdr.Name == filepath.ToSlash(addFileDesc.PathInsideContext) {
				logboek.Context(ctx).Default().LogF("Matched file %q for replacement in the archive %q by contextAddFile=%q directive\n", hdr.Name, path, addFileDesc.AddFile)
				continue CopyArchive
			}
		}

		tw.WriteHeader(hdr)

		if _, err := io.Copy(tw, tr); err != nil {
			return "", fmt.Errorf("error copying %q from %q archive to %q: %s", hdr.Name, originalArchivePath, path, err)
		}

		logboek.Context(ctx).Default().LogF("Copied %s from %q archive to %q\n", hdr.Name, originalArchivePath, path)
	}

	for _, addFileDesc := range contextAddFileDescriptors {
		sourceFilePath := filepath.Join(projectDir, addFileDesc.AddFile)
		tarEntryName := filepath.ToSlash(addFileDesc.PathInsideContext)
		if err := copyFileIntoTar(sourceFilePath, tarEntryName, tw); err != nil {
			return "", fmt.Errorf("unable to copy %q from workinto archive %q: %s", sourceFilePath, path, err)
		}
		logboek.Context(ctx).Default().LogF("Copied file %q in the archive %q with %q file from working directory (contextAddFile=%s directive)\n", tarEntryName, path, sourceFilePath, addFileDesc.AddFile)
	}

	return path, nil
}

func copyFileIntoTar(sourceFilePath string, tarEntryName string, tw *tar.Writer) error {
	sourceFileStat, err := os.Lstat(sourceFilePath)
	if err != nil {
		return fmt.Errorf("error accessing %q stat: %s", sourceFilePath, err)
	}

	isSymlink := sourceFileStat.Mode()&os.ModeSymlink != 0
	if isSymlink {
		linkname, err := os.Readlink(sourceFilePath)
		if err != nil {
			return fmt.Errorf("cannot read symlink %q: %s", sourceFilePath, err)
		}

		if err := tw.WriteHeader(&tar.Header{
			Format:     tar.FormatGNU,
			Typeflag:   tar.TypeSymlink,
			Name:       tarEntryName,
			Linkname:   linkname,
			Mode:       int64(sourceFileStat.Mode()),
			Size:       sourceFileStat.Size(),
			ModTime:    sourceFileStat.ModTime(),
			AccessTime: sourceFileStat.ModTime(),
			ChangeTime: sourceFileStat.ModTime(),
		}); err != nil {
			return fmt.Errorf("unable to write tar symlink header for file %s: %s", tarEntryName, err)
		}
	} else if sourceFileStat.Mode().IsRegular() {
		if err := tw.WriteHeader(&tar.Header{
			Format:     tar.FormatGNU,
			Name:       tarEntryName,
			Mode:       int64(sourceFileStat.Mode()),
			Size:       sourceFileStat.Size(),
			ModTime:    sourceFileStat.ModTime(),
			AccessTime: sourceFileStat.ModTime(),
			ChangeTime: sourceFileStat.ModTime(),
		}); err != nil {
			return fmt.Errorf("unable to write tar header for file %q: %s", tarEntryName, err)
		}

		f, err := os.Open(sourceFilePath)
		if err != nil {
			return fmt.Errorf("unable to open file %q: %s", sourceFilePath, err)
		}

		if _, err := io.Copy(tw, f); err != nil {
			return fmt.Errorf("unable to write data to tar archive from file %q: %s", sourceFilePath, err)
		}

		if err := f.Close(); err != nil {
			return fmt.Errorf("error closing file %q: %s", sourceFilePath, err)
		}
	}

	return nil
}
<|endoftext|>