<commit_before>package task

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"

	"github.com/avabot/ava/Godeps/_workspace/src/github.com/stripe/stripe-go"
	"github.com/avabot/ava/Godeps/_workspace/src/github.com/stripe/stripe-go/charge"
	"github.com/avabot/ava/shared/datatypes"
	"golang.org/x/crypto/bcrypt"
)

var regexNum = regexp.MustCompile(`\d+`)
var ErrNoAuth = errors.New("no auth found")
var ErrInvalidAuth = errors.New("invalid auth")

const (
	authStateStart float64 = iota
	authStateConfirm
)

const (
	// MethodZip requires the zip code associated with a credit card on
	// file. The user will be asked for a credit card if not on file.
	MethodZip = iota + 1

	// MethodWebCache allows a user to authenticate by clicking a link. If
	// their browser cookies have them already logged into Ava, they will be
	// authenticated. If they are not currently logged into Ava, they will
	// be asked to login. Once logged in, they will be authenticated.
	MethodWebCache

	// MethodWebLogin requires the user login to Ava on the web interface
	// using their username and password. This is the most secure option,
	// as it ensures no one has stolen the device or session token of a
	// user.
	MethodWebLogin
)

// RequestAuth ensures you're speaking to the correct user. Select the LOWEST
// level of authentication you'll allow based on a tolerance for fraud weighed
// against the convenience of the user experience. Methods are organized in
// least-secure to most-secure order. Therefore, MethodZip will allow any auth
// method, whereas MethodWebCache will only allow MethodWebCache and above. Ava
// will IMPROVE the quality of the authentication automatically whenever
// possible, selecting the highest authentication method for which the user has
// recently authenticated. Note that you'll never have to call RequestAuth in a
// Purchase flow. In order to drive a customer purchase, call Purchase directly,
// which will also authenticate the user.
func (t *Task) RequestAuth(m dt.Method) (bool, error) {
	log.Println("REQUESTAUTH")
	log.Println("state", t.getState())
	// check last authentication date and method
	authenticated, err := t.ctx.Msg.User.IsAuthenticated(m)
	if err != nil {
		log.Println("err checking last authentication")
		return false, err
	}
	if authenticated {
		return true, nil
	}
	switch t.getState() {
	case authStateStart:
		log.Println("hit authStateStart")
		t.setState(authStateConfirm)
		return t.askUserForAuth(m)
	case authStateConfirm:
		log.Println("hit authStateConfirm")
		switch m {
		case MethodZip:
			zip5 := []byte(
				regexNum.FindString(t.ctx.Msg.Input.Sentence))
			if len(zip5) != 5 {
				return false, ErrInvalidAuth
			}
			q := `SELECT zip5hash FROM cards WHERE userid=$1`
			rows, err := t.ctx.DB.Queryx(q, t.ctx.Msg.User.ID)
			if err != nil {
				return false, err
			}
			defer rows.Close()
			for rows.Next() {
				log.Println("hasCard is true")
				var zip5hash []byte
				if err = rows.Scan(&zip5hash); err != nil {
					return false, err
				}
				err = bcrypt.CompareHashAndPassword(zip5hash,
					zip5)
				if err == bcrypt.ErrMismatchedHashAndPassword ||
					err == bcrypt.ErrHashTooShort {
					continue
				} else if err != nil {
					return false, err
				} else {
					q = `
						SELECT authorizationid
						FROM users
						WHERE id=$1`
					authID := &sql.NullInt64{}
					err = t.ctx.DB.Get(authID, q,
						t.ctx.Msg.User.ID)
					if err == sql.ErrNoRows {
						return false, ErrNoAuth
					}
					if err != nil {
						return false, err
					}
					q = `
						SELECT id FROM authorizations
						WHERE id=$1 AND authmethod>=$2`
					err = t.ctx.DB.Get(authID, q, *authID,
						m)
					if err == sql.ErrNoRows {
						log.Println("no authorization")
						return false, ErrInvalidAuth
					}
					if err != nil {
						return false, err
					}
					if !authID.Valid {
						return false, ErrNoAuth
					}
					err = t.setAuthorized(authID)
					if err != nil {
						return false, err
					}
					return true, nil
				}
			}
			return false, ErrInvalidAuth
		case MethodWebCache, MethodWebLogin:
			q := `SELECT authorizationid FROM users WHERE id=$1`
			var authID *sql.NullInt64
			err := t.ctx.DB.Get(authID, q, t.ctx.Msg.User.ID)
			if err != nil {
				return false, err
			}
			if !authID.Valid {
				return false, ErrNoAuth
			}
			q = `
				SELECT id FROM authorizations
				WHERE id=$1
					AND authmethod>=$2
					AND authorizedat<>NULL`
			err = t.ctx.DB.Get(authID, q, authID.Int64, m)
			if err != nil {
				return false, err
			}
			if !authID.Valid {
				return false, ErrNoAuth
			}
			if err = t.setAuthorized(authID); err != nil {
				return false, err
			}
			return true, nil
		default:
			return false, errors.New("invalid auth state")
		}
	}
	return false, nil
}

// RequestPurchase will authenticate the user and then charge a card.
func (t *Task) RequestPurchase(m dt.Method, p *dt.Purchase) (bool, error) {
	t.typ = "Purchase"
	done, err := t.makePurchase(m, p)
	if done {
		t.setState(authStateStart)
	}
	return done, err
}

func (t *Task) askUserForAuth(m dt.Method) (bool, error) {
	log.Println("asking user for auth")
	cards, err := t.ctx.Msg.User.GetCards(t.ctx.DB)
	if len(cards) == 0 {
		log.Println("user has no cards")
		t.resp.Sentence = "Great! I'll need you to add your card here, first: https://avabot.co/?/cards/new. Let me know when you're done!"
		return false, nil
	}
	if err != nil {
		return false, err
	}
	switch m {
	case MethodZip:
		t.resp.Sentence = "To do that, please confirm your billing zip code."
		log.Println("asking user for auth: zip code")
	case MethodWebCache:
		t.resp.Sentence = "To do that, please prove you're logged in: https://www.avabot.com/?/profile"
	case MethodWebLogin:
		if err := t.ctx.Msg.User.DeleteSessions(t.ctx.DB); err != nil {
			return false, err
		}
		t.resp.Sentence = "To do that, please log in to prove it's you: https://www.avabot.com/?/login"
	}
	tx, err := t.ctx.DB.Beginx()
	if err != nil {
		return false, err
	}
	q := `INSERT INTO authorizations (authmethod) VALUES ($1) RETURNING id`
	var aid int
	if err = tx.QueryRowx(q, m).Scan(&aid); err != nil {
		return false, err
	}
	q = `UPDATE users SET authorizationid=$1 WHERE id=$2`
	if _, err = tx.Exec(q, aid, t.ctx.Msg.User.ID); err != nil {
		return false, err
	}
	if err = tx.Commit(); err != nil {
		return false, err
	}
	log.Println("asked user for auth")
	t.setState(authStateConfirm)
	return false, nil
}

func (t *Task) setAuthorized(authID *sql.NullInt64) error {
	tx, err := t.ctx.DB.Beginx()
	if err != nil {
		return err
	}
	q := `UPDATE users SET authorizationid=NULL WHERE id=$1`
	if _, err = tx.Exec(q, t.ctx.Msg.User.ID); err != nil {
		return err
	}
	q = `
		UPDATE authorizations
		SET authorizedat=CURRENT_TIMESTAMP
		WHERE id=$1`
	if _, err = tx.Exec(q, authID.Int64); err != nil {
		return err
	}
	if err = tx.Commit(); err != nil {
		return err
	}
	return nil
}

func (t *Task) makePurchase(m dt.Method, p *dt.Purchase) (bool, error) {
	authenticated, err := t.RequestAuth(m)
	if err != nil {
		return false, err
	}
	if !authenticated {
		return false, nil
	}
	desc := fmt.Sprintf("Purchase for $%.2f", float64(p.Total)/100)
	stripe.Key = os.Getenv("STRIPE_ACCESS_TOKEN")
	chargeParams := &stripe.ChargeParams{
		Amount:   p.Total,
		Currency: "usd",
		Desc:     desc,
		Customer: t.ctx.Msg.User.StripeCustomerID,
	}
	if _, err := charge.New(chargeParams); err != nil {
		return false, err
	}
	if err := t.ctx.SG.SendVendorRequest(p); err != nil {
		return false, err
	}
	if err := t.ctx.SG.SendPurchaseConfirmation(p); err != nil {
		return false, err
	}
	if err := p.UpdateEmailsSent(); err != nil {
		return false, err
	}
	return true, nil
}
<commit_msg>Fix no cards bug<commit_after>package task

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"

	"github.com/avabot/ava/Godeps/_workspace/src/github.com/stripe/stripe-go"
	"github.com/avabot/ava/Godeps/_workspace/src/github.com/stripe/stripe-go/charge"
	"github.com/avabot/ava/shared/datatypes"
	"golang.org/x/crypto/bcrypt"
)

var regexNum = regexp.MustCompile(`\d+`)
var ErrNoAuth = errors.New("no auth found")
var ErrInvalidAuth = errors.New("invalid auth")

const (
	authStateStart float64 = iota
	authStateConfirm
)

const (
	// MethodZip requires the zip code associated with a credit card on
	// file. The user will be asked for a credit card if not on file.
	MethodZip = iota + 1

	// MethodWebCache allows a user to authenticate by clicking a link. If
	// their browser cookies have them already logged into Ava, they will be
	// authenticated. If they are not currently logged into Ava, they will
	// be asked to login. Once logged in, they will be authenticated.
	MethodWebCache

	// MethodWebLogin requires the user login to Ava on the web interface
	// using their username and password. This is the most secure option,
	// as it ensures no one has stolen the device or session token of a
	// user.
	MethodWebLogin
)

// RequestAuth ensures you're speaking to the correct user. Select the LOWEST
// level of authentication you'll allow based on a tolerance for fraud weighed
// against the convenience of the user experience. Methods are organized in
// least-secure to most-secure order. Therefore, MethodZip will allow any auth
// method, whereas MethodWebCache will only allow MethodWebCache and above. Ava
// will IMPROVE the quality of the authentication automatically whenever
// possible, selecting the highest authentication method for which the user has
// recently authenticated. Note that you'll never have to call RequestAuth in a
// Purchase flow. In order to drive a customer purchase, call Purchase directly,
// which will also authenticate the user.
func (t *Task) RequestAuth(m dt.Method) (bool, error) {
	log.Println("REQUESTAUTH")
	log.Println("state", t.getState())
	// check last authentication date and method
	authenticated, err := t.ctx.Msg.User.IsAuthenticated(m)
	if err != nil {
		log.Println("err checking last authentication")
		return false, err
	}
	if authenticated {
		return true, nil
	}
	switch t.getState() {
	case authStateStart:
		log.Println("hit authStateStart")
		t.setState(authStateConfirm)
		return t.askUserForAuth(m)
	case authStateConfirm:
		log.Println("hit authStateConfirm")
		switch m {
		case MethodZip:
			zip5 := []byte(
				regexNum.FindString(t.ctx.Msg.Input.Sentence))
			if len(zip5) != 5 {
				return false, ErrInvalidAuth
			}
			q := `SELECT zip5hash FROM cards WHERE userid=$1`
			rows, err := t.ctx.DB.Queryx(q, t.ctx.Msg.User.ID)
			if err != nil {
				return false, err
			}
			defer rows.Close()
			for rows.Next() {
				log.Println("hasCard is true")
				var zip5hash []byte
				if err = rows.Scan(&zip5hash); err != nil {
					return false, err
				}
				err = bcrypt.CompareHashAndPassword(zip5hash,
					zip5)
				if err == bcrypt.ErrMismatchedHashAndPassword ||
					err == bcrypt.ErrHashTooShort {
					continue
				} else if err != nil {
					return false, err
				} else {
					q = `
						SELECT authorizationid
						FROM users
						WHERE id=$1`
					authID := &sql.NullInt64{}
					err = t.ctx.DB.Get(authID, q,
						t.ctx.Msg.User.ID)
					if err == sql.ErrNoRows {
						return false, ErrNoAuth
					}
					if err != nil {
						return false, err
					}
					q = `
						SELECT id FROM authorizations
						WHERE id=$1 AND authmethod>=$2`
					err = t.ctx.DB.Get(authID, q, *authID,
						m)
					if err == sql.ErrNoRows {
						log.Println("no authorization")
						return false, ErrInvalidAuth
					}
					if err != nil {
						return false, err
					}
					if !authID.Valid {
						return false, ErrNoAuth
					}
					err = t.setAuthorized(authID)
					if err != nil {
						return false, err
					}
					return true, nil
				}
			}
			return false, ErrInvalidAuth
		case MethodWebCache, MethodWebLogin:
			q := `SELECT authorizationid FROM users WHERE id=$1`
			var authID *sql.NullInt64
			err := t.ctx.DB.Get(authID, q, t.ctx.Msg.User.ID)
			if err != nil {
				return false, err
			}
			if !authID.Valid {
				return false, ErrNoAuth
			}
			q = `
				SELECT id FROM authorizations
				WHERE id=$1
					AND authmethod>=$2
					AND authorizedat<>NULL`
			err = t.ctx.DB.Get(authID, q, authID.Int64, m)
			if err != nil {
				return false, err
			}
			if !authID.Valid {
				return false, ErrNoAuth
			}
			if err = t.setAuthorized(authID); err != nil {
				return false, err
			}
			return true, nil
		default:
			return false, errors.New("invalid auth state")
		}
	}
	return false, nil
}

// RequestPurchase will authenticate the user and then charge a card.
func (t *Task) RequestPurchase(m dt.Method, p *dt.Purchase) (bool, error) {
	t.typ = "Purchase"
	done, err := t.makePurchase(m, p)
	if done {
		t.setState(authStateStart)
	}
	return done, err
}

func (t *Task) askUserForAuth(m dt.Method) (bool, error) {
	log.Println("asking user for auth")
	cards, err := t.ctx.Msg.User.GetCards(t.ctx.DB)
	if err != nil {
		return false, err
	}
	if len(cards) == 0 {
		log.Println("user has no cards")
		t.resp.Sentence = "Great! I'll need you to add your card here, first: https://avabot.co/?/cards/new. Let me know when you're done!"
		return false, nil
	}
	switch m {
	case MethodZip:
		t.resp.Sentence = "To do that, please confirm your billing zip code."
		log.Println("asking user for auth: zip code")
	case MethodWebCache:
		t.resp.Sentence = "To do that, please prove you're logged in: https://www.avabot.com/?/profile"
	case MethodWebLogin:
		if err := t.ctx.Msg.User.DeleteSessions(t.ctx.DB); err != nil {
			return false, err
		}
		t.resp.Sentence = "To do that, please log in to prove it's you: https://www.avabot.com/?/login"
	}
	tx, err := t.ctx.DB.Beginx()
	if err != nil {
		return false, err
	}
	q := `INSERT INTO authorizations (authmethod) VALUES ($1) RETURNING id`
	var aid int
	if err = tx.QueryRowx(q, m).Scan(&aid); err != nil {
		return false, err
	}
	q = `UPDATE users SET authorizationid=$1 WHERE id=$2`
	if _, err = tx.Exec(q, aid, t.ctx.Msg.User.ID); err != nil {
		return false, err
	}
	if err = tx.Commit(); err != nil {
		return false, err
	}
	log.Println("asked user for auth")
	t.setState(authStateConfirm)
	return false, nil
}

func (t *Task) setAuthorized(authID *sql.NullInt64) error {
	tx, err := t.ctx.DB.Beginx()
	if err != nil {
		return err
	}
	q := `UPDATE users SET authorizationid=NULL WHERE id=$1`
	if _, err = tx.Exec(q, t.ctx.Msg.User.ID); err != nil {
		return err
	}
	q = `
		UPDATE authorizations
		SET authorizedat=CURRENT_TIMESTAMP
		WHERE id=$1`
	if _, err = tx.Exec(q, authID.Int64); err != nil {
		return err
	}
	if err = tx.Commit(); err != nil {
		return err
	}
	return nil
}

func (t *Task) makePurchase(m dt.Method, p *dt.Purchase) (bool, error) {
	authenticated, err := t.RequestAuth(m)
	if err != nil {
		return false, err
	}
	if !authenticated {
		return false, nil
	}
	desc := fmt.Sprintf("Purchase for $%.2f", float64(p.Total)/100)
	stripe.Key = os.Getenv("STRIPE_ACCESS_TOKEN")
	chargeParams := &stripe.ChargeParams{
		Amount:   p.Total,
		Currency: "usd",
		Desc:     desc,
		Customer: t.ctx.Msg.User.StripeCustomerID,
	}
	if _, err := charge.New(chargeParams); err != nil {
		return false, err
	}
	if err := t.ctx.SG.SendVendorRequest(p); err != nil {
		return false, err
	}
	if err := t.ctx.SG.SendPurchaseConfirmation(p); err != nil {
		return false, err
	}
	if err := p.UpdateEmailsSent(); err != nil {
		return false, err
	}
	return true, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package watcher

import (
	"errors"
	"expvar"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"syscall"
	"testing"
	"time"
)

// This test requires disk access, and cannot be injected without internal
// knowledge of the fsnotify code. Make the wait deadlines long.
const deadline = 5 * time.Second

func TestLogWatcher(t *testing.T) {
	if testing.Short() {
		// This test is slow due to disk access.
		t.Skip("skipping log watcher test in short mode")
	}

	workdir, err := ioutil.TempDir("", "log_watcher_test")
	if err != nil {
		t.Fatalf("could not create temporary working directory: %s", err)
	}

	defer func() {
		if err := os.RemoveAll(workdir); err != nil {
			t.Fatalf("could not remove temp dir %s: %s:", workdir, err)
		}
	}()

	w, err := NewLogWatcher()
	if err != nil {
		t.Fatalf("couldn't create a watcher: %s\n", err)
	}
	defer func() {
		if err := w.Close(); err != nil {
			t.Fatal(err)
		}
	}()

	if err := w.Add(workdir); err != nil {
		t.Fatal(err)
	}
	f, err := os.Create(filepath.Join(workdir, "logfile"))
	if err != nil {
		t.Fatalf("couldn't make a logfile in temp dir: %s\n", err)
	}
	eventsChannel := w.Events()
	select {
	case e := <-eventsChannel:
		switch e := e.(type) {
		case CreateEvent:
			if e.Pathname != filepath.Join(workdir, "logfile") {
				t.Errorf("create doesn't match")
			}
		default:
			t.Errorf("Wrong event type: %q", e)
		}
	case <-time.After(deadline):
		t.Errorf("didn't receive create message before timeout")
	}
	if n, err := f.WriteString("hi"); err != nil {
		t.Fatal(err)
		if n != 2 {
			t.Fatalf("wrote %d instead of 2", n)
		}
	}
	if err := f.Close(); err != nil {
		t.Fatal(err)
	}
	select {
	case e := <-eventsChannel:
		switch e := e.(type) {
		case UpdateEvent:
			if e.Pathname != filepath.Join(workdir, "logfile") {
				t.Errorf("update doesn't match")
			}
		default:
			t.Errorf("Wrong event type: %q", e)
		}
	case <-time.After(deadline):
		t.Errorf("didn't receive update message before timeout")
	}
	if err := os.Chmod(filepath.Join(workdir, "logfile"), os.ModePerm); err != nil {
		t.Fatal(err)
	}
	select {
	case e := <-eventsChannel:
		switch e := e.(type) {
		case UpdateEvent:
			if e.Pathname != filepath.Join(workdir, "logfile") {
				t.Errorf("update doesnt' match")
			}
		default:

			t.Errorf("wrong event type: %v", e)
		}
	case <-time.After(deadline):
		t.Errorf("didn't receive update message befor timeout")
	}
	if err := os.Remove(filepath.Join(workdir, "logfile")); err != nil {
		t.Fatal(err)
	}
	select {
	case e := <-eventsChannel:
		switch e := e.(type) {
		case DeleteEvent:
			if e.Pathname != filepath.Join(workdir, "logfile") {
				t.Errorf("delete doesn't match")
			}
		default:
			t.Errorf("Wrong event type: %q", e)
		}
	case <-time.After(deadline):
		t.Errorf("didn't receive delete message before timeout")
	}
}

// This test may be OS specific; possibly break it out to a file with build tags.
func TestNewLogWatcherError(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}

	var rLimit syscall.Rlimit
	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
		t.Fatalf("coulnd't get rlimit: %s", err)
	}
	var zero = rLimit
	zero.Cur = 0
	if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &zero); err != nil {
		t.Fatalf("couldn't set rlimit: %s", err)
	}
	_, err := NewLogWatcher()
	if err == nil {
		t.Errorf("didn't fail as expected")
	}
	if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
		t.Fatalf("couldn't reset rlimit: %s", err)
	}
}

func TestLogWatcherAddError(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	u, err := user.Current()
	if err != nil {
		t.Skip(fmt.Sprintf("Couldn't determine current user id: %s", err))
	}
	if u.Uid == "0" {
		t.Skip("Skipping test when run as root")
	}

	workdir, err := ioutil.TempDir("", "log_watcher_test")
	if err != nil {
		t.Fatalf("could not create temporary working directory: %s", err)
	}

	defer func() {
		err := os.RemoveAll(workdir)
		if err != nil {
			t.Fatalf("could not remove temp dir %s: %s:", workdir, err)
		}
	}()

	w, err := NewLogWatcher()
	if err != nil {
		t.Fatalf("couldn't create a watcher: %s\n", err)
	}
	defer func() {
		if err := w.Close(); err != nil {
			t.Fatal(err)
		}
	}()

	filename := filepath.Join(workdir, "test")
	if _, err := os.Create(filename); err != nil {
		t.Fatalf("couldn't create file: %s", err)
	}
	if err := os.Chmod(filename, 0); err != nil {
		t.Fatalf("couldn't chmod file: %s", err)
	}
	err = w.Add(filename)
	if err == nil {
		t.Errorf("didn't fail to add file")
	}
	if err := os.Chmod(filename, 0777); err != nil {
		t.Fatalf("couldn't reset file perms: %s", err)
	}
}

func doOrTimeout(do func() (bool, error), deadline, interval time.Duration) (bool, error) {
	for {
		select {
		case <-time.After(deadline):
			return false, errors.New("timeout")
		case <-time.Tick(interval):
			ok, err := do()
			if err != nil {
				return false, err
			} else if ok {
				return true, nil
			}
		}
	}
}

func TestWatcherErrors(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	orig, err := strconv.ParseInt(expvar.Get("log_watcher_error_count").String(), 10, 64)
	if err != nil {
		t.Fatalf("couldn't convert expvar %q", expvar.Get("log_watcher_error_count").String())
	}
	w, err := NewLogWatcher()
	if err != nil {
		t.Fatalf("couldn't create a watcher")
	}
	w.Errors <- errors.New("Injected error for test")
	if err := w.Close(); err != nil {
		t.Fatalf("watcher close failed: %q", err)
	}
	t.Log("watcher now closed.")
	expected := strconv.FormatInt(orig+1, 10)
	check := func() (bool, error) {
		if expvar.Get("log_watcher_error_count").String() != expected {
			return false, nil
		}
		return true, nil
	}
	ok, err := doOrTimeout(check, 100*time.Millisecond, time.Millisecond)
	if err != nil {
		t.Fatal(err)
	}
	if !ok {
		t.Errorf("log watcher error count didn't increase\n\texpected: %s\n\treceived: %s", expected, expvar.Get("log_watcher_error_count").String())
	}
}
<commit_msg>Reorder code to indicate lack of ordering guarantee.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package watcher

import (
	"errors"
	"expvar"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"syscall"
	"testing"
	"time"
)

// This test requires disk access, and cannot be injected without internal
// knowledge of the fsnotify code. Make the wait deadlines long.
const deadline = 5 * time.Second

func TestLogWatcher(t *testing.T) {
	if testing.Short() {
		// This test is slow due to disk access.
		t.Skip("skipping log watcher test in short mode")
	}

	workdir, err := ioutil.TempDir("", "log_watcher_test")
	if err != nil {
		t.Fatalf("could not create temporary working directory: %s", err)
	}

	defer func() {
		if err := os.RemoveAll(workdir); err != nil {
			t.Fatalf("could not remove temp dir %s: %s:", workdir, err)
		}
	}()

	w, err := NewLogWatcher()
	if err != nil {
		t.Fatalf("couldn't create a watcher: %s\n", err)
	}
	defer func() {
		if err := w.Close(); err != nil {
			t.Fatal(err)
		}
	}()

	if err := w.Add(workdir); err != nil {
		t.Fatal(err)
	}
	f, err := os.Create(filepath.Join(workdir, "logfile"))
	if err != nil {
		t.Fatalf("couldn't make a logfile in temp dir: %s\n", err)
	}
	eventsChannel := w.Events()
	select {
	case e := <-eventsChannel:
		switch e := e.(type) {
		case CreateEvent:
			if e.Pathname != filepath.Join(workdir, "logfile") {
				t.Errorf("create doesn't match")
			}
		default:
			t.Errorf("Wrong event type: %q", e)
		}
	case <-time.After(deadline):
		t.Errorf("didn't receive create message before timeout")
	}
	if n, err := f.WriteString("hi"); err != nil {
		t.Fatal(err)
		if n != 2 {
			t.Fatalf("wrote %d instead of 2", n)
		}
	}
	if err := f.Close(); err != nil {
		t.Fatal(err)
	}
	select {
	case e := <-eventsChannel:
		switch e := e.(type) {
		case UpdateEvent:
			if e.Pathname != filepath.Join(workdir, "logfile") {
				t.Errorf("update doesn't match")
			}
		default:
			t.Errorf("Wrong event type: %q", e)
		}
	case <-time.After(deadline):
		t.Errorf("didn't receive update message before timeout")
	}
	if err := os.Chmod(filepath.Join(workdir, "logfile"), os.ModePerm); err != nil {
		t.Fatal(err)
	}
	select {
	case e := <-eventsChannel:
		switch e := e.(type) {
		case UpdateEvent:
			if e.Pathname != filepath.Join(workdir, "logfile") {
				t.Errorf("update doesnt' match")
			}
		default:

			t.Errorf("wrong event type: %v", e)
		}
	case <-time.After(deadline):
		t.Errorf("didn't receive update message befor timeout")
	}
	if err := os.Remove(filepath.Join(workdir, "logfile")); err != nil {
		t.Fatal(err)
	}
	select {
	case e := <-eventsChannel:
		switch e := e.(type) {
		case DeleteEvent:
			if e.Pathname != filepath.Join(workdir, "logfile") {
				t.Errorf("delete doesn't match")
			}
		default:
			t.Errorf("Wrong event type: %q", e)
		}
	case <-time.After(deadline):
		t.Errorf("didn't receive delete message before timeout")
	}
}

// This test may be OS specific; possibly break it out to a file with build tags.
func TestNewLogWatcherError(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}

	var rLimit syscall.Rlimit
	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
		t.Fatalf("coulnd't get rlimit: %s", err)
	}
	var zero = rLimit
	zero.Cur = 0
	if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &zero); err != nil {
		t.Fatalf("couldn't set rlimit: %s", err)
	}
	_, err := NewLogWatcher()
	if err == nil {
		t.Errorf("didn't fail as expected")
	}
	if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
		t.Fatalf("couldn't reset rlimit: %s", err)
	}
}

func TestLogWatcherAddError(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	u, err := user.Current()
	if err != nil {
		t.Skip(fmt.Sprintf("Couldn't determine current user id: %s", err))
	}
	if u.Uid == "0" {
		t.Skip("Skipping test when run as root")
	}

	workdir, err := ioutil.TempDir("", "log_watcher_test")
	if err != nil {
		t.Fatalf("could not create temporary working directory: %s", err)
	}

	defer func() {
		err := os.RemoveAll(workdir)
		if err != nil {
			t.Fatalf("could not remove temp dir %s: %s:", workdir, err)
		}
	}()

	w, err := NewLogWatcher()
	if err != nil {
		t.Fatalf("couldn't create a watcher: %s\n", err)
	}
	defer func() {
		if err := w.Close(); err != nil {
			t.Fatal(err)
		}
	}()

	filename := filepath.Join(workdir, "test")
	if _, err := os.Create(filename); err != nil {
		t.Fatalf("couldn't create file: %s", err)
	}
	if err := os.Chmod(filename, 0); err != nil {
		t.Fatalf("couldn't chmod file: %s", err)
	}
	err = w.Add(filename)
	if err == nil {
		t.Errorf("didn't fail to add file")
	}
	if err := os.Chmod(filename, 0777); err != nil {
		t.Fatalf("couldn't reset file perms: %s", err)
	}
}

func doOrTimeout(do func() (bool, error), deadline, interval time.Duration) (bool, error) {
	for {
		select {
		case <-time.After(deadline):
			return false, errors.New("timeout")
		case <-time.Tick(interval):
			ok, err := do()
			if err != nil {
				return false, err
			} else if ok {
				return true, nil
			}
		}
	}
}

func TestWatcherErrors(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	orig, err := strconv.ParseInt(expvar.Get("log_watcher_error_count").String(), 10, 64)
	if err != nil {
		t.Fatalf("couldn't convert expvar %q", expvar.Get("log_watcher_error_count").String())
	}
	w, err := NewLogWatcher()
	if err != nil {
		t.Fatalf("couldn't create a watcher")
	}
	w.Errors <- errors.New("Injected error for test")
	expected := strconv.FormatInt(orig+1, 10)
	check := func() (bool, error) {
		if expvar.Get("log_watcher_error_count").String() != expected {
			return false, nil
		}
		return true, nil
	}
	// Wait for the counter to be increased.  We can't strictly order this
	// becase the fsnotify code has nothing to hook on.
	ok, err := doOrTimeout(check, 100*time.Millisecond, time.Millisecond)
	if err != nil {
		t.Fatal(err)
	}
	if !ok {
		t.Errorf("log watcher error count didn't increase\n\texpected: %s\n\treceived: %s", expected, expvar.Get("log_watcher_error_count").String())
	}

	// Close only closes the channels, it does not guarantee the channel reader has finished working.
	if err := w.Close(); err != nil {
		t.Fatalf("watcher close failed: %q", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/boivie/lovebeat-go/alert"
	"github.com/boivie/lovebeat-go/backend"
	"github.com/boivie/lovebeat-go/config"
	"github.com/boivie/lovebeat-go/dashboard"
	"github.com/boivie/lovebeat-go/httpapi"
	"github.com/boivie/lovebeat-go/service"
	"github.com/boivie/lovebeat-go/tcpapi"
	"github.com/boivie/lovebeat-go/udpapi"
	"github.com/gorilla/mux"
	"github.com/op/go-logging"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
)

var log = logging.MustGetLogger("lovebeat")

const (
	VERSION                 = "0.1.0"
	MAX_UNPROCESSED_PACKETS = 1000
)

var (
	udpAddr     = flag.String("udp", ":8127", "UDP service address")
	tcpAddr     = flag.String("tcp", ":8127", "TCP service address")
	debug       = flag.Bool("debug", false, "Enable debug printouts")
	showVersion = flag.Bool("version", false, "Print version string")
	workDir     = flag.String("workdir", "work", "Working directory")
	cfgFile     = flag.String("config", "/etc/lovebeat.cfg", "Configuration file")
)

var (
	signalchan = make(chan os.Signal, 1)
)

func signalHandler(be backend.Backend) {
	for {
		select {
		case sig := <-signalchan:
			fmt.Printf("!! Caught signal %d... shutting down\n", sig)
			be.Sync()
			return
		}
	}
}

func httpServer(port int16, svcs *service.Services) {
	rtr := mux.NewRouter()
	httpapi.Register(rtr, svcs.GetClient())
	dashboard.Register(rtr, svcs.GetClient())
	http.Handle("/", rtr)
	log.Info("HTTP server running on port %d\n", port)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

func getHostname() string {
	var hostname, err = os.Hostname()
	if err != nil {
		return fmt.Sprintf("unknown_%d", os.Getpid())
	}
	return strings.Split(hostname, ".")[0]
}

func main() {
	flag.Parse()

	var format = logging.MustStringFormatter("%{level} %{message}")
	logging.SetFormatter(format)
	if *debug {
		logging.SetLevel(logging.DEBUG, "lovebeat")
	} else {
		logging.SetLevel(logging.INFO, "lovebeat")
	}
	log.Debug("Debug logs enabled")

	if *showVersion {
		fmt.Printf("lovebeats v%s (built w/%s)\n", VERSION, runtime.Version())
		return
	}

	var cfg = config.ReadConfig(*cfgFile)

	var hostname = getHostname()
	log.Info("Lovebeat v%s started as host %s, PID %d", VERSION, hostname, os.Getpid())

	var be = backend.NewFileBackend(*workDir)
	var alerters = []alert.Alerter{alert.NewMailAlerter(&cfg.Mail),
		alert.NewWebhooksAlerter()}
	var svcs = service.NewServices(be, alerters)

	signal.Notify(signalchan, syscall.SIGTERM)
	signal.Notify(signalchan, os.Interrupt)

	go svcs.Monitor()
	go httpServer(8080, svcs)
	go udpapi.Listener(*udpAddr, svcs.GetClient())
	go tcpapi.Listener(*tcpAddr, svcs.GetClient())

	// Ensure that the 'all' view exists
	svcs.GetClient().CreateOrUpdateView("all", "", "", "")

	log.Info("Ready to handle incoming connections")

	signalHandler(be)
}
<commit_msg>Bumping version to 0.8<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/boivie/lovebeat-go/alert"
	"github.com/boivie/lovebeat-go/backend"
	"github.com/boivie/lovebeat-go/config"
	"github.com/boivie/lovebeat-go/dashboard"
	"github.com/boivie/lovebeat-go/httpapi"
	"github.com/boivie/lovebeat-go/service"
	"github.com/boivie/lovebeat-go/tcpapi"
	"github.com/boivie/lovebeat-go/udpapi"
	"github.com/gorilla/mux"
	"github.com/op/go-logging"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
)

var log = logging.MustGetLogger("lovebeat")

const (
	VERSION = "0.8.0"
)

var (
	udpAddr     = flag.String("udp", ":8127", "UDP service address")
	tcpAddr     = flag.String("tcp", ":8127", "TCP service address")
	debug       = flag.Bool("debug", false, "Enable debug printouts")
	showVersion = flag.Bool("version", false, "Print version string")
	workDir     = flag.String("workdir", "work", "Working directory")
	cfgFile     = flag.String("config", "/etc/lovebeat.cfg", "Configuration file")
)

var (
	signalchan = make(chan os.Signal, 1)
)

func signalHandler(be backend.Backend) {
	for {
		select {
		case sig := <-signalchan:
			fmt.Printf("!! Caught signal %d... shutting down\n", sig)
			be.Sync()
			return
		}
	}
}

func httpServer(port int16, svcs *service.Services) {
	rtr := mux.NewRouter()
	httpapi.Register(rtr, svcs.GetClient())
	dashboard.Register(rtr, svcs.GetClient())
	http.Handle("/", rtr)
	log.Info("HTTP server running on port %d\n", port)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

func getHostname() string {
	var hostname, err = os.Hostname()
	if err != nil {
		return fmt.Sprintf("unknown_%d", os.Getpid())
	}
	return strings.Split(hostname, ".")[0]
}

func main() {
	flag.Parse()

	var format = logging.MustStringFormatter("%{level} %{message}")
	logging.SetFormatter(format)
	if *debug {
		logging.SetLevel(logging.DEBUG, "lovebeat")
	} else {
		logging.SetLevel(logging.INFO, "lovebeat")
	}
	log.Debug("Debug logs enabled")

	if *showVersion {
		fmt.Printf("lovebeats v%s (built w/%s)\n", VERSION, runtime.Version())
		return
	}

	var cfg = config.ReadConfig(*cfgFile)

	var hostname = getHostname()
	log.Info("Lovebeat v%s started as host %s, PID %d", VERSION, hostname, os.Getpid())

	var be = backend.NewFileBackend(*workDir)
	var alerters = []alert.Alerter{alert.NewMailAlerter(&cfg.Mail),
		alert.NewWebhooksAlerter()}
	var svcs = service.NewServices(be, alerters)

	signal.Notify(signalchan, syscall.SIGTERM)
	signal.Notify(signalchan, os.Interrupt)

	go svcs.Monitor()
	go httpServer(8080, svcs)
	go udpapi.Listener(*udpAddr, svcs.GetClient())
	go tcpapi.Listener(*tcpAddr, svcs.GetClient())

	// Ensure that the 'all' view exists
	svcs.GetClient().CreateOrUpdateView("all", "", "", "")

	log.Info("Ready to handle incoming connections")

	signalHandler(be)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"net"
	"os"
	"os/signal"
	"regexp"
	"runtime"
	"syscall"
	"strconv"
	"time"
	"io"
	"net/http"
	"github.com/gorilla/mux"
	"github.com/op/go-logging"
	"html/template"
	"github.com/boivie/lovebeat-go/service"
)

var log = logging.MustGetLogger("lovebeat")

const (
	VERSION                 = "0.1.0"
	MAX_UNPROCESSED_PACKETS = 1000
	MAX_UDP_PACKET_SIZE     = 512
)

var (
	serviceAddress   = flag.String("address", ":8127", "UDP service address")
	expiryInterval   = flag.Int64("expiry-interval", 1, "Expiry interval (seconds)")
	debug            = flag.Bool("debug", false, "print statistics sent to graphite")
	showVersion      = flag.Bool("version", false, "print version string")
)

const (
	ACTION_SET_WARN = "set-warn"
	ACTION_SET_ERR = "set-err"
	ACTION_BEAT = "beat"
)

type Cmd struct {
	Action   string
	Service  string
	Value    int
}

var (
	ServiceCmdChan    = make(chan *Cmd, MAX_UNPROCESSED_PACKETS)
	ViewCmdChan         = make(chan *service.ViewCmd, MAX_UNPROCESSED_PACKETS)
	signalchan chan os.Signal
)

func now() int64 { return time.Now().Unix() }

func monitor() {
	period := time.Duration(*expiryInterval) * time.Second
	ticker := time.NewTicker(period)
	for {
		select {
		case sig := <-signalchan:
			fmt.Printf("!! Caught signal %d... shutting down\n", sig)
			return
		case <-ticker.C:
			var ts = now()
			for _, s := range service.GetServices() {
				if (s.State == service.STATE_PAUSED || s.State == s.StateAt(ts)) {
					continue;
				}
				var ref = *s
				s.State = s.StateAt(ts)
				s.Save(&ref, ts)
				s.UpdateViews(ViewCmdChan)
			}
		case c := <-ViewCmdChan:
			var ts = now()
			switch c.Action {
			case service.ACTION_REFRESH_VIEW:
				log.Debug("Refresh view %s", c.View)
				var view = service.GetView(c.View)
				var ref = *view
				view.Refresh(ts)
				view.Save(&ref, ts);
			}
		case c := <-ServiceCmdChan:
			var ts = now()
			var s = service.GetService(c.Service)
			var ref = *s
			switch c.Action {
			case ACTION_SET_WARN:
				s.WarningTimeout = int64(c.Value)
			case ACTION_SET_ERR:
				s.ErrorTimeout = int64(c.Value)
			case ACTION_BEAT:
				if c.Value > 1 {
					s.ErrorTimeout = int64(c.Value)
				}
				s.LastBeat = ts
				var diff = ts - ref.LastBeat
				s.Log("%d|beat|%d", ts, diff)
				log.Debug("Beat from %s", s.Name)
			}
			if s.State != s.StateAt(ts) {
				s.State = s.StateAt(ts)
			}
			s.Save(&ref, ts)
			s.UpdateViews(ViewCmdChan)
		}
	}
}

var packetRegexp = regexp.MustCompile("^([^:]+)\\.(beat|warn|err):(-?[0-9]+)\\|(g|c|ms)(\\|@([0-9\\.]+))?\n?$")

func parseMessage(data []byte) []*Cmd {
	var output []*Cmd
	for _, line := range bytes.Split(data, []byte("\n")) {
		if len(line) == 0 {
			continue
		}

		item := packetRegexp.FindSubmatch(line)
		if len(item) == 0 {
			continue
		}

		var value int
		modifier := string(item[4])
		switch modifier {
		case "c":
			var vali, err = strconv.ParseInt(string(item[3]), 10, 64)
			if err != nil {
				log.Error("failed to ParseInt %s - %s", item[3], err)
				continue
			}
			value = int(vali)
		default:
			var valu, err = strconv.ParseUint(string(item[3]), 10, 64)
			if err != nil {
				log.Error("failed to ParseUint %s - %s", item[3], err)
				continue
			}
			value = int(valu)
		}
		var action string
		switch string(item[2]) {
		case "warn":
			action = ACTION_SET_WARN
		case "err":
			action = ACTION_SET_ERR
		case "beat":
			action = ACTION_BEAT
		}

		packet := &Cmd{
			Action: action,
			Service: string(item[1]),
			Value:    value,
		}
		output = append(output, packet)
	}
	return output
}

func udpListener() {
	address, _ := net.ResolveUDPAddr("udp", *serviceAddress)
	log.Info("UDP listener running on %s", address)
	listener, err := net.ListenUDP("udp", address)
	if err != nil {
		log.Fatalf("ListenUDP - %s", err)
	}
	defer listener.Close()

	message := make([]byte, MAX_UDP_PACKET_SIZE)
	for {
		n, remaddr, err := listener.ReadFromUDP(message)
		if err != nil {
			log.Error("reading UDP packet from %+v - %s", remaddr, err)
			continue
		}

		for _, p := range parseMessage(message[:n]) {
			ServiceCmdChan <- p
		}
	}
}

func tcpHandle(c *net.TCPConn) {
	defer c.Close()
	r := bufio.NewReaderSize(c, 4096)
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		var buf = scanner.Bytes()
		for _, p := range parseMessage(buf) {
			ServiceCmdChan <- p
		}
	}
}

func tcpListener() {
	address, _ := net.ResolveTCPAddr("tcp", *serviceAddress)
	log.Info("TCP listener running on %s", address)
	listener, err := net.ListenTCP("tcp", address)
	if err != nil {
		log.Fatalf("ListenTCP - %s", err)
	}
	for {
		c, err := listener.AcceptTCP()
		if nil != err {
			log.Error("Error: %s", err)
			break
		}
		go tcpHandle(c)
	}
}

func DashboardState(in string) string {
	return map[string]string {
		service.STATE_PAUSED:  "-PAUSED--",
		service.STATE_WARNING: "  WARN   ",
		service.STATE_ERROR:   "      ERR",
		service.STATE_OK:      "OK       ",
	}[in]
}

func DashboardHandler(w http.ResponseWriter, r *http.Request) {
	var services = service.GetServices()

	tc := make(map[string]interface{})
	tc["services"] = services

	templates := template.Must(template.ParseFiles("templates/base.html", "templates/index.html"))
	if err := templates.Execute(w, tc); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func StatusHandler(c http.ResponseWriter, req *http.Request) {
	var buffer bytes.Buffer
	var services = service.GetServices()
	var errors, warnings, ok = 0, 0, 0
	for _, s := range services {
		if s.State == service.STATE_WARNING {
			warnings++
		} else if s.State == service.STATE_ERROR {
			errors++
		} else {
			ok++
		}
	}
	buffer.WriteString(fmt.Sprintf("num_ok %d\nnum_warning %d\nnum_error %d\n",
		ok, warnings, errors))
	buffer.WriteString(fmt.Sprintf("has_warning %t\nhas_error %t\ngood %t\n",
		warnings > 0, errors > 0, warnings == 0 && errors == 0))
        body := buffer.String()
        c.Header().Add("Content-Type", "text/plain")
        c.Header().Add("Content-Length", strconv.Itoa(len(body)))
        io.WriteString(c, body)
}

func TriggerHandler(c http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	name := params["name"]

	var err = r.ParseForm()
	if err != nil {
		log.Error("error parsing form ", err)
		return
	}

	var errtmo, warntmo = r.FormValue("err-tmo"), r.FormValue("warn-tmo")

	ServiceCmdChan <- &Cmd{
		Action:  ACTION_BEAT,
		Service: name,
		Value:   1,
	}

	if val, err := strconv.Atoi(errtmo); err == nil {
		ServiceCmdChan <- &Cmd{
			Action:  ACTION_SET_ERR,
			Service: name,
			Value:   val,
		}
	}

	if val, err := strconv.Atoi(warntmo); err == nil {
		ServiceCmdChan <- &Cmd{
			Action:  ACTION_SET_WARN,
			Service: name,
			Value:   val,
		}
	}

        c.Header().Add("Content-Type", "text/plain")
        c.Header().Add("Content-Length", "3")
        io.WriteString(c, "ok\n")
}

func CreateViewHandler(c http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	view_name := params["name"]
	var expr = r.FormValue("regexp")
	if expr == "" {
		log.Error("No regexp provided")
		return
	}

	service.CreateView(view_name, expr, ViewCmdChan, now())
}

func httpServer(port int16) {
	rtr := mux.NewRouter()
	rtr.HandleFunc("/", DashboardHandler).Methods("GET")
	rtr.HandleFunc("/status", StatusHandler).Methods("GET")
	rtr.HandleFunc("/trigger/{name:[a-z0-9.]+}", TriggerHandler).Methods("POST")
	rtr.HandleFunc("/view/{name:[a-z0-9.]+}", CreateViewHandler).Methods("POST")
	http.Handle("/", rtr)
	log.Info("HTTP server running on port %d\n", port)
        http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

func main() {
	flag.Parse()
	if *showVersion {
		fmt.Printf("lovebeats v%s (built w/%s)\n", VERSION, runtime.Version())
		return
	}

	service.Startup()

	signalchan = make(chan os.Signal, 1)
	signal.Notify(signalchan, syscall.SIGTERM)

	var format = logging.MustStringFormatter("%{level} %{message}")
	logging.SetFormatter(format)
	if *debug {
		logging.SetLevel(logging.DEBUG, "lovebeat")
	} else {
		logging.SetLevel(logging.INFO, "lovebeat")
	}
	log.Debug("Debug logs enabled")

	go httpServer(8080)
	go udpListener()
	go tcpListener()
	monitor()
}
<commit_msg>Deprecating setting error timeout using beat value<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"net"
	"os"
	"os/signal"
	"regexp"
	"runtime"
	"syscall"
	"strconv"
	"time"
	"io"
	"net/http"
	"github.com/gorilla/mux"
	"github.com/op/go-logging"
	"html/template"
	"github.com/boivie/lovebeat-go/service"
)

var log = logging.MustGetLogger("lovebeat")

const (
	VERSION                 = "0.1.0"
	MAX_UNPROCESSED_PACKETS = 1000
	MAX_UDP_PACKET_SIZE     = 512
)

var (
	serviceAddress   = flag.String("address", ":8127", "UDP service address")
	expiryInterval   = flag.Int64("expiry-interval", 1, "Expiry interval (seconds)")
	debug            = flag.Bool("debug", false, "print statistics sent to graphite")
	showVersion      = flag.Bool("version", false, "print version string")
)

const (
	ACTION_SET_WARN = "set-warn"
	ACTION_SET_ERR = "set-err"
	ACTION_BEAT = "beat"
)

type Cmd struct {
	Action   string
	Service  string
	Value    int
}

var (
	ServiceCmdChan    = make(chan *Cmd, MAX_UNPROCESSED_PACKETS)
	ViewCmdChan         = make(chan *service.ViewCmd, MAX_UNPROCESSED_PACKETS)
	signalchan chan os.Signal
)

func now() int64 { return time.Now().Unix() }

func monitor() {
	period := time.Duration(*expiryInterval) * time.Second
	ticker := time.NewTicker(period)
	for {
		select {
		case sig := <-signalchan:
			fmt.Printf("!! Caught signal %d... shutting down\n", sig)
			return
		case <-ticker.C:
			var ts = now()
			for _, s := range service.GetServices() {
				if (s.State == service.STATE_PAUSED || s.State == s.StateAt(ts)) {
					continue;
				}
				var ref = *s
				s.State = s.StateAt(ts)
				s.Save(&ref, ts)
				s.UpdateViews(ViewCmdChan)
			}
		case c := <-ViewCmdChan:
			var ts = now()
			switch c.Action {
			case service.ACTION_REFRESH_VIEW:
				log.Debug("Refresh view %s", c.View)
				var view = service.GetView(c.View)
				var ref = *view
				view.Refresh(ts)
				view.Save(&ref, ts);
			}
		case c := <-ServiceCmdChan:
			var ts = now()
			var s = service.GetService(c.Service)
			var ref = *s
			switch c.Action {
			case ACTION_SET_WARN:
				s.WarningTimeout = int64(c.Value)
			case ACTION_SET_ERR:
				s.ErrorTimeout = int64(c.Value)
			case ACTION_BEAT:
				if c.Value > 0 {
					s.LastBeat = ts
					var diff = ts - ref.LastBeat
					s.Log("%d|beat|%d", ts, diff)
					log.Debug("Beat from %s", s.Name)
				}
			}
			if s.State != s.StateAt(ts) {
				s.State = s.StateAt(ts)
			}
			s.Save(&ref, ts)
			s.UpdateViews(ViewCmdChan)
		}
	}
}

var packetRegexp = regexp.MustCompile("^([^:]+)\\.(beat|warn|err):(-?[0-9]+)\\|(g|c|ms)(\\|@([0-9\\.]+))?\n?$")

func parseMessage(data []byte) []*Cmd {
	var output []*Cmd
	for _, line := range bytes.Split(data, []byte("\n")) {
		if len(line) == 0 {
			continue
		}

		item := packetRegexp.FindSubmatch(line)
		if len(item) == 0 {
			continue
		}

		var value int
		modifier := string(item[4])
		switch modifier {
		case "c":
			var vali, err = strconv.ParseInt(string(item[3]), 10, 64)
			if err != nil {
				log.Error("failed to ParseInt %s - %s", item[3], err)
				continue
			}
			value = int(vali)
		default:
			var valu, err = strconv.ParseUint(string(item[3]), 10, 64)
			if err != nil {
				log.Error("failed to ParseUint %s - %s", item[3], err)
				continue
			}
			value = int(valu)
		}
		var action string
		switch string(item[2]) {
		case "warn":
			action = ACTION_SET_WARN
		case "err":
			action = ACTION_SET_ERR
		case "beat":
			action = ACTION_BEAT
		}

		packet := &Cmd{
			Action: action,
			Service: string(item[1]),
			Value:    value,
		}
		output = append(output, packet)
	}
	return output
}

func udpListener() {
	address, _ := net.ResolveUDPAddr("udp", *serviceAddress)
	log.Info("UDP listener running on %s", address)
	listener, err := net.ListenUDP("udp", address)
	if err != nil {
		log.Fatalf("ListenUDP - %s", err)
	}
	defer listener.Close()

	message := make([]byte, MAX_UDP_PACKET_SIZE)
	for {
		n, remaddr, err := listener.ReadFromUDP(message)
		if err != nil {
			log.Error("reading UDP packet from %+v - %s", remaddr, err)
			continue
		}

		for _, p := range parseMessage(message[:n]) {
			ServiceCmdChan <- p
		}
	}
}

func tcpHandle(c *net.TCPConn) {
	defer c.Close()
	r := bufio.NewReaderSize(c, 4096)
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		var buf = scanner.Bytes()
		for _, p := range parseMessage(buf) {
			ServiceCmdChan <- p
		}
	}
}

func tcpListener() {
	address, _ := net.ResolveTCPAddr("tcp", *serviceAddress)
	log.Info("TCP listener running on %s", address)
	listener, err := net.ListenTCP("tcp", address)
	if err != nil {
		log.Fatalf("ListenTCP - %s", err)
	}
	for {
		c, err := listener.AcceptTCP()
		if nil != err {
			log.Error("Error: %s", err)
			break
		}
		go tcpHandle(c)
	}
}

func DashboardState(in string) string {
	return map[string]string {
		service.STATE_PAUSED:  "-PAUSED--",
		service.STATE_WARNING: "  WARN   ",
		service.STATE_ERROR:   "      ERR",
		service.STATE_OK:      "OK       ",
	}[in]
}

func DashboardHandler(w http.ResponseWriter, r *http.Request) {
	var services = service.GetServices()

	tc := make(map[string]interface{})
	tc["services"] = services

	templates := template.Must(template.ParseFiles("templates/base.html", "templates/index.html"))
	if err := templates.Execute(w, tc); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func StatusHandler(c http.ResponseWriter, req *http.Request) {
	var buffer bytes.Buffer
	var services = service.GetServices()
	var errors, warnings, ok = 0, 0, 0
	for _, s := range services {
		if s.State == service.STATE_WARNING {
			warnings++
		} else if s.State == service.STATE_ERROR {
			errors++
		} else {
			ok++
		}
	}
	buffer.WriteString(fmt.Sprintf("num_ok %d\nnum_warning %d\nnum_error %d\n",
		ok, warnings, errors))
	buffer.WriteString(fmt.Sprintf("has_warning %t\nhas_error %t\ngood %t\n",
		warnings > 0, errors > 0, warnings == 0 && errors == 0))
        body := buffer.String()
        c.Header().Add("Content-Type", "text/plain")
        c.Header().Add("Content-Length", strconv.Itoa(len(body)))
        io.WriteString(c, body)
}

func TriggerHandler(c http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	name := params["name"]

	var err = r.ParseForm()
	if err != nil {
		log.Error("error parsing form ", err)
		return
	}

	var errtmo, warntmo = r.FormValue("err-tmo"), r.FormValue("warn-tmo")

	ServiceCmdChan <- &Cmd{
		Action:  ACTION_BEAT,
		Service: name,
		Value:   1,
	}

	if val, err := strconv.Atoi(errtmo); err == nil {
		ServiceCmdChan <- &Cmd{
			Action:  ACTION_SET_ERR,
			Service: name,
			Value:   val,
		}
	}

	if val, err := strconv.Atoi(warntmo); err == nil {
		ServiceCmdChan <- &Cmd{
			Action:  ACTION_SET_WARN,
			Service: name,
			Value:   val,
		}
	}

        c.Header().Add("Content-Type", "text/plain")
        c.Header().Add("Content-Length", "3")
        io.WriteString(c, "ok\n")
}

func CreateViewHandler(c http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	view_name := params["name"]
	var expr = r.FormValue("regexp")
	if expr == "" {
		log.Error("No regexp provided")
		return
	}

	service.CreateView(view_name, expr, ViewCmdChan, now())
}

func httpServer(port int16) {
	rtr := mux.NewRouter()
	rtr.HandleFunc("/", DashboardHandler).Methods("GET")
	rtr.HandleFunc("/status", StatusHandler).Methods("GET")
	rtr.HandleFunc("/trigger/{name:[a-z0-9.]+}", TriggerHandler).Methods("POST")
	rtr.HandleFunc("/view/{name:[a-z0-9.]+}", CreateViewHandler).Methods("POST")
	http.Handle("/", rtr)
	log.Info("HTTP server running on port %d\n", port)
        http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

func main() {
	flag.Parse()
	if *showVersion {
		fmt.Printf("lovebeats v%s (built w/%s)\n", VERSION, runtime.Version())
		return
	}

	service.Startup()

	signalchan = make(chan os.Signal, 1)
	signal.Notify(signalchan, syscall.SIGTERM)

	var format = logging.MustStringFormatter("%{level} %{message}")
	logging.SetFormatter(format)
	if *debug {
		logging.SetLevel(logging.DEBUG, "lovebeat")
	} else {
		logging.SetLevel(logging.INFO, "lovebeat")
	}
	log.Debug("Debug logs enabled")

	go httpServer(8080)
	go udpListener()
	go tcpListener()
	monitor()
}
<|endoftext|>


<commit_before>package sort

import (
	"sort"
	"testing"
	"math/rand"
)

func sliceEq(xs, ys []float64) bool {
	if len(xs) != len(ys) {
		return false
	}
	for i := range xs {
		if xs[i] != ys[i] {
			return false
		}
	}

	return true
}

func randSlice(n int) []float64 {
	xs := make([]float64, n)
	for i := range xs {
		xs[i] = rand.Float64()
	}
	return xs
}

func TestReverse(t *testing.T) {
	if !sliceEq([]float64{1, 2, 3, 4, 5}, Reverse([]float64{5, 4, 3, 2, 1})) ||
		!sliceEq([]float64{2, 3, 4, 5}, Reverse([]float64{5, 4, 3, 2})) {
		t.Errorf("Welp, I hope you're proud of yourself.")
	}
}

func BenchmarkReverse10(b *testing.B) {
	xs := make([]float64, 10)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Reverse(xs)
	}
}

func BenchmarkReverse1000(b *testing.B) {
	xs := make([]float64, 1000)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Reverse(xs)
	}
}

func BenchmarkReverse1000000(b *testing.B) {
	xs := make([]float64, 1000000)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Reverse(xs)
	}
}

func BenchmarkShell10(b *testing.B) {
	xs := randSlice(10)
	buf := make([]float64, 10)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Shell(buf)
	}
}

func BenchmarkShell100(b *testing.B) {
	xs := randSlice(100)
	buf := make([]float64, 100)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Shell(buf)
	}
}

func BenchmarkShell1000(b *testing.B) {
	xs := randSlice(1000)
	buf := make([]float64, 1000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Shell(buf)
	}
}

func BenchmarkShell10000(b *testing.B) {
	xs := randSlice(10000)
	buf := make([]float64, 10000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Shell(buf)
	}
}

func BenchmarkQuick10(b *testing.B) {
	xs := randSlice(10)
	buf := make([]float64, 10)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Quick(buf)
	}
}

func BenchmarkQuick100(b *testing.B) {
	xs := randSlice(100)
	buf := make([]float64, 100)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Quick(buf)
	}
}

func BenchmarkQuick1000(b *testing.B) {
	xs := randSlice(1000)
	buf := make([]float64, 1000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Quick(buf)
	}
}

func BenchmarkQuick10000(b *testing.B) {
	xs := randSlice(10000)
	buf := make([]float64, 10000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Quick(buf)
	}
}

func BenchmarkGo10(b *testing.B) {
	xs := randSlice(10)
	buf := make([]float64, 10)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		sort.Float64s(buf)
	}
}

func BenchmarkGo100(b *testing.B) {
	xs := randSlice(100)
	buf := make([]float64, 100)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		sort.Float64s(buf)
	}
}

func BenchmarkGo1000(b *testing.B) {
	xs := randSlice(1000)
	buf := make([]float64, 1000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		sort.Float64s(buf)
	}
}

func BenchmarkGo10000(b *testing.B) {
	xs := randSlice(10000)
	buf := make([]float64, 10000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		sort.Float64s(buf)
	}
}

func TestShell(t *testing.T) {
	for i := 0; i < 10; i++ {
		xs := randSlice(1000)
		Shell(xs)
		if !sort.Float64sAreSorted(xs) {
			t.Errorf("Failed to sort.")
		}
	}
}

func TestQuick(t *testing.T) {
	for i := 0; i < 10; i++ {
		xs := randSlice(1000)
		Quick(xs)
		if !sort.Float64sAreSorted(xs) {
			t.Errorf("Failed to sort.")
		}
	}
}
<commit_msg>More tests.<commit_after>package sort

import (
	"sort"
	"testing"
	"math/rand"
)

func sliceEq(xs, ys []float64) bool {
	if len(xs) != len(ys) {
		return false
	}
	for i := range xs {
		if xs[i] != ys[i] {
			return false
		}
	}

	return true
}

func randSlice(n int) []float64 {
	xs := make([]float64, n)
	for i := range xs {
		xs[i] = rand.Float64()
	}
	return xs
}

func TestReverse(t *testing.T) {
	if !sliceEq([]float64{1, 2, 3, 4, 5}, Reverse([]float64{5, 4, 3, 2, 1})) ||
		!sliceEq([]float64{2, 3, 4, 5}, Reverse([]float64{5, 4, 3, 2})) {
		t.Errorf("Welp, I hope you're proud of yourself.")
	}
}

func BenchmarkReverse10(b *testing.B) {
	xs := make([]float64, 10)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Reverse(xs)
	}
}

func BenchmarkReverse1000(b *testing.B) {
	xs := make([]float64, 1000)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Reverse(xs)
	}
}

func BenchmarkReverse1000000(b *testing.B) {
	xs := make([]float64, 1000000)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Reverse(xs)
	}
}

func BenchmarkShell10(b *testing.B) {
	xs := randSlice(10)
	buf := make([]float64, 10)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Shell(buf)
	}
}

func BenchmarkShell100(b *testing.B) {
	xs := randSlice(100)
	buf := make([]float64, 100)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Shell(buf)
	}
}

func BenchmarkShell1000(b *testing.B) {
	xs := randSlice(1000)
	buf := make([]float64, 1000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Shell(buf)
	}
}

func BenchmarkShell10000(b *testing.B) {
	xs := randSlice(10000)
	buf := make([]float64, 10000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Shell(buf)
	}
}

func BenchmarkQuick10(b *testing.B) {
	xs := randSlice(10)
	buf := make([]float64, 10)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Quick(buf)
	}
}

func BenchmarkQuick100(b *testing.B) {
	xs := randSlice(100)
	buf := make([]float64, 100)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Quick(buf)
	}
}

func BenchmarkQuick1000(b *testing.B) {
	xs := randSlice(1000)
	buf := make([]float64, 1000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Quick(buf)
	}
}

func BenchmarkQuick10000(b *testing.B) {
	xs := randSlice(10000)
	buf := make([]float64, 10000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		Quick(buf)
	}
}

func BenchmarkGo10(b *testing.B) {
	xs := randSlice(10)
	buf := make([]float64, 10)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		sort.Float64s(buf)
	}
}

func BenchmarkGo100(b *testing.B) {
	xs := randSlice(100)
	buf := make([]float64, 100)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		sort.Float64s(buf)
	}
}

func BenchmarkGo1000(b *testing.B) {
	xs := randSlice(1000)
	buf := make([]float64, 1000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		sort.Float64s(buf)
	}
}

func BenchmarkGo10000(b *testing.B) {
	xs := randSlice(10000)
	buf := make([]float64, 10000)
	for i := 0; i < b.N; i++ {
		copy(buf, xs)
		sort.Float64s(buf)
	}
}

func BenchmarkMedian10(b *testing.B) {
	xs := randSlice(10)
	buf := make([]float64, len(xs))
	n := len(xs) / 2
	for i := 0; i < b.N; i++ {
		NthLargest(xs, n, buf)
	}
}

func BenchmarkMedian100(b *testing.B) {
	xs := randSlice(100)
	buf := make([]float64, len(xs))
	n := len(xs) / 2
	for i := 0; i < b.N; i++ {
		NthLargest(xs, n, buf)
	}
}

func BenchmarkMedian1000(b *testing.B) {
	xs := randSlice(1000)
	buf := make([]float64, len(xs))
	n := len(xs) / 2
	for i := 0; i < b.N; i++ {
		NthLargest(xs, n, buf)
	}
}

func BenchmarkMedian10000(b *testing.B) {
	xs := randSlice(10000)
	buf := make([]float64, len(xs))
	n := len(xs) / 2
	for i := 0; i < b.N; i++ {
		NthLargest(xs, n, buf)
	}
}

// Tests

func TestShell(t *testing.T) {
	for i := 0; i < 10; i++ {
		xs := randSlice(1000)
		Shell(xs)
		if !sort.Float64sAreSorted(xs) {
			t.Errorf("Failed to sort.")
		}
	}
}

func TestQuick(t *testing.T) {
	for i := 0; i < 10; i++ {
		xs := randSlice(1000)
		Quick(xs)
		if !sort.Float64sAreSorted(xs) {
			t.Errorf("Failed to sort.")
		}
	}
}

func TestMedian(t *testing.T) {
	buf := make([]float64, 1000)
	for i := 0; i < 10; i++ {
		xs := randSlice(len(buf))
		Quick(xs)

		perm := rand.Perm(len(buf))
		mixed := make([]float64, len(buf))
		for j := range mixed {
			mixed[j] = xs[perm[j]]
		}

		for j := 1; j <= len(buf); j++ {
			val := NthLargest(mixed, j, buf)
			if val != xs[len(xs) - j] {
				t.Errorf("Failed to find NthLargest.")
			}
		}
	}
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2017 Intel Corporation
//
// 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"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

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

const ksmString = "ksmrules"
const anonPagesMemory = 16777216 // Typically 4096 pages
const run = "1"
const interval = "10"
const scan = "1000"

func ksmTestPrepare() error {
	newKSMRoot, err := ioutil.TempDir("", "cc-ksm-test")
	if err != nil {
		return err
	}

	defaultKSMRoot = newKSMRoot

	memInfoFile, err := ioutil.TempFile("", "cc-ksm-meminfo")
	if err != nil {
		return err
	}

	memInfo = memInfoFile.Name()

	_, err = memInfoFile.WriteString(fmt.Sprintf("AnonPages: %v kB", anonPagesMemory))
	if err != nil {
		return err
	}

	ksmTestRun, err := os.Create(filepath.Join(defaultKSMRoot, ksmRunFile))
	if err != nil {
		return err
	}

	ksmTestPagesToScan, err := os.Create(filepath.Join(defaultKSMRoot, ksmPagesToScan))
	if err != nil {
		return err
	}

	ksmTestSleepMillisec, err := os.Create(filepath.Join(defaultKSMRoot, ksmSleepMillisec))
	if err != nil {
		return err
	}

	defer ksmTestRun.Close()
	defer ksmTestPagesToScan.Close()
	defer ksmTestSleepMillisec.Close()

	return nil
}

func ksmTestCleanup() {
	os.RemoveAll(defaultKSMRoot)
	os.RemoveAll(memInfo)
}

func TestKSMSysfsAttributeOpen(t *testing.T) {
	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmPagesToScan),
	}

	err := pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()

	assert.Nil(t, err)
}

func TestKSMSysfsAttributeOpenNonExistent(t *testing.T) {
	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, "foo"),
	}

	err := pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()

	assert.NotNil(t, err)
}

func TestKSMSysfsAttributeReadWrite(t *testing.T) {
	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmPagesToScan),
	}

	err := pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()

	assert.Nil(t, err)

	err = pagesToScanSysFs.write(ksmString)
	assert.Nil(t, err)

	s, err := pagesToScanSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, ksmString, "Wrong sysfs read: %s", s)
}

func initKSM(root string, t *testing.T) *ksm {
	k, err := newKSM(root)
	assert.Nil(t, err)

	return k
}

func TestKSMAvailabilityDummy(t *testing.T) {
	_, err := newKSM("foo")
	assert.NotNil(t, err)
}

func TestKSMAvailability(t *testing.T) {
	k := initKSM(defaultKSMRoot, t)

	err := k.isAvailable()
	assert.Nil(t, err)
}

func TestKSMAnonPages(t *testing.T) {
	pageSize := (int64)(os.Getpagesize())
	expectedAnonPages := (anonPagesMemory * 1024) / pageSize

	anonPages, err := anonPages()
	assert.Nil(t, err)
	assert.Equal(t, expectedAnonPages, anonPages, "Anonymous pages mismatch")
}

func TestKSMPagesToScan(t *testing.T) {
	setting, valid := ksmSettings[ksmAggressive]
	assert.True(t, valid)

	anonPages, err := anonPages()
	assert.Nil(t, err)
	expectedPagesToScan := fmt.Sprintf("%v", anonPages/setting.pagesPerScanFactor)

	pagesToScan, err := setting.pagesToScan()
	assert.Nil(t, err)
	assert.Equal(t, pagesToScan, expectedPagesToScan, "")
}

func TestKSMPagesToScanInvalidSetting(t *testing.T) {
	setting := ksmSetting{
		pagesPerScanFactor: 0,
	}

	_, err := setting.pagesToScan()
	assert.NotNil(t, err)
}

func TestKSMInit(t *testing.T) {
	runSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmRunFile),
	}

	err := runSysFs.open()
	defer runSysFs.close()
	assert.Nil(t, err)

	err = runSysFs.write(run)
	assert.Nil(t, err)

	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmPagesToScan),
	}

	err = pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()
	assert.Nil(t, err)

	err = pagesToScanSysFs.write(scan)
	assert.Nil(t, err)

	sleepIntervalSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmSleepMillisec),
	}

	err = sleepIntervalSysFs.open()
	defer sleepIntervalSysFs.close()
	assert.Nil(t, err)

	err = sleepIntervalSysFs.write(interval)
	assert.Nil(t, err)

	k := initKSM(defaultKSMRoot, t)

	assert.Equal(t, k.initialPagesToScan, scan)
	assert.Equal(t, k.initialSleepInterval, interval)
	assert.Equal(t, k.initialKSMRun, run)
}

func TestKSMRestore(t *testing.T) {
	runSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmRunFile),
	}

	err := runSysFs.open()
	defer runSysFs.close()
	assert.Nil(t, err)

	err = runSysFs.write(run)
	assert.Nil(t, err)

	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmPagesToScan),
	}

	err = pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()
	assert.Nil(t, err)

	err = pagesToScanSysFs.write(scan)
	assert.Nil(t, err)

	sleepIntervalSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmSleepMillisec),
	}

	err = sleepIntervalSysFs.open()
	defer sleepIntervalSysFs.close()
	assert.Nil(t, err)

	err = sleepIntervalSysFs.write(interval)
	assert.Nil(t, err)

	k := initKSM(defaultKSMRoot, t)

	// Write dummy values and read them back
	var newInterval = "foo"
	var newRun = "bar"
	var newScan = "foobar"

	err = sleepIntervalSysFs.write(newInterval)
	assert.Nil(t, err)

	s, err := sleepIntervalSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, newInterval)

	err = runSysFs.write(newRun)
	assert.Nil(t, err)
	s, err = runSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, newRun)

	err = pagesToScanSysFs.write(newScan)
	assert.Nil(t, err)
	s, err = pagesToScanSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, newScan)

	// Now restore and verify that we read the initial values back
	k.restore()

	s, err = pagesToScanSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, scan)

	s, err = runSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, run)

	s, err = sleepIntervalSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, interval)
}

func TestKSMKick(t *testing.T) {
	k := initKSM(defaultKSMRoot, t)

	timer := time.NewTimer(time.Second)
	go k.kick()

	select {
	case <-k.kickChannel:
		return

	case <-timer.C:
		t.Fatalf("KSM kick timeout")
	}
}
<commit_msg>ksm: ksm.tune() unit test<commit_after>//
// Copyright (c) 2017 Intel Corporation
//
// 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"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

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

const ksmString = "ksmrules"
const anonPagesMemory = 16777216 // Typically 4096 pages
const run = "1"
const interval = "10"
const scan = "1000"

func ksmTestPrepare() error {
	newKSMRoot, err := ioutil.TempDir("", "cc-ksm-test")
	if err != nil {
		return err
	}

	defaultKSMRoot = newKSMRoot

	memInfoFile, err := ioutil.TempFile("", "cc-ksm-meminfo")
	if err != nil {
		return err
	}

	memInfo = memInfoFile.Name()

	_, err = memInfoFile.WriteString(fmt.Sprintf("AnonPages: %v kB", anonPagesMemory))
	if err != nil {
		return err
	}

	ksmTestRun, err := os.Create(filepath.Join(defaultKSMRoot, ksmRunFile))
	if err != nil {
		return err
	}

	ksmTestPagesToScan, err := os.Create(filepath.Join(defaultKSMRoot, ksmPagesToScan))
	if err != nil {
		return err
	}

	ksmTestSleepMillisec, err := os.Create(filepath.Join(defaultKSMRoot, ksmSleepMillisec))
	if err != nil {
		return err
	}

	defer ksmTestRun.Close()
	defer ksmTestPagesToScan.Close()
	defer ksmTestSleepMillisec.Close()

	return nil
}

func ksmTestCleanup() {
	os.RemoveAll(defaultKSMRoot)
	os.RemoveAll(memInfo)
}

func TestKSMSysfsAttributeOpen(t *testing.T) {
	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmPagesToScan),
	}

	err := pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()

	assert.Nil(t, err)
}

func TestKSMSysfsAttributeOpenNonExistent(t *testing.T) {
	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, "foo"),
	}

	err := pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()

	assert.NotNil(t, err)
}

func TestKSMSysfsAttributeReadWrite(t *testing.T) {
	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmPagesToScan),
	}

	err := pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()

	assert.Nil(t, err)

	err = pagesToScanSysFs.write(ksmString)
	assert.Nil(t, err)

	s, err := pagesToScanSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, ksmString, "Wrong sysfs read: %s", s)
}

func initKSM(root string, t *testing.T) *ksm {
	k, err := newKSM(root)
	assert.Nil(t, err)

	return k
}

func TestKSMAvailabilityDummy(t *testing.T) {
	_, err := newKSM("foo")
	assert.NotNil(t, err)
}

func TestKSMAvailability(t *testing.T) {
	k := initKSM(defaultKSMRoot, t)

	err := k.isAvailable()
	assert.Nil(t, err)
}

func TestKSMAnonPages(t *testing.T) {
	pageSize := (int64)(os.Getpagesize())
	expectedAnonPages := (anonPagesMemory * 1024) / pageSize

	anonPages, err := anonPages()
	assert.Nil(t, err)
	assert.Equal(t, expectedAnonPages, anonPages, "Anonymous pages mismatch")
}

func TestKSMPagesToScan(t *testing.T) {
	setting, valid := ksmSettings[ksmAggressive]
	assert.True(t, valid)

	anonPages, err := anonPages()
	assert.Nil(t, err)
	expectedPagesToScan := fmt.Sprintf("%v", anonPages/setting.pagesPerScanFactor)

	pagesToScan, err := setting.pagesToScan()
	assert.Nil(t, err)
	assert.Equal(t, pagesToScan, expectedPagesToScan, "")
}

func TestKSMPagesToScanInvalidSetting(t *testing.T) {
	setting := ksmSetting{
		pagesPerScanFactor: 0,
	}

	_, err := setting.pagesToScan()
	assert.NotNil(t, err)
}

func TestKSMInit(t *testing.T) {
	runSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmRunFile),
	}

	err := runSysFs.open()
	defer runSysFs.close()
	assert.Nil(t, err)

	err = runSysFs.write(run)
	assert.Nil(t, err)

	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmPagesToScan),
	}

	err = pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()
	assert.Nil(t, err)

	err = pagesToScanSysFs.write(scan)
	assert.Nil(t, err)

	sleepIntervalSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmSleepMillisec),
	}

	err = sleepIntervalSysFs.open()
	defer sleepIntervalSysFs.close()
	assert.Nil(t, err)

	err = sleepIntervalSysFs.write(interval)
	assert.Nil(t, err)

	k := initKSM(defaultKSMRoot, t)

	assert.Equal(t, k.initialPagesToScan, scan)
	assert.Equal(t, k.initialSleepInterval, interval)
	assert.Equal(t, k.initialKSMRun, run)
}

func TestKSMRestore(t *testing.T) {
	runSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmRunFile),
	}

	err := runSysFs.open()
	defer runSysFs.close()
	assert.Nil(t, err)

	err = runSysFs.write(run)
	assert.Nil(t, err)

	pagesToScanSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmPagesToScan),
	}

	err = pagesToScanSysFs.open()
	defer pagesToScanSysFs.close()
	assert.Nil(t, err)

	err = pagesToScanSysFs.write(scan)
	assert.Nil(t, err)

	sleepIntervalSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmSleepMillisec),
	}

	err = sleepIntervalSysFs.open()
	defer sleepIntervalSysFs.close()
	assert.Nil(t, err)

	err = sleepIntervalSysFs.write(interval)
	assert.Nil(t, err)

	k := initKSM(defaultKSMRoot, t)

	// Write dummy values and read them back
	var newInterval = "foo"
	var newRun = "bar"
	var newScan = "foobar"

	err = sleepIntervalSysFs.write(newInterval)
	assert.Nil(t, err)

	s, err := sleepIntervalSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, newInterval)

	err = runSysFs.write(newRun)
	assert.Nil(t, err)
	s, err = runSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, newRun)

	err = pagesToScanSysFs.write(newScan)
	assert.Nil(t, err)
	s, err = pagesToScanSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, newScan)

	// Now restore and verify that we read the initial values back
	k.restore()

	s, err = pagesToScanSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, scan)

	s, err = runSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, run)

	s, err = sleepIntervalSysFs.read()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	assert.Equal(t, s, interval)
}

func TestKSMKick(t *testing.T) {
	k := initKSM(defaultKSMRoot, t)

	timer := time.NewTimer(time.Second)
	go k.kick()

	select {
	case <-k.kickChannel:
		return

	case <-timer.C:
		t.Fatalf("KSM kick timeout")
	}
}

func TestKSMTune(t *testing.T) {
	var err error
	var s string

	sleepIntervalSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmSleepMillisec),
	}

	runSysFs := sysfsAttribute{
		path: filepath.Join(defaultKSMRoot, ksmRunFile),
	}

	err = sleepIntervalSysFs.open()
	defer sleepIntervalSysFs.close()
	assert.Nil(t, err)

	err = runSysFs.open()
	defer runSysFs.close()
	assert.Nil(t, err)

	k := initKSM(defaultKSMRoot, t)

	for _, v := range ksmSettings {
		err = k.tune(v)
		assert.Nil(t, err)

		s, err = runSysFs.read()

		assert.Nil(t, err)
		assert.NotNil(t, s)
		if v.run {
			assert.Equal(t, s, "1", "Wrong run value")
		} else {
			assert.Equal(t, s, "0", "Wrong run value")
		}

		if !v.run {
			continue
		}

		s, err = sleepIntervalSysFs.read()

		assert.Nil(t, err)
		assert.NotNil(t, s)
		assert.Equal(t, s, fmt.Sprintf("%v", v.scanIntervalMS), "Wrong sleep interval")
	}
}
<|endoftext|>


<commit_before>package services

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
)

// HTTP server object (all fields are required).
type HTTP struct {
	Port          int16
	Backend       string
	SecurePort    int16
	SecureBackend string
	Cert          string
	Key           string
}

// Start initializes the HTTP server.
func (h *HTTP) Start() <-chan bool {
	log.Printf("HTTP: %s listening on %d/%d", h.Backend, h.Port, h.SecurePort)

	server := http.NewServeMux()
	server.HandleFunc("/", h.handler)

	exited := make(chan bool)

	go func() {
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", h.Port), server))
		close(exited)
	}()

	go func() {
		log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%v", h.SecurePort), h.Cert, h.Key, server))
		close(exited)
	}()

	return exited
}

// Helpers
func lower(m map[string][]string) (result map[string][]string) {
	result = make(map[string][]string)
	for k, v := range m {
		result[strings.ToLower(k)] = v
	}
	return result
}

func (h *HTTP) handler(w http.ResponseWriter, r *http.Request) {
	// Assume we're the clear side of the world.
	backend := h.Backend
	conntype := "CLR"

	var request = make(map[string]interface{})
	var url = make(map[string]interface{})
	request["url"] = url
	url["fragment"] = r.URL.Fragment
	url["host"] = r.URL.Host
	url["opaque"] = r.URL.Opaque
	url["path"] = r.URL.Path
	url["query"] = r.URL.Query()
	url["rawQuery"] = r.URL.RawQuery
	url["scheme"] = r.URL.Scheme
	if r.URL.User != nil {
		url["username"] = r.URL.User.Username()
		pw, ok := r.URL.User.Password()
		if ok {
			url["password"] = pw
		}
	}

	request["method"] = r.Method
	request["headers"] = lower(r.Header)
	request["host"] = r.Host
	var tls = make(map[string]interface{})
	request["tls"] = tls

	tls["enabled"] = r.TLS != nil

	if r.TLS != nil {
		// We're the secure side of the world, I guess.
		backend = h.SecureBackend
		conntype = "TLS"

		tls["version"] = r.TLS.Version
		tls["negotiated-protocol"] = r.TLS.NegotiatedProtocol
		tls["server-name"] = r.TLS.ServerName
	}

	// respond with the requested status
	status := r.Header.Get("Requested-Status")
	if status == "" {
		status = "200"
	}

	statusCode, err := strconv.Atoi(status)
	if err != nil {
		log.Print(err)
		statusCode = 500
	}

	// copy the requested headers into the response
	headers, ok := r.Header["Requested-Header"]
	if ok {
		for _, header := range headers {
			canonical := http.CanonicalHeaderKey(header)
			value, ok := r.Header[canonical]
			if ok {
				w.Header()[canonical] = value
			}
		}
	}

	cookies, ok := r.Header["Requested-Cookie"]
	if ok {
		for _, v := range strings.Split(cookies[0], ",") {
			val := strings.Trim(v, " ")
			http.SetCookie(w, &http.Cookie{
				Name:  val,
				Value: val,
			})
		}
	}

	// If they asked for a specific location to be returned, handle that too.
	location, ok := r.Header["Requested-Location"]

	if ok {
		w.Header()[http.CanonicalHeaderKey("Location")] = location
	}

	addExtauth := os.Getenv("INCLUDE_EXTAUTH_HEADER")

	if len(addExtauth) > 0 {
		extauth := make(map[string]interface{})
		extauth["request"] = request
		extauth["resp_headers"] = lower(w.Header())

		eaJSON, err := json.Marshal(extauth)

		if err != nil {
			eaJSON = []byte(fmt.Sprintf("err: %v", err))
		}

		eaArray := make([]string, 1, 1)
		eaArray[0] = string(eaJSON)

		w.Header()[http.CanonicalHeaderKey("extauth")] = eaArray
	}

	w.WriteHeader(statusCode)

	// Write out all request/response information
	var response = make(map[string]interface{})
	response["headers"] = lower(w.Header())

	var body = make(map[string]interface{})
	body["backend"] = backend
	body["request"] = request
	body["response"] = response

	b, err := json.MarshalIndent(body, "", "  ")
	if err != nil {
		b = []byte(fmt.Sprintf("Error: %v", err))
	}

	log.Printf("%s (%s): writing response HTTP %v", backend, conntype, statusCode)
	w.Write(b)
}
<commit_msg>added authorization request body to the authorization response header value<commit_after>package services

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"io/ioutil"
)

// HTTP server object (all fields are required).
type HTTP struct {
	Port          int16
	Backend       string
	SecurePort    int16
	SecureBackend string
	Cert          string
	Key           string
}

// Start initializes the HTTP server.
func (h *HTTP) Start() <-chan bool {
	log.Printf("HTTP: %s listening on %d/%d", h.Backend, h.Port, h.SecurePort)

	server := http.NewServeMux()
	server.HandleFunc("/", h.handler)

	exited := make(chan bool)

	go func() {
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", h.Port), server))
		close(exited)
	}()

	go func() {
		log.Fatal(http.ListenAndServeTLS(fmt.Sprintf(":%v", h.SecurePort), h.Cert, h.Key, server))
		close(exited)
	}()

	return exited
}

// Helpers
func lower(m map[string][]string) (result map[string][]string) {
	result = make(map[string][]string)
	for k, v := range m {
		result[strings.ToLower(k)] = v
	}
	return result
}

func (h *HTTP) handler(w http.ResponseWriter, r *http.Request) {
	// Assume we're the clear side of the world.
	backend := h.Backend
	conntype := "CLR"

	var request = make(map[string]interface{})
	var url = make(map[string]interface{})
	request["url"] = url
	url["fragment"] = r.URL.Fragment
	url["host"] = r.URL.Host
	url["opaque"] = r.URL.Opaque
	url["path"] = r.URL.Path
	url["query"] = r.URL.Query()
	url["rawQuery"] = r.URL.RawQuery
	url["scheme"] = r.URL.Scheme
	if r.URL.User != nil {
		url["username"] = r.URL.User.Username()
		pw, ok := r.URL.User.Password()
		if ok {
			url["password"] = pw
		}
	}

	request["method"] = r.Method
	request["headers"] = lower(r.Header)
	request["host"] = r.Host
	var tls = make(map[string]interface{})
	request["tls"] = tls

	tls["enabled"] = r.TLS != nil

	if r.TLS != nil {
		// We're the secure side of the world, I guess.
		backend = h.SecureBackend
		conntype = "TLS"

		tls["version"] = r.TLS.Version
		tls["negotiated-protocol"] = r.TLS.NegotiatedProtocol
		tls["server-name"] = r.TLS.ServerName
	}

	// respond with the requested status
	status := r.Header.Get("Requested-Status")
	if status == "" {
		status = "200"
	}

	statusCode, err := strconv.Atoi(status)
	if err != nil {
		log.Print(err)
		statusCode = 500
	}

	// copy the requested headers into the response
	headers, ok := r.Header["Requested-Header"]
	if ok {
		for _, header := range headers {
			canonical := http.CanonicalHeaderKey(header)
			value, ok := r.Header[canonical]
			if ok {
				w.Header()[canonical] = value
			}
		}
	}

	if b, _ := ioutil.ReadAll(r.Body); b != nil {
		w.Header()[http.CanonicalHeaderKey("Auth-Request-Body")] = []string{string(b)}
	}
	defer r.Body.Close()

	cookies, ok := r.Header["Requested-Cookie"]
	if ok {
		for _, v := range strings.Split(cookies[0], ",") {
			val := strings.Trim(v, " ")
			http.SetCookie(w, &http.Cookie{
				Name:  val,
				Value: val,
			})
		}
	}

	// If they asked for a specific location to be returned, handle that too.
	location, ok := r.Header["Requested-Location"]

	if ok {
		w.Header()[http.CanonicalHeaderKey("Location")] = location
	}

	addExtauth := os.Getenv("INCLUDE_EXTAUTH_HEADER")

	if len(addExtauth) > 0 {
		extauth := make(map[string]interface{})
		extauth["request"] = request
		extauth["resp_headers"] = lower(w.Header())

		eaJSON, err := json.Marshal(extauth)

		if err != nil {
			eaJSON = []byte(fmt.Sprintf("err: %v", err))
		}

		eaArray := make([]string, 1, 1)
		eaArray[0] = string(eaJSON)

		w.Header()[http.CanonicalHeaderKey("extauth")] = eaArray
	}

	w.WriteHeader(statusCode)

	// Write out all request/response information
	var response = make(map[string]interface{})
	response["headers"] = lower(w.Header())

	var body = make(map[string]interface{})
	body["backend"] = backend
	body["request"] = request
	body["response"] = response

	b, err := json.MarshalIndent(body, "", "  ")
	if err != nil {
		b = []byte(fmt.Sprintf("Error: %v", err))
	}

	log.Printf("%s (%s): writing response HTTP %v", backend, conntype, statusCode)
	w.Write(b)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/Unknwon/goconfig"
	"github.com/xiaojiong/memcachep"
	"runtime"
	"scanfile"
)

var ConfigServerPath string
var ConfigServerPort int
var mf *scanfile.MemFiles

func init() {
	fmt.Println("server Init.")

	runtime.GOMAXPROCS(8)
	memcachep.BindAction(memcachep.GET, GetAction)

	/* 获取配置文件信息 */
	ini, err := goconfig.LoadConfigFile("./scanfile.conf")
	if err != nil {
		panic(err)
	}

	ConfigServerPath, err = ini.GetValue("server", "path")
	if err != nil {
		panic("config not found server.path")
	}

	ConfigServerPort, err = ini.Int("server", "port")
	if err != nil {
		panic("config not found server.port")
	}
}

func main() {
	files := scanfile.PathFiles(ConfigServerPath)
	mf := scanfile.InitMemFiles(files)

	ls, e := net.Listen("tcp", fmt.Sprintf(":%d", ConfigServerPort))
	if e != nil {
		log.Fatalf("Got an error:  %s", e)
	}

	fmt.Println("server running.")

	memcachep.Listen(ls)
}

func GetAction(req *memcachep.MCRequest, res *memcachep.MCResponse) {
	res.Fatal = false
	key := req.Key
	content := scanfile.MemScan(mf, &key)
	res.Value = []byte(string(content))
}
<commit_msg>添加未加载的包<commit_after>package main

import (
	"fmt"
	"github.com/Unknwon/goconfig"
	"github.com/xiaojiong/memcachep"
	"log"
	"net"
	"runtime"
	"scanfile"
)

var ConfigServerPath string
var ConfigServerPort int
var mf *scanfile.MemFiles

func init() {
	fmt.Println("server Init.")

	runtime.GOMAXPROCS(8)
	memcachep.BindAction(memcachep.GET, GetAction)

	/* 获取配置文件信息 */
	ini, err := goconfig.LoadConfigFile("./scanfile.conf")
	if err != nil {
		panic(err)
	}

	ConfigServerPath, err = ini.GetValue("server", "path")
	if err != nil {
		panic("config not found server.path")
	}

	ConfigServerPort, err = ini.Int("server", "port")
	if err != nil {
		panic("config not found server.port")
	}
}

func main() {
	files := scanfile.PathFiles(ConfigServerPath)
	mf := scanfile.InitMemFiles(files)

	ls, e := net.Listen("tcp", fmt.Sprintf(":%d", ConfigServerPort))
	if e != nil {
		log.Fatalf("Got an error:  %s", e)
	}

	fmt.Println("server running.")

	memcachep.Listen(ls)
}

func GetAction(req *memcachep.MCRequest, res *memcachep.MCResponse) {
	res.Fatal = false
	key := req.Key
	content := scanfile.MemScan(mf, &key)
	res.Value = []byte(string(content))
}
<|endoftext|>