<commit_before>package gps

import "testing"

func TestVersionSorts(t *testing.T) {
	rev := Revision("flooboofoobooo")
	v1 := NewBranch("master").Is(rev)
	v2 := NewBranch("test").Is(rev)
	v3 := NewVersion("1.0.0").Is(rev)
	v4 := NewVersion("1.0.1")
	v5 := NewVersion("v2.0.5")
	v6 := NewVersion("2.0.5.2")
	v7 := newDefaultBranch("unwrapped")
	v8 := NewVersion("20.0.5.2")

	start := []Version{
		v1,
		v2,
		v3,
		v4,
		v5,
		v6,
		v7,
		v8,
		rev,
	}

	down := make([]Version, len(start))
	copy(down, start)
	up := make([]Version, len(start))
	copy(up, start)

	edown := []Version{
		v3, v4, v5, // semvers
		v7, v1, v2, // floating/branches
		v6, v8, // plain versions
		rev, // revs
	}

	eup := []Version{
		v5, v4, v3, // semvers
		v7, v1, v2, // floating/branches
		v6, v8, // plain versions
		rev, // revs
	}

	SortForUpgrade(up)
	var wrong []int
	for k, v := range up {
		if eup[k] != v {
			wrong = append(wrong, k)
			t.Errorf("Expected version %s in position %v on upgrade sort, but got %s", eup[k], k, v)
		}
	}
	if len(wrong) > 0 {
		// Just helps with readability a bit
		t.Errorf("Upgrade sort positions with wrong versions: %v", wrong)
	}

	SortForDowngrade(down)
	wrong = wrong[:0]
	for k, v := range down {
		if edown[k] != v {
			wrong = append(wrong, k)
			t.Errorf("Expected version %s in position %v on downgrade sort, but got %s", edown[k], k, v)
		}
	}
	if len(wrong) > 0 {
		// Just helps with readability a bit
		t.Errorf("Downgrade sort positions with wrong versions: %v", wrong)
	}

	// Now make sure we sort back the other way correctly...just because
	SortForUpgrade(down)
	wrong = wrong[:0]
	for k, v := range down {
		if eup[k] != v {
			wrong = append(wrong, k)
			t.Errorf("Expected version %s in position %v on down-then-upgrade sort, but got %s", eup[k], k, v)
		}
	}
	if len(wrong) > 0 {
		// Just helps with readability a bit
		t.Errorf("Down-then-upgrade sort positions with wrong versions: %v", wrong)
	}

	// Now make sure we sort back the other way correctly...just because
	SortForDowngrade(up)
	wrong = wrong[:0]
	for k, v := range up {
		if edown[k] != v {
			wrong = append(wrong, k)
			t.Errorf("Expected version %s in position %v on up-then-downgrade sort, but got %s", edown[k], k, v)
		}
	}
	if len(wrong) > 0 {
		// Just helps with readability a bit
		t.Errorf("Up-then-downgrade sort positions with wrong versions: %v", wrong)
	}
}
<commit_msg>Add semver with prerelease to version sort tests<commit_after>package gps

import "testing"

func TestVersionSorts(t *testing.T) {
	rev := Revision("flooboofoobooo")
	v1 := NewBranch("master").Is(rev)
	v2 := NewBranch("test").Is(rev)
	v3 := NewVersion("1.0.0").Is(rev)
	v4 := NewVersion("1.0.1")
	v5 := NewVersion("v2.0.5")
	v6 := NewVersion("2.0.5.2")
	v7 := newDefaultBranch("unwrapped")
	v8 := NewVersion("20.0.5.2")
	v9 := NewVersion("v1.5.5-beta.4")
	v10 := NewVersion("v3.0.1-alpha.1")

	start := []Version{
		v1,
		v2,
		v3,
		v4,
		v5,
		v6,
		v7,
		v8,
		v9,
		v10,
		rev,
	}

	down := make([]Version, len(start))
	copy(down, start)
	up := make([]Version, len(start))
	copy(up, start)

	edown := []Version{
		v3, v4, v5, // semvers
		v9, v10, // prerelease semver
		v7, v1, v2, // floating/branches
		v6, v8, // plain versions
		rev, // revs
	}

	eup := []Version{
		v5, v4, v3, // semvers
		v10, v9, // prerelease semver
		v7, v1, v2, // floating/branches
		v6, v8, // plain versions
		rev, // revs
	}

	SortForUpgrade(up)
	var wrong []int
	for k, v := range up {
		if eup[k] != v {
			wrong = append(wrong, k)
			t.Errorf("Expected version %s in position %v on upgrade sort, but got %s", eup[k], k, v)
		}
	}
	if len(wrong) > 0 {
		// Just helps with readability a bit
		t.Errorf("Upgrade sort positions with wrong versions: %v", wrong)
	}

	SortForDowngrade(down)
	wrong = wrong[:0]
	for k, v := range down {
		if edown[k] != v {
			wrong = append(wrong, k)
			t.Errorf("Expected version %s in position %v on downgrade sort, but got %s", edown[k], k, v)
		}
	}
	if len(wrong) > 0 {
		// Just helps with readability a bit
		t.Errorf("Downgrade sort positions with wrong versions: %v", wrong)
	}

	// Now make sure we sort back the other way correctly...just because
	SortForUpgrade(down)
	wrong = wrong[:0]
	for k, v := range down {
		if eup[k] != v {
			wrong = append(wrong, k)
			t.Errorf("Expected version %s in position %v on down-then-upgrade sort, but got %s", eup[k], k, v)
		}
	}
	if len(wrong) > 0 {
		// Just helps with readability a bit
		t.Errorf("Down-then-upgrade sort positions with wrong versions: %v", wrong)
	}

	// Now make sure we sort back the other way correctly...just because
	SortForDowngrade(up)
	wrong = wrong[:0]
	for k, v := range up {
		if edown[k] != v {
			wrong = append(wrong, k)
			t.Errorf("Expected version %s in position %v on up-then-downgrade sort, but got %s", edown[k], k, v)
		}
	}
	if len(wrong) > 0 {
		// Just helps with readability a bit
		t.Errorf("Up-then-downgrade sort positions with wrong versions: %v", wrong)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/nightlyone/lockfile"
	"github.com/tylerb/graceful"

	"github.com/stripe/sequins/backend"
	"github.com/stripe/sequins/multilock"
	"github.com/stripe/sequins/sharding"
	"github.com/stripe/sequins/zk"
)

var errDirLocked = errors.New("failed to acquire lock")

type sequins struct {
	config  sequinsConfig
	http    *http.Server
	backend backend.Backend

	dbs     map[string]*db
	dbsLock sync.RWMutex

	address   string
	peers     *sharding.Peers
	zkWatcher *zk.Watcher

	refreshLock   sync.Mutex
	buildLock     *multilock.Multilock
	refreshTicker *time.Ticker
	sighups       chan os.Signal

	storeLock lockfile.Lockfile
}

func newSequins(backend backend.Backend, config sequinsConfig) *sequins {
	return &sequins{
		config:      config,
		backend:     backend,
		refreshLock: sync.Mutex{},
	}
}

func (s *sequins) init() error {
	if s.config.Sharding.Enabled {
		err := s.initCluster()
		if err != nil {
			return err
		}
	}

	// Create local directories, and load any cached versions we have.
	err := s.initLocalStore()
	if err != nil {
		return fmt.Errorf("error initializing local store: %s", err)
	}

	// This lock limits load parallelism across all dbs.
	maxLoads := s.config.MaxParallelLoads
	if maxLoads != 0 {
		s.buildLock = multilock.New(maxLoads)
	}

	// Trigger loads before we start up.
	s.refreshAll()
	s.refreshLock.Lock()
	defer s.refreshLock.Unlock()

	// Automatically refresh, if configured to do so.
	refresh := s.config.RefreshPeriod.Duration
	if refresh != 0 {
		s.refreshTicker = time.NewTicker(refresh)
		go func() {
			log.Println("Automatically checking for new versions every", refresh.String())
			for range s.refreshTicker.C {
				s.refreshAll()
			}
		}()
	}

	// Refresh on SIGHUP.
	sighups := make(chan os.Signal)
	signal.Notify(sighups, syscall.SIGHUP)
	go func() {
		for range sighups {
			s.refreshAll()
		}
	}()

	s.sighups = sighups
	return nil
}

func (s *sequins) initCluster() error {
	// This config property is calculated if not set.
	if s.config.Sharding.ProxyStageTimeout.Duration == 0 {
		stageTimeout := s.config.Sharding.ProxyTimeout.Duration / time.Duration(s.config.Sharding.Replication)
		s.config.Sharding.ProxyStageTimeout = duration{stageTimeout}
	}

	prefix := path.Join("/", s.config.Sharding.ClusterName)
	zkWatcher, err := zk.Connect(s.config.ZK.Servers, prefix,
		s.config.ZK.ConnectTimeout.Duration, s.config.ZK.SessionTimeout.Duration)
	if err != nil {
		return err
	}

	go zkWatcher.TriggerCleanup()

	hostname := s.config.Sharding.AdvertisedHostname
	if hostname == "" {
		hostname, err = os.Hostname()
		if err != nil {
			return err
		}
	}

	_, port, err := net.SplitHostPort(s.config.Bind)
	if err != nil {
		return err
	}
	ipAddresses, err := net.LookupHost(hostname)
	ip := ""
	if err	!= nil {
		return err
	}
	if len(ipAddresses) < 1 {
		ip = hostname
	} else {
		ip = ipAddresses[0]
	}
	routableIpAddress := net.JoinHostPort(ip, port)
	routableAddress := net.JoinHostPort(hostname, port)
	shardID := s.config.Sharding.ShardID
	if shardID == "" {
		shardID = routableAddress
	}

	peers := sharding.WatchPeers(zkWatcher, shardID, routableIpAddress)
	peers.WaitToConverge(s.config.Sharding.TimeToConverge.Duration)

	s.address = routableAddress
	s.zkWatcher = zkWatcher
	s.peers = peers
	return nil
}

func (s *sequins) initLocalStore() error {
	dataPath := filepath.Join(s.config.LocalStore, "data")
	err := os.MkdirAll(dataPath, 0755|os.ModeDir)
	if err != nil {
		return err
	}

	lock, _ := lockfile.New(filepath.Join(s.config.LocalStore, "sequins.lock"))
	s.storeLock = lock
	err = s.storeLock.TryLock()
	if err != nil {
		p, err := s.storeLock.GetOwner()
		if err == nil {
			log.Printf("The local store at %s is locked by process %d", s.config.LocalStore, p.Pid)
		}

		return errDirLocked
	}

	return nil
}

func (s *sequins) start() {
	defer s.shutdown()

	var h http.Handler = s
	if s.config.Debug.Bind != "" && s.config.Debug.Expvars {
		h = trackQueries(s)
	}

	log.Println("Listening on", s.config.Bind)
	graceful.Run(s.config.Bind, time.Second, h)
}

func (s *sequins) shutdown() {
	log.Println("Shutting down...")
	signal.Stop(s.sighups)

	if s.refreshTicker != nil {
		s.refreshTicker.Stop()
	}

	zk := s.zkWatcher
	if zk != nil {
		zk.Close()
	}

	// TODO: figure out how to cancel in-progress downloads
	// s.dbsLock.Lock()
	// defer s.dbsLock.Unlock()

	// for _, db := range s.dbs {
	// 	db.close()
	// }

	s.storeLock.Unlock()
}

func (s *sequins) refreshAll() {
	s.refreshLock.Lock()
	defer s.refreshLock.Unlock()

	dbs, err := s.backend.ListDBs()
	if err != nil {
		log.Printf("Error listing DBs from %s: %s", s.backend.DisplayPath(""), err)
		return
	}

	// Add any new DBS, and trigger refreshes for existing ones. This
	// Lock pattern is a bit goofy, since the lock is on the request
	// path and we want to hold it for as short a time as possible.
	s.dbsLock.RLock()

	newDBs := make(map[string]*db)
	var backfills sync.WaitGroup
	for _, name := range dbs {
		db := s.dbs[name]
		if db == nil {
			db = newDB(s, name)

			backfills.Add(1)
			go func() {
				db.backfillVersions()
				backfills.Done()
			}()
		} else {
			go func() {
				err := db.refresh()
				if err != nil {
					log.Printf("Error refreshing %s: %s", db.name, err)
				}
			}()
		}

		newDBs[name] = db
	}

	backfills.Wait()

	// Now, grab the full lock to switch the new map in.
	s.dbsLock.RUnlock()
	s.dbsLock.Lock()

	oldDBs := s.dbs
	s.dbs = newDBs

	// Finally, close any dbs that we're removing.
	s.dbsLock.Unlock()
	s.dbsLock.RLock()

	for name, db := range oldDBs {
		if s.dbs[name] == nil {
			log.Println("Removing and clearing database", name)
			db.close()
			db.delete()
		}
	}

	s.dbsLock.RUnlock()

	// Cleanup any zkNodes for deleted versions and dbs.
	if s.zkWatcher != nil {
		s.zkWatcher.TriggerCleanup()
	}
}

func (s *sequins) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if r.URL.Path == "/" {
		s.serveStatus(w, r)
		return
	}

	var dbName, key string
	path := strings.TrimPrefix(r.URL.Path, "/")
	split := strings.Index(path, "/")
	if split == -1 {
		dbName = path
		key = ""
	} else {
		dbName = path[:split]
		key = path[split+1:]
	}

	if dbName == "" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	s.dbsLock.RLock()
	db := s.dbs[dbName]
	s.dbsLock.RUnlock()

	// If this is a proxy request, we don't want to confuse "we don't have this
	// db" with "we don't have this key"; if this is a proxied request, then the
	// peer apparently does have the db, and thinks we do too (which should never
	// happen). So we use 501 Not Implemented to indicate the former. To users,
	// we present a uniform 404.
	if db == nil {
		if r.URL.Query().Get("proxy") != "" {
			w.WriteHeader(http.StatusNotImplemented)
		} else {
			w.WriteHeader(http.StatusNotFound)
		}

		return
	}

	db.serveKey(w, r, key)
}
<commit_msg>address nits<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/nightlyone/lockfile"
	"github.com/tylerb/graceful"

	"github.com/stripe/sequins/backend"
	"github.com/stripe/sequins/multilock"
	"github.com/stripe/sequins/sharding"
	"github.com/stripe/sequins/zk"
)

var errDirLocked = errors.New("failed to acquire lock")

type sequins struct {
	config  sequinsConfig
	http    *http.Server
	backend backend.Backend

	dbs     map[string]*db
	dbsLock sync.RWMutex

	address   string
	peers     *sharding.Peers
	zkWatcher *zk.Watcher

	refreshLock   sync.Mutex
	buildLock     *multilock.Multilock
	refreshTicker *time.Ticker
	sighups       chan os.Signal

	storeLock lockfile.Lockfile
}

func newSequins(backend backend.Backend, config sequinsConfig) *sequins {
	return &sequins{
		config:      config,
		backend:     backend,
		refreshLock: sync.Mutex{},
	}
}

func (s *sequins) init() error {
	if s.config.Sharding.Enabled {
		err := s.initCluster()
		if err != nil {
			return err
		}
	}

	// Create local directories, and load any cached versions we have.
	err := s.initLocalStore()
	if err != nil {
		return fmt.Errorf("error initializing local store: %s", err)
	}

	// This lock limits load parallelism across all dbs.
	maxLoads := s.config.MaxParallelLoads
	if maxLoads != 0 {
		s.buildLock = multilock.New(maxLoads)
	}

	// Trigger loads before we start up.
	s.refreshAll()
	s.refreshLock.Lock()
	defer s.refreshLock.Unlock()

	// Automatically refresh, if configured to do so.
	refresh := s.config.RefreshPeriod.Duration
	if refresh != 0 {
		s.refreshTicker = time.NewTicker(refresh)
		go func() {
			log.Println("Automatically checking for new versions every", refresh.String())
			for range s.refreshTicker.C {
				s.refreshAll()
			}
		}()
	}

	// Refresh on SIGHUP.
	sighups := make(chan os.Signal)
	signal.Notify(sighups, syscall.SIGHUP)
	go func() {
		for range sighups {
			s.refreshAll()
		}
	}()

	s.sighups = sighups
	return nil
}

func (s *sequins) initCluster() error {
	// This config property is calculated if not set.
	if s.config.Sharding.ProxyStageTimeout.Duration == 0 {
		stageTimeout := s.config.Sharding.ProxyTimeout.Duration / time.Duration(s.config.Sharding.Replication)
		s.config.Sharding.ProxyStageTimeout = duration{stageTimeout}
	}

	prefix := path.Join("/", s.config.Sharding.ClusterName)
	zkWatcher, err := zk.Connect(s.config.ZK.Servers, prefix,
		s.config.ZK.ConnectTimeout.Duration, s.config.ZK.SessionTimeout.Duration)
	if err != nil {
		return err
	}

	go zkWatcher.TriggerCleanup()

	hostname := s.config.Sharding.AdvertisedHostname
	if hostname == "" {
		hostname, err = os.Hostname()
		if err != nil {
			return err
		}
	}

	_, port, err := net.SplitHostPort(s.config.Bind)
	if err != nil {
		return err
	}

	ip := ""
	ipAddresses, err := net.LookupHost(hostname)
	if err	!= nil {
		return err
	}
	if len(ipAddresses) != 1 {
		ip = hostname
	} else {
		ip = ipAddresses[0]
	}

	routableIpAddress := net.JoinHostPort(ip, port)
	routableAddress := net.JoinHostPort(hostname, port)
	shardID := s.config.Sharding.ShardID
	if shardID == "" {
		shardID = routableAddress
	}

	peers := sharding.WatchPeers(zkWatcher, shardID, routableIpAddress)
	peers.WaitToConverge(s.config.Sharding.TimeToConverge.Duration)

	s.address = routableAddress
	s.zkWatcher = zkWatcher
	s.peers = peers
	return nil
}

func (s *sequins) initLocalStore() error {
	dataPath := filepath.Join(s.config.LocalStore, "data")
	err := os.MkdirAll(dataPath, 0755|os.ModeDir)
	if err != nil {
		return err
	}

	lock, _ := lockfile.New(filepath.Join(s.config.LocalStore, "sequins.lock"))
	s.storeLock = lock
	err = s.storeLock.TryLock()
	if err != nil {
		p, err := s.storeLock.GetOwner()
		if err == nil {
			log.Printf("The local store at %s is locked by process %d", s.config.LocalStore, p.Pid)
		}

		return errDirLocked
	}

	return nil
}

func (s *sequins) start() {
	defer s.shutdown()

	var h http.Handler = s
	if s.config.Debug.Bind != "" && s.config.Debug.Expvars {
		h = trackQueries(s)
	}

	log.Println("Listening on", s.config.Bind)
	graceful.Run(s.config.Bind, time.Second, h)
}

func (s *sequins) shutdown() {
	log.Println("Shutting down...")
	signal.Stop(s.sighups)

	if s.refreshTicker != nil {
		s.refreshTicker.Stop()
	}

	zk := s.zkWatcher
	if zk != nil {
		zk.Close()
	}

	// TODO: figure out how to cancel in-progress downloads
	// s.dbsLock.Lock()
	// defer s.dbsLock.Unlock()

	// for _, db := range s.dbs {
	// 	db.close()
	// }

	s.storeLock.Unlock()
}

func (s *sequins) refreshAll() {
	s.refreshLock.Lock()
	defer s.refreshLock.Unlock()

	dbs, err := s.backend.ListDBs()
	if err != nil {
		log.Printf("Error listing DBs from %s: %s", s.backend.DisplayPath(""), err)
		return
	}

	// Add any new DBS, and trigger refreshes for existing ones. This
	// Lock pattern is a bit goofy, since the lock is on the request
	// path and we want to hold it for as short a time as possible.
	s.dbsLock.RLock()

	newDBs := make(map[string]*db)
	var backfills sync.WaitGroup
	for _, name := range dbs {
		db := s.dbs[name]
		if db == nil {
			db = newDB(s, name)

			backfills.Add(1)
			go func() {
				db.backfillVersions()
				backfills.Done()
			}()
		} else {
			go func() {
				err := db.refresh()
				if err != nil {
					log.Printf("Error refreshing %s: %s", db.name, err)
				}
			}()
		}

		newDBs[name] = db
	}

	backfills.Wait()

	// Now, grab the full lock to switch the new map in.
	s.dbsLock.RUnlock()
	s.dbsLock.Lock()

	oldDBs := s.dbs
	s.dbs = newDBs

	// Finally, close any dbs that we're removing.
	s.dbsLock.Unlock()
	s.dbsLock.RLock()

	for name, db := range oldDBs {
		if s.dbs[name] == nil {
			log.Println("Removing and clearing database", name)
			db.close()
			db.delete()
		}
	}

	s.dbsLock.RUnlock()

	// Cleanup any zkNodes for deleted versions and dbs.
	if s.zkWatcher != nil {
		s.zkWatcher.TriggerCleanup()
	}
}

func (s *sequins) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if r.URL.Path == "/" {
		s.serveStatus(w, r)
		return
	}

	var dbName, key string
	path := strings.TrimPrefix(r.URL.Path, "/")
	split := strings.Index(path, "/")
	if split == -1 {
		dbName = path
		key = ""
	} else {
		dbName = path[:split]
		key = path[split+1:]
	}

	if dbName == "" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	s.dbsLock.RLock()
	db := s.dbs[dbName]
	s.dbsLock.RUnlock()

	// If this is a proxy request, we don't want to confuse "we don't have this
	// db" with "we don't have this key"; if this is a proxied request, then the
	// peer apparently does have the db, and thinks we do too (which should never
	// happen). So we use 501 Not Implemented to indicate the former. To users,
	// we present a uniform 404.
	if db == nil {
		if r.URL.Query().Get("proxy") != "" {
			w.WriteHeader(http.StatusNotImplemented)
		} else {
			w.WriteHeader(http.StatusNotFound)
		}

		return
	}

	db.serveKey(w, r, key)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/http/httptest"
	"testing"

	"github.com/gorilla/mux"
	"github.com/proullon/ramsql/engine/log"
	"github.com/stretchr/testify/assert"

	"github.com/ovh/cds/engine/api/auth"
	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/engine/api/pipeline"
	"github.com/ovh/cds/engine/api/project"
	"github.com/ovh/cds/engine/api/sessionstore"
	"github.com/ovh/cds/engine/api/test"
	"github.com/ovh/cds/engine/api/testwithdb"
	"github.com/ovh/cds/engine/api/user"
	"github.com/ovh/cds/sdk"
)

// TestVerifyUserToken test token verification when OK
func TestVerifyUserToken(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestVerifyUserToken", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	token, hashedToken, err := user.GeneratePassword()
	if err != nil {
		t.Fatalf("Cannot create token: %s", err)
	}

	a := &sdk.Auth{
		HashedTokenVerify: hashedToken,
	}

	err = user.InsertUser(db, u, a)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	u2, err := user.LoadUserAndAuth(db, "foo")
	if err != nil {
		t.Fatalf("Cannot load %s: %s\n", "foo", err)
	}

	password, hashedpassword, err := user.Verify(u2, token)
	if err != nil {
		t.Fatalf("User shoud be verified : %s", err)
	}
	if password == "" {
		t.Fatalf("Password should not be empty")
	}
	if hashedpassword == "" {
		t.Fatalf("hashedPassword should not be empty")
	}
}

// TestWrongTokenUser  test token verificaiton when token is wrong
func TestWrongTokenUser(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestWrongTokenUser", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	_, hashedToken, err := user.GeneratePassword()
	if err != nil {
		t.Fatalf("Cannot create token: %s", err)
	}

	a := &sdk.Auth{
		HashedTokenVerify: hashedToken,
	}

	err = user.InsertUser(db, u, a)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	u2, err := user.LoadUserAndAuth(db, "foo")
	if err != nil {
		t.Fatalf("Cannot load %s: %s\n", "foo", err)
	}

	_, _, err = user.Verify(u2, "blabla")
	if err == nil {
		t.Fatalf("User shoud not be verified : %s", err)
	}
}

// TestVerifyResetExpired test validating reset token when time expired
func TestVerifyResetExpired(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestVerifyResetExpired", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	token, hashedToken, err := user.GeneratePassword()
	if err != nil {
		t.Fatalf("Cannot create token: %s", err)
	}

	a := &sdk.Auth{
		HashedTokenVerify: hashedToken,
		DateReset:         1,
		EmailVerified:     true,
	}

	err = user.InsertUser(db, u, a)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	u2, err := user.LoadUserAndAuth(db, "foo")
	if err != nil {
		t.Fatalf("Cannot load %s: %s\n", "foo", err)
	}

	_, _, err = user.Verify(u2, token)
	if err == nil {
		t.Fatalf("User shoud not be verified : %s", err)
	}
	if err.Error() != "Reset operation expired" {
		t.Fatalf("Reset sould be expired")
	}
}

// TestVerifyAlreadyDone test token verification when it's already done
func TestVerifyAlreadyDone(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestVerifyAlreadyDone", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	token, hashedToken, err := user.GeneratePassword()
	if err != nil {
		t.Fatalf("Cannot create token: %s", err)
	}

	a := &sdk.Auth{
		HashedTokenVerify: hashedToken,
		DateReset:         0,
		EmailVerified:     true,
	}

	err = user.InsertUser(db, u, a)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	u2, err := user.LoadUserAndAuth(db, "foo")
	if err != nil {
		t.Fatalf("Cannot load %s: %s\n", "foo", err)
	}

	_, _, err = user.Verify(u2, token)
	if err == nil {
		t.Fatalf("User shoud not be verified : %s", err)
	}
	if err.Error() != "Account already verified" {
		t.Fatalf("Account should be already verified")
	}
}

// TestVerifyAlreadyDone test token verification when it's already done
func TestLoadUserWithGroup(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestLoadUserWithGroup", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	err := user.InsertUser(db, u, nil)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	project1 := &sdk.Project{
		Key:  "foo",
		Name: "foo",
	}
	project2 := &sdk.Project{
		Key:  "bar",
		Name: "bar",
	}

	err = project.InsertProject(db, project1)
	if err != nil {
		t.Fatalf("cannot insert project1: %s", err)
	}
	err = project.InsertProject(db, project2)
	if err != nil {
		t.Fatalf("cannot insert project2: %s", err)
	}

	pipelinePip1 := &sdk.Pipeline{
		Name:      "PIP1",
		ProjectID: project1.ID,
		Type:      sdk.BuildPipeline,
	}

	err = pipeline.InsertPipeline(db, pipelinePip1)
	if err != nil {
		t.Fatalf("cannot insert pipeline: %s", err)
	}

	groupInsert := &sdk.Group{
		Name: "GroupeFoo",
	}

	err = group.InsertGroup(db, groupInsert)
	if err != nil {
		t.Fatalf("cannot insert group: %s", err)
	}

	err = group.InsertGroupInProject(db, project1.ID, groupInsert.ID, 4)
	if err != nil {
		t.Fatalf("cannot insert project1 in group: %s", err)
	}
	err = group.InsertGroupInProject(db, project2.ID, groupInsert.ID, 5)
	if err != nil {
		t.Fatalf("cannot insert project1 in group: %s", err)
	}
	err = group.InsertGroupInPipeline(db, pipelinePip1.ID, groupInsert.ID, 7)
	if err != nil {
		t.Fatalf("cannot insert pipeline1 in group: %s", err)
	}

	err = group.InsertUserInGroup(db, groupInsert.ID, u.ID, false)
	if err != nil {
		t.Fatalf("cannot insert user1 in group: %s", err)
	}

	err = user.LoadUserPermissions(db, u)
	if err != nil {
		t.Fatalf("cannot load user group and project: %s", err)
	}

	if len(u.Groups) != 1 {
		t.Fatalf("Missing/TooMuch group on user, need 1, got %d", len(u.Groups))
	}
	if len(u.Groups[0].ProjectGroups) != 2 {
		t.Fatalf("Missing/TooMuch project on group.Need 2, got %d", len(u.Groups[0].ProjectGroups))
	}
	if len(u.Groups[0].PipelineGroups) != 1 {
		t.Fatalf("Missing/TooMuch pipeline on group.Need 1, got %d", len(u.Groups[0].PipelineGroups))
	}
}

func Test_getUserGroupsHandler(t *testing.T) {
	if testwithdb.DBDriver == "" {
		t.SkipNow()
		return
	}
	db, err := testwithdb.SetupPG(t, bootstrap.InitiliazeDB)
	assert.NoError(t, err)

	authDriver, _ := auth.GetDriver("local", nil, sessionstore.Options{Mode: "local", TTL: 30})
	router = &Router{authDriver, mux.NewRouter(), "/Test_getUserGroupsHandler"}
	router.init()

	g1 := &sdk.Group{
		Name: testwithdb.RandomString(t, 10),
	}

	g2 := &sdk.Group{
		Name: testwithdb.RandomString(t, 10),
	}

	u, pass, err := testwithdb.InsertLambaUser(t, db, g1, g2)
	assert.NoError(t, err)
	assert.NotZero(t, u)
	assert.NotZero(t, pass)

	uri := router.getRoute("GET", getUserGroupsHandler, map[string]string{"name": u.Username})
	if uri == "" {
		t.Fatal("Route not found")
	}

	req := testwithdb.NewAuthentifiedRequest(t, u, pass, "GET", uri, nil)

	//Do the request
	w := httptest.NewRecorder()
	router.mux.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)

	t.Logf("Body: %s", w.Body.String())

	res := map[string][]sdk.Group{}
	if err := json.Unmarshal(w.Body.Bytes(), &res); err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, 2, len(res["groups"]))
	assert.Equal(t, 0, len(res["groups_admin"]))

}
<commit_msg>fix (test): missing import (#82)<commit_after>package main

import (
	"encoding/json"
	"net/http/httptest"
	"testing"

	"github.com/gorilla/mux"
	"github.com/proullon/ramsql/engine/log"
	"github.com/stretchr/testify/assert"

	"github.com/ovh/cds/engine/api/auth"
	"github.com/ovh/cds/engine/api/bootstrap"
	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/engine/api/pipeline"
	"github.com/ovh/cds/engine/api/project"
	"github.com/ovh/cds/engine/api/sessionstore"
	"github.com/ovh/cds/engine/api/test"
	"github.com/ovh/cds/engine/api/testwithdb"
	"github.com/ovh/cds/engine/api/user"
	"github.com/ovh/cds/sdk"
)

// TestVerifyUserToken test token verification when OK
func TestVerifyUserToken(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestVerifyUserToken", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	token, hashedToken, err := user.GeneratePassword()
	if err != nil {
		t.Fatalf("Cannot create token: %s", err)
	}

	a := &sdk.Auth{
		HashedTokenVerify: hashedToken,
	}

	err = user.InsertUser(db, u, a)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	u2, err := user.LoadUserAndAuth(db, "foo")
	if err != nil {
		t.Fatalf("Cannot load %s: %s\n", "foo", err)
	}

	password, hashedpassword, err := user.Verify(u2, token)
	if err != nil {
		t.Fatalf("User shoud be verified : %s", err)
	}
	if password == "" {
		t.Fatalf("Password should not be empty")
	}
	if hashedpassword == "" {
		t.Fatalf("hashedPassword should not be empty")
	}
}

// TestWrongTokenUser  test token verificaiton when token is wrong
func TestWrongTokenUser(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestWrongTokenUser", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	_, hashedToken, err := user.GeneratePassword()
	if err != nil {
		t.Fatalf("Cannot create token: %s", err)
	}

	a := &sdk.Auth{
		HashedTokenVerify: hashedToken,
	}

	err = user.InsertUser(db, u, a)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	u2, err := user.LoadUserAndAuth(db, "foo")
	if err != nil {
		t.Fatalf("Cannot load %s: %s\n", "foo", err)
	}

	_, _, err = user.Verify(u2, "blabla")
	if err == nil {
		t.Fatalf("User shoud not be verified : %s", err)
	}
}

// TestVerifyResetExpired test validating reset token when time expired
func TestVerifyResetExpired(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestVerifyResetExpired", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	token, hashedToken, err := user.GeneratePassword()
	if err != nil {
		t.Fatalf("Cannot create token: %s", err)
	}

	a := &sdk.Auth{
		HashedTokenVerify: hashedToken,
		DateReset:         1,
		EmailVerified:     true,
	}

	err = user.InsertUser(db, u, a)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	u2, err := user.LoadUserAndAuth(db, "foo")
	if err != nil {
		t.Fatalf("Cannot load %s: %s\n", "foo", err)
	}

	_, _, err = user.Verify(u2, token)
	if err == nil {
		t.Fatalf("User shoud not be verified : %s", err)
	}
	if err.Error() != "Reset operation expired" {
		t.Fatalf("Reset sould be expired")
	}
}

// TestVerifyAlreadyDone test token verification when it's already done
func TestVerifyAlreadyDone(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestVerifyAlreadyDone", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	token, hashedToken, err := user.GeneratePassword()
	if err != nil {
		t.Fatalf("Cannot create token: %s", err)
	}

	a := &sdk.Auth{
		HashedTokenVerify: hashedToken,
		DateReset:         0,
		EmailVerified:     true,
	}

	err = user.InsertUser(db, u, a)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	u2, err := user.LoadUserAndAuth(db, "foo")
	if err != nil {
		t.Fatalf("Cannot load %s: %s\n", "foo", err)
	}

	_, _, err = user.Verify(u2, token)
	if err == nil {
		t.Fatalf("User shoud not be verified : %s", err)
	}
	if err.Error() != "Account already verified" {
		t.Fatalf("Account should be already verified")
	}
}

// TestVerifyAlreadyDone test token verification when it's already done
func TestLoadUserWithGroup(t *testing.T) {
	log.UseTestLogger(t)
	db := test.Setup("TestLoadUserWithGroup", t)

	u := &sdk.User{
		Username: "foo",
		Email:    "foo.bar@ovh.com",
		Fullname: "foo bar",
	}

	err := user.InsertUser(db, u, nil)
	if err != nil {
		t.Fatalf("Cannot insert user: %s", err)
	}

	project1 := &sdk.Project{
		Key:  "foo",
		Name: "foo",
	}
	project2 := &sdk.Project{
		Key:  "bar",
		Name: "bar",
	}

	err = project.InsertProject(db, project1)
	if err != nil {
		t.Fatalf("cannot insert project1: %s", err)
	}
	err = project.InsertProject(db, project2)
	if err != nil {
		t.Fatalf("cannot insert project2: %s", err)
	}

	pipelinePip1 := &sdk.Pipeline{
		Name:      "PIP1",
		ProjectID: project1.ID,
		Type:      sdk.BuildPipeline,
	}

	err = pipeline.InsertPipeline(db, pipelinePip1)
	if err != nil {
		t.Fatalf("cannot insert pipeline: %s", err)
	}

	groupInsert := &sdk.Group{
		Name: "GroupeFoo",
	}

	err = group.InsertGroup(db, groupInsert)
	if err != nil {
		t.Fatalf("cannot insert group: %s", err)
	}

	err = group.InsertGroupInProject(db, project1.ID, groupInsert.ID, 4)
	if err != nil {
		t.Fatalf("cannot insert project1 in group: %s", err)
	}
	err = group.InsertGroupInProject(db, project2.ID, groupInsert.ID, 5)
	if err != nil {
		t.Fatalf("cannot insert project1 in group: %s", err)
	}
	err = group.InsertGroupInPipeline(db, pipelinePip1.ID, groupInsert.ID, 7)
	if err != nil {
		t.Fatalf("cannot insert pipeline1 in group: %s", err)
	}

	err = group.InsertUserInGroup(db, groupInsert.ID, u.ID, false)
	if err != nil {
		t.Fatalf("cannot insert user1 in group: %s", err)
	}

	err = user.LoadUserPermissions(db, u)
	if err != nil {
		t.Fatalf("cannot load user group and project: %s", err)
	}

	if len(u.Groups) != 1 {
		t.Fatalf("Missing/TooMuch group on user, need 1, got %d", len(u.Groups))
	}
	if len(u.Groups[0].ProjectGroups) != 2 {
		t.Fatalf("Missing/TooMuch project on group.Need 2, got %d", len(u.Groups[0].ProjectGroups))
	}
	if len(u.Groups[0].PipelineGroups) != 1 {
		t.Fatalf("Missing/TooMuch pipeline on group.Need 1, got %d", len(u.Groups[0].PipelineGroups))
	}
}

func Test_getUserGroupsHandler(t *testing.T) {
	if testwithdb.DBDriver == "" {
		t.SkipNow()
		return
	}
	db, err := testwithdb.SetupPG(t, bootstrap.InitiliazeDB)
	assert.NoError(t, err)

	authDriver, _ := auth.GetDriver("local", nil, sessionstore.Options{Mode: "local", TTL: 30})
	router = &Router{authDriver, mux.NewRouter(), "/Test_getUserGroupsHandler"}
	router.init()

	g1 := &sdk.Group{
		Name: testwithdb.RandomString(t, 10),
	}

	g2 := &sdk.Group{
		Name: testwithdb.RandomString(t, 10),
	}

	u, pass, err := testwithdb.InsertLambaUser(t, db, g1, g2)
	assert.NoError(t, err)
	assert.NotZero(t, u)
	assert.NotZero(t, pass)

	uri := router.getRoute("GET", getUserGroupsHandler, map[string]string{"name": u.Username})
	if uri == "" {
		t.Fatal("Route not found")
	}

	req := testwithdb.NewAuthentifiedRequest(t, u, pass, "GET", uri, nil)

	//Do the request
	w := httptest.NewRecorder()
	router.mux.ServeHTTP(w, req)

	assert.Equal(t, 200, w.Code)

	t.Logf("Body: %s", w.Body.String())

	res := map[string][]sdk.Group{}
	if err := json.Unmarshal(w.Body.Bytes(), &res); err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, 2, len(res["groups"]))
	assert.Equal(t, 0, len(res["groups_admin"]))

}
<|endoftext|>


<commit_before>package plugin

import (
	"encoding/binary"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/vektra/cypress"
	"github.com/vektra/neko"
)

func TestSpool(t *testing.T) {
	n := neko.Start(t)

	root, err := ioutil.TempDir("", "spool")
	require.NoError(t, err)

	defer os.RemoveAll(root)

	tmpdir := filepath.Join(root, "spool")

	var sf *Spool

	n.Setup(func() {
		os.Mkdir(tmpdir, 0755)
		var err error
		sf, err = NewSpool(tmpdir)

		require.NoError(t, err)
	})

	n.Cleanup(func() {
		os.RemoveAll(tmpdir)
	})

	n.It("writes message to the current file in the spool dir", func() {
		m := cypress.Log()
		m.Add("hello", "world")

		err := sf.Read(m)
		require.NoError(t, err)

		data, err := ioutil.ReadFile(filepath.Join(tmpdir, "current"))
		require.NoError(t, err)

		assert.Equal(t, byte('+'), data[0])

		marsh, err := m.Marshal()
		require.NoError(t, err)

		l := binary.BigEndian.Uint64(data[1:])
		assert.Equal(t, l, uint64(len(marsh)))

		m2 := &cypress.Message{}

		err = m2.Unmarshal(data[9 : 9+l])
		require.NoError(t, err)

		assert.Equal(t, m.GetTimestamp(), m2.GetTimestamp())
		subject, ok := m2.GetString("hello")
		require.True(t, ok)

		assert.Equal(t, "world", subject)
	})

	n.It("provides a generator for reading messages back from the spool", func() {
		m := cypress.Log()
		m.Add("hello", "world")

		err := sf.Read(m)
		require.NoError(t, err)

		gen, err := sf.Generator()
		require.NoError(t, err)

		m2, err := gen.Generate()
		require.NoError(t, err)

		defer gen.Close()

		assert.Equal(t, m.GetTimestamp(), m2.GetTimestamp())
		subject, ok := m2.GetString("hello")
		require.True(t, ok)

		assert.Equal(t, "world", subject)
	})

	n.Meow()
}
<commit_msg>Fix spool test by using decoder<commit_after>package plugin

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/vektra/cypress"
	"github.com/vektra/neko"
)

func TestSpool(t *testing.T) {
	n := neko.Start(t)

	root, err := ioutil.TempDir("", "spool")
	require.NoError(t, err)

	defer os.RemoveAll(root)

	tmpdir := filepath.Join(root, "spool")

	var sf *Spool

	n.Setup(func() {
		os.Mkdir(tmpdir, 0755)
		var err error
		sf, err = NewSpool(tmpdir)

		require.NoError(t, err)
	})

	n.Cleanup(func() {
		os.RemoveAll(tmpdir)
	})

	n.It("writes message to the current file in the spool dir", func() {
		m := cypress.Log()
		m.Add("hello", "world")

		err := sf.Read(m)
		require.NoError(t, err)

		f, err := os.Open(filepath.Join(tmpdir, "current"))
		require.NoError(t, err)

		dec := cypress.NewDecoder()

		m2, err := dec.DecodeFrom(f)
		require.NoError(t, err)

		assert.Equal(t, m.GetTimestamp(), m2.GetTimestamp())
		subject, ok := m2.GetString("hello")
		require.True(t, ok)

		assert.Equal(t, "world", subject)
	})

	n.It("provides a generator for reading messages back from the spool", func() {
		m := cypress.Log()
		m.Add("hello", "world")

		err := sf.Read(m)
		require.NoError(t, err)

		gen, err := sf.Generator()
		require.NoError(t, err)

		m2, err := gen.Generate()
		require.NoError(t, err)

		defer gen.Close()

		assert.Equal(t, m.GetTimestamp(), m2.GetTimestamp())
		subject, ok := m2.GetString("hello")
		require.True(t, ok)

		assert.Equal(t, "world", subject)
	})

	n.Meow()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

type elasticEngine struct {
	client  *http.Client
	baseURL string
}

func NewElasticEngine(elasticURL string) Engine {
	transport := &http.Transport{MaxIdleConnsPerHost: 30}
	e := &elasticEngine{
		client:  &http.Client{Transport: transport},
		baseURL: elasticURL,
	}
	for strings.HasSuffix(e.baseURL, "/") {
		e.baseURL = e.baseURL[0 : len(e.baseURL)-1]
	}

	return e
}

func (mi *elasticEngine) Drop(collection string) {
	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/store/%s/", mi.baseURL, collection), nil)
	if err != nil {
		panic(err)
	}
	resp, err := mi.client.Do(req)
	if err != nil {
		panic(err)
	}
	if resp.StatusCode != 200 && resp.StatusCode != 404 {
		panic("drop fail")
	}
}

func (mi *elasticEngine) Write(collection, id string, cont Document) error {
	if id == "" {
		return errors.New("missing id")
	}

	r, w := io.Pipe()
	go func() {
		je := json.NewEncoder(w)
		err := je.Encode(cont)
		if err != nil {
			panic(err)
		}
		w.Close()
	}()

	req, err := http.NewRequest("PUT", fmt.Sprintf("%s/store/%s/%s", mi.baseURL, collection, id), r)
	if err != nil {
		panic(err)
	}
	resp, err := mi.client.Do(req)
	if err != nil {
		panic(err)
	}
	io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()

	return nil
}

func (ee *elasticEngine) Count(collection string) int {
	res, err := ee.client.Get(fmt.Sprintf("%s/store/%s/_count", ee.baseURL, collection))
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode == 200 {
		dec := json.NewDecoder(res.Body)
		var result esCountResult
		err := dec.Decode(&result)
		if err != nil {
			panic(err)
		}
		return result.Count
	}
	panic(res.StatusCode)
}

type esCountResult struct {
	Count int `json:"count"`
}

func (ee *elasticEngine) Load(collection, id string) (bool, Document, error) {
	res, err := ee.client.Get(fmt.Sprintf("%s/store/%s/%s", ee.baseURL, collection, id))
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	switch {
	case res.StatusCode == 200:
		dec := json.NewDecoder(res.Body)
		var result esGetResult
		err := dec.Decode(&result)
		if err != nil {
			panic(err)
		}
		var doc Document = result.Source
		return true, doc, nil
	case res.StatusCode == 404:
		return false, Document{}, nil
	default:
		panic(res.StatusCode)
	}
}

type esGetResult struct {
	Source Document `json:"_source"`
}

func (ee elasticEngine) All(collection string, closechan chan struct{}) (chan Document, error) {
	q := fmt.Sprintf("{\"query\":{\"match_all\": {}}, \"fields\":[], \"size\": %d,  \"from\": 0}", ee.Count(collection)+1000)
	return ee.query(collection, q, closechan)
}

func (ee elasticEngine) query(collection, q string, closechan chan struct{}) (chan Document, error) {
	cont := make(chan Document)

	res, err := ee.client.Post(fmt.Sprintf("%s/store/%s/_search", ee.baseURL, collection), "application/json", strings.NewReader(q))
	if err != nil {
		panic(err)
	}

	switch {
	case res.StatusCode == 200:
	case res.StatusCode == 400:
		res.Body.Close()
		return nil, ErrInvalidQuery
	default:
		panic(res.StatusCode)
	}

	go func() {
		defer close(cont)

		defer res.Body.Close()
		dec := json.NewDecoder(res.Body)
		var result esRecentResult
		err := dec.Decode(&result)
		if err != nil {
			panic(err)
		}
		for _, h := range result.Hits.Hits {
			id := h.Id
			found, c, err := ee.Load(collection, id)
			if err != nil {
				panic(err)
			}
			if !found {
				panic("fail")
			}
			select {
			case cont <- c:
			case <-closechan:
				break
			}
		}
	}()

	return cont, nil
}

func (ee elasticEngine) Close() {
}

type esRecentResult struct {
	Hits esRecentHit `json:"hits"`
}
type esRecentHit struct {
	Hits []esRecentId `json:"hits"`
}
type esRecentId struct {
	Id string `json:"_id"`
}

type esSearchResult struct {
	Hits hitResult `json:"hits"`
}
type hitResult struct {
	Hits []esSearchId `json:"hits"`
}
type esSearchId struct {
	Id string `json:"_id"`
}
<commit_msg>fix variable names<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

type elasticEngine struct {
	client  *http.Client
	baseURL string
}

func NewElasticEngine(elasticURL string) Engine {
	transport := &http.Transport{MaxIdleConnsPerHost: 30}
	e := &elasticEngine{
		client:  &http.Client{Transport: transport},
		baseURL: elasticURL,
	}
	for strings.HasSuffix(e.baseURL, "/") {
		e.baseURL = e.baseURL[0 : len(e.baseURL)-1]
	}

	return e
}

func (ee *elasticEngine) Drop(collection string) {
	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/store/%s/", ee.baseURL, collection), nil)
	if err != nil {
		panic(err)
	}
	resp, err := ee.client.Do(req)
	if err != nil {
		panic(err)
	}
	if resp.StatusCode != 200 && resp.StatusCode != 404 {
		panic("drop fail")
	}
}

func (ee *elasticEngine) Write(collection, id string, cont Document) error {
	if id == "" {
		return errors.New("missing id")
	}

	r, w := io.Pipe()
	go func() {
		je := json.NewEncoder(w)
		err := je.Encode(cont)
		if err != nil {
			panic(err)
		}
		w.Close()
	}()

	req, err := http.NewRequest("PUT", fmt.Sprintf("%s/store/%s/%s", ee.baseURL, collection, id), r)
	if err != nil {
		panic(err)
	}
	resp, err := ee.client.Do(req)
	if err != nil {
		panic(err)
	}
	io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()

	return nil
}

func (ee *elasticEngine) Count(collection string) int {
	res, err := ee.client.Get(fmt.Sprintf("%s/store/%s/_count", ee.baseURL, collection))
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode == 200 {
		dec := json.NewDecoder(res.Body)
		var result esCountResult
		err := dec.Decode(&result)
		if err != nil {
			panic(err)
		}
		return result.Count
	}
	panic(res.StatusCode)
}

type esCountResult struct {
	Count int `json:"count"`
}

func (ee *elasticEngine) Load(collection, id string) (bool, Document, error) {
	res, err := ee.client.Get(fmt.Sprintf("%s/store/%s/%s", ee.baseURL, collection, id))
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	switch {
	case res.StatusCode == 200:
		dec := json.NewDecoder(res.Body)
		var result esGetResult
		err := dec.Decode(&result)
		if err != nil {
			panic(err)
		}
		var doc Document = result.Source
		return true, doc, nil
	case res.StatusCode == 404:
		return false, Document{}, nil
	default:
		panic(res.StatusCode)
	}
}

type esGetResult struct {
	Source Document `json:"_source"`
}

func (ee elasticEngine) All(collection string, closechan chan struct{}) (chan Document, error) {
	q := fmt.Sprintf("{\"query\":{\"match_all\": {}}, \"fields\":[], \"size\": %d,  \"from\": 0}", ee.Count(collection)+1000)
	return ee.query(collection, q, closechan)
}

func (ee elasticEngine) query(collection, q string, closechan chan struct{}) (chan Document, error) {
	cont := make(chan Document)

	res, err := ee.client.Post(fmt.Sprintf("%s/store/%s/_search", ee.baseURL, collection), "application/json", strings.NewReader(q))
	if err != nil {
		panic(err)
	}

	switch {
	case res.StatusCode == 200:
	case res.StatusCode == 400:
		res.Body.Close()
		return nil, ErrInvalidQuery
	default:
		panic(res.StatusCode)
	}

	go func() {
		defer close(cont)

		defer res.Body.Close()
		dec := json.NewDecoder(res.Body)
		var result esRecentResult
		err := dec.Decode(&result)
		if err != nil {
			panic(err)
		}
		for _, h := range result.Hits.Hits {
			id := h.Id
			found, c, err := ee.Load(collection, id)
			if err != nil {
				panic(err)
			}
			if !found {
				panic("fail")
			}
			select {
			case cont <- c:
			case <-closechan:
				break
			}
		}
	}()

	return cont, nil
}

func (ee elasticEngine) Close() {
}

type esRecentResult struct {
	Hits esRecentHit `json:"hits"`
}
type esRecentHit struct {
	Hits []esRecentId `json:"hits"`
}
type esRecentId struct {
	Id string `json:"_id"`
}

type esSearchResult struct {
	Hits hitResult `json:"hits"`
}
type hitResult struct {
	Hits []esSearchId `json:"hits"`
}
type esSearchId struct {
	Id string `json:"_id"`
}
<|endoftext|>


<commit_before>package webapi

import (
	"encoding/json"
	"net/http"
)

const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT"
	DELETE = "DELETE"
)

type Handler func(*Request) (int, Response)
type Middleware func(Handler) Handler

type GetSupported interface {
	Get(*Request) (int, Response)
}

type PostSupported interface {
	Post(*Request) (int, Response)
}

type PutSupported interface {
	Put(*Request) (int, Response)
}

type DeleteSupported interface {
	Delete(*Request) (int, Response)
}

type WebApi struct {
	mux        *Mux
	middleware []Middleware
}

func NewAPI() *WebApi {
	return &WebApi{&Mux{}, nil}
}

// Handlers lets an enpoint return multiple handlers and thus
// return different responses based on whatever conditional you
// may come up with.
func Handlers(r *Request, handlers []Handler) (int, Response) {
	var code int
	var data Response
	for _, handler := range handlers {
		code, data = handler(r)

		if code > 0 {
			return code, data
		}
	}
	return code, data
}

// Returns a zero status code and empty response will make the
// Handlers function move on to the next handler
func Next() (int, Response) {
	return 0, Response{}
}

// Apply applies all the given middleware to provided
// handler function and then returns it
func Apply(handler Handler, middleware ...Middleware) Handler {
	h := handler
	for _, m := range middleware {
		h = m(h)
	}
	return h
}

func (webApi *WebApi) requestHandler(resource interface{}, middleware ...Middleware) HandlerFunc {
	return func(rw http.ResponseWriter, r *Request) {
		var handler Handler

		switch r.Method {
		case GET:
			if resource, ok := resource.(GetSupported); ok {
				handler = resource.Get
			}
		case POST:
			if resource, ok := resource.(PostSupported); ok {
				handler = resource.Post
			}
		case PUT:
			if resource, ok := resource.(PutSupported); ok {
				handler = resource.Put
			}
		case DELETE:
			if resource, ok := resource.(DeleteSupported); ok {
				handler = resource.Delete
			}
		}

		if handler == nil {
			rw.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		for _, m := range append(webApi.middleware, middleware...) {
			handler = m(handler)
		}

		code, data := handler(r)
		content, err := json.Marshal(data)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}

		rw.Header().Add("Content-Type", "application/json")
		rw.WriteHeader(code)
		rw.Write(content)
	}
}

func (webApi *WebApi) Apply(middleware ...Middleware) {
	webApi.middleware = middleware
}

func (webApi *WebApi) Add(path string, resource interface{}, middleware ...Middleware) {
	webApi.mux.HandleFunc(path, webApi.requestHandler(resource, middleware...))
}

func (webApi *WebApi) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	webApi.mux.ServeHTTP(rw, r)
}
<commit_msg>Add godocs to webapi.go<commit_after>package webapi

import (
	"encoding/json"
	"net/http"
)

// HTTP methods
const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT"
	DELETE = "DELETE"
)

// Handler function type used to handle HTTP requests
type Handler func(*Request) (int, Response)

// Middleware any function that takes and returns a handler
type Middleware func(Handler) Handler

// GetSupported is the interface that a resource has
// to implement in order to receive GET HTTP requests
type GetSupported interface {
	Get(*Request) (int, Response)
}

// PostSupported is the interface that a resource has
// to implement in order to receive POST HTTP requests
type PostSupported interface {
	Post(*Request) (int, Response)
}

// PutSupported is the interface that a resource has
// to implement in order to receive PUT HTTP requests
type PutSupported interface {
	Put(*Request) (int, Response)
}

// DeleteSupported is the interface that a resource has
// to implement in order to receive DELETE HTTP requests
type DeleteSupported interface {
	Delete(*Request) (int, Response)
}

// An API manages the resource endpoints that has been added
// to it and routes requests to the appropriate handler function(s).
//
// Since WebApi implements the net/http Handler interface, you can
// add any number of APIs under the same port/server, prefix your
// API and so on. For instance
//
// 	http.Handle("/api/v1", apiv1)
// 	http.Handle("/api/v2", apiv2)
type WebApi struct {
	mux        *Mux
	middleware []Middleware
}

// NewAPI returns a new instance of WebApi
func NewAPI() *WebApi {
	return &WebApi{&Mux{}, nil}
}

// Handlers lets an enpoint return multiple handlers and thus
// return different responses based on whatever conditional you
// may come up with.
func Handlers(r *Request, handlers []Handler) (int, Response) {
	var code int
	var data Response
	for _, handler := range handlers {
		code, data = handler(r)

		if code > 0 {
			return code, data
		}
	}
	return code, data
}

// Returns a zero status code and empty response will make the
// Handlers function move on to the next handler
func Next() (int, Response) {
	return 0, Response{}
}

// Apply applies all the given middleware to provided
// handler function and then returns it
func Apply(handler Handler, middleware ...Middleware) Handler {
	h := handler
	for _, m := range middleware {
		h = m(h)
	}
	return h
}

func (webApi *WebApi) requestHandler(resource interface{}, middleware ...Middleware) HandlerFunc {
	return func(rw http.ResponseWriter, r *Request) {
		var handler Handler

		switch r.Method {
		case GET:
			if resource, ok := resource.(GetSupported); ok {
				handler = resource.Get
			}
		case POST:
			if resource, ok := resource.(PostSupported); ok {
				handler = resource.Post
			}
		case PUT:
			if resource, ok := resource.(PutSupported); ok {
				handler = resource.Put
			}
		case DELETE:
			if resource, ok := resource.(DeleteSupported); ok {
				handler = resource.Delete
			}
		}

		if handler == nil {
			rw.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		for _, m := range append(webApi.middleware, middleware...) {
			handler = m(handler)
		}

		code, data := handler(r)
		content, err := json.Marshal(data)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}

		rw.Header().Add("Content-Type", "application/json")
		rw.WriteHeader(code)
		rw.Write(content)
	}
}

// Apply applies middleware to all subsequently added resources
func (webApi *WebApi) Apply(middleware ...Middleware) {
	webApi.middleware = middleware
}

// Add adds a resource to the API
func (webApi *WebApi) Add(path string, resource interface{}, middleware ...Middleware) {
	webApi.mux.HandleFunc(path, webApi.requestHandler(resource, middleware...))
}

// ServeHTTP aliases the webApi.mux.ServeHTTP function
func (webApi *WebApi) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	webApi.mux.ServeHTTP(rw, r)
}
<|endoftext|>


<commit_before>package ethutil

import (
	"fmt"
	"math/big"
)

// The different number of units
var (
	Douglas  = BigPow(10, 42)
	Einstein = BigPow(10, 21)
	Ether    = BigPow(10, 18)
	Finney   = BigPow(10, 15)
	Szabo    = BigPow(10, 12)
	Shannon  = BigPow(10, 9)
	Babbage  = BigPow(10, 6)
	Ada      = BigPow(10, 3)
	Wei      = big.NewInt(1)
)

//
// Currency to string
// Returns a string representing a human readable format
func CurrencyToString(num *big.Int) string {
	switch {
	case num.Cmp(Douglas) >= 0:
		return fmt.Sprintf("%v Douglas", new(big.Int).Div(num, Douglas))
	case num.Cmp(Einstein) >= 0:
		return fmt.Sprintf("%v Einstein", new(big.Int).Div(num, Einstein))
	case num.Cmp(Ether) >= 0:
		return fmt.Sprintf("%v Ether", new(big.Int).Div(num, Ether))
	case num.Cmp(Finney) >= 0:
		return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney))
	case num.Cmp(Szabo) >= 0:
		return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo))
	case num.Cmp(Shannon) >= 0:
		return fmt.Sprintf("%v Shannon", new(big.Int).Div(num, Shannon))
	case num.Cmp(Babbage) >= 0:
		return fmt.Sprintf("%v Babbage", new(big.Int).Div(num, Babbage))
	case num.Cmp(Ada) >= 0:
		return fmt.Sprintf("%v Ada", new(big.Int).Div(num, Ada))
	}

	return fmt.Sprintf("%v Wei", num)
}

// Common big integers often used
var (
	Big1   = big.NewInt(1)
	Big2   = big.NewInt(2)
	Big0   = big.NewInt(0)
	Big32  = big.NewInt(32)
	Big256 = big.NewInt(0xff)
)
<commit_msg>Add windows helper functions<commit_after>package ethutil

import (
	"fmt"
	"math/big"
	"runtime"
)

func IsWindows() bool {
	return runtime.GOOS == "windows"
}

func WindonizePath(path string) string {
	if string(path[0]) == "/" && IsWindows() {
		path = path[1:]
	}
	return path
}

// The different number of units
var (
	Douglas  = BigPow(10, 42)
	Einstein = BigPow(10, 21)
	Ether    = BigPow(10, 18)
	Finney   = BigPow(10, 15)
	Szabo    = BigPow(10, 12)
	Shannon  = BigPow(10, 9)
	Babbage  = BigPow(10, 6)
	Ada      = BigPow(10, 3)
	Wei      = big.NewInt(1)
)

//
// Currency to string
// Returns a string representing a human readable format
func CurrencyToString(num *big.Int) string {
	switch {
	case num.Cmp(Douglas) >= 0:
		return fmt.Sprintf("%v Douglas", new(big.Int).Div(num, Douglas))
	case num.Cmp(Einstein) >= 0:
		return fmt.Sprintf("%v Einstein", new(big.Int).Div(num, Einstein))
	case num.Cmp(Ether) >= 0:
		return fmt.Sprintf("%v Ether", new(big.Int).Div(num, Ether))
	case num.Cmp(Finney) >= 0:
		return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney))
	case num.Cmp(Szabo) >= 0:
		return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo))
	case num.Cmp(Shannon) >= 0:
		return fmt.Sprintf("%v Shannon", new(big.Int).Div(num, Shannon))
	case num.Cmp(Babbage) >= 0:
		return fmt.Sprintf("%v Babbage", new(big.Int).Div(num, Babbage))
	case num.Cmp(Ada) >= 0:
		return fmt.Sprintf("%v Ada", new(big.Int).Div(num, Ada))
	}

	return fmt.Sprintf("%v Wei", num)
}

// Common big integers often used
var (
	Big1   = big.NewInt(1)
	Big2   = big.NewInt(2)
	Big0   = big.NewInt(0)
	Big32  = big.NewInt(32)
	Big256 = big.NewInt(0xff)
)
<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"errors"
	"os"
	"path"
	"strings"

	"github.com/MJKWoolnough/byteio"
)

func setupServer(f *os.File, r *byteio.StickyReader, w *byteio.StickyWriter) error {
	stat, err := f.Stat()
	if err != nil {
		return err
	}
	zr, err := zip.NewReader(f, stat.Size())
	if err != nil {
		return err
	}
	l := r.ReadUint8()
	name := make([]byte, l)
	r.Read(name)
	jars := make([]*zip.File, 0, 16)
	for _, file := range zr.File {
		if strings.HasSuffix(file.Name, ".jar") {
			jars = append(jars, file)
		}
	}
	d, err := setupServerDir(string(name))
	if len(jars) == 0 {
		err = os.Rename(f.Name(), path.Join(d, "server.jar"))
		if err != nil {
			os.RemoveAll(d)
			return err
		}
	} else {
		if len(jars) > 1 {
			w.WriteInt8(2)
			w.WriteInt16(int16(len(jars)))
			p := r.ReadUint16()
			if int(p) >= len(jars) {
				return ErrNoServer
			}
			jars[0] = jars[p]
		}
		err = unzip(zr, d)
	}
	w.WriteUint8(1)
	config.createServer(string(name), d)
	return nil
}

func setupServerDir(name string) (string, error) {
	return "", nil
}

// Errors
var (
	ErrNoName   = errors.New("no name received")
	ErrNoServer = errors.New("no server found")
)
<commit_msg>Adder jar renaming<commit_after>package main

import (
	"archive/zip"
	"errors"
	"os"
	"path"
	"strings"

	"github.com/MJKWoolnough/byteio"
)

func setupServer(f *os.File, r *byteio.StickyReader, w *byteio.StickyWriter) error {
	stat, err := f.Stat()
	if err != nil {
		return err
	}
	zr, err := zip.NewReader(f, stat.Size())
	if err != nil {
		return err
	}
	l := r.ReadUint8()
	name := make([]byte, l)
	r.Read(name)
	jars := make([]*zip.File, 0, 16)
	for _, file := range zr.File {
		if strings.HasSuffix(file.Name, ".jar") {
			jars = append(jars, file)
		}
	}
	d, err := setupServerDir(string(name))
	if len(jars) == 0 {
		err = os.Rename(f.Name(), path.Join(d, "server.jar"))
	} else {
		if len(jars) > 1 {
			w.WriteInt8(2)
			w.WriteInt16(int16(len(jars)))
			p := r.ReadUint16()
			if int(p) >= len(jars) {
				return ErrNoServer
			}
			jars[0] = jars[p]
		}
		err = unzip(zr, d)
		if err == nil {
			err = os.Rename(path.Join(d, jars[0]), path.Join(d, "server.jar"))
		}
	}
	if err != nil {
		os.RemoveAll(d)
		return err
	}
	w.WriteUint8(1)
	config.createServer(string(name), d)
	return nil
}

func setupServerDir(name string) (string, error) {
	return "", nil
}

// Errors
var (
	ErrNoName   = errors.New("no name received")
	ErrNoServer = errors.New("no server found")
)
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"

	"gopkg.in/tomb.v2"

	log "github.com/Sirupsen/logrus"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
)

type DatadogDecoder struct {
	eth           layers.Ethernet
	dot1q         layers.Dot1Q
	ip4           layers.IPv4
	ip6           layers.IPv6
	ip6extensions layers.IPv6ExtensionSkipper
	tcp           layers.TCP
	payload       gopacket.Payload
	parser        *gopacket.DecodingLayerParser
	decoded       []gopacket.LayerType
}

func NewDatadogDecoder() *DatadogDecoder {
	d := &DatadogDecoder{
		decoded: make([]gopacket.LayerType, 0, 4),
	}
	d.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,
		&d.eth, &d.dot1q, &d.ip4, &d.ip6,
		&d.ip6extensions, &d.tcp, &d.payload)

	return d
}

// We use a DecodingLayerParser here instead of a simpler PacketSource.
// This approach should be measurably faster, but is also more rigid.
// PacketSource will handle any known type of packet safely and easily,
// but DecodingLayerParser will only handle those packet types we
// specifically pass in.  This trade-off can be quite useful, though, in
// high-throughput situations.
type DatadogSniffer struct {
	Iface      string
	Snaplen    int
	Filter     string
	ExpTTL     int
	IdleTTL    int
	Soften     bool
	statsdIP   string
	statsdPort int32
	pcapHandle *pcap.Handle
	decoder    *DatadogDecoder
	hostIPs    map[string]bool
	flows      *FlowMap
	reporter   *Client
	config     Config
	t          tomb.Tomb
}

func NewDatadogSniffer(instcfg InitConfig, cfg Config, filter string) (*DatadogSniffer, error) {
	//log.Printf("new stream %v:%v started", net, transport)
	d := &DatadogSniffer{
		Iface:      cfg.Interface,
		Snaplen:    instcfg.Snaplen,
		Filter:     filter,
		ExpTTL:     instcfg.ExpTTL,
		IdleTTL:    instcfg.IdleTTL,
		Soften:     false,
		statsdIP:   instcfg.StatsdIP,
		statsdPort: int32(instcfg.StatsdPort),
		pcapHandle: nil,
		hostIPs:    make(map[string]bool),
		flows:      NewFlowMap(),
		config:     cfg,
	}
	d.decoder = NewDatadogDecoder()
	var err error
	d.reporter, err = NewClient(net.ParseIP(d.statsdIP), d.statsdPort, statsdSleep, d.flows, d.config.Tags)
	if err != nil {
		return nil, err
	}

	return d, nil
}

func readUint32(data []byte) (ret uint32) {
	buf := bytes.NewBuffer(data)
	binary.Read(buf, binary.BigEndian, &ret)
	return
}

func GetTimestamps(tcp *layers.TCP) (uint32, uint32, error) {
	for i := range tcp.Options {
		if tcp.Options[i].OptionType == 8 {
			ts := readUint32(tcp.Options[i].OptionData[:4])
			tsecr := readUint32(tcp.Options[i].OptionData[4:])
			return ts, tsecr, nil
		}
	}
	return 0, 0, errors.New("No TCP timestamp Options!")
}

func (d *DatadogSniffer) Start() {
	d.t.Go(d.Sniff)
}

func (d *DatadogSniffer) Stop() error {
	d.t.Kill(nil)
	return d.t.Wait()
}

//Unexported - we only call this ourselves.
func (d *DatadogSniffer) die(err error) {
	d.t.Kill(err)
}

func (d *DatadogSniffer) Running() bool {
	return d.t.Alive()
}

func (d *DatadogSniffer) SetPcapHandle(handle *pcap.Handle) {
	d.pcapHandle = handle
}

func (d *DatadogSniffer) handlePacket(data []byte, ci *gopacket.CaptureInfo) error {
	err := d.decoder.parser.DecodeLayers(data, &d.decoder.decoded)
	if err != nil {
		log.Infof("error decoding packet: %v", err)
		return err
	}
	// Find either the IPv4 or IPv6 address to use as our network
	// layer.
	foundNetLayer := false
	foundIPv4Layer := false
	for _, typ := range d.decoder.decoded {
		switch typ {
		case layers.LayerTypeIPv4:
			foundNetLayer = true
			foundIPv4Layer = true
		case layers.LayerTypeIPv6:
			foundNetLayer = true
		case layers.LayerTypeTCP:
			if foundNetLayer && foundIPv4Layer {
				//do we have this flow? Build key
				var src, dst string
				ourIP := d.hostIPs[d.decoder.ip4.SrcIP.String()]

				// consider us always the SRC (this will help us keep just one tag for
				// all comms between two ip's
				if ourIP {
					src = net.JoinHostPort(d.decoder.ip4.SrcIP.String(), strconv.Itoa(int(d.decoder.tcp.SrcPort)))
					dst = net.JoinHostPort(d.decoder.ip4.DstIP.String(), strconv.Itoa(int(d.decoder.tcp.DstPort)))
				} else {
					src = net.JoinHostPort(d.decoder.ip4.DstIP.String(), strconv.Itoa(int(d.decoder.tcp.DstPort)))
					dst = net.JoinHostPort(d.decoder.ip4.SrcIP.String(), strconv.Itoa(int(d.decoder.tcp.SrcPort)))
				}

				idle := time.Duration(d.IdleTTL * int(time.Second))
				flow, exists := d.flows.Get(src + "-" + dst)
				if exists == false {
					// TCPAccounting objects self-expire if they are inactive for a period of time >idle
					if ourIP {
						flow = NewTCPAccounting(d.decoder.ip4.SrcIP, d.decoder.ip4.DstIP, d.decoder.tcp.SrcPort, d.decoder.tcp.DstPort, idle, &d.flows.Expire)
					} else {
						flow = NewTCPAccounting(d.decoder.ip4.DstIP, d.decoder.ip4.SrcIP, d.decoder.tcp.DstPort, d.decoder.tcp.SrcPort, idle, &d.flows.Expire)
					}
					flow.Lock()
					d.flows.Add(src+"-"+dst, flow)
					flow.SetExpiration(idle, src+"-"+dst)
				} else {
					//flow still alive - reset timer
					flow.Lock()
					flow.Alive.Reset(idle)
				}

				if d.ExpTTL > 0 && d.decoder.tcp.ACK && d.decoder.tcp.FIN && !flow.Done {
					expTTL := time.Duration(d.ExpTTL * int(time.Second))

					// Here we clean up flows that have expired by the book - that is, we have seen
					// the TCP stream come to an end FIN/ACK and have kept these around so short-lived
					// flows actually get reported.

					//set timer
					flow.Done = true
					flow.SetExpiration(expTTL, src+"-"+dst)
				}

				tcp_payload_sz := uint32(d.decoder.ip4.Length) - uint32((d.decoder.ip4.IHL+d.decoder.tcp.DataOffset)*4)
				if ourIP && tcp_payload_sz > 0 {
					var t TCPKey
					//get the TS
					ts, _, _ := GetTimestamps(&d.decoder.tcp)
					t.TS = ts
					t.Seq = d.decoder.tcp.Seq

					//insert or update
					flow.Timed[t] = ci.Timestamp.UnixNano()

				} else if !ourIP {
					var t TCPKey
					//get the TS
					_, tsecr, _ := GetTimestamps(&d.decoder.tcp)
					t.TS = tsecr
					t.Seq = d.decoder.tcp.Ack

					if flow.Timed[t] != 0 {
						if flow.Seen[d.decoder.tcp.Ack] == false && d.decoder.tcp.ACK {
							//we can't receive an ACK for packet we haven't seen sent - we're the source
							rtt := uint64(ci.Timestamp.UnixNano() - flow.Timed[t])
							flow.CalcSRTT(rtt, d.Soften)
							flow.CalcJitter(rtt, d.Soften)
							flow.MaxRTT(rtt)
							flow.MinRTT(rtt)
							flow.Last = rtt
							flow.Sampled++
						}
						flow.Seen[d.decoder.tcp.Ack] = true
					}
				}
				flow.Unlock()
			}
		}
	}
	return nil
}

func (d *DatadogSniffer) SniffLive() {

	quit := false
	for !quit {

		// Although desirable we're currently unable to use the ZeroCopy method
		// for reading packet data. Unfortunately successive calls invalidate the
		// data slice we're operating on. Giving place to bad results.
		// Keep this in mind as a viable optimization for the future:
		//   - packet retrieval using  ZeroCopyReadPacketData.
		data, ci, err := d.pcapHandle.ReadPacketData()

		if err == nil {
			d.handlePacket(data, &ci)
		}
		select {
		case <-d.t.Dying():
			log.Infof("Done sniffing.")
			quit = true
		default:
			continue
		}
	}
}

func (d *DatadogSniffer) SniffOffline() {
	packetSource := gopacket.NewPacketSource(d.pcapHandle, d.pcapHandle.LinkType())

	for packet := range packetSource.Packets() {
		//Grab Packet CaptureInfo metadata
		ci := packet.Metadata().CaptureInfo
		d.handlePacket(packet.Data(), &ci)
		select {
		case <-d.t.Dying():
			log.Infof("Done sniffing.")
			break
		default:
			continue
		}
	}
}

func (d *DatadogSniffer) Sniff() error {

	if d.pcapHandle == nil {

		log.Infof("starting capture on interface %q", d.Iface)

		if d.Iface != fileInterface {
			// Set up pcap packet capture
			inactive, err := pcap.NewInactiveHandle(d.Iface)
			if err != nil {
				log.Errorf("Unable to create inactive handle for %q", d.Iface)
				d.reporter.Stop()
				d.die(err)
				return err
			}
			defer inactive.CleanUp()

			inactive.SetSnapLen(d.Snaplen)
			inactive.SetPromisc(false)
			inactive.SetTimeout(time.Second)

			// Maybe we should make the timestamp source selectable - Not all OS will allow that.
			//ts_sources := inactive.SupportedTimestamps()
			//for i := range ts_sources {
			//	log.Printf("TS source: %v:%v", ts_sources[i], ts_sources[i].String())
			//}

			handle, err := inactive.Activate()
			if err != nil {
				log.Errorf("Unable to activate %q", d.Iface)
				d.reporter.Stop()
				d.die(err)
				return err
			}
			d.pcapHandle = handle
		} else {
			handle, err := pcap.OpenOffline(d.config.Pcap)
			if err != nil {
				log.Errorf("Unable to open pcap file %q", d.config.Pcap)
				d.reporter.Stop()
				d.die(err)
				return err
			}
			d.pcapHandle = handle
		}
	}

	ifaces, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatalf("Error getting interface details: %s", err)
	}

	ifaceFound := false
	ifaceDetails := make([]pcap.Interface, len(ifaces)-1)
	for i := range ifaces {
		if ifaces[i].Name == d.Iface {
			ifaceDetails[i] = ifaces[i]
			ifaceFound = true
		}
	}

	if !ifaceFound && d.Iface != fileInterface {
		log.Fatalf("Could not find interface details for: %s", d.Iface)
	}

	// we need to identify if we're the source/destination
	hosts := make([]string, 0)
	for i := range ifaceDetails {
		for j := range ifaceDetails[i].Addresses {
			ipStr := ifaceDetails[i].Addresses[j].IP.String()
			if strings.Contains(ipStr, "::") {
				log.Infof("IPv6 currently unsupported ignoring: %s", ipStr)
			} else {
				hosts = append(hosts, fmt.Sprintf("host %s", ipStr))
				d.hostIPs[ipStr] = true
			}
		}
	}
	for i := range d.config.Ips {
		hosts = append(hosts, fmt.Sprintf("host %s", d.config.Ips[i]))
	}

	bpfFilter := strings.Join(hosts, " or ")

	d.Filter += " and not host 127.0.0.1"
	if len(hosts) > 0 {
		d.Filter += " and " + " (" + bpfFilter + ")"
	}

	log.Infof("Setting BPF filter: %s", d.Filter)
	if err := d.pcapHandle.SetBPFFilter(d.Filter); err != nil {
		log.Fatalf("error setting BPF filter: %s", err)
	}

	log.Infof("reading in packets")
	if d.Iface == fileInterface {
		d.SniffOffline()
	} else {
		d.SniffLive()
	}

	for k := range d.flows.FlowMapKeyIterator() {
		flow, e := d.flows.Get(k)
		if e && flow.Sampled > 0 {
			if d.Soften {
				log.Infof("Flow %s\t w/ %d packets\tRTT:%6.2f ms", k, flow.Sampled, float64(int64(flow.SRTT)*int64(time.Nanosecond))/float64(time.Millisecond))
			} else {
				log.Infof("Flow %s\t w/ %d packets\tRTT:%6.2f ms", k, flow.Sampled, float64(flow.SRTT)*float64(time.Nanosecond)/float64(time.Millisecond))
			}
		}
	}

	//Shutdown reporter thread
	return d.reporter.Stop()
}
<commit_msg>[dd-tcp-rtt] removing commented code.<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"

	"gopkg.in/tomb.v2"

	log "github.com/Sirupsen/logrus"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
)

type DatadogDecoder struct {
	eth           layers.Ethernet
	dot1q         layers.Dot1Q
	ip4           layers.IPv4
	ip6           layers.IPv6
	ip6extensions layers.IPv6ExtensionSkipper
	tcp           layers.TCP
	payload       gopacket.Payload
	parser        *gopacket.DecodingLayerParser
	decoded       []gopacket.LayerType
}

func NewDatadogDecoder() *DatadogDecoder {
	d := &DatadogDecoder{
		decoded: make([]gopacket.LayerType, 0, 4),
	}
	d.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet,
		&d.eth, &d.dot1q, &d.ip4, &d.ip6,
		&d.ip6extensions, &d.tcp, &d.payload)

	return d
}

// We use a DecodingLayerParser here instead of a simpler PacketSource.
// This approach should be measurably faster, but is also more rigid.
// PacketSource will handle any known type of packet safely and easily,
// but DecodingLayerParser will only handle those packet types we
// specifically pass in.  This trade-off can be quite useful, though, in
// high-throughput situations.
type DatadogSniffer struct {
	Iface      string
	Snaplen    int
	Filter     string
	ExpTTL     int
	IdleTTL    int
	Soften     bool
	statsdIP   string
	statsdPort int32
	pcapHandle *pcap.Handle
	decoder    *DatadogDecoder
	hostIPs    map[string]bool
	flows      *FlowMap
	reporter   *Client
	config     Config
	t          tomb.Tomb
}

func NewDatadogSniffer(instcfg InitConfig, cfg Config, filter string) (*DatadogSniffer, error) {
	//log.Printf("new stream %v:%v started", net, transport)
	d := &DatadogSniffer{
		Iface:      cfg.Interface,
		Snaplen:    instcfg.Snaplen,
		Filter:     filter,
		ExpTTL:     instcfg.ExpTTL,
		IdleTTL:    instcfg.IdleTTL,
		Soften:     false,
		statsdIP:   instcfg.StatsdIP,
		statsdPort: int32(instcfg.StatsdPort),
		pcapHandle: nil,
		hostIPs:    make(map[string]bool),
		flows:      NewFlowMap(),
		config:     cfg,
	}
	d.decoder = NewDatadogDecoder()
	var err error
	d.reporter, err = NewClient(net.ParseIP(d.statsdIP), d.statsdPort, statsdSleep, d.flows, d.config.Tags)
	if err != nil {
		return nil, err
	}

	return d, nil
}

func readUint32(data []byte) (ret uint32) {
	buf := bytes.NewBuffer(data)
	binary.Read(buf, binary.BigEndian, &ret)
	return
}

func GetTimestamps(tcp *layers.TCP) (uint32, uint32, error) {
	for i := range tcp.Options {
		if tcp.Options[i].OptionType == 8 {
			ts := readUint32(tcp.Options[i].OptionData[:4])
			tsecr := readUint32(tcp.Options[i].OptionData[4:])
			return ts, tsecr, nil
		}
	}
	return 0, 0, errors.New("No TCP timestamp Options!")
}

func (d *DatadogSniffer) Start() {
	d.t.Go(d.Sniff)
}

func (d *DatadogSniffer) Stop() error {
	d.t.Kill(nil)
	return d.t.Wait()
}

//Unexported - we only call this ourselves.
func (d *DatadogSniffer) die(err error) {
	d.t.Kill(err)
}

func (d *DatadogSniffer) Running() bool {
	return d.t.Alive()
}

func (d *DatadogSniffer) SetPcapHandle(handle *pcap.Handle) {
	d.pcapHandle = handle
}

func (d *DatadogSniffer) handlePacket(data []byte, ci *gopacket.CaptureInfo) error {
	err := d.decoder.parser.DecodeLayers(data, &d.decoder.decoded)
	if err != nil {
		log.Infof("error decoding packet: %v", err)
		return err
	}
	// Find either the IPv4 or IPv6 address to use as our network
	// layer.
	foundNetLayer := false
	foundIPv4Layer := false
	for _, typ := range d.decoder.decoded {
		switch typ {
		case layers.LayerTypeIPv4:
			foundNetLayer = true
			foundIPv4Layer = true
		case layers.LayerTypeIPv6:
			foundNetLayer = true
		case layers.LayerTypeTCP:
			if foundNetLayer && foundIPv4Layer {
				//do we have this flow? Build key
				var src, dst string
				ourIP := d.hostIPs[d.decoder.ip4.SrcIP.String()]

				// consider us always the SRC (this will help us keep just one tag for
				// all comms between two ip's
				if ourIP {
					src = net.JoinHostPort(d.decoder.ip4.SrcIP.String(), strconv.Itoa(int(d.decoder.tcp.SrcPort)))
					dst = net.JoinHostPort(d.decoder.ip4.DstIP.String(), strconv.Itoa(int(d.decoder.tcp.DstPort)))
				} else {
					src = net.JoinHostPort(d.decoder.ip4.DstIP.String(), strconv.Itoa(int(d.decoder.tcp.DstPort)))
					dst = net.JoinHostPort(d.decoder.ip4.SrcIP.String(), strconv.Itoa(int(d.decoder.tcp.SrcPort)))
				}

				idle := time.Duration(d.IdleTTL * int(time.Second))
				flow, exists := d.flows.Get(src + "-" + dst)
				if exists == false {
					// TCPAccounting objects self-expire if they are inactive for a period of time >idle
					if ourIP {
						flow = NewTCPAccounting(d.decoder.ip4.SrcIP, d.decoder.ip4.DstIP, d.decoder.tcp.SrcPort, d.decoder.tcp.DstPort, idle, &d.flows.Expire)
					} else {
						flow = NewTCPAccounting(d.decoder.ip4.DstIP, d.decoder.ip4.SrcIP, d.decoder.tcp.DstPort, d.decoder.tcp.SrcPort, idle, &d.flows.Expire)
					}
					flow.Lock()
					d.flows.Add(src+"-"+dst, flow)
					flow.SetExpiration(idle, src+"-"+dst)
				} else {
					//flow still alive - reset timer
					flow.Lock()
					flow.Alive.Reset(idle)
				}

				if d.ExpTTL > 0 && d.decoder.tcp.ACK && d.decoder.tcp.FIN && !flow.Done {
					expTTL := time.Duration(d.ExpTTL * int(time.Second))

					// Here we clean up flows that have expired by the book - that is, we have seen
					// the TCP stream come to an end FIN/ACK and have kept these around so short-lived
					// flows actually get reported.

					//set timer
					flow.Done = true
					flow.SetExpiration(expTTL, src+"-"+dst)
				}

				tcp_payload_sz := uint32(d.decoder.ip4.Length) - uint32((d.decoder.ip4.IHL+d.decoder.tcp.DataOffset)*4)
				if ourIP && tcp_payload_sz > 0 {
					var t TCPKey
					//get the TS
					ts, _, _ := GetTimestamps(&d.decoder.tcp)
					t.TS = ts
					t.Seq = d.decoder.tcp.Seq

					//insert or update
					flow.Timed[t] = ci.Timestamp.UnixNano()

				} else if !ourIP {
					var t TCPKey
					//get the TS
					_, tsecr, _ := GetTimestamps(&d.decoder.tcp)
					t.TS = tsecr
					t.Seq = d.decoder.tcp.Ack

					if flow.Timed[t] != 0 {
						if flow.Seen[d.decoder.tcp.Ack] == false && d.decoder.tcp.ACK {
							//we can't receive an ACK for packet we haven't seen sent - we're the source
							rtt := uint64(ci.Timestamp.UnixNano() - flow.Timed[t])
							flow.CalcSRTT(rtt, d.Soften)
							flow.CalcJitter(rtt, d.Soften)
							flow.MaxRTT(rtt)
							flow.MinRTT(rtt)
							flow.Last = rtt
							flow.Sampled++
						}
						flow.Seen[d.decoder.tcp.Ack] = true
					}
				}
				flow.Unlock()
			}
		}
	}
	return nil
}

func (d *DatadogSniffer) SniffLive() {

	quit := false
	for !quit {

		// Although desirable we're currently unable to use the ZeroCopy method
		// for reading packet data. Unfortunately successive calls invalidate the
		// data slice we're operating on. Giving place to bad results.
		// Keep this in mind as a viable optimization for the future:
		//   - packet retrieval using  ZeroCopyReadPacketData.
		data, ci, err := d.pcapHandle.ReadPacketData()

		if err == nil {
			d.handlePacket(data, &ci)
		}
		select {
		case <-d.t.Dying():
			log.Infof("Done sniffing.")
			quit = true
		default:
			continue
		}
	}
}

func (d *DatadogSniffer) SniffOffline() {
	packetSource := gopacket.NewPacketSource(d.pcapHandle, d.pcapHandle.LinkType())

	for packet := range packetSource.Packets() {
		//Grab Packet CaptureInfo metadata
		ci := packet.Metadata().CaptureInfo
		d.handlePacket(packet.Data(), &ci)
		select {
		case <-d.t.Dying():
			log.Infof("Done sniffing.")
			break
		default:
			continue
		}
	}
}

func (d *DatadogSniffer) Sniff() error {

	if d.pcapHandle == nil {

		log.Infof("starting capture on interface %q", d.Iface)

		if d.Iface != fileInterface {
			// Set up pcap packet capture
			inactive, err := pcap.NewInactiveHandle(d.Iface)
			if err != nil {
				log.Errorf("Unable to create inactive handle for %q", d.Iface)
				d.reporter.Stop()
				d.die(err)
				return err
			}
			defer inactive.CleanUp()

			inactive.SetSnapLen(d.Snaplen)
			inactive.SetPromisc(false)
			inactive.SetTimeout(time.Second)

			// TODO: Make the timestamp source selectable - Not all OS will allow that.
			//       call SupportedTimestamps() on handle to check what's available
			handle, err := inactive.Activate()
			if err != nil {
				log.Errorf("Unable to activate %q", d.Iface)
				d.reporter.Stop()
				d.die(err)
				return err
			}
			d.pcapHandle = handle
		} else {
			handle, err := pcap.OpenOffline(d.config.Pcap)
			if err != nil {
				log.Errorf("Unable to open pcap file %q", d.config.Pcap)
				d.reporter.Stop()
				d.die(err)
				return err
			}
			d.pcapHandle = handle
		}
	}

	ifaces, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatalf("Error getting interface details: %s", err)
	}

	ifaceFound := false
	ifaceDetails := make([]pcap.Interface, len(ifaces)-1)
	for i := range ifaces {
		if ifaces[i].Name == d.Iface {
			ifaceDetails[i] = ifaces[i]
			ifaceFound = true
		}
	}

	if !ifaceFound && d.Iface != fileInterface {
		log.Fatalf("Could not find interface details for: %s", d.Iface)
	}

	// we need to identify if we're the source/destination
	hosts := make([]string, 0)
	for i := range ifaceDetails {
		for j := range ifaceDetails[i].Addresses {
			ipStr := ifaceDetails[i].Addresses[j].IP.String()
			if strings.Contains(ipStr, "::") {
				log.Infof("IPv6 currently unsupported ignoring: %s", ipStr)
			} else {
				hosts = append(hosts, fmt.Sprintf("host %s", ipStr))
				d.hostIPs[ipStr] = true
			}
		}
	}
	for i := range d.config.Ips {
		hosts = append(hosts, fmt.Sprintf("host %s", d.config.Ips[i]))
	}

	bpfFilter := strings.Join(hosts, " or ")

	d.Filter += " and not host 127.0.0.1"
	if len(hosts) > 0 {
		d.Filter += " and " + " (" + bpfFilter + ")"
	}

	log.Infof("Setting BPF filter: %s", d.Filter)
	if err := d.pcapHandle.SetBPFFilter(d.Filter); err != nil {
		log.Fatalf("error setting BPF filter: %s", err)
	}

	log.Infof("reading in packets")
	if d.Iface == fileInterface {
		d.SniffOffline()
	} else {
		d.SniffLive()
	}

	for k := range d.flows.FlowMapKeyIterator() {
		flow, e := d.flows.Get(k)
		if e && flow.Sampled > 0 {
			if d.Soften {
				log.Infof("Flow %s\t w/ %d packets\tRTT:%6.2f ms", k, flow.Sampled, float64(int64(flow.SRTT)*int64(time.Nanosecond))/float64(time.Millisecond))
			} else {
				log.Infof("Flow %s\t w/ %d packets\tRTT:%6.2f ms", k, flow.Sampled, float64(flow.SRTT)*float64(time.Nanosecond)/float64(time.Millisecond))
			}
		}
	}

	//Shutdown reporter thread
	return d.reporter.Stop()
}
<|endoftext|>


<commit_before>package lxd

import (
	"fmt"
	"net/url"

	"github.com/lxc/lxd/shared/api"
)

// GetNetworkACLs returns a list of Network ACL structs.
func (r *ProtocolLXD) GetNetworkACLs() ([]api.NetworkACL, error) {
	if !r.HasExtension("network_acl") {
		return nil, fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	acls := []api.NetworkACL{}

	// Fetch the raw value.
	_, err := r.queryStruct("GET", "/network-acls?recursion=1", nil, "", &acls)
	if err != nil {
		return nil, err
	}

	return acls, nil
}

// GetNetworkACL returns a Network ACL entry for the provided name.
func (r *ProtocolLXD) GetNetworkACL(name string) (*api.NetworkACL, string, error) {
	if !r.HasExtension("network_acl") {
		return nil, "", fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	acl := api.NetworkACL{}

	// Fetch the raw value.
	etag, err := r.queryStruct("GET", fmt.Sprintf("/network-acls/%s", url.PathEscape(name)), nil, "", &acl)
	if err != nil {
		return nil, "", err
	}

	return &acl, etag, nil
}

// CreateNetworkACL defines a new network ACL using the provided struct.
func (r *ProtocolLXD) CreateNetworkACL(acl api.NetworkACLsPost) error {
	if !r.HasExtension("network_acl") {
		return fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	// Send the request.
	_, _, err := r.query("POST", "/network-acls", acl, "")
	if err != nil {
		return err
	}

	return nil
}

// UpdateNetworkACL updates the network ACL to match the provided struct.
func (r *ProtocolLXD) UpdateNetworkACL(name string, acl api.NetworkACLPut, ETag string) error {
	if !r.HasExtension("network_acl") {
		return fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	// Send the request.
	_, _, err := r.query("PUT", fmt.Sprintf("/network-acls/%s", url.PathEscape(name)), acl, ETag)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>client/lxc/network/acls: Implements RenameNetworkACL function<commit_after>package lxd

import (
	"fmt"
	"net/url"

	"github.com/lxc/lxd/shared/api"
)

// GetNetworkACLs returns a list of Network ACL structs.
func (r *ProtocolLXD) GetNetworkACLs() ([]api.NetworkACL, error) {
	if !r.HasExtension("network_acl") {
		return nil, fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	acls := []api.NetworkACL{}

	// Fetch the raw value.
	_, err := r.queryStruct("GET", "/network-acls?recursion=1", nil, "", &acls)
	if err != nil {
		return nil, err
	}

	return acls, nil
}

// GetNetworkACL returns a Network ACL entry for the provided name.
func (r *ProtocolLXD) GetNetworkACL(name string) (*api.NetworkACL, string, error) {
	if !r.HasExtension("network_acl") {
		return nil, "", fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	acl := api.NetworkACL{}

	// Fetch the raw value.
	etag, err := r.queryStruct("GET", fmt.Sprintf("/network-acls/%s", url.PathEscape(name)), nil, "", &acl)
	if err != nil {
		return nil, "", err
	}

	return &acl, etag, nil
}

// CreateNetworkACL defines a new network ACL using the provided struct.
func (r *ProtocolLXD) CreateNetworkACL(acl api.NetworkACLsPost) error {
	if !r.HasExtension("network_acl") {
		return fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	// Send the request.
	_, _, err := r.query("POST", "/network-acls", acl, "")
	if err != nil {
		return err
	}

	return nil
}

// UpdateNetworkACL updates the network ACL to match the provided struct.
func (r *ProtocolLXD) UpdateNetworkACL(name string, acl api.NetworkACLPut, ETag string) error {
	if !r.HasExtension("network_acl") {
		return fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	// Send the request.
	_, _, err := r.query("PUT", fmt.Sprintf("/network-acls/%s", url.PathEscape(name)), acl, ETag)
	if err != nil {
		return err
	}

	return nil
}

// RenameNetworkACL renames an existing network ACL entry.
func (r *ProtocolLXD) RenameNetworkACL(name string, acl api.NetworkACLPost) error {
	if !r.HasExtension("network_acl") {
		return fmt.Errorf(`The server is missing the required "network_acl" API extension`)
	}

	// Send the request.
	_, _, err := r.query("POST", fmt.Sprintf("/network-acls/%s", url.PathEscape(name)), acl, "")
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>