<commit_before>package st300

import (
	"testing"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/larixsource/suntech/st"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func equalEMG(t *testing.T, expected *EmergencyReport, actual *EmergencyReport) {
	require.NotNil(t, expected)
	require.NotNil(t, actual)
	assert.Equal(t, expected.DevID, actual.DevID)
	assert.Equal(t, expected.Model, actual.Model)
	assert.Equal(t, expected.SwVer, actual.SwVer)
	assert.Equal(t, expected.Timestamp, actual.Timestamp)
	assert.Equal(t, expected.Cell, actual.Cell)
	assert.InEpsilon(t, expected.Latitude, actual.Latitude, epsilon)
	assert.InEpsilon(t, expected.Longitude, actual.Longitude, epsilon)
	assert.Equal(t, expected.Speed, actual.Speed)
	assert.Equal(t, expected.Course, actual.Course)
	assert.Equal(t, expected.Satellites, actual.Satellites)
	assert.Equal(t, expected.GPSFixed, actual.GPSFixed)
	assert.Equal(t, expected.Distance, actual.Distance)
	assert.Equal(t, expected.PowerVolt, actual.PowerVolt)
	assert.Equal(t, expected.IO, actual.IO)
	assert.Equal(t, expected.EmgID, actual.EmgID)
	assert.Equal(t, expected.DrivingHourMeter, actual.DrivingHourMeter)
	assert.Equal(t, expected.BackupVolt, actual.BackupVolt)
	assert.Equal(t, expected.RealTime, actual.RealTime)
}

var testEMG = EmergencyReport{
	Hdr:              EMGReport,
	DevID:            "100850000",
	Model:            st.ST300,
	SwVer:            10,
	Timestamp:        time.Date(2008, 10, 17, 7, 41, 56, 0, time.UTC),
	Cell:             "00100",
	Latitude:         37.478519,
	Longitude:        126.886819,
	Speed:            0.012,
	Course:           0,
	Satellites:       9,
	GPSFixed:         true,
	Distance:         0,
	PowerVolt:        15.3,
	IO:               "001100",
	EmgID:            st.PanicButtonEmg,
	DrivingHourMeter: 0,
	BackupVolt:       4.5,
	RealTime:         true,
}

func TestEMG300PanicButton(t *testing.T) {
	expectedEMG := testEMG

	frame := "ST300EMG;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;1;0;4.5;1\r"
	p := ParseString(frame, ParserOpts{})
	assert.True(t, p.Next())
	assert.Nil(t, p.Error())
	msg := p.Msg()
	require.NotNil(t, msg)
	spew.Dump(msg)

	assert.EqualValues(t, st.ST300, msg.Model)
	assert.Equal(t, []byte(frame), msg.Frame)
	assert.Nil(t, msg.ParsingError)

	assert.Equal(t, msg.Type, EMGReport)
	equalEMG(t, &expectedEMG, msg.EMG)

	assert.False(t, p.Next())
}

func TestEMG300ParkingLock(t *testing.T) {
	expectedEMG := testEMG
	expectedEMG.EmgID = st.ParkingLockEmg

	frame := "ST300EMG;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;2;0;4.5;1\r"
	p := ParseString(frame, ParserOpts{})
	assert.True(t, p.Next())
	assert.Nil(t, p.Error())
	msg := p.Msg()
	require.NotNil(t, msg)
	spew.Dump(msg)

	assert.EqualValues(t, st.ST300, msg.Model)
	assert.Equal(t, []byte(frame), msg.Frame)
	assert.Nil(t, msg.ParsingError)

	assert.Equal(t, msg.Type, EMGReport)
	equalEMG(t, &expectedEMG, msg.EMG)

	assert.False(t, p.Next())
}

func TestEMG300RemovingMainPower(t *testing.T) {
	expectedEMG := testEMG
	expectedEMG.EmgID = st.RemovingMainPowerEmg

	frame := "ST300EMG;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;3;0;4.5;1\r"
	p := ParseString(frame, ParserOpts{})
	assert.True(t, p.Next())
	assert.Nil(t, p.Error())
	msg := p.Msg()
	require.NotNil(t, msg)
	spew.Dump(msg)

	assert.EqualValues(t, st.ST300, msg.Model)
	assert.Equal(t, []byte(frame), msg.Frame)
	assert.Nil(t, msg.ParsingError)

	assert.Equal(t, msg.Type, EMGReport)
	equalEMG(t, &expectedEMG, msg.EMG)

	assert.False(t, p.Next())
}

func TestEMG300AntiThef(t *testing.T) {
	expectedEMG := testEMG
	expectedEMG.EmgID = st.AntiTheftEmg

	frame := "ST300EMG;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;5;0;4.5;1\r"
	p := ParseString(frame, ParserOpts{})
	assert.True(t, p.Next())
	assert.Nil(t, p.Error())
	msg := p.Msg()
	require.NotNil(t, msg)
	spew.Dump(msg)

	assert.EqualValues(t, st.ST300, msg.Model)
	assert.Equal(t, []byte(frame), msg.Frame)
	assert.Nil(t, msg.ParsingError)

	assert.Equal(t, msg.Type, EMGReport)
	equalEMG(t, &expectedEMG, msg.EMG)

	assert.False(t, p.Next())
}

func TestEMG300AntiTheftDoor(t *testing.T) {
	expectedEMG := testEMG
	expectedEMG.EmgID = st.AntiTheftDoorEmg

	frame := "ST300EMG;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;6;0;4.5;1\r"
	p := ParseString(frame, ParserOpts{})
	assert.True(t, p.Next())
	assert.Nil(t, p.Error())
	msg := p.Msg()
	require.NotNil(t, msg)
	spew.Dump(msg)

	assert.EqualValues(t, st.ST300, msg.Model)
	assert.Equal(t, []byte(frame), msg.Frame)
	assert.Nil(t, msg.ParsingError)

	assert.Equal(t, msg.Type, EMGReport)
	equalEMG(t, &expectedEMG, msg.EMG)

	assert.False(t, p.Next())
}

func TestEMG300Motion(t *testing.T) {
	expectedEMG := testEMG
	expectedEMG.EmgID = st.MotionEmg

	frame := "ST300EMG;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;7;0;4.5;1\r"
	p := ParseString(frame, ParserOpts{})
	assert.True(t, p.Next())
	assert.Nil(t, p.Error())
	msg := p.Msg()
	require.NotNil(t, msg)
	spew.Dump(msg)

	assert.EqualValues(t, st.ST300, msg.Model)
	assert.Equal(t, []byte(frame), msg.Frame)
	assert.Nil(t, msg.ParsingError)

	assert.Equal(t, msg.Type, EMGReport)
	equalEMG(t, &expectedEMG, msg.EMG)

	assert.False(t, p.Next())
}

func TestEMG300AntiTheftShock(t *testing.T) {
	expectedEMG := testEMG
	expectedEMG.EmgID = st.AntiTheftShockEmg

	frame := "ST300EMG;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;8;0;4.5;1\r"
	p := ParseString(frame, ParserOpts{})
	assert.True(t, p.Next())
	assert.Nil(t, p.Error())
	msg := p.Msg()
	require.NotNil(t, msg)
	spew.Dump(msg)

	assert.EqualValues(t, st.ST300, msg.Model)
	assert.Equal(t, []byte(frame), msg.Frame)
	assert.Nil(t, msg.ParsingError)

	assert.Equal(t, msg.Type, EMGReport)
	equalEMG(t, &expectedEMG, msg.EMG)

	assert.False(t, p.Next())
}
<commit_msg>EMG tests "simplified"<commit_after>package st300

import (
	"bytes"
	"fmt"
	"testing"
	"time"

	"github.com/larixsource/suntech/st"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func equalEMG(t *testing.T, expected *EmergencyReport, actual *EmergencyReport) {
	require.NotNil(t, expected)
	require.NotNil(t, actual)
	assert.Equal(t, expected.DevID, actual.DevID)
	assert.Equal(t, expected.Model, actual.Model)
	assert.Equal(t, expected.SwVer, actual.SwVer)
	assert.Equal(t, expected.Timestamp, actual.Timestamp)
	assert.Equal(t, expected.Cell, actual.Cell)
	assert.InEpsilon(t, expected.Latitude, actual.Latitude, epsilon)
	assert.InEpsilon(t, expected.Longitude, actual.Longitude, epsilon)
	assert.Equal(t, expected.Speed, actual.Speed)
	assert.Equal(t, expected.Course, actual.Course)
	assert.Equal(t, expected.Satellites, actual.Satellites)
	assert.Equal(t, expected.GPSFixed, actual.GPSFixed)
	assert.Equal(t, expected.Distance, actual.Distance)
	assert.Equal(t, expected.PowerVolt, actual.PowerVolt)
	assert.Equal(t, expected.IO, actual.IO)
	assert.Equal(t, expected.EmgID, actual.EmgID)
	assert.Equal(t, expected.DrivingHourMeter, actual.DrivingHourMeter)
	assert.Equal(t, expected.BackupVolt, actual.BackupVolt)
	assert.Equal(t, expected.RealTime, actual.RealTime)
}

var testEMG = EmergencyReport{
	Hdr:              EMGReport,
	DevID:            "100850000",
	Model:            st.ST300,
	SwVer:            10,
	Timestamp:        time.Date(2008, 10, 17, 7, 41, 56, 0, time.UTC),
	Cell:             "00100",
	Latitude:         37.478519,
	Longitude:        126.886819,
	Speed:            0.012,
	Course:           0,
	Satellites:       9,
	GPSFixed:         true,
	Distance:         0,
	PowerVolt:        15.3,
	IO:               "001100",
	EmgID:            st.PanicButtonEmg,
	DrivingHourMeter: 0,
	BackupVolt:       4.5,
	RealTime:         true,
}

func TestEMG300(t *testing.T) {
	// add to buf all type of EMG frames
	frameTemplate := "ST300EMG;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;%d;0;4.5;1\r"
	idList := []int{1, 2, 3, 5, 6, 7, 8}
	var frames []string
	var buf bytes.Buffer
	for _, id := range idList {
		frame := fmt.Sprintf(frameTemplate, id)
		frames = append(frames, frame)
		buf.WriteString(frame)
	}

	// parse content of buf, save extracted msgs
	var msgs []*Msg
	p := ParseBytes(buf.Bytes(), ParserOpts{})
	for p.Next() {
		msgs = append(msgs, p.Msg())
	}
	assert.Nil(t, p.Error())

	// check every extracted msg
	expectedEMG := testEMG
	expectedEMGID := []st.EmergencyType{st.PanicButtonEmg, st.ParkingLockEmg, st.RemovingMainPowerEmg,
		st.AntiTheftEmg, st.AntiTheftDoorEmg, st.MotionEmg, st.AntiTheftShockEmg}
	assert.Len(t, msgs, len(idList))
	for i, msg := range msgs {
		expectedEMG.EmgID = expectedEMGID[i]

		assert.EqualValues(t, st.ST300, msg.Model)
		assert.Equal(t, []byte(frames[i]), msg.Frame)
		assert.Nil(t, msg.ParsingError)

		assert.Equal(t, msg.Type, EMGReport)
		equalEMG(t, &expectedEMG, msg.EMG)
	}
	assert.False(t, p.Next())
}
<|endoftext|>


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

import (
	"fmt"
	"sort"
	"time"

	"github.com/Unknwon/com"
	"github.com/Unknwon/i18n"
)

// MapToSortedStrings converts a string map to a alphabet sorted slice without duplication.
func MapToSortedStrings(m map[string]bool) []string {
	strs := make([]string, 0, len(m))
	for s := range m {
		strs = append(strs, s)
	}
	sort.Strings(strs)
	return strs
}

func Int64(v int) int64 {
	return int64(v)
}

// Seconds-based time units
const (
	Minute = 60
	Hour   = 60 * Minute
	Day    = 24 * Hour
	Week   = 7 * Day
	Month  = 30 * Day
	Year   = 12 * Month
)

func TimeSince(then time.Time, lang string) string {
	now := time.Now()

	lbl := i18n.Tr(lang, "tool.ago")
	diff := now.Unix() - then.Unix()
	if then.After(now) {
		lbl = i18n.Tr(lang, "tool.from_now")
		diff = then.Unix() - now.Unix()
	}

	switch {
	case diff <= 0:
		return i18n.Tr(lang, "tool.now")
	case diff <= 2:
		return i18n.Tr(lang, "tool.1s", lbl)
	case diff < 1*Minute:
		return i18n.Tr(lang, "tool.seconds", diff, lbl)

	case diff < 2*Minute:
		return i18n.Tr(lang, "tool.1m", lbl)
	case diff < 1*Hour:
		return i18n.Tr(lang, "tool.minutes", diff/Minute, lbl)

	case diff < 2*Hour:
		return i18n.Tr(lang, "tool.1h", lbl)
	case diff < 1*Day:
		return i18n.Tr(lang, "tool.hours", diff/Hour, lbl)

	case diff < 2*Day:
		return i18n.Tr(lang, "tool.1d", lbl)
	case diff < 1*Week:
		return i18n.Tr(lang, "tool.days", diff/Day, lbl)

	case diff < 2*Week:
		return i18n.Tr(lang, "tool.1w", lbl)
	case diff < 1*Month:
		return i18n.Tr(lang, "tool.weeks", diff/Week, lbl)

	case diff < 2*Month:
		return i18n.Tr(lang, "tool.1mon", lbl)
	case diff < 1*Year:
		return i18n.Tr(lang, "tool.months", diff/Month, lbl)

	case diff < 2*Year:
		return i18n.Tr(lang, "tool.1y", lbl)
	default:
		return i18n.Tr(lang, "tool.years", diff/Year, lbl)
	}
}

func FormatNumString(num int64) (s string) {
	if num < 1000 {
		return com.ToStr(num)
	}

	for {
		d := num / 1000
		r := num % 1000
		if d == 0 {
			// Don't need leading 0's.
			s = com.ToStr(r) + s
			break
		}
		s = fmt.Sprintf(",%03d", r) + s
		num = d
	}
	return s[1:]
}

// Int64sToStrings converts a slice of int64 to a slice of string.
// CR: copied from github.com/gogits/gogs/modules/base/tool.go
func Int64sToStrings(ints []int64) []string {
	strs := make([]string, len(ints))
	for i := range ints {
		strs[i] = com.ToStr(ints[i])
	}
	return strs
}
<commit_msg>Fix again…<commit_after>// Copyright 2015 Unknwon
//
// 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 base

import (
	"fmt"
	"sort"
	"time"

	"github.com/Unknwon/com"
	"github.com/Unknwon/i18n"
)

// MapToSortedStrings converts a string map to a alphabet sorted slice without duplication.
func MapToSortedStrings(m map[string]bool) []string {
	strs := make([]string, 0, len(m))
	for s := range m {
		strs = append(strs, s)
	}
	sort.Strings(strs)
	return strs
}

func Int64(v int) int64 {
	return int64(v)
}

// Seconds-based time units
const (
	Minute = 60
	Hour   = 60 * Minute
	Day    = 24 * Hour
	Week   = 7 * Day
	Month  = 30 * Day
	Year   = 12 * Month
)

func TimeSince(then time.Time, lang string) string {
	now := time.Now()

	lbl := i18n.Tr(lang, "tool.ago")
	diff := now.Unix() - then.Unix()
	if then.After(now) {
		lbl = i18n.Tr(lang, "tool.from_now")
		diff = then.Unix() - now.Unix()
	}

	switch {
	case diff <= 0:
		return i18n.Tr(lang, "tool.now")
	case diff <= 2:
		return i18n.Tr(lang, "tool.1s", lbl)
	case diff < 1*Minute:
		return i18n.Tr(lang, "tool.seconds", diff, lbl)

	case diff < 2*Minute:
		return i18n.Tr(lang, "tool.1m", lbl)
	case diff < 1*Hour:
		return i18n.Tr(lang, "tool.minutes", diff/Minute, lbl)

	case diff < 2*Hour:
		return i18n.Tr(lang, "tool.1h", lbl)
	case diff < 1*Day:
		return i18n.Tr(lang, "tool.hours", diff/Hour, lbl)

	case diff < 2*Day:
		return i18n.Tr(lang, "tool.1d", lbl)
	case diff < 1*Week:
		return i18n.Tr(lang, "tool.days", diff/Day, lbl)

	case diff < 2*Week:
		return i18n.Tr(lang, "tool.1w", lbl)
	case diff < 1*Month:
		return i18n.Tr(lang, "tool.weeks", diff/Week, lbl)

	case diff < 2*Month:
		return i18n.Tr(lang, "tool.1mon", lbl)
	case diff < 1*Year:
		return i18n.Tr(lang, "tool.months", diff/Month, lbl)

	case diff < 2*Year:
		return i18n.Tr(lang, "tool.1y", lbl)
	default:
		return i18n.Tr(lang, "tool.years", diff/Year, lbl)
	}
}

func FormatNumString(num int64) (s string) {
	if num < 1000 {
		return com.ToStr(num)
	}

	for {
		d := num / 1000
		r := num % 1000
		if d == 0 {
			// Don't need leading 0's.
			s = "," + com.ToStr(r) + s
			break
		}
		s = fmt.Sprintf(",%03d", r) + s
		num = d
	}
	return s[1:]
}

// Int64sToStrings converts a slice of int64 to a slice of string.
// CR: copied from github.com/gogits/gogs/modules/base/tool.go
func Int64sToStrings(ints []int64) []string {
	strs := make([]string, len(ints))
	for i := range ints {
		strs[i] = com.ToStr(ints[i])
	}
	return strs
}
<|endoftext|>


<commit_before>package storage

import (
	"testing"

	"github.com/khlieng/dispatch/Godeps/_workspace/src/github.com/stretchr/testify/assert"
)

func TestGetSetUsers(t *testing.T) {
	channelStore := NewChannelStore()
	users := []string{"a,b"}
	channelStore.SetUsers(users, "srv", "#chan")
	assert.Equal(t, channelStore.GetUsers("srv", "#chan"), users)
}

func TestAddRemoveUser(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("user", "srv", "#chan")
	channelStore.AddUser("user2", "srv", "#chan")
	assert.Equal(t, channelStore.GetUsers("srv", "#chan"), []string{"user", "user2"})
	channelStore.RemoveUser("user", "srv", "#chan")
	assert.Equal(t, []string{"user2"}, channelStore.GetUsers("srv", "#chan"))
}

func TestRemoveUserAll(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("user", "srv", "#chan1")
	channelStore.AddUser("user", "srv", "#chan2")
	channelStore.RemoveUserAll("user", "srv")
	assert.Empty(t, channelStore.GetUsers("srv", "#chan1"))
	assert.Empty(t, channelStore.GetUsers("srv", "#chan2"))
}

func TestRenameUser(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("user", "srv", "#chan1")
	channelStore.AddUser("user", "srv", "#chan2")
	channelStore.RenameUser("user", "new", "srv")
	assert.Equal(t, []string{"new"}, channelStore.GetUsers("srv", "#chan1"))
	assert.Equal(t, []string{"new"}, channelStore.GetUsers("srv", "#chan2"))
}

func TestMode(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("+user", "srv", "#chan")
	channelStore.SetMode("srv", "#chan", "user", "o", "v")
	assert.Equal(t, []string{"@user"}, channelStore.GetUsers("srv", "#chan"))
	channelStore.SetMode("srv", "#chan", "user", "v", "")
	assert.Equal(t, []string{"+user"}, channelStore.GetUsers("srv", "#chan"))
	channelStore.SetMode("srv", "#chan", "user", "", "v")
	assert.Equal(t, []string{"user"}, channelStore.GetUsers("srv", "#chan"))
}

func TestTopic(t *testing.T) {
	channelStore := NewChannelStore()
	assert.Equal(t, "", channelStore.GetTopic("srv", "#chan"))
	channelStore.SetTopic("the topic", "srv", "#chan")
	assert.Equal(t, "the topic", channelStore.GetTopic("srv", "#chan"))
}

func TestFindUserChannels(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("user", "srv", "#chan1")
	channelStore.AddUser("user", "srv", "#chan2")
	channelStore.AddUser("user2", "srv", "#chan3")
	channelStore.AddUser("user", "srv2", "#chan4")
	assert.Equal(t, []string{"#chan1", "#chan2"}, channelStore.FindUserChannels("user", "srv"))
}
<commit_msg>Fix FindUserChannels test<commit_after>package storage

import (
	"testing"

	"github.com/khlieng/dispatch/Godeps/_workspace/src/github.com/stretchr/testify/assert"
)

func TestGetSetUsers(t *testing.T) {
	channelStore := NewChannelStore()
	users := []string{"a,b"}
	channelStore.SetUsers(users, "srv", "#chan")
	assert.Equal(t, channelStore.GetUsers("srv", "#chan"), users)
}

func TestAddRemoveUser(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("user", "srv", "#chan")
	channelStore.AddUser("user2", "srv", "#chan")
	assert.Equal(t, channelStore.GetUsers("srv", "#chan"), []string{"user", "user2"})
	channelStore.RemoveUser("user", "srv", "#chan")
	assert.Equal(t, []string{"user2"}, channelStore.GetUsers("srv", "#chan"))
}

func TestRemoveUserAll(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("user", "srv", "#chan1")
	channelStore.AddUser("user", "srv", "#chan2")
	channelStore.RemoveUserAll("user", "srv")
	assert.Empty(t, channelStore.GetUsers("srv", "#chan1"))
	assert.Empty(t, channelStore.GetUsers("srv", "#chan2"))
}

func TestRenameUser(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("user", "srv", "#chan1")
	channelStore.AddUser("user", "srv", "#chan2")
	channelStore.RenameUser("user", "new", "srv")
	assert.Equal(t, []string{"new"}, channelStore.GetUsers("srv", "#chan1"))
	assert.Equal(t, []string{"new"}, channelStore.GetUsers("srv", "#chan2"))
}

func TestMode(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("+user", "srv", "#chan")
	channelStore.SetMode("srv", "#chan", "user", "o", "v")
	assert.Equal(t, []string{"@user"}, channelStore.GetUsers("srv", "#chan"))
	channelStore.SetMode("srv", "#chan", "user", "v", "")
	assert.Equal(t, []string{"+user"}, channelStore.GetUsers("srv", "#chan"))
	channelStore.SetMode("srv", "#chan", "user", "", "v")
	assert.Equal(t, []string{"user"}, channelStore.GetUsers("srv", "#chan"))
}

func TestTopic(t *testing.T) {
	channelStore := NewChannelStore()
	assert.Equal(t, "", channelStore.GetTopic("srv", "#chan"))
	channelStore.SetTopic("the topic", "srv", "#chan")
	assert.Equal(t, "the topic", channelStore.GetTopic("srv", "#chan"))
}

func TestFindUserChannels(t *testing.T) {
	channelStore := NewChannelStore()
	channelStore.AddUser("user", "srv", "#chan1")
	channelStore.AddUser("user", "srv", "#chan2")
	channelStore.AddUser("user2", "srv", "#chan3")
	channelStore.AddUser("user", "srv2", "#chan4")

	channels := channelStore.FindUserChannels("user", "srv")
	assert.Len(t, channels, 2)
	assert.Contains(t, channels, "#chan1")
	assert.Contains(t, channels, "#chan2")
}
<|endoftext|>


<commit_before>package stores

import (
	"github.com/Everlane/evan/common"
)

// Stores deployments in the process's local memory.
type ProcessLocalStore struct {
	// Two-level map: first is application repository canonical name, second
	// is environment.
	applications map[string]map[string]common.Deployment
}

func (store *ProcessLocalStore) SaveDeployment(deployment common.Deployment) error {
	applicationKey := store.keyForApplication(deployment.Application())
	environment := deployment.Environment()
	store.applications[applicationKey][environment] = deployment
	return nil
}

func (store *ProcessLocalStore) keyForApplication(application common.Application) string {
	return common.CanonicalNameForRepository(application.Repository())
}

func (store *ProcessLocalStore) FindDeployment(application common.Application, environment string) (common.Deployment, error) {
	applicationKey := store.keyForApplication(application)
	return store.applications[applicationKey][environment], nil
}
<commit_msg>Ensure store map exists before assigning it<commit_after>package stores

import (
	"github.com/Everlane/evan/common"
)

// Stores deployments in the process's local memory.
type ProcessLocalStore struct {
	// Two-level map: first is application repository canonical name, second
	// is environment.
	applications map[string]map[string]common.Deployment
}

func NewProcessLocalStore() *ProcessLocalStore {
	return &ProcessLocalStore{
		applications: make(map[string]map[string]common.Deployment),
	}
}

func (store *ProcessLocalStore) SaveDeployment(deployment common.Deployment) error {
	application := store.keyForApplication(deployment.Application())
	environment := deployment.Environment()

	if store.applications[application] == nil {
		store.applications[application] = make(map[string]common.Deployment)
	}
	store.applications[application][environment] = deployment
	return nil
}

func (store *ProcessLocalStore) keyForApplication(application common.Application) string {
	return common.CanonicalNameForRepository(application.Repository())
}

func (store *ProcessLocalStore) FindDeployment(application common.Application, environment string) (common.Deployment, error) {
	applicationKey := store.keyForApplication(application)
	return store.applications[applicationKey][environment], nil
}
<|endoftext|>


<commit_before>package statsd

import (
	"log"
	"os"
	"strings"
	"time"

	"github.com/quipo/statsd/event"
)

// request to close the buffered statsd collector
type closeRequest struct {
	reply chan error
}

// StatsdBuffer is a client library to aggregate events in memory before
// flushing aggregates to StatsD, useful if the frequency of events is extremely high
// and sampling is not desirable
type StatsdBuffer struct {
	statsd        *StatsdClient
	flushInterval time.Duration
	eventChannel  chan event.Event
	events        map[string]event.Event
	closeChannel  chan closeRequest
	Logger        *log.Logger
}

// NewStatsdBuffer Factory
func NewStatsdBuffer(interval time.Duration, client *StatsdClient) *StatsdBuffer {
	sb := &StatsdBuffer{
		flushInterval: interval,
		statsd:        client,
		eventChannel:  make(chan event.Event, 100),
		events:        make(map[string]event.Event, 0),
		closeChannel:  make(chan closeRequest, 0),
		Logger:        log.New(os.Stdout, "[BufferedStatsdClient] ", log.Ldate|log.Ltime),
	}
	go sb.collector()
	return sb
}

// CreateSocket creates a UDP connection to a StatsD server
func (sb *StatsdBuffer) CreateSocket() error {
	return sb.statsd.CreateSocket()
}

// Incr - Increment a counter metric. Often used to note a particular event
func (sb *StatsdBuffer) Incr(stat string, count int64) error {
	if 0 != count {
		sb.eventChannel <- &event.Increment{Name: stat, Value: count}
	}
	return nil
}

// Decr - Decrement a counter metric. Often used to note a particular event
func (sb *StatsdBuffer) Decr(stat string, count int64) error {
	if 0 != count {
		sb.eventChannel <- &event.Increment{Name: stat, Value: -count}
	}
	return nil
}

// Timing - Track a duration event
func (sb *StatsdBuffer) Timing(stat string, delta int64) error {
	sb.eventChannel <- event.NewTiming(stat, delta)
	return nil
}

// PrecisionTiming - Track a duration event
// the time delta has to be a duration
func (sb *StatsdBuffer) PrecisionTiming(stat string, delta time.Duration) error {
	sb.eventChannel <- event.NewPrecisionTiming(stat, time.Duration(float64(delta)/float64(time.Millisecond)))
	return nil
}

// Gauge - Gauges are a constant data type. They are not subject to averaging,
// and they don’t change unless you change them. That is, once you set a gauge value,
// it will be a flat line on the graph until you change it again
func (sb *StatsdBuffer) Gauge(stat string, value int64) error {
	sb.eventChannel <- &event.Gauge{Name: stat, Value: value}
	return nil
}

func (sb *StatsdBuffer) GaugeDelta(stat string, value int64) error {
	sb.eventChannel <- &event.GaugeDelta{Name: stat, Value: value}
	return nil
}

func (sb *StatsdBuffer) FGauge(stat string, value float64) error {
	sb.eventChannel <- &event.FGauge{Name: stat, Value: value}
	return nil
}

func (sb *StatsdBuffer) FGaugeDelta(stat string, value float64) error {
	sb.eventChannel <- &event.FGaugeDelta{Name: stat, Value: value}
	return nil
}

// Absolute - Send absolute-valued metric (not averaged/aggregated)
func (sb *StatsdBuffer) Absolute(stat string, value int64) error {
	sb.eventChannel <- &event.Absolute{Name: stat, Values: []int64{value}}
	return nil
}

// FAbsolute - Send absolute-valued metric (not averaged/aggregated)
func (sb *StatsdBuffer) FAbsolute(stat string, value float64) error {
	sb.eventChannel <- &event.FAbsolute{Name: stat, Values: []float64{value}}
	return nil
}

// Total - Send a metric that is continously increasing, e.g. read operations since boot
func (sb *StatsdBuffer) Total(stat string, value int64) error {
	sb.eventChannel <- &event.Total{Name: stat, Value: value}
	return nil
}

// handle flushes and updates in one single thread (instead of locking the events map)
func (sb *StatsdBuffer) collector() {
	// on a panic event, flush all the pending stats before panicking
	defer func(sb *StatsdBuffer) {
		if r := recover(); r != nil {
			sb.Logger.Println("Caught panic, flushing stats before throwing the panic again")
			sb.flush()
			panic(r)
		}
	}(sb)

	ticker := time.NewTicker(sb.flushInterval)

	for {
		select {
		case <-ticker.C:
			//sb.Logger.Println("Flushing stats")
			sb.flush()
		case e := <-sb.eventChannel:
			//sb.Logger.Println("Received ", e.String())
			// convert %HOST% in key
			k := strings.Replace(e.Key(), "%HOST%", Hostname, 1)
			e.SetKey(k)

			if e2, ok := sb.events[k]; ok {
				//sb.Logger.Println("Updating existing event")
				e2.Update(e)
				sb.events[k] = e2
			} else {
				//sb.Logger.Println("Adding new event")
				sb.events[k] = e
			}
		case c := <-sb.closeChannel:
			sb.Logger.Println("Asked to terminate. Flushing stats before returning.")
			c.reply <- sb.flush()
			break
		}
	}
}

// Close sends a close event to the collector asking to stop & flush pending stats
// and closes the statsd client
func (sb *StatsdBuffer) Close() (err error) {
	// 1. send a close event to the collector
	req := closeRequest{reply: make(chan error, 0)}
	sb.closeChannel <- req
	// 2. wait for the collector to drain the queue and respond
	err = <-req.reply
	// 3. close the statsd client
	err2 := sb.statsd.Close()
	if err != nil {
		return err
	}
	return err2
}

// send the events to StatsD and reset them.
// This function is NOT thread-safe, so it must only be invoked synchronously
// from within the collector() goroutine
func (sb *StatsdBuffer) flush() (err error) {
	n := len(sb.events)
	if n == 0 {
		return nil
	}
	err = sb.statsd.CreateSocket()
	if nil != err {
		sb.Logger.Println("Error establishing UDP connection for sending statsd events:", err)
	}
	for k, v := range sb.events {
		err := sb.statsd.SendEvent(v)
		if nil != err {
			sb.Logger.Println(err)
		}
		//sb.Logger.Println("Sent", v.String())
		delete(sb.events, k)
	}

	return nil
}
<commit_msg>make collector exit on close request<commit_after>package statsd

import (
	"log"
	"os"
	"strings"
	"time"

	"github.com/quipo/statsd/event"
)

// request to close the buffered statsd collector
type closeRequest struct {
	reply chan error
}

// StatsdBuffer is a client library to aggregate events in memory before
// flushing aggregates to StatsD, useful if the frequency of events is extremely high
// and sampling is not desirable
type StatsdBuffer struct {
	statsd        *StatsdClient
	flushInterval time.Duration
	eventChannel  chan event.Event
	events        map[string]event.Event
	closeChannel  chan closeRequest
	Logger        *log.Logger
}

// NewStatsdBuffer Factory
func NewStatsdBuffer(interval time.Duration, client *StatsdClient) *StatsdBuffer {
	sb := &StatsdBuffer{
		flushInterval: interval,
		statsd:        client,
		eventChannel:  make(chan event.Event, 100),
		events:        make(map[string]event.Event, 0),
		closeChannel:  make(chan closeRequest, 0),
		Logger:        log.New(os.Stdout, "[BufferedStatsdClient] ", log.Ldate|log.Ltime),
	}
	go sb.collector()
	return sb
}

// CreateSocket creates a UDP connection to a StatsD server
func (sb *StatsdBuffer) CreateSocket() error {
	return sb.statsd.CreateSocket()
}

// Incr - Increment a counter metric. Often used to note a particular event
func (sb *StatsdBuffer) Incr(stat string, count int64) error {
	if 0 != count {
		sb.eventChannel <- &event.Increment{Name: stat, Value: count}
	}
	return nil
}

// Decr - Decrement a counter metric. Often used to note a particular event
func (sb *StatsdBuffer) Decr(stat string, count int64) error {
	if 0 != count {
		sb.eventChannel <- &event.Increment{Name: stat, Value: -count}
	}
	return nil
}

// Timing - Track a duration event
func (sb *StatsdBuffer) Timing(stat string, delta int64) error {
	sb.eventChannel <- event.NewTiming(stat, delta)
	return nil
}

// PrecisionTiming - Track a duration event
// the time delta has to be a duration
func (sb *StatsdBuffer) PrecisionTiming(stat string, delta time.Duration) error {
	sb.eventChannel <- event.NewPrecisionTiming(stat, time.Duration(float64(delta)/float64(time.Millisecond)))
	return nil
}

// Gauge - Gauges are a constant data type. They are not subject to averaging,
// and they don’t change unless you change them. That is, once you set a gauge value,
// it will be a flat line on the graph until you change it again
func (sb *StatsdBuffer) Gauge(stat string, value int64) error {
	sb.eventChannel <- &event.Gauge{Name: stat, Value: value}
	return nil
}

func (sb *StatsdBuffer) GaugeDelta(stat string, value int64) error {
	sb.eventChannel <- &event.GaugeDelta{Name: stat, Value: value}
	return nil
}

func (sb *StatsdBuffer) FGauge(stat string, value float64) error {
	sb.eventChannel <- &event.FGauge{Name: stat, Value: value}
	return nil
}

func (sb *StatsdBuffer) FGaugeDelta(stat string, value float64) error {
	sb.eventChannel <- &event.FGaugeDelta{Name: stat, Value: value}
	return nil
}

// Absolute - Send absolute-valued metric (not averaged/aggregated)
func (sb *StatsdBuffer) Absolute(stat string, value int64) error {
	sb.eventChannel <- &event.Absolute{Name: stat, Values: []int64{value}}
	return nil
}

// FAbsolute - Send absolute-valued metric (not averaged/aggregated)
func (sb *StatsdBuffer) FAbsolute(stat string, value float64) error {
	sb.eventChannel <- &event.FAbsolute{Name: stat, Values: []float64{value}}
	return nil
}

// Total - Send a metric that is continously increasing, e.g. read operations since boot
func (sb *StatsdBuffer) Total(stat string, value int64) error {
	sb.eventChannel <- &event.Total{Name: stat, Value: value}
	return nil
}

// handle flushes and updates in one single thread (instead of locking the events map)
func (sb *StatsdBuffer) collector() {
	// on a panic event, flush all the pending stats before panicking
	defer func(sb *StatsdBuffer) {
		if r := recover(); r != nil {
			sb.Logger.Println("Caught panic, flushing stats before throwing the panic again")
			sb.flush()
			panic(r)
		}
	}(sb)

	ticker := time.NewTicker(sb.flushInterval)

	for {
		select {
		case <-ticker.C:
			//sb.Logger.Println("Flushing stats")
			sb.flush()
		case e := <-sb.eventChannel:
			//sb.Logger.Println("Received ", e.String())
			// convert %HOST% in key
			k := strings.Replace(e.Key(), "%HOST%", Hostname, 1)
			e.SetKey(k)

			if e2, ok := sb.events[k]; ok {
				//sb.Logger.Println("Updating existing event")
				e2.Update(e)
				sb.events[k] = e2
			} else {
				//sb.Logger.Println("Adding new event")
				sb.events[k] = e
			}
		case c := <-sb.closeChannel:
			sb.Logger.Println("Asked to terminate. Flushing stats before returning.")
			c.reply <- sb.flush()
			return
		}
	}
}

// Close sends a close event to the collector asking to stop & flush pending stats
// and closes the statsd client
func (sb *StatsdBuffer) Close() (err error) {
	// 1. send a close event to the collector
	req := closeRequest{reply: make(chan error, 0)}
	sb.closeChannel <- req
	// 2. wait for the collector to drain the queue and respond
	err = <-req.reply
	// 3. close the statsd client
	err2 := sb.statsd.Close()
	if err != nil {
		return err
	}
	return err2
}

// send the events to StatsD and reset them.
// This function is NOT thread-safe, so it must only be invoked synchronously
// from within the collector() goroutine
func (sb *StatsdBuffer) flush() (err error) {
	n := len(sb.events)
	if n == 0 {
		return nil
	}
	err = sb.statsd.CreateSocket()
	if nil != err {
		sb.Logger.Println("Error establishing UDP connection for sending statsd events:", err)
	}
	for k, v := range sb.events {
		err := sb.statsd.SendEvent(v)
		if nil != err {
			sb.Logger.Println(err)
		}
		//sb.Logger.Println("Sent", v.String())
		delete(sb.events, k)
	}

	return nil
}
<|endoftext|>


<commit_before>package plugins

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/cenk/backoff"
	"github.com/fatih/color"
	marathon "github.com/gambol99/go-marathon"
	consul "github.com/hashicorp/consul/api"

	"github.com/InnovaCo/serve/manifest"
)

func init() {
	manifest.PluginRegestry.Add("deploy.marathon", DeployMarathon{})
}

type DeployMarathon struct{}

func (p DeployMarathon) Run(data manifest.Manifest) error {
	if data.GetBool("purge") {
		return p.Uninstall(data)
	} else {
		return p.Install(data)
	}
}

func (p DeployMarathon) Install(data manifest.Manifest) error {
	marathonApi, err := MarathonClient(data.GetString("marathon-host"))
	if err != nil {
		return err
	}

	fullName := data.GetString("app-name")

	bs, bf, bmax := 1.0, 2.0, 30.0
	app := &marathon.Application{
		BackoffSeconds:        &bs,
		BackoffFactor:         &bf,
		MaxLaunchDelaySeconds: &bmax,
	}

	app.Name(fullName)
	app.Command(fmt.Sprintf("serve-tools consul supervisor --service '%s' --port $PORT0 start %s", fullName, data.GetString("cmd")))
	app.Count(data.GetInt("instances"))
	app.Memory(float64(data.GetInt("mem")))

	if cpu, err := strconv.ParseFloat(data.GetString("cpu"), 64); err == nil {
		app.CPU(cpu)
	}

	if constrs := data.GetString("constraints"); constrs != "" {
		cs := strings.SplitN(constrs, ":", 2)
		app.AddConstraint(cs[0], "CLUSTER", cs[1])
		app.AddLabel(cs[0], cs[1])
	}

	for k, v := range data.GetMap("environment") {
		app.AddEnv(k, fmt.Sprintf("%s", v.Unwrap()))
	}

	app.AddUris(data.GetString("package-uri"))

	if _, err := marathonApi.UpdateApplication(app, false); err != nil {
		color.Yellow("marathon <- %s", app)
		return err
	}

	color.Green("marathon <- %s", app)

	consulApi, err := ConsulClient(data.GetString("consul-host"))
	if err != nil {
		return err
	}

	if err := registerPluginData("deploy.marathon", data.GetString("app-name"), data.String(), data.GetString("consul-host")); err != nil {
		return err
	}

	return backoff.Retry(func() error {
		services, _, err := consulApi.Health().Service(fullName, "", true, nil)

		if err != nil {
			log.Println(color.RedString("Error in check health in consul: %v", err))
			return err
		}

		if len(services) == 0 {
			log.Printf("Service `%s` not started yet! Retry...", fullName)
			return fmt.Errorf("Service `%s` not started!", fullName)
		}

		log.Println(color.GreenString("Service `%s` successfully started!", fullName))
		return nil
	}, backoff.NewExponentialBackOff())
}

func (p DeployMarathon) Uninstall(data manifest.Manifest) error {
	marathonApi, err := MarathonClient(data.GetString("marathon-host"))
	if err != nil {
		return err
	}

	if _, err := marathonApi.DeleteApplication(data.GetString("app-name"), false); err != nil {
		return err
	}

	return deletePluginData("deploy.marathon", data.GetString("app-name"), data.GetString("consul-host"))
}

func MarathonClient(marathonHost string) (marathon.Marathon, error) {
	conf := marathon.NewDefaultConfig()
	conf.URL = fmt.Sprintf("http://%s:8080", marathonHost)
	conf.LogOutput = os.Stdout
	return marathon.NewClient(conf)
}

func ConsulClient(consulHost string) (*consul.Client, error) {
	conf := consul.DefaultConfig()
	conf.Address = consulHost + ":8500"
	return consul.NewClient(conf)
}

func putConsulKv(client *consul.Client, key string, value string) error {
	log.Printf("consul put `%s`: %s", key, value)
	_, err := client.KV().Put(&consul.KVPair{Key: strings.TrimPrefix(key, "/"), Value: []byte(value)}, nil)
	return err
}

func listConsulKv(client *consul.Client, prefix string, q *consul.QueryOptions) (consul.KVPairs, error) {
	log.Printf("consul list `%s`", prefix)
	list, _, err := client.KV().List(prefix, q)
	return list, err
}

func delConsulKv(client *consul.Client, key string) error {
	log.Printf("consul delete `%s`", key)
	_, err := client.KV().Delete(strings.TrimPrefix(key, "/"), nil)
	return err
}

func registerPluginData(plugin string, packageName string, data string, consulHost string) error {
	consulApi, err := ConsulClient(consulHost)
	if err != nil {
		return err
	}

	return putConsulKv(consulApi, "services/data/"+packageName+"/"+plugin, data)
}

func deletePluginData(plugin string, packageName string, consulHost string) error {
	log.Println(color.YellowString("Delete %s for %s package in consul", plugin, packageName))
	consulApi, err := ConsulClient(consulHost)
	if err != nil {
		return err
	}

	return delConsulKv(consulApi, "services/data/"+packageName+"/"+plugin)
}

func markAsOutdated(client *consul.Client, name string, delay time.Duration) error {
	log.Printf("Mark service `%s` as outdated\n", name)
	json := fmt.Sprintf(`{"endOfLife":%d}`, time.Now().Add(delay).UnixNano()/int64(time.Millisecond))
	return putConsulKv(client, "services/outdated/"+name, json)
}
<commit_msg>= purge marathon: ignore non exists apps<commit_after>package plugins

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/cenk/backoff"
	"github.com/fatih/color"
	marathon "github.com/gambol99/go-marathon"
	consul "github.com/hashicorp/consul/api"

	"github.com/InnovaCo/serve/manifest"
)

func init() {
	manifest.PluginRegestry.Add("deploy.marathon", DeployMarathon{})
}

type DeployMarathon struct{}

func (p DeployMarathon) Run(data manifest.Manifest) error {
	if data.GetBool("purge") {
		return p.Uninstall(data)
	} else {
		return p.Install(data)
	}
}

func (p DeployMarathon) Install(data manifest.Manifest) error {
	marathonApi, err := MarathonClient(data.GetString("marathon-host"))
	if err != nil {
		return err
	}

	fullName := data.GetString("app-name")

	bs, bf, bmax := 1.0, 2.0, 30.0
	app := &marathon.Application{
		BackoffSeconds:        &bs,
		BackoffFactor:         &bf,
		MaxLaunchDelaySeconds: &bmax,
	}

	app.Name(fullName)
	app.Command(fmt.Sprintf("serve-tools consul supervisor --service '%s' --port $PORT0 start %s", fullName, data.GetString("cmd")))
	app.Count(data.GetInt("instances"))
	app.Memory(float64(data.GetInt("mem")))

	if cpu, err := strconv.ParseFloat(data.GetString("cpu"), 64); err == nil {
		app.CPU(cpu)
	}

	if constrs := data.GetString("constraints"); constrs != "" {
		cs := strings.SplitN(constrs, ":", 2)
		app.AddConstraint(cs[0], "CLUSTER", cs[1])
		app.AddLabel(cs[0], cs[1])
	}

	for k, v := range data.GetMap("environment") {
		app.AddEnv(k, fmt.Sprintf("%s", v.Unwrap()))
	}

	app.AddUris(data.GetString("package-uri"))

	if _, err := marathonApi.UpdateApplication(app, false); err != nil {
		color.Yellow("marathon <- %s", app)
		return err
	}

	color.Green("marathon <- %s", app)

	consulApi, err := ConsulClient(data.GetString("consul-host"))
	if err != nil {
		return err
	}

	if err := registerPluginData("deploy.marathon", data.GetString("app-name"), data.String(), data.GetString("consul-host")); err != nil {
		return err
	}

	return backoff.Retry(func() error {
		services, _, err := consulApi.Health().Service(fullName, "", true, nil)

		if err != nil {
			log.Println(color.RedString("Error in check health in consul: %v", err))
			return err
		}

		if len(services) == 0 {
			log.Printf("Service `%s` not started yet! Retry...", fullName)
			return fmt.Errorf("Service `%s` not started!", fullName)
		}

		log.Println(color.GreenString("Service `%s` successfully started!", fullName))
		return nil
	}, backoff.NewExponentialBackOff())
}

func (p DeployMarathon) Uninstall(data manifest.Manifest) error {
	marathonApi, err := MarathonClient(data.GetString("marathon-host"))
	if err != nil {
		return err
	}

	name := data.GetString("app-name")

	if _, err := marathonApi.Application(name); err == nil {
		if _, err := marathonApi.DeleteApplication(name, false); err != nil {
			return err
		}
	} else {
		log.Println(color.YellowString("App `%s` doesnt exists in marathon!", name))
	}

	return deletePluginData("deploy.marathon", name, data.GetString("consul-host"))
}

func MarathonClient(marathonHost string) (marathon.Marathon, error) {
	conf := marathon.NewDefaultConfig()
	conf.URL = fmt.Sprintf("http://%s:8080", marathonHost)
	conf.LogOutput = os.Stdout
	return marathon.NewClient(conf)
}

func ConsulClient(consulHost string) (*consul.Client, error) {
	conf := consul.DefaultConfig()
	conf.Address = consulHost + ":8500"
	return consul.NewClient(conf)
}

func putConsulKv(client *consul.Client, key string, value string) error {
	log.Printf("consul put `%s`: %s", key, value)
	_, err := client.KV().Put(&consul.KVPair{Key: strings.TrimPrefix(key, "/"), Value: []byte(value)}, nil)
	return err
}

func listConsulKv(client *consul.Client, prefix string, q *consul.QueryOptions) (consul.KVPairs, error) {
	log.Printf("consul list `%s`", prefix)
	list, _, err := client.KV().List(prefix, q)
	return list, err
}

func delConsulKv(client *consul.Client, key string) error {
	log.Printf("consul delete `%s`", key)
	_, err := client.KV().Delete(strings.TrimPrefix(key, "/"), nil)
	return err
}

func registerPluginData(plugin string, packageName string, data string, consulHost string) error {
	consulApi, err := ConsulClient(consulHost)
	if err != nil {
		return err
	}

	return putConsulKv(consulApi, "services/data/"+packageName+"/"+plugin, data)
}

func deletePluginData(plugin string, packageName string, consulHost string) error {
	log.Println(color.YellowString("Delete %s for %s package in consul", plugin, packageName))
	consulApi, err := ConsulClient(consulHost)
	if err != nil {
		return err
	}

	return delConsulKv(consulApi, "services/data/"+packageName+"/"+plugin)
}

func markAsOutdated(client *consul.Client, name string, delay time.Duration) error {
	log.Printf("Mark service `%s` as outdated\n", name)
	json := fmt.Sprintf(`{"endOfLife":%d}`, time.Now().Add(delay).UnixNano()/int64(time.Millisecond))
	return putConsulKv(client, "services/outdated/"+name, json)
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"

	"github.com/d2g/dhcp4"
	"github.com/d2g/dhcp4client"
	"github.com/vishvananda/netlink"

	"github.com/appc/cni/pkg/ns"
	"github.com/appc/cni/pkg/plugin"
)

// RFC 2131 suggests using exponential backoff, starting with 4sec
// and randomized to +/- 1sec
const resendDelay0 = 4 * time.Second
const resendDelayMax = 32 * time.Second

const (
	leaseStateBound = iota
	leaseStateRenewing
	leaseStateRebinding
)

// This implementation uses 1 OS thread per lease. This is because
// all the network operations have to be done in network namespace
// of the interface. This can be improved by switching to the proper
// namespace for network ops and using fewer threads. However, this
// needs to be done carefully as dhcp4client ops are blocking.

type DHCPLease struct {
	clientID      string
	ack           *dhcp4.Packet
	opts          dhcp4.Options
	link          netlink.Link
	renewalTime   time.Time
	rebindingTime time.Time
	expireTime    time.Time
	stop          chan struct{}
	wg            sync.WaitGroup
}

// AcquireLease gets an DHCP lease and then maintains it in the background
// by periodically renewing it. The acquired lease can be released by
// calling DHCPLease.Stop()
func AcquireLease(clientID, netns, ifName string) (*DHCPLease, error) {
	errCh := make(chan error, 1)
	l := &DHCPLease{
		clientID: clientID,
		stop:     make(chan struct{}),
	}

	log.Printf("%v: acquiring lease", clientID)

	l.wg.Add(1)
	go ns.WithNetNSPath(netns, true, func(_ *os.File) (e error) {
		defer l.wg.Done()

		link, err := netlink.LinkByName(ifName)
		if err != nil {
			errCh <- fmt.Errorf("error looking up %q", ifName)
			return
		}

		l.link = link

		if err = l.acquire(); err != nil {
			errCh <- err
			return
		}

		log.Printf("%v: lease acquired, expiration is %v", l.clientID, l.expireTime)

		errCh <- nil

		l.maintain()
		return
	})

	err := <-errCh
	if err != nil {
		return nil, err
	}

	return l, nil
}

// Stop terminates the background task that maintains the lease
// and issues a DHCP Release
func (l *DHCPLease) Stop() {
	close(l.stop)
	l.wg.Wait()
}

func (l *DHCPLease) acquire() error {
	c, err := newDHCPClient(l.link)
	if err != nil {
		return err
	}
	defer c.Close()

	pkt, err := backoffRetry(func() (*dhcp4.Packet, error) {
		ok, ack, err := c.Request()
		switch {
		case err != nil:
			return nil, err
		case !ok:
			return nil, fmt.Errorf("DHCP server NACK'd own offer")
		default:
			return &ack, nil
		}
	})
	if err != nil {
		return err
	}

	return l.commit(pkt)
}

func (l *DHCPLease) commit(ack *dhcp4.Packet) error {
	opts := ack.ParseOptions()

	leaseTime, err := parseLeaseTime(opts)
	if err != nil {
		return err
	}

	rebindingTime, err := parseRebindingTime(opts)
	if err != nil || rebindingTime > leaseTime {
		// Per RFC 2131 Section 4.4.5, it should default to 85% of lease time
		rebindingTime = leaseTime * 85 / 100
	}

	renewalTime, err := parseRenewalTime(opts)
	if err != nil || renewalTime > rebindingTime {
		// Per RFC 2131 Section 4.4.5, it should default to 50% of lease time
		renewalTime = leaseTime / 2
	}

	now := time.Now()
	l.expireTime = now.Add(leaseTime)
	l.renewalTime = now.Add(renewalTime)
	l.rebindingTime = now.Add(rebindingTime)
	l.ack = ack
	l.opts = opts

	return nil
}

func (l *DHCPLease) maintain() {
	state := leaseStateBound

	for {
		var sleepDur time.Duration

		switch state {
		case leaseStateBound:
			sleepDur = l.renewalTime.Sub(time.Now())
			if sleepDur <= 0 {
				log.Printf("%v: renewing lease", l.clientID)
				state = leaseStateRenewing
				continue
			}

		case leaseStateRenewing:
			if err := l.renew(); err != nil {
				log.Printf("%v: %v", l.clientID, err)

				if time.Now().After(l.rebindingTime) {
					log.Printf("%v: renawal time expired, rebinding", l.clientID)
					state = leaseStateRebinding
				}
			} else {
				log.Printf("%v: lease renewed, expiration is %v", l.clientID, l.expireTime)
				state = leaseStateBound
			}

		case leaseStateRebinding:
			if err := l.acquire(); err != nil {
				log.Printf("%v: %v", l.clientID, err)

				if time.Now().After(l.expireTime) {
					log.Printf("%v: lease expired, bringing interface DOWN", l.clientID)
					l.downIface()
					return
				}
			} else {
				log.Printf("%v: lease rebound, expiration is %v", l.clientID, l.expireTime)
				state = leaseStateBound
			}
		}

		select {
		case <-time.After(sleepDur):

		case <-l.stop:
			if err := l.release(); err != nil {
				log.Printf("%v: failed to release DHCP lease: %v", l.clientID, err)
			}
			return
		}
	}
}

func (l *DHCPLease) downIface() {
	if err := netlink.LinkSetDown(l.link); err != nil {
		log.Printf("%v: failed to bring %v interface DOWN: %v", l.clientID, l.link.Attrs().Name, err)
	}
}

func (l *DHCPLease) renew() error {
	c, err := newDHCPClient(l.link)
	if err != nil {
		return err
	}
	defer c.Close()

	pkt, err := backoffRetry(func() (*dhcp4.Packet, error) {
		ok, ack, err := c.Renew(*l.ack)
		switch {
		case err != nil:
			return nil, err
		case !ok:
			return nil, fmt.Errorf("DHCP server did not renew lease")
		default:
			return &ack, nil
		}
	})
	if err != nil {
		return err
	}

	l.commit(pkt)
	return nil
}

func (l *DHCPLease) release() error {
	log.Printf("%v: releasing lease", l.clientID)

	c, err := newDHCPClient(l.link)
	if err != nil {
		return err
	}
	defer c.Close()

	if err = c.Release(*l.ack); err != nil {
		return fmt.Errorf("failed to send DHCPRELEASE")
	}

	return nil
}

func (l *DHCPLease) IPNet() (*net.IPNet, error) {
	mask := parseSubnetMask(l.opts)
	if mask == nil {
		return nil, fmt.Errorf("DHCP option Subnet Mask not found in DHCPACK")
	}

	return &net.IPNet{
		IP:   l.ack.YIAddr(),
		Mask: mask,
	}, nil
}

func (l *DHCPLease) Gateway() net.IP {
	return parseRouter(l.opts)
}

func (l *DHCPLease) Routes() []plugin.Route {
	routes := parseRoutes(l.opts)
	return append(routes, parseCIDRRoutes(l.opts)...)
}

// jitter returns a random value within [-span, span) range
func jitter(span time.Duration) time.Duration {
	return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0))
}

func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
	var baseDelay time.Duration = resendDelay0

	for i := 0; i < resendCount; i++ {
		pkt, err := f()
		if err == nil {
			return pkt, nil
		}

		log.Print(err)

		time.Sleep(baseDelay + jitter(time.Second))

		if baseDelay < resendDelayMax {
			baseDelay *= 2
		}
	}

	return nil, errNoMoreTries
}

func newDHCPClient(link netlink.Link) (*dhcp4client.Client, error) {
	pktsock, err := dhcp4client.NewPacketSock(link.Attrs().Index)
	if err != nil {
		return nil, err
	}

	return dhcp4client.New(
		dhcp4client.HardwareAddr(link.Attrs().HardwareAddr),
		dhcp4client.Timeout(5*time.Second),
		dhcp4client.Broadcast(false),
		dhcp4client.Connection(pktsock),
	)
}
<commit_msg>dhcp: don't swallow err from netns switch<commit_after>// Copyright 2015 CoreOS, 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"

	"github.com/d2g/dhcp4"
	"github.com/d2g/dhcp4client"
	"github.com/vishvananda/netlink"

	"github.com/appc/cni/pkg/ns"
	"github.com/appc/cni/pkg/plugin"
)

// RFC 2131 suggests using exponential backoff, starting with 4sec
// and randomized to +/- 1sec
const resendDelay0 = 4 * time.Second
const resendDelayMax = 32 * time.Second

const (
	leaseStateBound = iota
	leaseStateRenewing
	leaseStateRebinding
)

// This implementation uses 1 OS thread per lease. This is because
// all the network operations have to be done in network namespace
// of the interface. This can be improved by switching to the proper
// namespace for network ops and using fewer threads. However, this
// needs to be done carefully as dhcp4client ops are blocking.

type DHCPLease struct {
	clientID      string
	ack           *dhcp4.Packet
	opts          dhcp4.Options
	link          netlink.Link
	renewalTime   time.Time
	rebindingTime time.Time
	expireTime    time.Time
	stop          chan struct{}
	wg            sync.WaitGroup
}

// AcquireLease gets an DHCP lease and then maintains it in the background
// by periodically renewing it. The acquired lease can be released by
// calling DHCPLease.Stop()
func AcquireLease(clientID, netns, ifName string) (*DHCPLease, error) {
	errCh := make(chan error, 1)
	l := &DHCPLease{
		clientID: clientID,
		stop:     make(chan struct{}),
	}

	log.Printf("%v: acquiring lease", clientID)

	l.wg.Add(1)
	go func() {
		errCh <- ns.WithNetNSPath(netns, true, func(_ *os.File) error {
			defer l.wg.Done()

			link, err := netlink.LinkByName(ifName)
			if err != nil {
				return fmt.Errorf("error looking up %q: %v", ifName, err)
			}

			l.link = link

			if err = l.acquire(); err != nil {
				return err
			}

			log.Printf("%v: lease acquired, expiration is %v", l.clientID, l.expireTime)

			errCh <- nil

			l.maintain()
			return nil
		})
	}()

	if err := <-errCh; err != nil {
		return nil, err
	}

	return l, nil
}

// Stop terminates the background task that maintains the lease
// and issues a DHCP Release
func (l *DHCPLease) Stop() {
	close(l.stop)
	l.wg.Wait()
}

func (l *DHCPLease) acquire() error {
	c, err := newDHCPClient(l.link)
	if err != nil {
		return err
	}
	defer c.Close()

	pkt, err := backoffRetry(func() (*dhcp4.Packet, error) {
		ok, ack, err := c.Request()
		switch {
		case err != nil:
			return nil, err
		case !ok:
			return nil, fmt.Errorf("DHCP server NACK'd own offer")
		default:
			return &ack, nil
		}
	})
	if err != nil {
		return err
	}

	return l.commit(pkt)
}

func (l *DHCPLease) commit(ack *dhcp4.Packet) error {
	opts := ack.ParseOptions()

	leaseTime, err := parseLeaseTime(opts)
	if err != nil {
		return err
	}

	rebindingTime, err := parseRebindingTime(opts)
	if err != nil || rebindingTime > leaseTime {
		// Per RFC 2131 Section 4.4.5, it should default to 85% of lease time
		rebindingTime = leaseTime * 85 / 100
	}

	renewalTime, err := parseRenewalTime(opts)
	if err != nil || renewalTime > rebindingTime {
		// Per RFC 2131 Section 4.4.5, it should default to 50% of lease time
		renewalTime = leaseTime / 2
	}

	now := time.Now()
	l.expireTime = now.Add(leaseTime)
	l.renewalTime = now.Add(renewalTime)
	l.rebindingTime = now.Add(rebindingTime)
	l.ack = ack
	l.opts = opts

	return nil
}

func (l *DHCPLease) maintain() {
	state := leaseStateBound

	for {
		var sleepDur time.Duration

		switch state {
		case leaseStateBound:
			sleepDur = l.renewalTime.Sub(time.Now())
			if sleepDur <= 0 {
				log.Printf("%v: renewing lease", l.clientID)
				state = leaseStateRenewing
				continue
			}

		case leaseStateRenewing:
			if err := l.renew(); err != nil {
				log.Printf("%v: %v", l.clientID, err)

				if time.Now().After(l.rebindingTime) {
					log.Printf("%v: renawal time expired, rebinding", l.clientID)
					state = leaseStateRebinding
				}
			} else {
				log.Printf("%v: lease renewed, expiration is %v", l.clientID, l.expireTime)
				state = leaseStateBound
			}

		case leaseStateRebinding:
			if err := l.acquire(); err != nil {
				log.Printf("%v: %v", l.clientID, err)

				if time.Now().After(l.expireTime) {
					log.Printf("%v: lease expired, bringing interface DOWN", l.clientID)
					l.downIface()
					return
				}
			} else {
				log.Printf("%v: lease rebound, expiration is %v", l.clientID, l.expireTime)
				state = leaseStateBound
			}
		}

		select {
		case <-time.After(sleepDur):

		case <-l.stop:
			if err := l.release(); err != nil {
				log.Printf("%v: failed to release DHCP lease: %v", l.clientID, err)
			}
			return
		}
	}
}

func (l *DHCPLease) downIface() {
	if err := netlink.LinkSetDown(l.link); err != nil {
		log.Printf("%v: failed to bring %v interface DOWN: %v", l.clientID, l.link.Attrs().Name, err)
	}
}

func (l *DHCPLease) renew() error {
	c, err := newDHCPClient(l.link)
	if err != nil {
		return err
	}
	defer c.Close()

	pkt, err := backoffRetry(func() (*dhcp4.Packet, error) {
		ok, ack, err := c.Renew(*l.ack)
		switch {
		case err != nil:
			return nil, err
		case !ok:
			return nil, fmt.Errorf("DHCP server did not renew lease")
		default:
			return &ack, nil
		}
	})
	if err != nil {
		return err
	}

	l.commit(pkt)
	return nil
}

func (l *DHCPLease) release() error {
	log.Printf("%v: releasing lease", l.clientID)

	c, err := newDHCPClient(l.link)
	if err != nil {
		return err
	}
	defer c.Close()

	if err = c.Release(*l.ack); err != nil {
		return fmt.Errorf("failed to send DHCPRELEASE")
	}

	return nil
}

func (l *DHCPLease) IPNet() (*net.IPNet, error) {
	mask := parseSubnetMask(l.opts)
	if mask == nil {
		return nil, fmt.Errorf("DHCP option Subnet Mask not found in DHCPACK")
	}

	return &net.IPNet{
		IP:   l.ack.YIAddr(),
		Mask: mask,
	}, nil
}

func (l *DHCPLease) Gateway() net.IP {
	return parseRouter(l.opts)
}

func (l *DHCPLease) Routes() []plugin.Route {
	routes := parseRoutes(l.opts)
	return append(routes, parseCIDRRoutes(l.opts)...)
}

// jitter returns a random value within [-span, span) range
func jitter(span time.Duration) time.Duration {
	return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0))
}

func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
	var baseDelay time.Duration = resendDelay0

	for i := 0; i < resendCount; i++ {
		pkt, err := f()
		if err == nil {
			return pkt, nil
		}

		log.Print(err)

		time.Sleep(baseDelay + jitter(time.Second))

		if baseDelay < resendDelayMax {
			baseDelay *= 2
		}
	}

	return nil, errNoMoreTries
}

func newDHCPClient(link netlink.Link) (*dhcp4client.Client, error) {
	pktsock, err := dhcp4client.NewPacketSock(link.Attrs().Index)
	if err != nil {
		return nil, err
	}

	return dhcp4client.New(
		dhcp4client.HardwareAddr(link.Attrs().HardwareAddr),
		dhcp4client.Timeout(5*time.Second),
		dhcp4client.Broadcast(false),
		dhcp4client.Connection(pktsock),
	)
}
<|endoftext|>


<commit_before>package morphemekor

import (
	"bytes"
	"strings"
)

// bs := []byte{}
// b := New().Add("morphemekor")
// for {
// 	n, err := b.ReadByte()
// 	if err != nil {
// 		log.Println(err)
// 		break
// 	}
// 	bs = append(bs, n)
// }
// fmt.Println(bs)
// fmt.Println(string(bs))

// Stream is a stream of bytes.
type Stream struct {
	*bytes.Buffer
}

// New returns *morphemekor.Stream.
func New() *Stream {
	out := new(Stream)
	out.Buffer = new(bytes.Buffer)
	return out
}

// Init initializes Stream.
func (s *Stream) Init() *Stream {
	// *s = *New()
	s.Buffer.Reset()
	return s
}

// Add adds a string to the Stream.
func (s *Stream) Add(str string) *Stream {
	s.Buffer.WriteString(str)
	return s
}

// Get returns the string from the String.
func (s *Stream) Get() string {
	return s.Buffer.String()
}

// Segment segments Korean with morphemic approach.
// (Reference: https://github.com/gyuho/learn_other/blob/master/ling105/gyuho.pdf)
func Segment(str string) string {
	if len(str)/3 < 4 {
		return str
	}
	isInflection1 := make(map[string]bool)
	for _, elem := range strings.Split("이히리기었렀", "") {
		isInflection1[elem] = true
	}
	isTargetEnding1 := make(map[string]bool)
	for _, elem := range strings.Split("다,고,지,마,지", ",") {
		isTargetEnding1[elem] = true
	}
	isTargetEnding2 := make(map[string]bool)
	for _, elem := range strings.Split("지만", ",") {
		isTargetEnding2[elem] = true
	}
	isPostposition1 := make(map[string]bool)
	for _, elem := range strings.Split("은,는,이,가,도,을,를,께,로,의,와,과", ",") {
		isPostposition1[elem] = true
	}
	isPostposition2 := make(map[string]bool)
	for _, elem := range strings.Split("에,한,으", ",") {
		isPostposition2[elem] = true
	}
	isPostposition3 := make(map[string]bool)
	for _, elem := range strings.Split("에게,에서,한테,으로", ",") {
		isPostposition3[elem] = true
	}
	buffer := New()
	characters := strings.Split(str, "")
	marker := 0 // another index in word phrase
	for idx := 0; idx < len(characters); idx++ {
		elem := characters[idx]
		if idx == 0 || marker == 0 {
			buffer.Add(elem)
			marker++
			continue
		}
		if elem == " " {
			buffer.Add(elem)
			marker++
			continue
		}
		// Check if the character is inflection
		if _, ok := isInflection1[elem]; ok {
			buffer.Add(elem)
			// Check the next chracter
			if len(characters) > idx+1 {
				nelem1 := characters[idx+1]
				if _, ok := isTargetEnding1[nelem1]; ok {
					// Check the second next chracter
					if len(characters) > idx+2 {
						nelem2 := characters[idx+2]
						if _, ok := isTargetEnding2[nelem1+nelem2]; ok {
							buffer.Add(nelem1 + nelem2)
							buffer.Add(" ")
							idx = idx + 2
							marker = 0
							continue
						}
					}
					buffer.Add(nelem1)
					buffer.Add(" ")
					idx++
					marker = 0
					continue
				}
			}
			marker++
			continue
		}
		// Check if the character is postposition
		if _, ok := isPostposition1[elem]; ok {
			buffer.Add(elem)
			buffer.Add(" ")
			marker = 0
			continue
		} else if _, ok := isPostposition2[elem]; ok {
			if len(characters) > idx+1 {
				nelem2 := characters[idx+1]
				if _, ok := isPostposition3[elem+nelem2]; ok {
					buffer.Add(elem + nelem2)
					buffer.Add(" ")
					idx++
					marker = 0
					continue
				}
			}
		}
		buffer.Add(elem)
		marker++
	}
	return strings.TrimSpace(buffer.Get())
}
<commit_msg>Update morphemekor.go<commit_after>package morphemekor

import (
	"bytes"
	"strings"
)

// bs := []byte{}
// b := New().Add("morphemekor")
// for {
// 	n, err := b.ReadByte()
// 	if err != nil {
// 		log.Println(err)
// 		break
// 	}
// 	bs = append(bs, n)
// }
// fmt.Println(bs)
// fmt.Println(string(bs))

// Stream is a stream of bytes.
type Stream struct {
	*bytes.Buffer
}

// New returns *morphemekor.Stream.
func New() *Stream {
	out := new(Stream)
	out.Buffer = new(bytes.Buffer)
	return out
}

// Init initializes Stream.
func (s *Stream) Init() *Stream {
	// *s = *New()
	s.Buffer.Reset()
	return s
}

// Add adds a string to the Stream.
func (s *Stream) Add(str string) *Stream {
	s.Buffer.WriteString(str)
	return s
}

// Get returns the string from the String.
func (s *Stream) Get() string {
	return s.Buffer.String()
}

// Segment segments Korean with morphemic approach.
// (Reference: https://github.com/gyuho/learn_other/blob/master/ling105/gyuho.pdf)
func Segment(str string) string {
	if len(str)/3 < 4 {
		return str
	}
	isInflection1 := make(map[string]bool)
	for _, elem := range strings.Split("이히리기었렀", "") {
		isInflection1[elem] = true
	}
	isTargetEnding1 := make(map[string]bool)
	for _, elem := range strings.Split("다,고,지,마,지", ",") {
		isTargetEnding1[elem] = true
	}
	isTargetEnding2 := make(map[string]bool)
	for _, elem := range strings.Split("지만", ",") {
		isTargetEnding2[elem] = true
	}
	isPostposition1 := make(map[string]bool)
	for _, elem := range strings.Split("은,는,이,가,도,을,를,께,로,의,와,과", ",") {
		isPostposition1[elem] = true
	}
	isPostposition2 := make(map[string]bool)
	for _, elem := range strings.Split("에,한,으", ",") {
		isPostposition2[elem] = true
	}
	isPostposition3 := make(map[string]bool)
	for _, elem := range strings.Split("에게,에서,한테,으로", ",") {
		isPostposition3[elem] = true
	}
	buffer := New()
	characters := strings.Split(str, "")
	marker := 0 // another index in word phrase
	for idx := 0; idx < len(characters); idx++ {
		elem := characters[idx]
		if idx == 0 || marker == 0 {
			buffer.Add(elem)
			marker++
			continue
		}
		if elem == " " {
			buffer.Add(elem)
			marker++
			continue
		}
		// Check if the character is inflection
		if _, ok := isInflection1[elem]; ok {
			buffer.Add(elem)
			// Check the next character
			if len(characters) > idx+1 {
				nelem1 := characters[idx+1]
				if _, ok := isTargetEnding1[nelem1]; ok {
					// Check the second next character
					if len(characters) > idx+2 {
						nelem2 := characters[idx+2]
						if _, ok := isTargetEnding2[nelem1+nelem2]; ok {
							buffer.Add(nelem1 + nelem2)
							buffer.Add(" ")
							idx = idx + 2
							marker = 0
							continue
						}
					}
					buffer.Add(nelem1)
					buffer.Add(" ")
					idx++
					marker = 0
					continue
				}
			}
			marker++
			continue
		}
		// Check if the character is postposition
		if _, ok := isPostposition1[elem]; ok {
			buffer.Add(elem)
			buffer.Add(" ")
			marker = 0
			continue
		} else if _, ok := isPostposition2[elem]; ok {
			if len(characters) > idx+1 {
				nelem2 := characters[idx+1]
				if _, ok := isPostposition3[elem+nelem2]; ok {
					buffer.Add(elem + nelem2)
					buffer.Add(" ")
					idx++
					marker = 0
					continue
				}
			}
		}
		buffer.Add(elem)
		marker++
	}
	return strings.TrimSpace(buffer.Get())
}
<|endoftext|>


<commit_before>package mysql

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
)

var (
	tableName string
)

func genConnStr(user, password, host, dbname string) string {
	return fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=True", user, password, host, dbname)
}

func (c *Client) Boot(dbType, user, password, host, dbname string) error {
	var err error
	db, err := gorm.Open(dbType, genConnStr(user, password, host, dbname))
	if err != nil {
		return err
	}
	c.SetDb(&db)
	c.db.DB()
	return c.db.DB().Ping()
}

func (c *Client) SetTable(table string) {
	tableName = table
	c.table = table
}
<commit_msg>fixed #20<commit_after>package mysql

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
)

var (
	tableName string
)

func genConnStr(user, password, host, dbname string) string {
	return fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=True", user, password, host, dbname)
}

func (c *Client) Boot(dbType, user, password, host, dbname string) error {
	var err error
	db, err := gorm.Open(dbType, genConnStr(user, password, host, dbname))
	if err != nil {
		return err
	}
	c.SetDb(db)
	c.db.DB()
	return c.db.DB().Ping()
}

func (c *Client) SetTable(table string) {
	tableName = table
	c.table = table
}
<|endoftext|>


<commit_before>package tag

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/weaveworks/scope/report"
)

const (
	start    = "start"
	die      = "die"
	endpoint = "unix:///var/run/docker.sock"
)

// These constants are keys used in node metadata
// TODO: use these constants in report/{mapping.go, detailed_node.go} - pending some circular references
const (
	ContainerID   = "docker_container_id"
	ContainerName = "docker_container_name"
	ImageID       = "docker_image_id"
	ImageName     = "docker_image_name"
)

var (
	newDockerClientStub = newDockerClient
	newPIDTreeStub      = NewPIDTree
)

// DockerTagger is a tagger that tags Docker container information to process
// nodes that have a PID.
type DockerTagger struct {
	sync.RWMutex
	quit     chan struct{}
	interval time.Duration
	client   dockerClient

	containers      map[string]*dockerContainer
	containersByPID map[int]*dockerContainer
	images          map[string]*docker.APIImages

	procRoot string
	pidTree  *PIDTree
}

// Sub-interface for mocking.
type dockerClient interface {
	ListContainers(docker.ListContainersOptions) ([]docker.APIContainers, error)
	InspectContainer(string) (*docker.Container, error)
	ListImages(docker.ListImagesOptions) ([]docker.APIImages, error)
	AddEventListener(chan<- *docker.APIEvents) error
	RemoveEventListener(chan *docker.APIEvents) error
}

func newDockerClient(endpoint string) (dockerClient, error) {
	return docker.NewClient(endpoint)
}

// NewDockerTagger returns a usable DockerTagger. Don't forget to Stop it.
func NewDockerTagger(procRoot string, interval time.Duration) (*DockerTagger, error) {
	pidTree, err := newPIDTreeStub(procRoot)
	if err != nil {
		return nil, err
	}

	t := DockerTagger{
		containers:      map[string]*dockerContainer{},
		containersByPID: map[int]*dockerContainer{},
		images:          map[string]*docker.APIImages{},

		procRoot: procRoot,
		pidTree:  pidTree,

		interval: interval,
		quit:     make(chan struct{}),
	}

	go t.loop()
	return &t, nil
}

// Stop stops the Docker tagger's event subscriber.
func (t *DockerTagger) Stop() {
	close(t.quit)
}

func (t *DockerTagger) loop() {
	if !t.update() {
		return
	}

	ticker := time.Tick(t.interval)
	for {
		select {
		case <-ticker:
			if !t.update() {
				return
			}

		case <-t.quit:
			return
		}
	}
}

func (t *DockerTagger) update() bool {
	client, err := newDockerClientStub(endpoint)
	if err != nil {
		log.Printf("docker mapper: %s", err)
		return true
	}
	t.client = client

	events := make(chan *docker.APIEvents)
	if err := client.AddEventListener(events); err != nil {
		log.Printf("docker mapper: %s", err)
		return true
	}
	defer func() {
		if err := client.RemoveEventListener(events); err != nil {
			log.Printf("docker mapper: %s", err)
		}
	}()

	if err := t.updateContainers(); err != nil {
		log.Printf("docker mapper: %s", err)
		return true
	}

	if err := t.updateImages(); err != nil {
		log.Printf("docker mapper: %s", err)
		return true
	}

	otherUpdates := time.Tick(t.interval)
	for {
		select {
		case event := <-events:
			t.handleEvent(event)

		case <-otherUpdates:
			if err := t.updatePIDTree(); err != nil {
				log.Printf("docker mapper: %s", err)
				continue
			}

			if err := t.updateImages(); err != nil {
				log.Printf("docker mapper: %s", err)
				continue
			}

		case <-t.quit:
			return false
		}
	}
}

func (t *DockerTagger) updateContainers() error {
	apiContainers, err := t.client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return err
	}

	for _, apiContainer := range apiContainers {
		if err := t.addContainer(apiContainer.ID); err != nil {
			log.Printf("docker mapper: %s", err)
		}
	}

	return nil
}

func (t *DockerTagger) updateImages() error {
	images, err := t.client.ListImages(docker.ListImagesOptions{})
	if err != nil {
		return err
	}

	t.Lock()
	for i := range images {
		image := &images[i]
		t.images[image.ID] = image
	}
	t.Unlock()

	return nil
}

func (t *DockerTagger) handleEvent(event *docker.APIEvents) {
	switch event.Status {
	case die:
		containerID := event.ID
		t.removeContainer(containerID)

	case start:
		containerID := event.ID
		if err := t.addContainer(containerID); err != nil {
			log.Printf("docker mapper: %s", err)
		}
	}
}

func (t *DockerTagger) updatePIDTree() error {
	pidTree, err := newPIDTreeStub(t.procRoot)
	if err != nil {
		return err
	}

	t.Lock()
	t.pidTree = pidTree
	t.Unlock()
	return nil
}

func (t *DockerTagger) addContainer(containerID string) error {
	container, err := t.client.InspectContainer(containerID)
	if err != nil {
		// Don't spam the logs if the container was short lived
		if _, ok := err.(*docker.NoSuchContainer); !ok {
			return err
		}
		return nil
	}

	if !container.State.Running {
		return fmt.Errorf("docker mapper: container %s not running", containerID)
	}

	t.Lock()
	defer t.Unlock()

	dockerContainer := &dockerContainer{Container: container}

	t.containers[containerID] = dockerContainer
	t.containersByPID[container.State.Pid] = dockerContainer

	return dockerContainer.startGatheringStats(containerID)
}

func (t *DockerTagger) removeContainer(containerID string) {
	t.Lock()
	defer t.Unlock()

	container, ok := t.containers[containerID]
	if !ok {
		return
	}

	delete(t.containers, containerID)
	delete(t.containersByPID, container.State.Pid)
	container.stopGatheringStats(containerID)
}

// Containers returns the Containers the DockerTagger knows about.
func (t *DockerTagger) Containers() []*docker.Container {
	containers := []*docker.Container{}

	t.RLock()
	for _, container := range t.containers {
		containers = append(containers, container.Container)
	}
	t.RUnlock()

	return containers
}

// Tag implements Tagger.
func (t *DockerTagger) Tag(r report.Report) report.Report {
	for nodeID, nodeMetadata := range r.Process.NodeMetadatas {
		pidStr, ok := nodeMetadata["pid"]
		if !ok {
			//log.Printf("dockerTagger: %q: no process node ID", id)
			continue
		}
		pid, err := strconv.ParseUint(pidStr, 10, 64)
		if err != nil {
			//log.Printf("dockerTagger: %q: bad process node PID (%v)", id, err)
			continue
		}

		var (
			container *dockerContainer
			candidate = int(pid)
		)

		t.RLock()
		for {
			container, ok = t.containersByPID[candidate]
			if ok {
				break
			}
			candidate, err = t.pidTree.getParent(candidate)
			if err != nil {
				break
			}
		}
		t.RUnlock()

		if !ok {
			continue
		}

		md := report.NodeMetadata{
			ContainerID: container.ID,
			ImageID:     container.Image,
		}

		t.RLock()
		image, ok := t.images[container.Image]
		t.RUnlock()

		if ok && len(image.RepoTags) > 0 {
			md[ImageName] = image.RepoTags[0]
		}

		r.Process.NodeMetadatas[nodeID].Merge(md)
	}

	return r
}

// ContainerTopology produces a Toplogy of Containers
func (t *DockerTagger) ContainerTopology(scope string) report.Topology {
	t.RLock()
	defer t.RUnlock()

	result := report.NewTopology()
	for _, container := range t.containers {
		nmd := report.NodeMetadata{
			ContainerID:   container.ID,
			ContainerName: strings.TrimPrefix(container.Name, "/"),
			ImageID:       container.Image,
		}

		image, ok := t.images[container.Image]
		if ok && len(image.RepoTags) > 0 {
			nmd[ImageName] = image.RepoTags[0]
		}

		nmd.Merge(container.getStats())

		nodeID := report.MakeContainerNodeID(scope, container.ID)
		result.NodeMetadatas[nodeID] = nmd
	}
	return result
}
<commit_msg>Restore docker tagging to the endpoint topology until we have the docker images topology.<commit_after>package tag

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/weaveworks/scope/report"
)

const (
	start    = "start"
	die      = "die"
	endpoint = "unix:///var/run/docker.sock"
)

// These constants are keys used in node metadata
// TODO: use these constants in report/{mapping.go, detailed_node.go} - pending some circular references
const (
	ContainerID   = "docker_container_id"
	ContainerName = "docker_container_name"
	ImageID       = "docker_image_id"
	ImageName     = "docker_image_name"
)

var (
	newDockerClientStub = newDockerClient
	newPIDTreeStub      = NewPIDTree
)

// DockerTagger is a tagger that tags Docker container information to process
// nodes that have a PID.
type DockerTagger struct {
	sync.RWMutex
	quit     chan struct{}
	interval time.Duration
	client   dockerClient

	containers      map[string]*dockerContainer
	containersByPID map[int]*dockerContainer
	images          map[string]*docker.APIImages

	procRoot string
	pidTree  *PIDTree
}

// Sub-interface for mocking.
type dockerClient interface {
	ListContainers(docker.ListContainersOptions) ([]docker.APIContainers, error)
	InspectContainer(string) (*docker.Container, error)
	ListImages(docker.ListImagesOptions) ([]docker.APIImages, error)
	AddEventListener(chan<- *docker.APIEvents) error
	RemoveEventListener(chan *docker.APIEvents) error
}

func newDockerClient(endpoint string) (dockerClient, error) {
	return docker.NewClient(endpoint)
}

// NewDockerTagger returns a usable DockerTagger. Don't forget to Stop it.
func NewDockerTagger(procRoot string, interval time.Duration) (*DockerTagger, error) {
	pidTree, err := newPIDTreeStub(procRoot)
	if err != nil {
		return nil, err
	}

	t := DockerTagger{
		containers:      map[string]*dockerContainer{},
		containersByPID: map[int]*dockerContainer{},
		images:          map[string]*docker.APIImages{},

		procRoot: procRoot,
		pidTree:  pidTree,

		interval: interval,
		quit:     make(chan struct{}),
	}

	go t.loop()
	return &t, nil
}

// Stop stops the Docker tagger's event subscriber.
func (t *DockerTagger) Stop() {
	close(t.quit)
}

func (t *DockerTagger) loop() {
	if !t.update() {
		return
	}

	ticker := time.Tick(t.interval)
	for {
		select {
		case <-ticker:
			if !t.update() {
				return
			}

		case <-t.quit:
			return
		}
	}
}

func (t *DockerTagger) update() bool {
	client, err := newDockerClientStub(endpoint)
	if err != nil {
		log.Printf("docker mapper: %s", err)
		return true
	}
	t.client = client

	events := make(chan *docker.APIEvents)
	if err := client.AddEventListener(events); err != nil {
		log.Printf("docker mapper: %s", err)
		return true
	}
	defer func() {
		if err := client.RemoveEventListener(events); err != nil {
			log.Printf("docker mapper: %s", err)
		}
	}()

	if err := t.updateContainers(); err != nil {
		log.Printf("docker mapper: %s", err)
		return true
	}

	if err := t.updateImages(); err != nil {
		log.Printf("docker mapper: %s", err)
		return true
	}

	otherUpdates := time.Tick(t.interval)
	for {
		select {
		case event := <-events:
			t.handleEvent(event)

		case <-otherUpdates:
			if err := t.updatePIDTree(); err != nil {
				log.Printf("docker mapper: %s", err)
				continue
			}

			if err := t.updateImages(); err != nil {
				log.Printf("docker mapper: %s", err)
				continue
			}

		case <-t.quit:
			return false
		}
	}
}

func (t *DockerTagger) updateContainers() error {
	apiContainers, err := t.client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return err
	}

	for _, apiContainer := range apiContainers {
		if err := t.addContainer(apiContainer.ID); err != nil {
			log.Printf("docker mapper: %s", err)
		}
	}

	return nil
}

func (t *DockerTagger) updateImages() error {
	images, err := t.client.ListImages(docker.ListImagesOptions{})
	if err != nil {
		return err
	}

	t.Lock()
	for i := range images {
		image := &images[i]
		t.images[image.ID] = image
	}
	t.Unlock()

	return nil
}

func (t *DockerTagger) handleEvent(event *docker.APIEvents) {
	switch event.Status {
	case die:
		containerID := event.ID
		t.removeContainer(containerID)

	case start:
		containerID := event.ID
		if err := t.addContainer(containerID); err != nil {
			log.Printf("docker mapper: %s", err)
		}
	}
}

func (t *DockerTagger) updatePIDTree() error {
	pidTree, err := newPIDTreeStub(t.procRoot)
	if err != nil {
		return err
	}

	t.Lock()
	t.pidTree = pidTree
	t.Unlock()
	return nil
}

func (t *DockerTagger) addContainer(containerID string) error {
	container, err := t.client.InspectContainer(containerID)
	if err != nil {
		// Don't spam the logs if the container was short lived
		if _, ok := err.(*docker.NoSuchContainer); !ok {
			return err
		}
		return nil
	}

	if !container.State.Running {
		return fmt.Errorf("docker mapper: container %s not running", containerID)
	}

	t.Lock()
	defer t.Unlock()

	dockerContainer := &dockerContainer{Container: container}

	t.containers[containerID] = dockerContainer
	t.containersByPID[container.State.Pid] = dockerContainer

	return dockerContainer.startGatheringStats(containerID)
}

func (t *DockerTagger) removeContainer(containerID string) {
	t.Lock()
	defer t.Unlock()

	container, ok := t.containers[containerID]
	if !ok {
		return
	}

	delete(t.containers, containerID)
	delete(t.containersByPID, container.State.Pid)
	container.stopGatheringStats(containerID)
}

// Containers returns the Containers the DockerTagger knows about.
func (t *DockerTagger) Containers() []*docker.Container {
	containers := []*docker.Container{}

	t.RLock()
	for _, container := range t.containers {
		containers = append(containers, container.Container)
	}
	t.RUnlock()

	return containers
}

// Tag implements Tagger.
func (t *DockerTagger) Tag(r report.Report) report.Report {
	t.tag(&r.Process)
	t.tag(&r.Endpoint)
	return r
}

func (t *DockerTagger) tag(topology *report.Topology) {
	for nodeID, nodeMetadata := range topology.NodeMetadatas {
		pidStr, ok := nodeMetadata["pid"]
		if !ok {
			//log.Printf("dockerTagger: %q: no process node ID", id)
			continue
		}
		pid, err := strconv.ParseUint(pidStr, 10, 64)
		if err != nil {
			//log.Printf("dockerTagger: %q: bad process node PID (%v)", id, err)
			continue
		}

		var (
			container *dockerContainer
			candidate = int(pid)
		)

		t.RLock()
		for {
			container, ok = t.containersByPID[candidate]
			if ok {
				break
			}
			candidate, err = t.pidTree.getParent(candidate)
			if err != nil {
				break
			}
		}
		t.RUnlock()

		if !ok {
			continue
		}

		md := report.NodeMetadata{
			ContainerID: container.ID,
			ImageID:     container.Image,
		}

		t.RLock()
		image, ok := t.images[container.Image]
		t.RUnlock()

		if ok && len(image.RepoTags) > 0 {
			md[ImageName] = image.RepoTags[0]
		}

		topology.NodeMetadatas[nodeID].Merge(md)
	}
}

// ContainerTopology produces a Toplogy of Containers
func (t *DockerTagger) ContainerTopology(scope string) report.Topology {
	t.RLock()
	defer t.RUnlock()

	result := report.NewTopology()
	for _, container := range t.containers {
		nmd := report.NodeMetadata{
			ContainerID:   container.ID,
			ContainerName: strings.TrimPrefix(container.Name, "/"),
			ImageID:       container.Image,
		}

		image, ok := t.images[container.Image]
		if ok && len(image.RepoTags) > 0 {
			nmd[ImageName] = image.RepoTags[0]
		}

		nmd.Merge(container.getStats())

		nodeID := report.MakeContainerNodeID(scope, container.ID)
		result.NodeMetadatas[nodeID] = nmd
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	//w "eaciit/wfdemo-git/processapp/opcdata/filewatcher"
	d "eaciit/wfdemo-git/processapp/opcdata/datareader"
	"github.com/eaciit/dbox"
	_ "github.com/eaciit/dbox/dbc/mongo"
	_ "github.com/eaciit/orm"
	tk "github.com/eaciit/toolkit"
	"os"
	"time"
	"fmt"
	"strings"
)

var (
	wd = func() string {
		d, _ := os.Getwd()
		return d + "/"
	}()
)

func main() {
	FileWatcher()
}
func FileIsExist(dirSources string)(bool,string){
	now := time.Now()
	year,month,day:=now.Date()
	hour,_,_:=now.Clock()

	targetFileName := fmt.Sprintf("DataFile%d%02d%02d-%02d.csv",year,month,day,hour)
	tk.Println(dirSources+"\\"+targetFileName)
	_,err:=os.Open(dirSources+"\\"+targetFileName)
	if err!=nil{
		if os.IsNotExist(err){
			tk.Println("File Not Exist")

		}else{
			tk.Println(err.Error())
		}
		return false,""
	}
	return true,targetFileName
}
func FileWatcher() {
	config := ReadConfig()
	dirSources := config["FileSources"]
	dirProcess := config["FileProcess"]
	scpDir := config["UploadDirectory"]
	sshUser := config["SSHUser"]
	sshServer := config["SSHServer"]
	var FileName string
	var e bool
	//watcher := w.NewFileWatcher(dirSources, dirProcess, wd,scpDir)
	if e,FileName=FileIsExist(dirSources);!e{
		os.Exit(1)
	}
	d.NewDataReader(dirSources+"\\"+FileName, dirProcess, wd,scpDir,sshUser,sshServer).Start()
	//watcher.StartWatcher()
}

func CsvExtractor() {
	//conn, err := PrepareConnection()
	// if err != nil {
	// 	tk.Println("Error connection: ", err.Error())
	// }
	//ctx := orm.New(conn)

	// config := ReadConfig()
	// dirSources := config["FileSources"]
}

func PrepareConnection() (dbox.IConnection, error) {
	config := ReadConfig()

	ci := &dbox.ConnectionInfo{config["host"], config["database"], config["username"], config["password"], tk.M{}.Set("timeout", 3000)}

	c, e := dbox.NewConnection("mongo", ci)

	if e != nil {
		return nil, e
	}

	e = c.Connect()
	if e != nil {
		return nil, e
	}

	return c, nil
}

func ReadConfig() map[string]string {
	ret := make(map[string]string)
	file, err := os.Open(wd + "conf/app.conf")
	if err == nil {
		defer file.Close()

		reader := bufio.NewReader(file)
		for {
			line, _, e := reader.ReadLine()
			if e != nil {
				break
			}

			sval := strings.Split(string(line), "=")
			ret[sval[0]] = sval[1]
		}
	} else {
		tk.Println(err.Error())
	}

	return ret
}
<commit_msg>another commmit<commit_after>package main

import (
	"bufio"
	//w "eaciit/wfdemo-git-dev/processapp/opcdata/filewatcher"
	d "eaciit/wfdemo-git-dev/processapp/opcdata/datareader"
	"github.com/eaciit/dbox"
	_ "github.com/eaciit/dbox/dbc/mongo"
	_ "github.com/eaciit/orm"
	tk "github.com/eaciit/toolkit"
	"os"
	"time"
	"fmt"
	"strings"
)

var (
	wd = func() string {
		d, _ := os.Getwd()
		return d + "/"
	}()
)

func main() {
	FileWatcher()
}
func FileIsExist(dirSources string)(bool,string){
	now := time.Now()
	year,month,day:=now.Date()
	hour,_,_:=now.Clock()

	targetFileName := fmt.Sprintf("DataFile%d%02d%02d-%02d.csv",year,month,day,hour)
	tk.Println(dirSources+"\\"+targetFileName)
	_,err:=os.Open(dirSources+"\\"+targetFileName)
	if err!=nil{
		if os.IsNotExist(err){
			tk.Println("File Not Exist")

		}else{
			tk.Println(err.Error())
		}
		return false,""
	}
	return true,targetFileName
}
func FileWatcher() {
	config := ReadConfig()
	dirSources := config["FileSources"]
	dirProcess := config["FileProcess"]
	scpDir := config["UploadDirectory"]
	sshUser := config["SSHUser"]
	sshServer := config["SSHServer"]
	var FileName string
	var e bool
	//watcher := w.NewFileWatcher(dirSources, dirProcess, wd,scpDir)
	if e,FileName=FileIsExist(dirSources);!e{
		os.Exit(1)
	}
	d.NewDataReader(dirSources+"\\"+FileName, dirProcess, wd,scpDir,sshUser,sshServer).Start()
	//watcher.StartWatcher()
}

func CsvExtractor() {
	//conn, err := PrepareConnection()
	// if err != nil {
	// 	tk.Println("Error connection: ", err.Error())
	// }
	//ctx := orm.New(conn)

	// config := ReadConfig()
	// dirSources := config["FileSources"]
}

func PrepareConnection() (dbox.IConnection, error) {
	config := ReadConfig()

	ci := &dbox.ConnectionInfo{config["host"], config["database"], config["username"], config["password"], tk.M{}.Set("timeout", 3000)}

	c, e := dbox.NewConnection("mongo", ci)

	if e != nil {
		return nil, e
	}

	e = c.Connect()
	if e != nil {
		return nil, e
	}

	return c, nil
}

func ReadConfig() map[string]string {
	ret := make(map[string]string)
	file, err := os.Open(wd + "conf/app.conf")
	if err == nil {
		defer file.Close()

		reader := bufio.NewReader(file)
		for {
			line, _, e := reader.ReadLine()
			if e != nil {
				break
			}

			sval := strings.Split(string(line), "=")
			ret[sval[0]] = sval[1]
		}
	} else {
		tk.Println(err.Error())
	}

	return ret
}
<|endoftext|>