<commit_before>package scanner

import (
	"bufio"
	"encoding/gob"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/fsutil"
	"github.com/Symantec/Dominator/lib/hash"
	"io"
	"os"
	"path"
	"time"
)

type unreferencedObject struct {
	Hash   hash.Hash
	Length uint64
	Age    time.Time
}

type unreferencedObjectsEntry struct {
	object unreferencedObject
	prev   *unreferencedObjectsEntry
	next   *unreferencedObjectsEntry
}

type unreferencedObjectsList struct {
	length              uint64
	totalBytes          uint64
	oldest              *unreferencedObjectsEntry
	newest              *unreferencedObjectsEntry
	hashToEntry         map[hash.Hash]*unreferencedObjectsEntry
	lastRegeneratedTime time.Time
}

func loadUnreferencedObjects(fName string) (*unreferencedObjectsList, error) {
	file, err := os.Open(fName)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
		return &unreferencedObjectsList{
			hashToEntry: make(map[hash.Hash]*unreferencedObjectsEntry),
		}, nil
	}
	defer file.Close()
	reader := fsutil.NewChecksumReader(bufio.NewReader(file))
	decoder := gob.NewDecoder(reader)
	var length uint64
	if err := decoder.Decode(&length); err != nil {
		return nil, err
	}
	list := &unreferencedObjectsList{
		length:      length,
		hashToEntry: make(map[hash.Hash]*unreferencedObjectsEntry, length),
	}
	for count := uint64(0); count < length; count++ {
		var object unreferencedObject
		if err := decoder.Decode(&object); err != nil {
			return nil, err
		}
		entry := &unreferencedObjectsEntry{object: object}
		list.addEntry(entry)
	}
	if err := reader.VerifyChecksum(); err != nil {
		return nil, err
	}
	return list, nil
}

func (list *unreferencedObjectsList) write(w io.Writer) error {
	writer := fsutil.NewChecksumWriter(w)
	encoder := gob.NewEncoder(writer)
	if err := encoder.Encode(list.length); err != nil {
		return err
	}
	for entry := list.oldest; entry != nil; entry = entry.next {
		if err := encoder.Encode(entry.object); err != nil {
			return err
		}
	}
	return writer.WriteChecksum()
}

func (list *unreferencedObjectsList) addEntry(entry *unreferencedObjectsEntry) {
	entry.prev = list.newest
	if list.oldest == nil {
		list.oldest = entry
	} else {
		list.newest.next = entry
	}
	list.newest = entry
	list.hashToEntry[entry.object.Hash] = entry
	list.length++
	list.totalBytes += entry.object.Length
}

func (list *unreferencedObjectsList) addObject(hashVal hash.Hash,
	length uint64) {
	if _, ok := list.hashToEntry[hashVal]; !ok {
		object := unreferencedObject{hashVal, length, time.Now()}
		list.addEntry(&unreferencedObjectsEntry{object: object})
	}
}

func (list *unreferencedObjectsList) removeObject(hashVal hash.Hash) bool {
	entry := list.hashToEntry[hashVal]
	if entry == nil {
		return false
	}
	if entry.prev == nil {
		list.oldest = entry.next
	} else {
		entry.prev.next = entry.next
	}
	if entry.next == nil {
		list.newest = entry.prev
	} else {
		entry.next.prev = entry.prev
	}
	list.length--
	list.totalBytes -= entry.object.Length
	return true
}

func (imdb *ImageDataBase) maybeAddToUnreferencedObjectsList(
	inodeTable filesystem.InodeTable) {
	// First get a list of objects in the image being deleted.
	objects := make(map[hash.Hash]uint64)
	for _, inode := range inodeTable {
		if inode, ok := inode.(*filesystem.RegularInode); ok {
			objects[inode.Hash] = inode.Size
		}
	}
	// Scan all remaining images and remove their objects from the list.
	for _, image := range imdb.imageMap {
		for _, inode := range image.FileSystem.InodeTable {
			if inode, ok := inode.(*filesystem.RegularInode); ok {
				delete(objects, inode.Hash)
			}
		}
	}
	for object, size := range objects {
		imdb.unreferencedObjects.addObject(object, size)
	}
}

func (imdb *ImageDataBase) removeFromUnreferencedObjectsList(
	inodeTable filesystem.InodeTable) {
	changed := false
	for _, inode := range inodeTable {
		if inode, ok := inode.(*filesystem.RegularInode); ok {
			if imdb.unreferencedObjects.removeObject(inode.Hash) {
				changed = true
			}
		}
	}
	if changed {
		if err := imdb.writeUnreferencedObjectsList(); err != nil {
			imdb.logger.Printf("Error writing unreferenced objects list: %s\n",
				err)
		}
	}
}

func (imdb *ImageDataBase) writeUnreferencedObjectsList() error {
	filename := path.Join(imdb.baseDir, unreferencedObjectsFile)
	file, err := fsutil.CreateRenamingWriter(filename, filePerms)
	if err != nil {
		return err
	}
	defer file.Close()
	writer := bufio.NewWriter(file)
	defer writer.Flush()
	return imdb.unreferencedObjects.write(writer)
}

func (imdb *ImageDataBase) garbageCollector() {
}

func (imdb *ImageDataBase) regenerateUnreferencedObjectsList() {
	scanTime := time.Now()
	// First generate list of currently unused objects.
	objectsMap := imdb.objectServer.ListObjectSizes()
	for _, imageName := range imdb.ListImages() {
		image := imdb.GetImage(imageName)
		if image == nil {
			continue
		}
		for _, inode := range image.FileSystem.InodeTable {
			if inode, ok := inode.(*filesystem.RegularInode); ok {
				delete(objectsMap, inode.Hash)
			}
		}
	}
	// Now add unused objects to cached list.
	for hashVal, length := range objectsMap {
		imdb.unreferencedObjects.addObject(hashVal, length)
	}
	// Finally remove objects from cached list which are no longer unreferenced.
	for entry := imdb.unreferencedObjects.oldest; entry != nil; {
		hashVal := entry.object.Hash
		entry = entry.next
		if _, ok := objectsMap[hashVal]; ok {
			imdb.unreferencedObjects.removeObject(hashVal)
		}
	}
	imdb.unreferencedObjects.lastRegeneratedTime = scanTime
}
<commit_msg>Add more methods to manage unreferenced object list.<commit_after>package scanner

import (
	"bufio"
	"encoding/gob"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/fsutil"
	"github.com/Symantec/Dominator/lib/hash"
	"io"
	"os"
	"path"
	"time"
)

type unreferencedObject struct {
	Hash   hash.Hash
	Length uint64
	Age    time.Time
}

type unreferencedObjectsEntry struct {
	object unreferencedObject
	prev   *unreferencedObjectsEntry
	next   *unreferencedObjectsEntry
}

type unreferencedObjectsList struct {
	length              uint64
	totalBytes          uint64
	oldest              *unreferencedObjectsEntry
	newest              *unreferencedObjectsEntry
	hashToEntry         map[hash.Hash]*unreferencedObjectsEntry
	lastRegeneratedTime time.Time
}

func loadUnreferencedObjects(fName string) (*unreferencedObjectsList, error) {
	file, err := os.Open(fName)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
		return &unreferencedObjectsList{
			hashToEntry: make(map[hash.Hash]*unreferencedObjectsEntry),
		}, nil
	}
	defer file.Close()
	reader := fsutil.NewChecksumReader(bufio.NewReader(file))
	decoder := gob.NewDecoder(reader)
	var length uint64
	if err := decoder.Decode(&length); err != nil {
		return nil, err
	}
	list := &unreferencedObjectsList{
		length:      length,
		hashToEntry: make(map[hash.Hash]*unreferencedObjectsEntry, length),
	}
	for count := uint64(0); count < length; count++ {
		var object unreferencedObject
		if err := decoder.Decode(&object); err != nil {
			return nil, err
		}
		entry := &unreferencedObjectsEntry{object: object}
		list.addEntry(entry)
	}
	if err := reader.VerifyChecksum(); err != nil {
		return nil, err
	}
	return list, nil
}

func (list *unreferencedObjectsList) write(w io.Writer) error {
	writer := fsutil.NewChecksumWriter(w)
	encoder := gob.NewEncoder(writer)
	if err := encoder.Encode(list.length); err != nil {
		return err
	}
	for entry := list.oldest; entry != nil; entry = entry.next {
		if err := encoder.Encode(entry.object); err != nil {
			return err
		}
	}
	return writer.WriteChecksum()
}

func (list *unreferencedObjectsList) addEntry(entry *unreferencedObjectsEntry) {
	entry.prev = list.newest
	if list.oldest == nil {
		list.oldest = entry
	} else {
		list.newest.next = entry
	}
	list.newest = entry
	list.hashToEntry[entry.object.Hash] = entry
	list.length++
	list.totalBytes += entry.object.Length
}

func (list *unreferencedObjectsList) addObject(hashVal hash.Hash,
	length uint64) {
	if _, ok := list.hashToEntry[hashVal]; !ok {
		object := unreferencedObject{hashVal, length, time.Now()}
		list.addEntry(&unreferencedObjectsEntry{object: object})
	}
}

func (list *unreferencedObjectsList) list() map[hash.Hash]uint64 {
	objectsMap := make(map[hash.Hash]uint64, list.length)
	for entry := list.oldest; entry != nil; entry = entry.next {
		objectsMap[entry.object.Hash] = entry.object.Length
	}
	return objectsMap
}

func (list *unreferencedObjectsList) removeObject(hashVal hash.Hash) bool {
	entry := list.hashToEntry[hashVal]
	if entry == nil {
		return false
	}
	if entry.prev == nil {
		list.oldest = entry.next
	} else {
		entry.prev.next = entry.next
	}
	if entry.next == nil {
		list.newest = entry.prev
	} else {
		entry.next.prev = entry.prev
	}
	list.length--
	list.totalBytes -= entry.object.Length
	return true
}

func (imdb *ImageDataBase) maybeAddToUnreferencedObjectsList(
	inodeTable filesystem.InodeTable) {
	// First get a list of objects in the image being deleted.
	objects := make(map[hash.Hash]uint64)
	for _, inode := range inodeTable {
		if inode, ok := inode.(*filesystem.RegularInode); ok {
			objects[inode.Hash] = inode.Size
		}
	}
	// Scan all remaining images and remove their objects from the list.
	for _, image := range imdb.imageMap {
		for _, inode := range image.FileSystem.InodeTable {
			if inode, ok := inode.(*filesystem.RegularInode); ok {
				delete(objects, inode.Hash)
			}
		}
	}
	for object, size := range objects {
		imdb.unreferencedObjects.addObject(object, size)
	}
}

func (imdb *ImageDataBase) removeFromUnreferencedObjectsList(
	inodeTable filesystem.InodeTable) {
	changed := false
	for _, inode := range inodeTable {
		if inode, ok := inode.(*filesystem.RegularInode); ok {
			if imdb.unreferencedObjects.removeObject(inode.Hash) {
				changed = true
			}
		}
	}
	if changed {
		if err := imdb.writeUnreferencedObjectsList(); err != nil {
			imdb.logger.Printf("Error writing unreferenced objects list: %s\n",
				err)
		}
	}
}

func (imdb *ImageDataBase) writeUnreferencedObjectsList() error {
	filename := path.Join(imdb.baseDir, unreferencedObjectsFile)
	file, err := fsutil.CreateRenamingWriter(filename, filePerms)
	if err != nil {
		return err
	}
	defer file.Close()
	writer := bufio.NewWriter(file)
	defer writer.Flush()
	return imdb.unreferencedObjects.write(writer)
}

func (imdb *ImageDataBase) garbageCollector() {
}

func (imdb *ImageDataBase) maybeRegenerateUnreferencedObjectsList() {
	lastMutationTime := imdb.objectServer.LastMutationTime()
	if lastMutationTime.After(imdb.unreferencedObjects.lastRegeneratedTime) {
		imdb.regenerateUnreferencedObjectsList()
	}
}

func (imdb *ImageDataBase) regenerateUnreferencedObjectsList() {
	scanTime := time.Now()
	// First generate list of currently unused objects.
	objectsMap := imdb.objectServer.ListObjectSizes()
	for _, imageName := range imdb.ListImages() {
		image := imdb.GetImage(imageName)
		if image == nil {
			continue
		}
		for _, inode := range image.FileSystem.InodeTable {
			if inode, ok := inode.(*filesystem.RegularInode); ok {
				delete(objectsMap, inode.Hash)
			}
		}
	}
	// Now add unused objects to cached list.
	for hashVal, length := range objectsMap {
		imdb.unreferencedObjects.addObject(hashVal, length)
	}
	// Finally remove objects from cached list which are no longer unreferenced.
	for entry := imdb.unreferencedObjects.oldest; entry != nil; {
		hashVal := entry.object.Hash
		entry = entry.next
		if _, ok := objectsMap[hashVal]; ok {
			imdb.unreferencedObjects.removeObject(hashVal)
		}
	}
	imdb.unreferencedObjects.lastRegeneratedTime = scanTime
}
<|endoftext|>


<commit_before>// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package allocator

import (
	"fmt"
	"log"
	"net"
	"os"

	"github.com/containernetworking/cni/pkg/ip"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/shwinpiocess/bonding-consul/backend"
)

type IPAllocator struct {
	// start is inclusive and may be allocated
	start net.IP
	// end is inclusive and may be allocated
	end   net.IP
	conf  *IPAMConfig
	store backend.Store
}

func NewIPAllocator(conf *IPAMConfig, store backend.Store) (*IPAllocator, error) {
	// Can't create an allocator for a network with no addresses, eg
	// a /32 or /31
	ones, masklen := conf.Subnet.Mask.Size()
	if ones > masklen-2 {
		return nil, fmt.Errorf("Network %v too small to allocate from", conf.Subnet)
	}

	var (
		start net.IP
		end   net.IP
		err   error
	)
	start, end, err = networkRange((*net.IPNet)(&conf.Subnet))
	if err != nil {
		return nil, err
	}

	// skip the .0 address
	start = ip.NextIP(start)

	if conf.RangeStart != nil {
		if err := validateRangeIP(conf.RangeStart, (*net.IPNet)(&conf.Subnet), start, end); err != nil {
			return nil, err
		}
		start = conf.RangeStart
	}
	if conf.RangeEnd != nil {
		if err := validateRangeIP(conf.RangeEnd, (*net.IPNet)(&conf.Subnet), start, end); err != nil {
			return nil, err
		}
		end = conf.RangeEnd
	}
	return &IPAllocator{start, end, conf, store}, nil
}

func canonicalizeIP(ip net.IP) (net.IP, error) {
	if ip.To4() != nil {
		return ip.To4(), nil
	} else if ip.To16() != nil {
		return ip.To16(), nil
	}
	return nil, fmt.Errorf("IP %s not v4 nor v6", ip)
}

// Ensures @ip is within @ipnet, and (if given) inclusive of @start and @end
func validateRangeIP(ip net.IP, ipnet *net.IPNet, start net.IP, end net.IP) error {
	var err error

	// Make sure we can compare IPv4 addresses directly
	ip, err = canonicalizeIP(ip)
	if err != nil {
		return err
	}

	if !ipnet.Contains(ip) {
		return fmt.Errorf("%s not in network: %s", ip, ipnet)
	}

	if start != nil {
		start, err = canonicalizeIP(start)
		if err != nil {
			return err
		}
		if len(ip) != len(start) {
			return fmt.Errorf("%s %d not same size IP address as start %s %d", ip, len(ip), start, len(start))
		}
		for i := 0; i < len(ip); i++ {
			if ip[i] > start[i] {
				break
			} else if ip[i] < start[i] {
				return fmt.Errorf("%s outside of network %s with start %s", ip, ipnet, start)
			}
		}
	}

	if end != nil {
		end, err = canonicalizeIP(end)
		if err != nil {
			return err
		}
		if len(ip) != len(end) {
			return fmt.Errorf("%s %d not same size IP address as end %s %d", ip, len(ip), end, len(end))
		}
		for i := 0; i < len(ip); i++ {
			if ip[i] < end[i] {
				break
			} else if ip[i] > end[i] {
				return fmt.Errorf("%s outside of network %s with end %s", ip, ipnet, end)
			}
		}
	}
	return nil
}

// Returns newly allocated IP along with its config
func (a *IPAllocator) Get(id string) (*current.IPConfig, []*types.Route, error) {
	a.store.Lock()
	defer a.store.Unlock()

	gw := a.conf.Gateway
	if gw == nil {
		gw = ip.NextIP(a.conf.Subnet.IP)
	}

	var requestedIP net.IP
	if a.conf.Args != nil {
		requestedIP = a.conf.Args.IP
	}

	if requestedIP != nil {
		if gw != nil && gw.Equal(a.conf.Args.IP) {
			return nil, nil, fmt.Errorf("requested IP must differ gateway IP")
		}

		subnet := net.IPNet{
			IP:   a.conf.Subnet.IP,
			Mask: a.conf.Subnet.Mask,
		}
		err := validateRangeIP(requestedIP, &subnet, a.start, a.end)
		if err != nil {
			return nil, nil, err
		}

		reserved, err := a.store.Reserve(id, requestedIP)
		if err != nil {
			return nil, nil, err
		}

		if reserved {
			ipConfig := &current.IPConfig{
				Version: "4",
				Address: net.IPNet{IP: requestedIP, Mask: a.conf.Subnet.Mask},
				Gateway: gw,
			}
			routes := convertRoutesToCurrent(a.conf.Routes)
			return ipConfig, routes, nil
		}
		return nil, nil, fmt.Errorf("requested IP address %q is not available in network: %s", requestedIP, a.conf.Name)
	}

	// startIP, endIP := a.getSearchRange()
	// for cur := startIP; ; cur = a.nextIP(cur) {
	for cur := a.start; !cur.Equal(a.end); cur = a.nextIP(cur) {
		// don't allocate gateway IP
		if gw != nil && cur.Equal(gw) {
			continue
		}

		reserved, err := a.store.Reserve(id, cur)
		if err != nil {
			return nil, nil, err
		}
		if reserved {
			ipConfig := &current.IPConfig{
				Version: "4",
				Address: net.IPNet{IP: cur, Mask: a.conf.Subnet.Mask},
				Gateway: gw,
			}
			routes := convertRoutesToCurrent(a.conf.Routes)
			return ipConfig, routes, nil
		}
		// break here to complete the loop
		if cur.Equal(endIP) {
			break
		}
	}
	return nil, nil, fmt.Errorf("no IP addresses available in network: %s", a.conf.Name)
}

// Releases all IPs allocated for the container with given ID
func (a *IPAllocator) Release(id string) error {
	a.store.Lock()
	defer a.store.Unlock()

	return a.store.ReleaseByID(id)
}

// Return the start and end IP addresses of a given subnet, excluding
// the broadcast address (eg, 192.168.1.255)
func networkRange(ipnet *net.IPNet) (net.IP, net.IP, error) {
	if ipnet.IP == nil {
		return nil, nil, fmt.Errorf("missing field %q in IPAM configuration", "subnet")
	}
	ip, err := canonicalizeIP(ipnet.IP)
	if err != nil {
		return nil, nil, fmt.Errorf("IP not v4 nor v6")
	}

	if len(ip) != len(ipnet.Mask) {
		return nil, nil, fmt.Errorf("IPNet IP and Mask version mismatch")
	}

	var end net.IP
	for i := 0; i < len(ip); i++ {
		end = append(end, ip[i]|^ipnet.Mask[i])
	}

	// Exclude the broadcast address for IPv4
	if ip.To4() != nil {
		end[3]--
	}

	return ipnet.IP, end, nil
}

// nextIP returns the next ip of curIP within ipallocator's subnet
func (a *IPAllocator) nextIP(curIP net.IP) net.IP {
	if curIP.Equal(a.end) {
		return a.start
	}
	return ip.NextIP(curIP)
}

// getSearchRange returns the start and end ip based on the last reserved ip
func (a *IPAllocator) getSearchRange() (net.IP, net.IP) {
	var startIP net.IP
	var endIP net.IP
	startFromLastReservedIP := false
	lastReservedIP, err := a.store.LastReservedIP()
	if err != nil && !os.IsNotExist(err) {
		log.Printf("Error retriving last reserved ip: %v", err)
	} else if lastReservedIP != nil {
		subnet := net.IPNet{
			IP:   a.conf.Subnet.IP,
			Mask: a.conf.Subnet.Mask,
		}
		err := validateRangeIP(lastReservedIP, &subnet, a.start, a.end)
		if err == nil {
			startFromLastReservedIP = true
		}
	}
	if startFromLastReservedIP {
		startIP = a.nextIP(lastReservedIP)
		endIP = lastReservedIP
	} else {
		startIP = a.start
		endIP = a.end
	}
	return startIP, endIP
}
<commit_msg>修改allocator规则<commit_after>// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package allocator

import (
	"fmt"
	"log"
	"net"
	"os"

	"github.com/containernetworking/cni/pkg/ip"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/shwinpiocess/bonding-consul/backend"
)

type IPAllocator struct {
	// start is inclusive and may be allocated
	start net.IP
	// end is inclusive and may be allocated
	end   net.IP
	conf  *IPAMConfig
	store backend.Store
}

func NewIPAllocator(conf *IPAMConfig, store backend.Store) (*IPAllocator, error) {
	// Can't create an allocator for a network with no addresses, eg
	// a /32 or /31
	ones, masklen := conf.Subnet.Mask.Size()
	if ones > masklen-2 {
		return nil, fmt.Errorf("Network %v too small to allocate from", conf.Subnet)
	}

	var (
		start net.IP
		end   net.IP
		err   error
	)
	start, end, err = networkRange((*net.IPNet)(&conf.Subnet))
	if err != nil {
		return nil, err
	}

	// skip the .0 address
	start = ip.NextIP(start)

	if conf.RangeStart != nil {
		if err := validateRangeIP(conf.RangeStart, (*net.IPNet)(&conf.Subnet), start, end); err != nil {
			return nil, err
		}
		start = conf.RangeStart
	}
	if conf.RangeEnd != nil {
		if err := validateRangeIP(conf.RangeEnd, (*net.IPNet)(&conf.Subnet), start, end); err != nil {
			return nil, err
		}
		end = conf.RangeEnd
	}
	return &IPAllocator{start, end, conf, store}, nil
}

func canonicalizeIP(ip net.IP) (net.IP, error) {
	if ip.To4() != nil {
		return ip.To4(), nil
	} else if ip.To16() != nil {
		return ip.To16(), nil
	}
	return nil, fmt.Errorf("IP %s not v4 nor v6", ip)
}

// Ensures @ip is within @ipnet, and (if given) inclusive of @start and @end
func validateRangeIP(ip net.IP, ipnet *net.IPNet, start net.IP, end net.IP) error {
	var err error

	// Make sure we can compare IPv4 addresses directly
	ip, err = canonicalizeIP(ip)
	if err != nil {
		return err
	}

	if !ipnet.Contains(ip) {
		return fmt.Errorf("%s not in network: %s", ip, ipnet)
	}

	if start != nil {
		start, err = canonicalizeIP(start)
		if err != nil {
			return err
		}
		if len(ip) != len(start) {
			return fmt.Errorf("%s %d not same size IP address as start %s %d", ip, len(ip), start, len(start))
		}
		for i := 0; i < len(ip); i++ {
			if ip[i] > start[i] {
				break
			} else if ip[i] < start[i] {
				return fmt.Errorf("%s outside of network %s with start %s", ip, ipnet, start)
			}
		}
	}

	if end != nil {
		end, err = canonicalizeIP(end)
		if err != nil {
			return err
		}
		if len(ip) != len(end) {
			return fmt.Errorf("%s %d not same size IP address as end %s %d", ip, len(ip), end, len(end))
		}
		for i := 0; i < len(ip); i++ {
			if ip[i] < end[i] {
				break
			} else if ip[i] > end[i] {
				return fmt.Errorf("%s outside of network %s with end %s", ip, ipnet, end)
			}
		}
	}
	return nil
}

// Returns newly allocated IP along with its config
func (a *IPAllocator) Get(id string) (*current.IPConfig, []*types.Route, error) {
	a.store.Lock()
	defer a.store.Unlock()

	gw := a.conf.Gateway
	if gw == nil {
		gw = ip.NextIP(a.conf.Subnet.IP)
	}

	var requestedIP net.IP
	if a.conf.Args != nil {
		requestedIP = a.conf.Args.IP
	}

	if requestedIP != nil {
		if gw != nil && gw.Equal(a.conf.Args.IP) {
			return nil, nil, fmt.Errorf("requested IP must differ gateway IP")
		}

		subnet := net.IPNet{
			IP:   a.conf.Subnet.IP,
			Mask: a.conf.Subnet.Mask,
		}
		err := validateRangeIP(requestedIP, &subnet, a.start, a.end)
		if err != nil {
			return nil, nil, err
		}

		reserved, err := a.store.Reserve(id, requestedIP)
		if err != nil {
			return nil, nil, err
		}

		if reserved {
			ipConfig := &current.IPConfig{
				Version: "4",
				Address: net.IPNet{IP: requestedIP, Mask: a.conf.Subnet.Mask},
				Gateway: gw,
			}
			routes := convertRoutesToCurrent(a.conf.Routes)
			return ipConfig, routes, nil
		}
		return nil, nil, fmt.Errorf("requested IP address %q is not available in network: %s", requestedIP, a.conf.Name)
	}

	// startIP, endIP := a.getSearchRange()
	// for cur := startIP; ; cur = a.nextIP(cur) {
	for cur := a.start; !cur.Equal(a.end); cur = a.nextIP(cur) {
		// don't allocate gateway IP
		if gw != nil && cur.Equal(gw) {
			continue
		}

		reserved, err := a.store.Reserve(id, cur)
		if err != nil {
			return nil, nil, err
		}
		if reserved {
			ipConfig := &current.IPConfig{
				Version: "4",
				Address: net.IPNet{IP: cur, Mask: a.conf.Subnet.Mask},
				Gateway: gw,
			}
			routes := convertRoutesToCurrent(a.conf.Routes)
			return ipConfig, routes, nil
		}
		// break here to complete the loop
		// if cur.Equal(endIP) {
		// 	break
		// }
	}
	return nil, nil, fmt.Errorf("no IP addresses available in network: %s", a.conf.Name)
}

// Releases all IPs allocated for the container with given ID
func (a *IPAllocator) Release(id string) error {
	a.store.Lock()
	defer a.store.Unlock()

	return a.store.ReleaseByID(id)
}

// Return the start and end IP addresses of a given subnet, excluding
// the broadcast address (eg, 192.168.1.255)
func networkRange(ipnet *net.IPNet) (net.IP, net.IP, error) {
	if ipnet.IP == nil {
		return nil, nil, fmt.Errorf("missing field %q in IPAM configuration", "subnet")
	}
	ip, err := canonicalizeIP(ipnet.IP)
	if err != nil {
		return nil, nil, fmt.Errorf("IP not v4 nor v6")
	}

	if len(ip) != len(ipnet.Mask) {
		return nil, nil, fmt.Errorf("IPNet IP and Mask version mismatch")
	}

	var end net.IP
	for i := 0; i < len(ip); i++ {
		end = append(end, ip[i]|^ipnet.Mask[i])
	}

	// Exclude the broadcast address for IPv4
	if ip.To4() != nil {
		end[3]--
	}

	return ipnet.IP, end, nil
}

// nextIP returns the next ip of curIP within ipallocator's subnet
func (a *IPAllocator) nextIP(curIP net.IP) net.IP {
	if curIP.Equal(a.end) {
		return a.start
	}
	return ip.NextIP(curIP)
}

// getSearchRange returns the start and end ip based on the last reserved ip
// func (a *IPAllocator) getSearchRange() (net.IP, net.IP) {
// 	var startIP net.IP
// 	var endIP net.IP
// 	startFromLastReservedIP := false
// 	lastReservedIP, err := a.store.LastReservedIP()
// 	if err != nil && !os.IsNotExist(err) {
// 		log.Printf("Error retriving last reserved ip: %v", err)
// 	} else if lastReservedIP != nil {
// 		subnet := net.IPNet{
// 			IP:   a.conf.Subnet.IP,
// 			Mask: a.conf.Subnet.Mask,
// 		}
// 		err := validateRangeIP(lastReservedIP, &subnet, a.start, a.end)
// 		if err == nil {
// 			startFromLastReservedIP = true
// 		}
// 	}
// 	if startFromLastReservedIP {
// 		startIP = a.nextIP(lastReservedIP)
// 		endIP = lastReservedIP
// 	} else {
// 		startIP = a.start
// 		endIP = a.end
// 	}
// 	return startIP, endIP
// }
<|endoftext|>


<commit_before>package medias

import (
	"encoding/json"
	"errors"
	"github.com/mitchellh/mapstructure"
	"io/ioutil"
	"log"
	"os"
	"fmt"
	"github.com/Zenika/MARCEL/backend/commons"
	"github.com/Zenika/MARCEL/backend/plugins"
	"github.com/Zenika/MARCEL/backend/containers"
	"strconv"
	"strings"
)

type Manager struct {
	configPath     string
	configFileName string
	configFullpath string
	Config         *Configuration

	pluginManager *plugins.Manager
}

func NewManager(pluginManager *plugins.Manager, configPath, configFilename string) *Manager {
	manager := new(Manager)

	manager.configPath = configPath
	manager.configFileName = configFilename

	manager.pluginManager = pluginManager

	manager.configFullpath = fmt.Sprintf("%s%c%s", configPath, os.PathSeparator, configFilename)
	manager.Config = NewConfiguration()

	return manager
}

// LoadMedias loads medias configuration from DB and stor it in memory
func (m *Manager) Load() {
	log.Printf("Start Loading Medias from DB.")

	m.CreateSaveFileIfNotExist(m.configPath, m.configFileName)

	//Medias configurations are loaded from a JSON file on the FS.
	content, err := ioutil.ReadFile(m.configFullpath)
	commons.Check(err)

	var obj interface{}
	json.Unmarshal([]byte(content), &obj)

	if obj == nil {
		obj = make(map[string]interface{})
	}
	err = mapstructure.Decode(obj.(map[string]interface{}), m.Config)
	if err != nil {
		panic(err)
	}

	log.Print("Medias configurations is loaded...")
}

func (m *Manager) GetConfiguration() (*Configuration) {
	log.Println("Getting global medias config")

	return m.Config
}

func (m *Manager) GetAll() ([]Media) {
	log.Println("Getting all medias")

	return m.Config.Medias
}

// GetMedia Return the media with this id
func (m *Manager) Get(idMedia int) (*Media, error) {

	log.Println("Getting media with id: ", idMedia)
	for _, media := range m.Config.Medias {
		if idMedia == media.ID {
			return &media, nil
		}
	}

	return nil, errors.New("NO_MEDIA_FOUND")
}

// CreateMedia Create a new Media, save it into memory and commit
func (m *Manager) CreateEmpty() (*Media) {

	log.Println("Creating media")

	newMedia := NewMedia()
	newMedia.ID = m.GetNextID()

	//save it into the MediasConfiguration
	m.Save(newMedia)
	m.Commit()

	return newMedia
}

// RemoveMedia Remove media from memory
func (m *Manager) Remove(media *Media) {
	log.Println("Removing media")
	i := m.getPosition(media)

	if i >= 0 {
		m.Config.Medias = append(m.Config.Medias[:i], m.Config.Medias[i+1:]...)
	}
}

// SaveMedia Save media information in memory.
func (m *Manager) Save(media *Media) {
	log.Println("Saving media")
	m.Remove(media)
	m.Config.Medias = append(m.Config.Medias, *media)
}

// Commit Save all medias in DB.
// Here DB is a JSON file
func (m *Manager) Commit() {
	content, _ := json.Marshal(m.Config)

	err := ioutil.WriteFile(m.configFullpath, content, 0644)

	if err != nil {
		log.Println("Cannot save medias configuration:")
		log.Panic(err)
	}
}

// CreateSaveFileIfNotExist check if the save file for medias exists and create it if not.
func (m *Manager) CreateSaveFileIfNotExist(filePath string, fileName string) {

	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		log.Println("Data directory did not exist. Create it.")
		os.Mkdir(filePath, 0755)
	}

	var fullPath string = fmt.Sprintf("%s%c%s", filePath, os.PathSeparator, fileName)
	if _, err := os.Stat(fullPath); os.IsNotExist(err) {

		f, err := os.Create(fullPath)
		commons.Check(err)

		log.Println("Medias configuration file created at %v", fullPath)

		f.Close()

		//commit a first time to ensure the configuration has been saved
		m.Commit()
	}
}

func (m *Manager) Activate(media *Media) error {

	sep := string(os.PathSeparator)

	for _, mp := range media.Plugins {

		plugin, err := m.pluginManager.Get(mp.EltName)
		if err != nil {
			//plugin does not exist (anymore ?) in the catalog. Obviously, it should never append.
			log.Println(err.Error())
			//Don't return an error now, we need to activate the other plugins
		}

		// 1 : duplicate plugin files into "medias/{idMedia}/{plugins_EltName}/{idInstance}"
		mpPath := "medias" + sep + strconv.Itoa(media.ID) + sep + mp.EltName + sep + mp.InstanceId
		err = commons.CopyDir("plugins"+sep+mp.EltName, mpPath)

		if err != nil {
			return err
		}

		if mp.BackEnd != nil {
			retour, err := containers.InstallImage(mpPath + sep + "back" + sep + plugin.Backend.Dockerimage)

			if err != nil {
				//Don't return an error now, we need to activate the other plugins
				log.Println(err.Error())
			}
			imageName := strings.TrimSpace(strings.TrimPrefix(retour, "Loaded image: "))
			externalPort := m.GetPortNumberForPlugin()

			dockerContainerId, err := containers.StartContainer(imageName, plugin.Backend.Port, externalPort, mp.BackEnd.Props)
			if err != nil {
				//Don't return an error now, we need to activate the other plugins
				log.Println(err.Error())
			} else {
				mp.BackEnd.Port = externalPort
				mp.BackEnd.DockerImageName = imageName
				mp.BackEnd.DockerContainerId = strings.TrimSpace(dockerContainerId)
			}
		}
	}

	media.IsActive = true

	m.Save(media)

	return nil
}

func (m *Manager) Deactivate(media *Media) error {

	//stop all backends instances and free ports number
	for _, mp := range media.Plugins {
		if mp.BackEnd != nil {

			containers.StopContainer(mp.BackEnd.DockerContainerId)

			m.FreePortNumberForPlugin(mp.BackEnd.Port)
		}
	}

	//remove plugins files
	sep := string(os.PathSeparator)
	os.RemoveAll("medias" + sep + strconv.Itoa(media.ID))

	media.IsActive = false

	m.Save(media)

	return nil
}

// GetMediaPosition Return position of a media in the list
func (m *Manager) getPosition(media *Media) int {
	for p, m := range m.Config.Medias {
		if m.ID == media.ID {
			return p
		}
	}
	return -1
}

func (m *Manager) GetPortNumberForPlugin() int {

	// 1 : try to pop a port number from the pool
	if len(m.Config.PortsPool) > 0 {
		p := m.Config.PortsPool[0]
		//remove the first number
		m.Config.PortsPool = m.Config.PortsPool[1:]
		return p
	}

	// 2 : if pool is empty, just increment the counter
	p := m.Config.NextFreePortNumber
	m.Config.NextFreePortNumber += 1

	return p
}

func (m *Manager) FreePortNumberForPlugin(portNumber int) {
	for i, v := range m.Config.PortsPool {
		if portNumber == v {
			m.Config.PortsPool = append(m.Config.PortsPool[:i], m.Config.PortsPool[i+1:]...)
		}
	}
}

func (m *Manager) GetNextID() int {
	m.Config.LastID = m.Config.LastID + 1
	return m.Config.LastID
}
<commit_msg>[Fix] Add error handler<commit_after>package medias

import (
	"encoding/json"
	"errors"
	"github.com/mitchellh/mapstructure"
	"io/ioutil"
	"log"
	"os"
	"fmt"
	"github.com/Zenika/MARCEL/backend/commons"
	"github.com/Zenika/MARCEL/backend/plugins"
	"github.com/Zenika/MARCEL/backend/containers"
	"strconv"
	"strings"
)

type Manager struct {
	configPath     string
	configFileName string
	configFullpath string
	Config         *Configuration

	pluginManager *plugins.Manager
}

func NewManager(pluginManager *plugins.Manager, configPath, configFilename string) *Manager {
	manager := new(Manager)

	manager.configPath = configPath
	manager.configFileName = configFilename

	manager.pluginManager = pluginManager

	manager.configFullpath = fmt.Sprintf("%s%c%s", configPath, os.PathSeparator, configFilename)
	manager.Config = NewConfiguration()

	return manager
}

// LoadMedias loads medias configuration from DB and stor it in memory
func (m *Manager) Load() {
	log.Printf("Start Loading Medias from DB.")

	m.CreateSaveFileIfNotExist(m.configPath, m.configFileName)

	//Medias configurations are loaded from a JSON file on the FS.
	content, err := ioutil.ReadFile(m.configFullpath)
	commons.Check(err)

	var obj interface{}
	json.Unmarshal([]byte(content), &obj)

	if obj == nil {
		obj = make(map[string]interface{})
	}
	err = mapstructure.Decode(obj.(map[string]interface{}), m.Config)
	if err != nil {
		panic(err)
	}

	log.Print("Medias configurations is loaded...")
}

func (m *Manager) GetConfiguration() (*Configuration) {
	log.Println("Getting global medias config")

	return m.Config
}

func (m *Manager) GetAll() ([]Media) {
	log.Println("Getting all medias")

	return m.Config.Medias
}

// GetMedia Return the media with this id
func (m *Manager) Get(idMedia int) (*Media, error) {

	log.Println("Getting media with id: ", idMedia)
	for _, media := range m.Config.Medias {
		if idMedia == media.ID {
			return &media, nil
		}
	}

	return nil, errors.New("NO_MEDIA_FOUND")
}

// CreateMedia Create a new Media, save it into memory and commit
func (m *Manager) CreateEmpty() (*Media) {

	log.Println("Creating media")

	newMedia := NewMedia()
	newMedia.ID = m.GetNextID()

	//save it into the MediasConfiguration
	m.Save(newMedia)
	m.Commit()

	return newMedia
}

// RemoveMedia Remove media from memory
func (m *Manager) Remove(media *Media) {
	log.Println("Removing media")
	i := m.getPosition(media)

	if i >= 0 {
		m.Config.Medias = append(m.Config.Medias[:i], m.Config.Medias[i+1:]...)
	}
}

// SaveMedia Save media information in memory.
func (m *Manager) Save(media *Media) {
	log.Println("Saving media")
	m.Remove(media)
	m.Config.Medias = append(m.Config.Medias, *media)
}

// Commit Save all medias in DB.
// Here DB is a JSON file
func (m *Manager) Commit() {
	content, _ := json.Marshal(m.Config)

	err := ioutil.WriteFile(m.configFullpath, content, 0644)

	if err != nil {
		log.Println("Cannot save medias configuration:")
		log.Panic(err)
	}
}

// CreateSaveFileIfNotExist check if the save file for medias exists and create it if not.
func (m *Manager) CreateSaveFileIfNotExist(filePath string, fileName string) {

	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		log.Println("Data directory did not exist. Create it.")
		os.Mkdir(filePath, 0755)
	}

	var fullPath string = fmt.Sprintf("%s%c%s", filePath, os.PathSeparator, fileName)
	if _, err := os.Stat(fullPath); os.IsNotExist(err) {

		f, err := os.Create(fullPath)
		commons.Check(err)

		log.Println("Medias configuration file created at %v", fullPath)

		f.Close()

		//commit a first time to ensure the configuration has been saved
		m.Commit()
	}
}

func (m *Manager) Activate(media *Media) error {

	sep := string(os.PathSeparator)

	for _, mp := range media.Plugins {

		plugin, err := m.pluginManager.Get(mp.EltName)
		if err != nil {
			//plugin does not exist (anymore ?) in the catalog. Obviously, it should never append.
			log.Println(err.Error())
			//Don't return an error now, we need to activate the other plugins
		}

		// 1 : duplicate plugin files into "medias/{idMedia}/{plugins_EltName}/{idInstance}"
		mpPath := "medias" + sep + strconv.Itoa(media.ID) + sep + mp.EltName + sep + mp.InstanceId
		err = commons.CopyDir("plugins"+sep+mp.EltName, mpPath)

		if err != nil {
			return err
		}

		if mp.BackEnd != nil {
			retour, err := containers.InstallImage(mpPath + sep + "back" + sep + plugin.Backend.Dockerimage)

			if err != nil {
				//Don't return an error now, we need to activate the other plugins
				log.Println(err.Error())
			}
			imageName := strings.TrimSpace(strings.TrimPrefix(retour, "Loaded image: "))
			externalPort := m.GetPortNumberForPlugin()

			dockerContainerId, err := containers.StartContainer(imageName, plugin.Backend.Port, externalPort, mp.BackEnd.Props)
			if err != nil {
				//Don't return an error now, we need to activate the other plugins
				log.Println(err.Error())
			} else {
				mp.BackEnd.Port = externalPort
				mp.BackEnd.DockerImageName = imageName
				mp.BackEnd.DockerContainerId = strings.TrimSpace(dockerContainerId)
			}
		}
	}

	media.IsActive = true

	m.Save(media)

	return nil
}

func (m *Manager) Deactivate(media *Media) error {

	//stop all backends instances and free ports number
	for _, mp := range media.Plugins {
		if mp.BackEnd != nil {

			containers.StopContainer(mp.BackEnd.DockerContainerId)

			m.FreePortNumberForPlugin(mp.BackEnd.Port)
		}
	}

	//remove plugins files
	sep := string(os.PathSeparator)
	err := os.RemoveAll("medias" + sep + strconv.Itoa(media.ID))

	if err != nil {
		return err
	}

	media.IsActive = false

	m.Save(media)

	return nil
}

// GetMediaPosition Return position of a media in the list
func (m *Manager) getPosition(media *Media) int {
	for p, m := range m.Config.Medias {
		if m.ID == media.ID {
			return p
		}
	}
	return -1
}

func (m *Manager) GetPortNumberForPlugin() int {

	// 1 : try to pop a port number from the pool
	if len(m.Config.PortsPool) > 0 {
		p := m.Config.PortsPool[0]
		//remove the first number
		m.Config.PortsPool = m.Config.PortsPool[1:]
		return p
	}

	// 2 : if pool is empty, just increment the counter
	p := m.Config.NextFreePortNumber
	m.Config.NextFreePortNumber += 1

	return p
}

func (m *Manager) FreePortNumberForPlugin(portNumber int) {
	for i, v := range m.Config.PortsPool {
		if portNumber == v {
			m.Config.PortsPool = append(m.Config.PortsPool[:i], m.Config.PortsPool[i+1:]...)
		}
	}
}

func (m *Manager) GetNextID() int {
	m.Config.LastID = m.Config.LastID + 1
	return m.Config.LastID
}
<|endoftext|>


<commit_before>package simple

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

	"github.com/clawio/clawiod/entities"
	"github.com/clawio/clawiod/services/metadata/metadatacontroller"
	"github.com/satori/go.uuid"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

var user = &entities.User{Username: "test"}

type TestSuite struct {
	suite.Suite
	metadataController metadatacontroller.MetaDataController
	controller         *controller
}

func Test(t *testing.T) {
	suite.Run(t, new(TestSuite))
}
func (suite *TestSuite) SetupTest() {
	opts := &Options{
		MetaDataDir: "/tmp",
		TempDir:     "/tmp",
	}
	metadataController := New(opts)
	// create homedir for user test
	err := os.MkdirAll("/tmp/t/test", 0755)
	require.Nil(suite.T(), err)
	suite.metadataController = metadataController
	suite.controller = suite.metadataController.(*controller)
}
func (suite *TestSuite) TeardownTest() {
	os.RemoveAll("/tmp/t")
}
func (suite *TestSuite) New() {
	opts := &Options{
		MetaDataDir: "/tmp",
		TempDir:     "/tmp",
	}
	require.IsType(suite.T(), &controller{}, New(opts))
}
func (suite *TestSuite) TestNew_withNilOptions() {
	require.IsType(suite.T(), &controller{}, New(nil))
}
func (suite *TestSuite) TestInit() {
	err := suite.metadataController.Init(user)
	require.Nil(suite.T(), err)
}
func (suite *TestSuite) TestInit_withError() {
	suite.controller.metaDataDir = "/i/cannot/write/here"
	err := suite.metadataController.Init(user)
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestExamineObject() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "myblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	info, err := suite.metadataController.ExamineObject(user, "myblob")
	require.Nil(suite.T(), err)
	require.Equal(suite.T(), "myblob", info.PathSpec)
	require.Equal(suite.T(), int64(1), info.Size)
	require.Equal(suite.T(), "", info.Checksum)
	require.Equal(suite.T(), entities.ObjectTypeBLOBMimeType, info.MimeType)
	require.Equal(suite.T(), entities.ObjectTypeBLOB, info.Type)
}

func (suite *TestSuite) TestExamineObject_withNotFound() {
	_, err := suite.metadataController.ExamineObject(user, "notexists")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestListTree() {
	err := os.MkdirAll(suite.controller.getStoragePath(user, "testlisttree"), 0755)
	require.Nil(suite.T(), err)
	err = os.MkdirAll(suite.controller.getStoragePath(user, "testlisttree/othertree"), 0755)
	require.Nil(suite.T(), err)
	infos, err := suite.metadataController.ListTree(user, "testlisttree")
	require.Nil(suite.T(), err)
	require.Equal(suite.T(), 1, len(infos))
}

func (suite *TestSuite) TestListTree_withNotFound() {
	_, err := suite.metadataController.ListTree(user, "notexists")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestDeleteObject() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "myblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.DeleteObject(user, "myblob")
	require.Nil(suite.T(), err)
}

func (suite *TestSuite) TestCreateTree() {
	err := suite.metadataController.CreateTree(user, uuid.NewV4().String())
	require.Nil(suite.T(), err)
}

func (suite *TestSuite) TestCreateTree_withError() {
	err := suite.metadataController.CreateTree(user, "/this/does/not/exist")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestMoveBLOBObject() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "testmoveblobobject"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "testmoveblobobject", "othertestmoveblobobject")
	require.Nil(suite.T(), err)
}
func (suite *TestSuite) TestMoveTreeObject() {
	err := os.MkdirAll(suite.controller.getStoragePath(user, "testmovetree"), 0755)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "testmovetree", "othertestmovetree")
	require.Nil(suite.T(), err)
}

func (suite *TestSuite) TestMoveBLOBObject_overExistingBLOB() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "myblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = ioutil.WriteFile(suite.controller.getStoragePath(user, "myblob2"), []byte("2"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "myblob", "myblob2")
	require.Nil(suite.T(), err)
}
func (suite *TestSuite) TestMoveBLOBObject_overExistingTree() {
	err := os.MkdirAll(suite.controller.getStoragePath(user, "mytree"), 0755)
	require.Nil(suite.T(), err)
	err = ioutil.WriteFile(suite.controller.getStoragePath(user, "mytree/myblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = ioutil.WriteFile(suite.controller.getStoragePath(user, "myblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "myblob", "mytree")
	require.NotNil(suite.T(), err)
	// err is the following
	// &os.LinkError{Op:"rename", Old:"/tmp/t/test/myblob", New:"/tmp/t/test/mytree", Err:0x15}
	// Err = "rename /tmp/t/test/myblob /tmp/t/test/mytree: is a directory"
}
func (suite *TestSuite) TestMoveTreeObject_overExistingBLOB() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "testmovetreeoverblobblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = os.MkdirAll(suite.controller.getStoragePath(user, "testmovetreeoverblobtree"), 0755)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "testmovetreeoverblobtree", "testmovetreeoverblobblob")
	require.NotNil(suite.T(), err)
	// err is the following
	// &os.LinkError{Op:"rename", Old:"/tmp/t/test/testmovetreeoverblobtree", New:"/tmp/t/test/testmovetreeoverblobblob", Err:0x14}
	// Err = "rename /tmp/t/test/testmovetreeoverblobtree /tmp/t/test/testmovetreeoverblobblob: not a directory"
}

func (suite *TestSuite) TestMoveTreeObject_overExistingTree() {
	err := os.MkdirAll(suite.controller.getStoragePath(user, "testmovetreeobjectmytreeovertree"), 0755)
	require.Nil(suite.T(), err)
	err = os.MkdirAll(suite.controller.getStoragePath(user, "testmovetreeobjectotheremptytree"), 0755)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "testmovetreeobjectmytreeovertre", "testmovetreeobjectotheremptytree")
	require.NotNil(suite.T(), err)
	// err is the following
	// &os.LinkError{Op:"rename", Old:"/tmp/t/test/mytreeovertree", New:"/tmp/t/test/otheremptytree", Err:0x42}
	// Err = rename /tmp/t/test/mytreeovertree /tmp/t/test/otheremptytree: directory not empty
}
func (suite *TestSuite) TestMoveObject_withTargetNotFound() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "myblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "myblob", "notexists/otherblob")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestMoveObject_withSourceNotFound() {
	err := suite.metadataController.MoveObject(user, "notexists", "otherblob")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestListTree_withBLOB() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "myblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	_, err = suite.metadataController.ListTree(user, "myblob")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestgetMimeType() {
	mime := suite.controller.getMimeType("", entities.ObjectTypeTree)
	require.Equal(suite.T(), entities.ObjectTypeTreeMimeType, mime)
}

func (suite *TestSuite) TestgetMimeType_pdf() {
	mime := suite.controller.getMimeType("myblob.pdf", entities.ObjectTypeBLOB)
	require.Equal(suite.T(), "application/pdf", mime)
}

func (suite *TestSuite) TestsecureJoin() {
	paths := []struct {
		given    []string
		expected string
	}{
		{
			[]string{"relativePath/t/test"},
			"relativePath/t/test",
		},
		{
			[]string{"../../relativePath/t/test"},
			"../../relativePath/t/test",
		},
		{
			[]string{"../../relativePath/t/test", "../../../../"},
			"../../relativePath/t/test",
		},
		{
			[]string{"/abspath/t/test"},
			"/abspath/t/test",
		},
		{
			[]string{"/abspath/t/test", "../../.."},
			"/abspath/t/test",
		},
	}

	for _, v := range paths {
		require.Equal(suite.T(), v.expected, secureJoin(v.given...))
	}
}
<commit_msg>Refactor tests<commit_after>package simple

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

	"github.com/clawio/clawiod/entities"
	"github.com/clawio/clawiod/services/metadata/metadatacontroller"
	"github.com/satori/go.uuid"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

var user = &entities.User{Username: "test"}

type TestSuite struct {
	suite.Suite
	metadataController metadatacontroller.MetaDataController
	controller         *controller
}

func Test(t *testing.T) {
	suite.Run(t, new(TestSuite))
}
func (suite *TestSuite) SetupTest() {
	opts := &Options{
		MetaDataDir: "/tmp",
		TempDir:     "/tmp",
	}
	metadataController := New(opts)
	// create homedir for user test
	err := os.MkdirAll("/tmp/t/test", 0755)
	require.Nil(suite.T(), err)
	suite.metadataController = metadataController
	suite.controller = suite.metadataController.(*controller)
}
func (suite *TestSuite) TeardownTest() {
	os.RemoveAll("/tmp/t")
}
func (suite *TestSuite) New() {
	opts := &Options{
		MetaDataDir: "/tmp",
		TempDir:     "/tmp",
	}
	require.IsType(suite.T(), &controller{}, New(opts))
}
func (suite *TestSuite) TestNew_withNilOptions() {
	require.IsType(suite.T(), &controller{}, New(nil))
}
func (suite *TestSuite) TestInit() {
	err := suite.metadataController.Init(user)
	require.Nil(suite.T(), err)
}
func (suite *TestSuite) TestInit_withError() {
	suite.controller.metaDataDir = "/i/cannot/write/here"
	err := suite.metadataController.Init(user)
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestExamineObject() {
	testblob := uuid.NewV4().String()
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, testblob), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	info, err := suite.metadataController.ExamineObject(user, testblob)
	require.Nil(suite.T(), err)
	require.Equal(suite.T(), testblob, info.PathSpec)
	require.Equal(suite.T(), int64(1), info.Size)
	require.Equal(suite.T(), "", info.Checksum)
	require.Equal(suite.T(), entities.ObjectTypeBLOBMimeType, info.MimeType)
	require.Equal(suite.T(), entities.ObjectTypeBLOB, info.Type)
}

func (suite *TestSuite) TestExamineObject_withError() {
	testblob := uuid.NewV4().String()
	testtree := uuid.NewV4().String()
	err := os.MkdirAll(suite.controller.getStoragePath(user, testtree), 0755)
	require.Nil(suite.T(), err)
	err = ioutil.WriteFile(suite.controller.getStoragePath(user, testtree+"/"+testblob), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	// remove execute permissions on parent so stat will fail
	err = os.Chmod(suite.controller.getStoragePath(user, testtree), os.FileMode(os.O_WRONLY))
	require.Nil(suite.T(), err)
	_, err = suite.metadataController.ExamineObject(user, testtree+"/"+testblob)
	require.NotNil(suite.T(), err)
}
func (suite *TestSuite) TestExamineObject_withNotFound() {
	_, err := suite.metadataController.ExamineObject(user, "notexists")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestListTree() {
	testtree := uuid.NewV4().String()
	err := os.MkdirAll(suite.controller.getStoragePath(user, testtree), 0755)
	require.Nil(suite.T(), err)
	err = os.MkdirAll(suite.controller.getStoragePath(user, testtree+"/othertree"), 0755)
	require.Nil(suite.T(), err)
	infos, err := suite.metadataController.ListTree(user, testtree)
	require.Nil(suite.T(), err)
	require.Equal(suite.T(), 1, len(infos))
}

func (suite *TestSuite) TestListTree_withNotFound() {
	_, err := suite.metadataController.ListTree(user, "notexists")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestDeleteObject() {
	testblob := uuid.NewV4().String()
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, testblob), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.DeleteObject(user, testblob)
	require.Nil(suite.T(), err)
}

func (suite *TestSuite) TestCreateTree() {
	err := suite.metadataController.CreateTree(user, uuid.NewV4().String())
	require.Nil(suite.T(), err)
}

func (suite *TestSuite) TestCreateTree_withError() {
	err := suite.metadataController.CreateTree(user, "/this/does/not/exist")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestMoveBLOBObject() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "testmoveblobobject"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "testmoveblobobject", "othertestmoveblobobject")
	require.Nil(suite.T(), err)
}
func (suite *TestSuite) TestMoveTreeObject() {
	err := os.MkdirAll(suite.controller.getStoragePath(user, "testmovetree"), 0755)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "testmovetree", "othertestmovetree")
	require.Nil(suite.T(), err)
}

func (suite *TestSuite) TestMoveBLOBObject_overExistingBLOB() {
	testblob := uuid.NewV4().String()
	testblob2 := uuid.NewV4().String()
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, testblob), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = ioutil.WriteFile(suite.controller.getStoragePath(user, testblob2), []byte("2"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, testblob, testblob2)
	require.Nil(suite.T(), err)
}
func (suite *TestSuite) TestMoveBLOBObject_overExistingTree() {
	testtree := uuid.NewV4().String()
	testblob := uuid.NewV4().String()
	err := os.MkdirAll(suite.controller.getStoragePath(user, testtree), 0755)
	require.Nil(suite.T(), err)
	err = ioutil.WriteFile(suite.controller.getStoragePath(user, testtree+"/myblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = ioutil.WriteFile(suite.controller.getStoragePath(user, testblob), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, testblob, testtree)
	require.NotNil(suite.T(), err)
	// err is the following
	// &os.LinkError{Op:"rename", Old:"/tmp/t/test/myblob", New:"/tmp/t/test/mytree", Err:0x15}
	// Err = "rename /tmp/t/test/myblob /tmp/t/test/mytree: is a directory"
}
func (suite *TestSuite) TestMoveTreeObject_overExistingBLOB() {
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, "testmovetreeoverblobblob"), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = os.MkdirAll(suite.controller.getStoragePath(user, "testmovetreeoverblobtree"), 0755)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "testmovetreeoverblobtree", "testmovetreeoverblobblob")
	require.NotNil(suite.T(), err)
	// err is the following
	// &os.LinkError{Op:"rename", Old:"/tmp/t/test/testmovetreeoverblobtree", New:"/tmp/t/test/testmovetreeoverblobblob", Err:0x14}
	// Err = "rename /tmp/t/test/testmovetreeoverblobtree /tmp/t/test/testmovetreeoverblobblob: not a directory"
}

func (suite *TestSuite) TestMoveTreeObject_overExistingTree() {
	err := os.MkdirAll(suite.controller.getStoragePath(user, "testmovetreeobjectmytreeovertree"), 0755)
	require.Nil(suite.T(), err)
	err = os.MkdirAll(suite.controller.getStoragePath(user, "testmovetreeobjectotheremptytree"), 0755)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, "testmovetreeobjectmytreeovertre", "testmovetreeobjectotheremptytree")
	require.NotNil(suite.T(), err)
	// err is the following
	// &os.LinkError{Op:"rename", Old:"/tmp/t/test/mytreeovertree", New:"/tmp/t/test/otheremptytree", Err:0x42}
	// Err = rename /tmp/t/test/mytreeovertree /tmp/t/test/otheremptytree: directory not empty
}
func (suite *TestSuite) TestMoveObject_withTargetNotFound() {
	testblob := uuid.NewV4().String()
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, testblob), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	err = suite.metadataController.MoveObject(user, testblob, "notexists/otherblob")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestMoveObject_withSourceNotFound() {
	err := suite.metadataController.MoveObject(user, "notexists", "otherblob")
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestListTree_withBLOB() {
	testblob := uuid.NewV4().String()
	err := ioutil.WriteFile(suite.controller.getStoragePath(user, testblob), []byte("1"), 0644)
	require.Nil(suite.T(), err)
	_, err = suite.metadataController.ListTree(user, testblob)
	require.NotNil(suite.T(), err)
}

func (suite *TestSuite) TestgetMimeType() {
	mime := suite.controller.getMimeType("", entities.ObjectTypeTree)
	require.Equal(suite.T(), entities.ObjectTypeTreeMimeType, mime)
}

func (suite *TestSuite) TestgetMimeType_pdf() {
	mime := suite.controller.getMimeType("myblob.pdf", entities.ObjectTypeBLOB)
	require.Equal(suite.T(), "application/pdf", mime)
}

func (suite *TestSuite) TestsecureJoin() {
	paths := []struct {
		given    []string
		expected string
	}{
		{
			[]string{"relativePath/t/test"},
			"relativePath/t/test",
		},
		{
			[]string{"../../relativePath/t/test"},
			"../../relativePath/t/test",
		},
		{
			[]string{"../../relativePath/t/test", "../../../../"},
			"../../relativePath/t/test",
		},
		{
			[]string{"/abspath/t/test"},
			"/abspath/t/test",
		},
		{
			[]string{"/abspath/t/test", "../../.."},
			"/abspath/t/test",
		},
	}

	for _, v := range paths {
		require.Equal(suite.T(), v.expected, secureJoin(v.given...))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"

	"cloud.google.com/go/errorreporting"

	"github.com/gorilla/mux"

	"code.sajari.com/docconv"
	"code.sajari.com/docconv/docd/internal"
)

var (
	listenAddr = flag.String("addr", ":8888", "The address to listen on (e.g. 127.0.0.1:8888)")

	inputPath = flag.String("input", "", "The file path to convert and exit; no server")

	errorReporting                 = flag.Bool("error-reporting", false, "Whether or not to enable GCP Error Reporting")
	errorReportingGCPProjectID     = flag.String("error-reporting-gcp-project-id", "", "The GCP project to use for Error Reporting")
	errorReportingAppEngineService = flag.String("error-reporting-app-engine-service", "", "The App Engine service to use for Error Reporting")

	logLevel = flag.Uint("log-level", 0, "The verbosity of the log")

	readabilityLengthLow          = flag.Int("readability-length-low", 70, "Sets the readability length low")
	readabilityLengthHigh         = flag.Int("readability-length-high", 200, "Sets the readability length high")
	readabilityStopwordsLow       = flag.Float64("readability-stopwords-low", 0.2, "Sets the readability stopwords low")
	readabilityStopwordsHigh      = flag.Float64("readability-stopwords-high", 0.3, "Sets the readability stopwords high")
	readabilityMaxLinkDensity     = flag.Float64("readability-max-link-density", 0.2, "Sets the readability max link density")
	readabilityMaxHeadingDistance = flag.Int("readability-max-heading-distance", 200, "Sets the readability max heading distance")
	readabilityUseClasses         = flag.String("readability-use-classes", "good,neargood", "Comma separated list of readability classes to use")
)

func main() {
	flag.Parse()

	var er internal.ErrorReporter = &internal.NopErrorReporter{}
	if *errorReporting {
		var err error
		er, err = errorreporting.NewClient(context.Background(), *errorReportingGCPProjectID, errorreporting.Config{
			ServiceName: *errorReportingAppEngineService,
			OnError: func(err error) {
				log.Printf("Could not report error to Error Reporting service: %v", err)
			},
		})
		if err != nil {
			log.Fatalf("Could not create Error Reporting client: %v", err)
		}
	}

	cs := &convertServer{
		er: er,
	}

	// TODO: Improve this (remove the need for it!)
	docconv.HTMLReadabilityOptionsValues = docconv.HTMLReadabilityOptions{
		LengthLow:             *readabilityLengthLow,
		LengthHigh:            *readabilityLengthHigh,
		StopwordsLow:          *readabilityStopwordsLow,
		StopwordsHigh:         *readabilityStopwordsHigh,
		MaxLinkDensity:        *readabilityMaxLinkDensity,
		MaxHeadingDistance:    *readabilityMaxHeadingDistance,
		ReadabilityUseClasses: *readabilityUseClasses,
	}

	if *inputPath != "" {
		resp, err := docconv.ConvertPath(*inputPath)
		if err != nil {
			log.Printf("error converting file '%v': %v", *inputPath, err)
		}
		fmt.Print(string(resp.Body))
		return
	}

	serve(er, cs)
}

// Start the conversion web service
func serve(er internal.ErrorReporter, cs *convertServer) {
	r := mux.NewRouter()
	r.HandleFunc("/convert", cs.convert)

	// Start webserver
	log.Println("Setting log level to", *logLevel)
	log.Println("Starting docconv on", *listenAddr)
	log.Fatal(http.ListenAndServe(*listenAddr, internal.RecoveryHandler(er)(r)))
}
<commit_msg>docd: fallback to AppEngine env vars when flags not provided (#128)<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"

	"cloud.google.com/go/errorreporting"

	"github.com/gorilla/mux"

	"code.sajari.com/docconv"
	"code.sajari.com/docconv/docd/internal"
)

var (
	listenAddr = flag.String("addr", ":8888", "The address to listen on (e.g. 127.0.0.1:8888)")

	inputPath = flag.String("input", "", "The file path to convert and exit; no server")

	errorReporting                 = flag.Bool("error-reporting", false, "Whether or not to enable GCP Error Reporting")
	errorReportingGCPProjectID     = flag.String("error-reporting-gcp-project-id", "", "The GCP project to use for Error Reporting")
	errorReportingAppEngineService = flag.String("error-reporting-app-engine-service", "", "The App Engine service to use for Error Reporting")

	logLevel = flag.Uint("log-level", 0, "The verbosity of the log")

	readabilityLengthLow          = flag.Int("readability-length-low", 70, "Sets the readability length low")
	readabilityLengthHigh         = flag.Int("readability-length-high", 200, "Sets the readability length high")
	readabilityStopwordsLow       = flag.Float64("readability-stopwords-low", 0.2, "Sets the readability stopwords low")
	readabilityStopwordsHigh      = flag.Float64("readability-stopwords-high", 0.3, "Sets the readability stopwords high")
	readabilityMaxLinkDensity     = flag.Float64("readability-max-link-density", 0.2, "Sets the readability max link density")
	readabilityMaxHeadingDistance = flag.Int("readability-max-heading-distance", 200, "Sets the readability max heading distance")
	readabilityUseClasses         = flag.String("readability-use-classes", "good,neargood", "Comma separated list of readability classes to use")
)

func main() {
	flag.Parse()

	var er internal.ErrorReporter = &internal.NopErrorReporter{}
	if *errorReporting {
		if *errorReportingGCPProjectID == "" {
			*errorReportingGCPProjectID = os.Getenv("GOOGLE_CLOUD_PROJECT")
		}
		if *errorReportingAppEngineService == "" {
			*errorReportingAppEngineService = os.Getenv("GAE_SERVICE")
		}
		var err error
		er, err = errorreporting.NewClient(context.Background(), *errorReportingGCPProjectID, errorreporting.Config{
			ServiceName: *errorReportingAppEngineService,
			OnError: func(err error) {
				log.Printf("Could not report error to Error Reporting service: %v", err)
			},
		})
		if err != nil {
			log.Fatalf("Could not create Error Reporting client: %v", err)
		}
	}

	cs := &convertServer{
		er: er,
	}

	// TODO: Improve this (remove the need for it!)
	docconv.HTMLReadabilityOptionsValues = docconv.HTMLReadabilityOptions{
		LengthLow:             *readabilityLengthLow,
		LengthHigh:            *readabilityLengthHigh,
		StopwordsLow:          *readabilityStopwordsLow,
		StopwordsHigh:         *readabilityStopwordsHigh,
		MaxLinkDensity:        *readabilityMaxLinkDensity,
		MaxHeadingDistance:    *readabilityMaxHeadingDistance,
		ReadabilityUseClasses: *readabilityUseClasses,
	}

	if *inputPath != "" {
		resp, err := docconv.ConvertPath(*inputPath)
		if err != nil {
			log.Printf("error converting file '%v': %v", *inputPath, err)
		}
		fmt.Print(string(resp.Body))
		return
	}

	serve(er, cs)
}

// Start the conversion web service
func serve(er internal.ErrorReporter, cs *convertServer) {
	r := mux.NewRouter()
	r.HandleFunc("/convert", cs.convert)

	// Start webserver
	log.Println("Setting log level to", *logLevel)
	log.Println("Starting docconv on", *listenAddr)
	log.Fatal(http.ListenAndServe(*listenAddr, internal.RecoveryHandler(er)(r)))
}
<|endoftext|>


<commit_before>package kawasaki

import (
	"encoding/json"
	"fmt"
	"net"
	"strconv"
	"strings"

	"code.cloudfoundry.org/lager"
	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/guardian/gardener"
	"github.com/cloudfoundry-incubator/guardian/kawasaki/subnets"
)

// generic gardener properties
const containerIpKey = gardener.ContainerIPKey
const bridgeIpKey = gardener.BridgeIPKey
const externalIpKey = gardener.ExternalIPKey

// kawasaki-specific state properties
const hostIntfKey = "kawasaki.host-interface"
const containerIntfKey = "kawasaki.container-interface"
const bridgeIntfKey = "kawasaki.bridge-interface"
const subnetKey = "kawasaki.subnet"
const iptablePrefixKey = "kawasaki.iptable-prefix"
const iptableInstanceKey = "kawasaki.iptable-inst"
const mtuKey = "kawasaki.mtu"
const dnsServerKey = "kawasaki.dns-servers"

//go:generate counterfeiter . SpecParser

type SpecParser interface {
	Parse(log lager.Logger, spec string) (subnets.SubnetSelector, subnets.IPSelector, error)
}

//go:generate counterfeiter . ConfigCreator

type ConfigCreator interface {
	Create(log lager.Logger, handle string, subnet *net.IPNet, ip net.IP) (NetworkConfig, error)
}

//go:generate counterfeiter . Configurer

type Configurer interface {
	Apply(log lager.Logger, cfg NetworkConfig, pid int) error
	DestroyBridge(log lager.Logger, cfg NetworkConfig) error
	DestroyIPTablesRules(log lager.Logger, cfg NetworkConfig) error
}

//go:generate counterfeiter . ConfigStore

type ConfigStore interface {
	Set(handle string, name string, value string)
	Get(handle string, name string) (string, bool)
}

//go:generate counterfeiter . PortPool

type PortPool interface {
	Acquire() (uint32, error)
	Release(uint32)
	Remove(uint32) error
}

//go:generate counterfeiter . PortForwarder

type PortForwarder interface {
	Forward(spec PortForwarderSpec) error
}

type PortForwarderSpec struct {
	InstanceID  string
	FromPort    uint32
	ToPort      uint32
	ContainerIP net.IP
	ExternalIP  net.IP
}

//go:generate counterfeiter . FirewallOpener

type FirewallOpener interface {
	Open(log lager.Logger, instance string, rule garden.NetOutRule) error
}

//go:generate counterfeiter . Networker

type Networker interface {
	Capacity() uint64
	Network(log lager.Logger, spec garden.ContainerSpec, pid int) error
	Destroy(log lager.Logger, handle string) error
	NetIn(log lager.Logger, handle string, externalPort, containerPort uint32) (uint32, uint32, error)
	NetOut(log lager.Logger, handle string, rule garden.NetOutRule) error
	Restore(log lager.Logger, handle string) error
}

type networker struct {
	iptablesBin string

	specParser     SpecParser
	subnetPool     subnets.Pool
	configCreator  ConfigCreator
	configStore    ConfigStore
	portForwarder  PortForwarder
	portPool       PortPool
	firewallOpener FirewallOpener
	configurer     Configurer
}

func New(
	iptablesBin string,
	specParser SpecParser,
	subnetPool subnets.Pool,
	configCreator ConfigCreator,
	configStore ConfigStore,
	configurer Configurer,
	portPool PortPool,
	portForwarder PortForwarder,
	firewallOpener FirewallOpener,
) *networker {
	return &networker{
		iptablesBin: iptablesBin,

		specParser:    specParser,
		subnetPool:    subnetPool,
		configCreator: configCreator,
		configStore:   configStore,
		configurer:    configurer,

		portForwarder: portForwarder,
		portPool:      portPool,

		firewallOpener: firewallOpener,
	}
}

func (n *networker) Network(log lager.Logger, containerSpec garden.ContainerSpec, pid int) error {
	log = log.Session("network", lager.Data{
		"handle": containerSpec.Handle,
		"spec":   containerSpec.Network,
	})

	log.Info("started")
	defer log.Info("finished")

	subnetReq, ipReq, err := n.specParser.Parse(log, containerSpec.Network)
	if err != nil {
		log.Error("parse-failed", err)
		return err
	}

	subnet, ip, err := n.subnetPool.Acquire(log, subnetReq, ipReq)
	if err != nil {
		log.Error("acquire-failed", err)
		return err
	}

	config, err := n.configCreator.Create(log, containerSpec.Handle, subnet, ip)
	if err != nil {
		log.Error("create-config-failed", err)
		return fmt.Errorf("create network config: %s", err)
	}
	log.Info("config-create", lager.Data{"config": config})

	if err := save(n.configStore, containerSpec.Handle, config); err != nil {
		return err
	}

	if err := n.configurer.Apply(log, config, pid); err != nil {
		return err
	}
	return nil
}

// Capacity returns the number of subnets this network can host
func (n *networker) Capacity() uint64 {
	return uint64(n.subnetPool.Capacity())
}

func (n *networker) NetIn(log lager.Logger, handle string, externalPort, containerPort uint32) (uint32, uint32, error) {
	cfg, err := load(n.configStore, handle)
	if err != nil {
		return 0, 0, err
	}

	if externalPort == 0 {
		externalPort, err = n.portPool.Acquire()
		if err != nil {
			return 0, 0, err
		}
	}

	if containerPort == 0 {
		containerPort = externalPort
	}

	err = n.portForwarder.Forward(PortForwarderSpec{
		InstanceID:  cfg.IPTableInstance,
		FromPort:    externalPort,
		ToPort:      containerPort,
		ContainerIP: cfg.ContainerIP,
		ExternalIP:  cfg.ExternalIP,
	})

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

	if err := addPortMapping(log, n.configStore, handle, garden.PortMapping{
		HostPort:      externalPort,
		ContainerPort: containerPort,
	}); err != nil {
		return 0, 0, err
	}

	return externalPort, containerPort, nil
}

func (n *networker) NetOut(log lager.Logger, handle string, rule garden.NetOutRule) error {
	cfg, err := load(n.configStore, handle)
	if err != nil {
		return err
	}

	return n.firewallOpener.Open(log, cfg.IPTableInstance, rule)
}

func (n *networker) Destroy(log lager.Logger, handle string) error {
	cfg, err := load(n.configStore, handle)
	if err != nil {
		log.Debug("no-properties-for-container-skipping-destroy-network")
		return nil
	}

	if err := n.subnetPool.Release(cfg.Subnet, cfg.ContainerIP); err != nil && err != subnets.ErrReleasedUnallocatedSubnet {
		log.Error("release-failed", err)
		return err
	}

	if ports, ok := n.configStore.Get(handle, gardener.MappedPortsKey); ok {
		mappings, err := portsFromJson(ports)
		if err != nil {
			return err
		}

		for _, m := range mappings {
			n.portPool.Release(m.HostPort)
		}
	}

	if err := n.configurer.DestroyIPTablesRules(log, cfg); err != nil {
		return err
	}

	n.subnetPool.RunIfFree(cfg.Subnet, func() error {
		n.configurer.DestroyBridge(log, cfg)
		return nil
	})

	return nil
}

func (n *networker) Restore(log lager.Logger, handle string) error {
	networkConfig, err := load(n.configStore, handle)
	if err != nil {
		return fmt.Errorf("loading %s: %v", handle, err)
	}

	err = n.subnetPool.Remove(networkConfig.Subnet, networkConfig.ContainerIP)
	if err != nil {
		return fmt.Errorf("subnet pool removing %s: %v", handle, err)
	}

	currentMappingsJson, ok := n.configStore.Get(handle, gardener.MappedPortsKey)
	if !ok {
		return nil
	}

	currentMappings, err := portsFromJson(currentMappingsJson)
	if err != nil {
		return fmt.Errorf("unmarshaling port mappings %s: %v", handle, err)
	}

	for _, mapping := range currentMappings {
		if err = n.portPool.Remove(mapping.HostPort); err != nil {
			return fmt.Errorf("port pool removing %s: %v", handle, err)
		}
	}

	return nil
}

func addPortMapping(logger lager.Logger, configStore ConfigStore, handle string, newMapping garden.PortMapping) error {
	var currentMappings portMappingList
	if currentMappingsJson, ok := configStore.Get(handle, gardener.MappedPortsKey); ok {
		var err error
		currentMappings, err = portsFromJson(currentMappingsJson)
		if err != nil {
			return err
		}
	}

	updatedMappings := append(currentMappings, newMapping)
	configStore.Set(handle, gardener.MappedPortsKey, updatedMappings.toJson())
	return nil
}

func getAll(config ConfigStore, handle string, key ...string) (vals []string, err error) {
	for _, k := range key {
		v, ok := config.Get(handle, k)
		if !ok {
			return nil, fmt.Errorf("property not found: %s", k)
		}

		vals = append(vals, v)
	}

	return vals, nil
}

func save(config ConfigStore, handle string, netConfig NetworkConfig) error {
	config.Set(handle, hostIntfKey, netConfig.HostIntf)
	config.Set(handle, containerIntfKey, netConfig.ContainerIntf)
	config.Set(handle, bridgeIntfKey, netConfig.BridgeName)
	config.Set(handle, bridgeIpKey, netConfig.BridgeIP.String())
	config.Set(handle, containerIpKey, netConfig.ContainerIP.String())
	config.Set(handle, subnetKey, netConfig.Subnet.String())
	config.Set(handle, iptablePrefixKey, netConfig.IPTablePrefix)
	config.Set(handle, iptableInstanceKey, netConfig.IPTableInstance)
	config.Set(handle, mtuKey, strconv.Itoa(netConfig.Mtu))
	config.Set(handle, externalIpKey, netConfig.ExternalIP.String())

	var dnsServers []string
	for _, dnsServer := range netConfig.DNSServers {
		dnsServers = append(dnsServers, dnsServer.String())
	}

	config.Set(handle, dnsServerKey, strings.Join(dnsServers, ", "))

	return nil
}

func appendIfNotNil(errors []error, err error) []error {
	if err != nil {
		return append(errors, err)
	}
	return errors
}

func load(config ConfigStore, handle string) (NetworkConfig, error) {
	vals, err := getAll(config, handle, hostIntfKey, containerIntfKey, bridgeIntfKey, bridgeIpKey, containerIpKey, subnetKey, iptablePrefixKey, iptableInstanceKey, mtuKey, externalIpKey, dnsServerKey)

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

	_, ipnet, err := net.ParseCIDR(vals[5])
	if err != nil {
		return NetworkConfig{}, err
	}

	mtu, err := strconv.Atoi(vals[8])
	if err != nil {
		return NetworkConfig{}, err
	}

	var dnsServers []net.IP
	for _, dnsServerName := range strings.Split(vals[10], ",") {
		dnsServerName = strings.TrimSpace(dnsServerName)
		if dnsServerName == "" {
			continue
		}
		ip := net.ParseIP(dnsServerName)
		if ip == nil {
			return NetworkConfig{}, fmt.Errorf("Failed to parse DNS server IP address %s", dnsServerName)
		}
		dnsServers = append(dnsServers, ip)
	}

	return NetworkConfig{
		HostIntf:        vals[0],
		ContainerIntf:   vals[1],
		BridgeName:      vals[2],
		BridgeIP:        net.ParseIP(vals[3]),
		ContainerIP:     net.ParseIP(vals[4]),
		ExternalIP:      net.ParseIP(vals[9]),
		Subnet:          ipnet,
		IPTablePrefix:   vals[6],
		IPTableInstance: vals[7],
		Mtu:             mtu,
		DNSServers:      dnsServers,
	}, nil
}

type portMappingList []garden.PortMapping

func (l portMappingList) toJson() string {
	b, err := json.Marshal(l)
	if err != nil {
		panic(err) // impossible, since []PortMapping is always encodable
	}

	return string(b)
}

func portsFromJson(s string) (portMappingList, error) {
	var mappings portMappingList
	if err := json.Unmarshal([]byte(s), &mappings); err != nil {
		return nil, err
	}

	return mappings, nil
}
<commit_msg>Ensure IPTablesRules are destroyed before Release<commit_after>package kawasaki

import (
	"encoding/json"
	"fmt"
	"net"
	"strconv"
	"strings"

	"code.cloudfoundry.org/lager"
	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/guardian/gardener"
	"github.com/cloudfoundry-incubator/guardian/kawasaki/subnets"
)

// generic gardener properties
const containerIpKey = gardener.ContainerIPKey
const bridgeIpKey = gardener.BridgeIPKey
const externalIpKey = gardener.ExternalIPKey

// kawasaki-specific state properties
const hostIntfKey = "kawasaki.host-interface"
const containerIntfKey = "kawasaki.container-interface"
const bridgeIntfKey = "kawasaki.bridge-interface"
const subnetKey = "kawasaki.subnet"
const iptablePrefixKey = "kawasaki.iptable-prefix"
const iptableInstanceKey = "kawasaki.iptable-inst"
const mtuKey = "kawasaki.mtu"
const dnsServerKey = "kawasaki.dns-servers"

//go:generate counterfeiter . SpecParser

type SpecParser interface {
	Parse(log lager.Logger, spec string) (subnets.SubnetSelector, subnets.IPSelector, error)
}

//go:generate counterfeiter . ConfigCreator

type ConfigCreator interface {
	Create(log lager.Logger, handle string, subnet *net.IPNet, ip net.IP) (NetworkConfig, error)
}

//go:generate counterfeiter . Configurer

type Configurer interface {
	Apply(log lager.Logger, cfg NetworkConfig, pid int) error
	DestroyBridge(log lager.Logger, cfg NetworkConfig) error
	DestroyIPTablesRules(log lager.Logger, cfg NetworkConfig) error
}

//go:generate counterfeiter . ConfigStore

type ConfigStore interface {
	Set(handle string, name string, value string)
	Get(handle string, name string) (string, bool)
}

//go:generate counterfeiter . PortPool

type PortPool interface {
	Acquire() (uint32, error)
	Release(uint32)
	Remove(uint32) error
}

//go:generate counterfeiter . PortForwarder

type PortForwarder interface {
	Forward(spec PortForwarderSpec) error
}

type PortForwarderSpec struct {
	InstanceID  string
	FromPort    uint32
	ToPort      uint32
	ContainerIP net.IP
	ExternalIP  net.IP
}

//go:generate counterfeiter . FirewallOpener

type FirewallOpener interface {
	Open(log lager.Logger, instance string, rule garden.NetOutRule) error
}

//go:generate counterfeiter . Networker

type Networker interface {
	Capacity() uint64
	Network(log lager.Logger, spec garden.ContainerSpec, pid int) error
	Destroy(log lager.Logger, handle string) error
	NetIn(log lager.Logger, handle string, externalPort, containerPort uint32) (uint32, uint32, error)
	NetOut(log lager.Logger, handle string, rule garden.NetOutRule) error
	Restore(log lager.Logger, handle string) error
}

type networker struct {
	iptablesBin string

	specParser     SpecParser
	subnetPool     subnets.Pool
	configCreator  ConfigCreator
	configStore    ConfigStore
	portForwarder  PortForwarder
	portPool       PortPool
	firewallOpener FirewallOpener
	configurer     Configurer
}

func New(
	iptablesBin string,
	specParser SpecParser,
	subnetPool subnets.Pool,
	configCreator ConfigCreator,
	configStore ConfigStore,
	configurer Configurer,
	portPool PortPool,
	portForwarder PortForwarder,
	firewallOpener FirewallOpener,
) *networker {
	return &networker{
		iptablesBin: iptablesBin,

		specParser:    specParser,
		subnetPool:    subnetPool,
		configCreator: configCreator,
		configStore:   configStore,
		configurer:    configurer,

		portForwarder: portForwarder,
		portPool:      portPool,

		firewallOpener: firewallOpener,
	}
}

func (n *networker) Network(log lager.Logger, containerSpec garden.ContainerSpec, pid int) error {
	log = log.Session("network", lager.Data{
		"handle": containerSpec.Handle,
		"spec":   containerSpec.Network,
	})

	log.Info("started")
	defer log.Info("finished")

	subnetReq, ipReq, err := n.specParser.Parse(log, containerSpec.Network)
	if err != nil {
		log.Error("parse-failed", err)
		return err
	}

	subnet, ip, err := n.subnetPool.Acquire(log, subnetReq, ipReq)
	if err != nil {
		log.Error("acquire-failed", err)
		return err
	}

	config, err := n.configCreator.Create(log, containerSpec.Handle, subnet, ip)
	if err != nil {
		log.Error("create-config-failed", err)
		return fmt.Errorf("create network config: %s", err)
	}
	log.Info("config-create", lager.Data{"config": config})

	if err := save(n.configStore, containerSpec.Handle, config); err != nil {
		return err
	}

	if err := n.configurer.Apply(log, config, pid); err != nil {
		return err
	}
	return nil
}

// Capacity returns the number of subnets this network can host
func (n *networker) Capacity() uint64 {
	return uint64(n.subnetPool.Capacity())
}

func (n *networker) NetIn(log lager.Logger, handle string, externalPort, containerPort uint32) (uint32, uint32, error) {
	cfg, err := load(n.configStore, handle)
	if err != nil {
		return 0, 0, err
	}

	if externalPort == 0 {
		externalPort, err = n.portPool.Acquire()
		if err != nil {
			return 0, 0, err
		}
	}

	if containerPort == 0 {
		containerPort = externalPort
	}

	err = n.portForwarder.Forward(PortForwarderSpec{
		InstanceID:  cfg.IPTableInstance,
		FromPort:    externalPort,
		ToPort:      containerPort,
		ContainerIP: cfg.ContainerIP,
		ExternalIP:  cfg.ExternalIP,
	})

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

	if err := addPortMapping(log, n.configStore, handle, garden.PortMapping{
		HostPort:      externalPort,
		ContainerPort: containerPort,
	}); err != nil {
		return 0, 0, err
	}

	return externalPort, containerPort, nil
}

func (n *networker) NetOut(log lager.Logger, handle string, rule garden.NetOutRule) error {
	cfg, err := load(n.configStore, handle)
	if err != nil {
		return err
	}

	return n.firewallOpener.Open(log, cfg.IPTableInstance, rule)
}

func (n *networker) Destroy(log lager.Logger, handle string) error {
	cfg, err := load(n.configStore, handle)
	if err != nil {
		log.Debug("no-properties-for-container-skipping-destroy-network")
		return nil
	}

	if err := n.configurer.DestroyIPTablesRules(log, cfg); err != nil {
		return err
	}

	if err := n.subnetPool.Release(cfg.Subnet, cfg.ContainerIP); err != nil && err != subnets.ErrReleasedUnallocatedSubnet {
		log.Error("release-failed", err)
		return err
	}

	if ports, ok := n.configStore.Get(handle, gardener.MappedPortsKey); ok {
		mappings, err := portsFromJson(ports)
		if err != nil {
			return err
		}

		for _, m := range mappings {
			n.portPool.Release(m.HostPort)
		}
	}

	n.subnetPool.RunIfFree(cfg.Subnet, func() error {
		n.configurer.DestroyBridge(log, cfg)
		return nil
	})

	return nil
}

func (n *networker) Restore(log lager.Logger, handle string) error {
	networkConfig, err := load(n.configStore, handle)
	if err != nil {
		return fmt.Errorf("loading %s: %v", handle, err)
	}

	err = n.subnetPool.Remove(networkConfig.Subnet, networkConfig.ContainerIP)
	if err != nil {
		return fmt.Errorf("subnet pool removing %s: %v", handle, err)
	}

	currentMappingsJson, ok := n.configStore.Get(handle, gardener.MappedPortsKey)
	if !ok {
		return nil
	}

	currentMappings, err := portsFromJson(currentMappingsJson)
	if err != nil {
		return fmt.Errorf("unmarshaling port mappings %s: %v", handle, err)
	}

	for _, mapping := range currentMappings {
		if err = n.portPool.Remove(mapping.HostPort); err != nil {
			return fmt.Errorf("port pool removing %s: %v", handle, err)
		}
	}

	return nil
}

func addPortMapping(logger lager.Logger, configStore ConfigStore, handle string, newMapping garden.PortMapping) error {
	var currentMappings portMappingList
	if currentMappingsJson, ok := configStore.Get(handle, gardener.MappedPortsKey); ok {
		var err error
		currentMappings, err = portsFromJson(currentMappingsJson)
		if err != nil {
			return err
		}
	}

	updatedMappings := append(currentMappings, newMapping)
	configStore.Set(handle, gardener.MappedPortsKey, updatedMappings.toJson())
	return nil
}

func getAll(config ConfigStore, handle string, key ...string) (vals []string, err error) {
	for _, k := range key {
		v, ok := config.Get(handle, k)
		if !ok {
			return nil, fmt.Errorf("property not found: %s", k)
		}

		vals = append(vals, v)
	}

	return vals, nil
}

func save(config ConfigStore, handle string, netConfig NetworkConfig) error {
	config.Set(handle, hostIntfKey, netConfig.HostIntf)
	config.Set(handle, containerIntfKey, netConfig.ContainerIntf)
	config.Set(handle, bridgeIntfKey, netConfig.BridgeName)
	config.Set(handle, bridgeIpKey, netConfig.BridgeIP.String())
	config.Set(handle, containerIpKey, netConfig.ContainerIP.String())
	config.Set(handle, subnetKey, netConfig.Subnet.String())
	config.Set(handle, iptablePrefixKey, netConfig.IPTablePrefix)
	config.Set(handle, iptableInstanceKey, netConfig.IPTableInstance)
	config.Set(handle, mtuKey, strconv.Itoa(netConfig.Mtu))
	config.Set(handle, externalIpKey, netConfig.ExternalIP.String())

	var dnsServers []string
	for _, dnsServer := range netConfig.DNSServers {
		dnsServers = append(dnsServers, dnsServer.String())
	}

	config.Set(handle, dnsServerKey, strings.Join(dnsServers, ", "))

	return nil
}

func appendIfNotNil(errors []error, err error) []error {
	if err != nil {
		return append(errors, err)
	}
	return errors
}

func load(config ConfigStore, handle string) (NetworkConfig, error) {
	vals, err := getAll(config, handle, hostIntfKey, containerIntfKey, bridgeIntfKey, bridgeIpKey, containerIpKey, subnetKey, iptablePrefixKey, iptableInstanceKey, mtuKey, externalIpKey, dnsServerKey)

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

	_, ipnet, err := net.ParseCIDR(vals[5])
	if err != nil {
		return NetworkConfig{}, err
	}

	mtu, err := strconv.Atoi(vals[8])
	if err != nil {
		return NetworkConfig{}, err
	}

	var dnsServers []net.IP
	for _, dnsServerName := range strings.Split(vals[10], ",") {
		dnsServerName = strings.TrimSpace(dnsServerName)
		if dnsServerName == "" {
			continue
		}
		ip := net.ParseIP(dnsServerName)
		if ip == nil {
			return NetworkConfig{}, fmt.Errorf("Failed to parse DNS server IP address %s", dnsServerName)
		}
		dnsServers = append(dnsServers, ip)
	}

	return NetworkConfig{
		HostIntf:        vals[0],
		ContainerIntf:   vals[1],
		BridgeName:      vals[2],
		BridgeIP:        net.ParseIP(vals[3]),
		ContainerIP:     net.ParseIP(vals[4]),
		ExternalIP:      net.ParseIP(vals[9]),
		Subnet:          ipnet,
		IPTablePrefix:   vals[6],
		IPTableInstance: vals[7],
		Mtu:             mtu,
		DNSServers:      dnsServers,
	}, nil
}

type portMappingList []garden.PortMapping

func (l portMappingList) toJson() string {
	b, err := json.Marshal(l)
	if err != nil {
		panic(err) // impossible, since []PortMapping is always encodable
	}

	return string(b)
}

func portsFromJson(s string) (portMappingList, error) {
	var mappings portMappingList
	if err := json.Unmarshal([]byte(s), &mappings); err != nil {
		return nil, err
	}

	return mappings, nil
}
<|endoftext|>


<commit_before>package instructions

import "testing"

func TestReadInt8(t *testing.T) {
    bytecodes := []byte{0xf1, 0x17}
    decoder := &InstructionDecoder{0, bytecodes}
    if b1 := decoder.readInt8(); b1 != -15 {
        t.Errorf("%v", b1)
    }
    if b2 := decoder.readInt8(); b2 != 23 {
        t.Errorf("%v", b2)
    }
}

func TestReadUint8(t *testing.T) {
    bytecodes := []byte{0xf1, 0x17}
    decoder := &InstructionDecoder{0, bytecodes}
    if b1 := decoder.readUint8(); b1 != 241 {
        t.Errorf("%v", b1)
    }
    if b2 := decoder.readUint8(); b2 != 23 {
        t.Errorf("%v", b2)
    }
}

func TestReadInt16(t *testing.T) {
    bytecodes := []byte{0xf1, 0x17}
    decoder := &InstructionDecoder{0, bytecodes}
    if x := decoder.readInt16(); x != -3817 {
        t.Errorf("%v", x)
    }
}

func TestReadUint16(t *testing.T) {
    bytecodes := []byte{0xf1, 0x17}
    decoder := &InstructionDecoder{0, bytecodes}
    if x := decoder.readUint16(); x != 61719 {
        t.Errorf("%v", x)
    }
}
<commit_msg>refactor unit tests<commit_after>package instructions

import (
    "testing"
    . "jvmgo/test"
)

func TestReadInt8(t *testing.T) {
    bytecodes := []byte{0xf1, 0x17}
    decoder := &InstructionDecoder{0, bytecodes}
    AssertEquals(-15, decoder.readInt8())
    AssertEquals(23, decoder.readInt8())
}

func TestReadUint8(t *testing.T) {
    bytecodes := []byte{0xf1, 0x17}
    decoder := &InstructionDecoder{0, bytecodes}
    AssertEquals(241, decoder.readUint8())
    AssertEquals(23, decoder.readUint8())
}

func TestReadInt16(t *testing.T) {
    bytecodes := []byte{0xf1, 0x17}
    decoder := &InstructionDecoder{0, bytecodes}
    AssertEquals(-3817, decoder.readInt16())
}

func TestReadUint16(t *testing.T) {
    bytecodes := []byte{0xf1, 0x17}
    decoder := &InstructionDecoder{0, bytecodes}
    AssertEquals(61719, decoder.readUint16())
}
<|endoftext|>


<commit_before>// tests for mschapv1 from https://tools.ietf.org/html/rfc2433#appendix-B.2
package mschapv1

import (
	"fmt"
	"testing"
	"bytes"
)

func TestNTPassword(t *testing.T) {
	ntPass := ntPassword("MyPw")
	if len(ntPass) != 8 {
		t.Error(fmt.Printf("NTPassword len expect=8 actual=%d", len(ntPass)))
	}

	expect := []byte{0x4D, 0x00, 0x79, 0x00, 0x50, 0x00, 0x77, 0x00}
	if bytes.Compare(ntPass, expect) != 0 {
		t.Fatal(fmt.Printf("NTPassword bytes wrong. expect=%d found=%d", expect, ntPass))
	}
}

func TestNTPasswordHash(t *testing.T) {
	val := ntPasswordHash(ntPassword("MyPw"))
	expect := []byte{
		0xFC, 0x15, 0x6A, 0xF7, 0xED, 0xCD, 0x6C,
		0x0E, 0xDD, 0xE3, 0x33, 0x7D, 0x42, 0x7F,
		0x4E, 0xAC,
	}
	if bytes.Compare(val, expect) != 0 {
		t.Fatal(fmt.Printf("NTPasswordHash bytes wrong. expect=%d found=%d", expect, val))
	}
}

func TestEncryptMSCHAP1(t *testing.T) {
	challenge := []byte{0x10, 0x2D, 0xB5, 0xDF, 0x08, 0x5D, 0x30, 0x41}
	expect := []byte{
		0x4E, 0x9D, 0x3C, 0x8F, 0x9C, 0xFD, 0x38, 0x5D,
		0x5B, 0xF4, 0xD3, 0x24, 0x67, 0x91, 0x95, 0x6C,
		0xA4, 0xC3, 0x51, 0xAB, 0x40, 0x9A, 0x3D, 0x61,
	}

	res, e := Encrypt(challenge, "MyPw")
	if e != nil {
		t.Fatal(e)
	}
	if bytes.Compare(res, expect) != 0 {
		t.Fatal(fmt.Printf("TestEncryptMSCHAP1 bytes wrong. expect=%d found=%d", expect, res))
	}
}

/*
 radclient test.
 MS-CHAP-Challenge = 0x15516e769584c6d8
 MS-CHAP-Response = 0x00010000000000000000000000000000000000000000000000003a3c9f1c7da79174dc167532d360ab5efd01853173c8214e
 */
func TestAnotherEncrypt(t *testing.T) {
	challenge := []byte{0x15, 0x51, 0x6e, 0x76, 0x95, 0x84, 0xc6, 0xd8}
	expect := []byte{
		0x3a, 0x3c, 0x9f, 0x1c, 0x7d, 0xa7, 0x91, 0x74,
		0xdc, 0x16, 0x75, 0x32, 0xd3, 0x60, 0xab, 0x5e,
		0xfd, 0x01, 0x85, 0x31, 0x73, 0xc8, 0x21, 0x4e,
	}

	res, e := Encrypt(challenge, "derpderp")
	if e != nil {
		t.Fatal(e)
	}
	if bytes.Compare(res, expect) != 0 {
		t.Fatal(fmt.Printf("TestAnotherEncrypt bytes wrong. expect=%d found=%d", expect, res))
	}
}<commit_msg>Test. Better naming and test OSX output<commit_after>// tests for mschapv1 from https://tools.ietf.org/html/rfc2433#appendix-B.2
package mschapv1

import (
	"fmt"
	"testing"
	"bytes"
)

func TestNTPassword(t *testing.T) {
	ntPass := ntPassword("MyPw")
	if len(ntPass) != 8 {
		t.Error(fmt.Printf("NTPassword len expect=8 actual=%d", len(ntPass)))
	}

	expect := []byte{0x4D, 0x00, 0x79, 0x00, 0x50, 0x00, 0x77, 0x00}
	if bytes.Compare(ntPass, expect) != 0 {
		t.Fatal(fmt.Printf("NTPassword bytes wrong. expect=%d found=%d", expect, ntPass))
	}
}

func TestNTPasswordHash(t *testing.T) {
	val := ntPasswordHash(ntPassword("MyPw"))
	expect := []byte{
		0xFC, 0x15, 0x6A, 0xF7, 0xED, 0xCD, 0x6C,
		0x0E, 0xDD, 0xE3, 0x33, 0x7D, 0x42, 0x7F,
		0x4E, 0xAC,
	}
	if bytes.Compare(val, expect) != 0 {
		t.Fatal(fmt.Printf("NTPasswordHash bytes wrong. expect=%d found=%d", expect, val))
	}
}

func TestEncryptMSCHAP1(t *testing.T) {
	challenge := []byte{0x10, 0x2D, 0xB5, 0xDF, 0x08, 0x5D, 0x30, 0x41}
	expect := []byte{
		0x4E, 0x9D, 0x3C, 0x8F, 0x9C, 0xFD, 0x38, 0x5D,
		0x5B, 0xF4, 0xD3, 0x24, 0x67, 0x91, 0x95, 0x6C,
		0xA4, 0xC3, 0x51, 0xAB, 0x40, 0x9A, 0x3D, 0x61,
	}

	res, e := Encrypt(challenge, "MyPw")
	if e != nil {
		t.Fatal(e)
	}
	if bytes.Compare(res, expect) != 0 {
		t.Fatal(fmt.Printf("TestEncryptMSCHAP1 bytes wrong. expect=%d found=%d", expect, res))
	}
}

/*
 radclient test.
 MS-CHAP-Challenge = 0x15516e769584c6d8
 MS-CHAP-Response = 0x00010000000000000000000000000000000000000000000000003a3c9f1c7da79174dc167532d360ab5efd01853173c8214e
 */
func TestRadclientEncrypt(t *testing.T) {
	challenge := []byte{0x15, 0x51, 0x6e, 0x76, 0x95, 0x84, 0xc6, 0xd8}
	expect := []byte{
		0x3a, 0x3c, 0x9f, 0x1c, 0x7d, 0xa7, 0x91, 0x74,
		0xdc, 0x16, 0x75, 0x32, 0xd3, 0x60, 0xab, 0x5e,
		0xfd, 0x01, 0x85, 0x31, 0x73, 0xc8, 0x21, 0x4e,
	}

	res, e := Encrypt(challenge, "derpderp")
	if e != nil {
		t.Fatal(e)
	}
	if bytes.Compare(res, expect) != 0 {
		t.Fatal(fmt.Printf("TestAnotherEncrypt bytes wrong. expect=%d found=%d", expect, res))
	}
}

/*
 osx test
 */
 func TestOSXEncrypt(t *testing.T) {
	challenge := []byte{94, 248, 116, 139, 0, 127, 216, 249}
	expect := []byte{
		253, 201, 156, 168, 136, 124, 25, 116,
		159, 134, 99, 33, 192, 40, 123, 163,
		159, 159, 88, 245, 191, 69, 57, 127,
	}

	res, e := Encrypt(challenge, "derpderp")
	if e != nil {
		t.Fatal(e)
	}
	if bytes.Compare(res, expect) != 0 {
		t.Fatal(fmt.Printf("TestAnotherEncrypt bytes wrong. expect=%d found=%d", expect, res))
	}
 }<|endoftext|>