<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build !gccgo

package vsphere

import (
	"github.com/juju/errors"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/imagemetadata"
	"github.com/juju/juju/environs/simplestreams"
)

/*
Vmware provider use "image-download" data type for simplestream. That's why we use custom implementation of imagemetadata.Fetch function.
We also use custom struct OvfFileMetadata that corresponds to the format used in "image-downloads" simplestream datatype.
Also we use custom append function to filter content of the stream and keep only items, that have ova FileType
*/

type OvaFileMetadata struct {
	Url      string
	Arch     string `json:"arch"`
	Size     int    `json:"size"`
	Path     string `json:"path"`
	FileType string `json:"ftype"`
	Sha256   string `json:"sha256"`
	Md5      string `json:"md5"`
}

func init() {
	simplestreams.RegisterStructTags(OvaFileMetadata{})
}

func findImageMetadata(env *environ, args environs.StartInstanceParams) (*OvaFileMetadata, error) {
	arches := args.Tools.Arches()
	series := args.Tools.OneSeries()
	ic := &imagemetadata.ImageConstraint{
		LookupParams: simplestreams.LookupParams{
			Series: []string{series},
			Arches: arches,
		},
	}
	sources, err := environs.ImageMetadataSources(env)
	if err != nil {
		return nil, errors.Trace(err)
	}

	matchingImages, err := imageMetadataFetch(sources, ic)
	if err != nil {
		return nil, errors.Trace(err)
	}
	if len(matchingImages) == 0 {
		return nil, errors.Errorf("no matching images found for given constraints: %v", ic)
	}

	return matchingImages[0], nil
}

func imageMetadataFetch(sources []simplestreams.DataSource, cons *imagemetadata.ImageConstraint) ([]*OvaFileMetadata, error) {
	params := simplestreams.GetMetadataParams{
		StreamsVersion:   imagemetadata.StreamsVersionV1,
		OnlySigned:       false,
		LookupConstraint: cons,
		ValueParams: simplestreams.ValueParams{
			DataType:      "image-downloads",
			FilterFunc:    appendMatchingFunc,
			ValueTemplate: OvaFileMetadata{},
		},
	}
	items, _, err := simplestreams.GetMetadata(sources, params)
	if err != nil {
		return nil, errors.Trace(err)
	}
	metadata := make([]*OvaFileMetadata, len(items))
	for i, md := range items {
		metadata[i] = md.(*OvaFileMetadata)
	}
	return metadata, nil
}

func appendMatchingFunc(source simplestreams.DataSource, matchingImages []interface{},
	images map[string]interface{}, cons simplestreams.LookupConstraint) []interface{} {

	for _, val := range images {
		file := val.(*OvaFileMetadata)
		if file.FileType == "ova" {
			//ignore error for url data source
			url, _ := source.URL(file.Path)
			file.Url = url
			matchingImages = append(matchingImages, file)
		}
	}
	return matchingImages
}
<commit_msg>Image stream resolution added<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build !gccgo

package vsphere

import (
	"github.com/juju/errors"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/imagemetadata"
	"github.com/juju/juju/environs/simplestreams"
)

/*
Vmware provider use "image-download" data type for simplestream. That's why we use custom implementation of imagemetadata.Fetch function.
We also use custom struct OvfFileMetadata that corresponds to the format used in "image-downloads" simplestream datatype.
Also we use custom append function to filter content of the stream and keep only items, that have ova FileType
*/

type OvaFileMetadata struct {
	Url      string
	Arch     string `json:"arch"`
	Size     int    `json:"size"`
	Path     string `json:"path"`
	FileType string `json:"ftype"`
	Sha256   string `json:"sha256"`
	Md5      string `json:"md5"`
}

func init() {
	simplestreams.RegisterStructTags(OvaFileMetadata{})
}

func findImageMetadata(env *environ, args environs.StartInstanceParams) (*OvaFileMetadata, error) {
	arches := args.Tools.Arches()
	series := args.Tools.OneSeries()
	ic := &imagemetadata.ImageConstraint{
		LookupParams: simplestreams.LookupParams{
			Series: []string{series},
			Arches: arches,
			Stream: env.ecfg.ImageStream(),
		},
	}
	sources, err := environs.ImageMetadataSources(env)
	if err != nil {
		return nil, errors.Trace(err)
	}

	matchingImages, err := imageMetadataFetch(sources, ic)
	if err != nil {
		return nil, errors.Trace(err)
	}
	if len(matchingImages) == 0 {
		return nil, errors.Errorf("no matching images found for given constraints: %v", ic)
	}

	return matchingImages[0], nil
}

func imageMetadataFetch(sources []simplestreams.DataSource, cons *imagemetadata.ImageConstraint) ([]*OvaFileMetadata, error) {
	params := simplestreams.GetMetadataParams{
		StreamsVersion:   imagemetadata.StreamsVersionV1,
		OnlySigned:       false,
		LookupConstraint: cons,
		ValueParams: simplestreams.ValueParams{
			DataType:      "image-downloads",
			FilterFunc:    appendMatchingFunc,
			ValueTemplate: OvaFileMetadata{},
		},
	}
	items, _, err := simplestreams.GetMetadata(sources, params)
	if err != nil {
		return nil, errors.Trace(err)
	}
	metadata := make([]*OvaFileMetadata, len(items))
	for i, md := range items {
		metadata[i] = md.(*OvaFileMetadata)
	}
	return metadata, nil
}

func appendMatchingFunc(source simplestreams.DataSource, matchingImages []interface{},
	images map[string]interface{}, cons simplestreams.LookupConstraint) []interface{} {

	for _, val := range images {
		file := val.(*OvaFileMetadata)
		if file.FileType == "ova" {
			//ignore error for url data source
			url, _ := source.URL(file.Path)
			file.Url = url
			matchingImages = append(matchingImages, file)
		}
	}
	return matchingImages
}
<|endoftext|>


<commit_before>package kdtree

import (
	"ellipsoid"
	"errors"
	"fmt"
	"geo"
	"graph"
	"math"
	"mm"
	"path"
)

var (
	e             ellipsoid.Ellipsoid
	clusterKdTree ClusterKdTree
)

func init() {
	e = ellipsoid.Init("WGS84", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
}

func LoadKdTree(clusterGraph *graph.ClusterGraph, base string) error {
	// TODO precompute coordinates for faster live queries?
	dummyCoordinates := make([]geo.Coordinate, 0)

	clusterKdTrees := make([]*KdTree, len(clusterGraph.Cluster))
	for i, g := range clusterGraph.Cluster {
		clusterDir := fmt.Sprintf("cluster%d/kdtree.ftf", i+1)
		var encodedSteps []uint32
		err := mm.Open(path.Join(base, clusterDir), &encodedSteps)
		if err != nil {
			return err
		}
		clusterKdTrees[i] = &KdTree{Graph: g, EncodedSteps: encodedSteps, Coordinates: dummyCoordinates}
	}

	var encodedSteps []uint32
	err := mm.Open(path.Join(base, "/overlay/kdtree.ftf"), &encodedSteps)
	if err != nil {
		return err
	}
	overlayKdTree := &KdTree{Graph: clusterGraph.Overlay, EncodedSteps: encodedSteps, Coordinates: dummyCoordinates}

	var bboxesFile []int32
	err = mm.Open(path.Join(base, "bboxes.ftf"), &bboxesFile)
	if err != nil {
		return err
	}
	if len(bboxesFile)/4 != clusterGraph.Overlay.ClusterCount() {
		return errors.New("size of bboxes file does not match cluster count")
	}
	bboxes := make([]geo.BBox, len(bboxesFile)/4)
	for i, _ := range bboxes {
		bboxes[i] = geo.DecodeBBox(bboxesFile[4*i : 4*i+4])
	}

	clusterKdTree = ClusterKdTree{Overlay: overlayKdTree, Cluster: clusterKdTrees, BBoxes: bboxes}
	return nil
}

// NearestNeighbor can fail and then returns -1 as an index
func NearestNeighbor(x geo.Coordinate, forward bool, trans graph.Transport) (int, []graph.Way) {
	edges := []graph.Edge(nil)

	minDistance := math.Inf(1)
	var bestEncodedStep uint32
	clusterIndex := -1
	for i, b := range clusterKdTree.BBoxes {
		if b.Contains(x) {
			kdTree := clusterKdTree.Cluster[i]
			encodedStep := binarySearch(kdTree, kdTree.EncodedSteps, x, true /* compareLat */, trans, &edges)
			coord := decodeCoordinate(kdTree.Graph, encodedStep, trans, &edges)
			dist, _ := e.To(x.Lat, x.Lng, coord.Lat, coord.Lng)
			if dist < minDistance {
				minDistance = dist
				bestEncodedStep = encodedStep
				clusterIndex = i
			}
		}
	}
	if clusterIndex == -1 {
		fmt.Printf("NearestNeighbor did not find a matching cluster\n")
		return -1, nil
	}

	g := clusterKdTree.Cluster[clusterIndex].Graph
	return clusterIndex, decodeWays(g, bestEncodedStep, forward, trans, &edges)
}

func binarySearch(kdTree *KdTree, nodes []uint32, x geo.Coordinate, compareLat bool,
	trans graph.Transport, edges *[]graph.Edge) uint32 {
	g := kdTree.Graph
	if len(nodes) == 0 {
		panic("nearestNeighbor: recursion to dead end")
	} else if len(nodes) == 1 {
		return nodes[0]
	}
	middle := len(nodes) / 2

	// exact hit
	middleCoord := decodeCoordinate(g, nodes[middle], trans, edges)
	if x.Lat == middleCoord.Lat && x.Lng == middleCoord.Lng {
		return nodes[middle]
	}

	// corner case where the nearest point can be on both sides of the middle
	if (compareLat && x.Lat == middleCoord.Lat) || (!compareLat && x.Lng == middleCoord.Lng) {
		// recursion on both halfs
		leftRecEnc := binarySearch(kdTree, nodes[:middle], x, !compareLat, trans, edges)
		rightRecEnc := binarySearch(kdTree, nodes[middle+1:], x, !compareLat, trans, edges)
		leftCoord := decodeCoordinate(g, leftRecEnc, trans, edges)
		rightCoord := decodeCoordinate(g, rightRecEnc, trans, edges)

		// TODO exact distance on Coordinates?
		distMiddle, _ := e.To(x.Lat, x.Lng, middleCoord.Lat, middleCoord.Lng)
		distRecursionLeft, _ := e.To(x.Lat, x.Lng, leftCoord.Lat, leftCoord.Lng)
		distRecursionRight, _ := e.To(x.Lat, x.Lng, rightCoord.Lat, rightCoord.Lng)
		if distRecursionLeft < distRecursionRight {
			if distRecursionLeft < distMiddle {
				return leftRecEnc
			}
			return nodes[middle]
		}
		if distRecursionRight < distMiddle {
			return rightRecEnc
		}
		return nodes[middle]
	}

	var left bool
	if compareLat {
		left = x.Lat < middleCoord.Lat
	} else {
		left = x.Lng < middleCoord.Lng
	}
	if left {
		// stop if there is nothing left of the middle
		if middle == 0 {
			return nodes[middle]
		}
		// recursion on the left half
		recEnc := binarySearch(kdTree, nodes[:middle], x, !compareLat, trans, edges)
		recCoord := decodeCoordinate(g, recEnc, trans, edges)

		// compare middle and result from the left
		distMiddle, _ := e.To(x.Lat, x.Lng, middleCoord.Lat, middleCoord.Lng)
		distRecursion, _ := e.To(x.Lat, x.Lng, recCoord.Lat, recCoord.Lng)
		if distMiddle < distRecursion {
			return nodes[middle]
		}
		return recEnc
	}
	// stop if there is nothing right of the middle
	if middle == len(nodes)-1 {
		return nodes[middle]
	}
	// recursion on the right half
	recEnc := binarySearch(kdTree, nodes[middle+1:], x, !compareLat, trans, edges)
	recCoord := decodeCoordinate(g, recEnc, trans, edges)

	// compare middle and result from the right
	distMiddle, _ := e.To(x.Lat, x.Lng, middleCoord.Lat, middleCoord.Lng)
	distRecursion, _ := e.To(x.Lat, x.Lng, recCoord.Lat, recCoord.Lng)
	if distMiddle < distRecursion {
		return nodes[middle]
	}
	return recEnc
}

func decodeCoordinate(g graph.Graph, ec uint32, trans graph.Transport, edges *[]graph.Edge) geo.Coordinate {
	vertexIndex := ec >> (EdgeOffsetBits + StepOffsetBits)
	edgeOffset := (ec >> StepOffsetBits) & MaxEdgeOffset
	stepOffset := ec & MaxStepOffset
	vertex := graph.Vertex(vertexIndex)
	if edgeOffset == MaxEdgeOffset && stepOffset == MaxStepOffset {
		// it is a vertex and not a step
		return g.VertexCoordinate(vertex)
	}

	(*edges) = g.VertexEdges(vertex, true /* out */, trans, *edges)
	for i, e := range *edges {
		if i == int(edgeOffset) {
			steps := g.EdgeSteps(e, vertex)
			return steps[stepOffset]
		}
		i++
	}
	panic("incorrect encoding: no matching edge found")
}

func decodeWays(g graph.Graph, ec uint32, forward bool, trans graph.Transport, edges *[]graph.Edge) []graph.Way {
	vertexIndex := ec >> (EdgeOffsetBits + StepOffsetBits)
	edgeOffset := (ec >> StepOffsetBits) & MaxEdgeOffset
	offset := ec & MaxStepOffset
	vertex := graph.Vertex(vertexIndex)

	if edgeOffset == MaxEdgeOffset && offset == MaxStepOffset {
		// The easy case, where we hit some vertex exactly.
		w := make([]graph.Way, 1)
		target := g.VertexCoordinate(vertex)
		w[0] = graph.Way{Length: 0, Vertex: vertex, Steps: nil, Target: target}
		return w
	}

	var edge graph.Edge
	(*edges) = g.VertexEdges(vertex, true /* out */, trans, *edges)
	for i, e := range *edges {
		if i == int(edgeOffset) {
			edge = e
			break
		}
		i++
	}
	t1 := vertex                       // start vertex
	t2 := g.EdgeOpposite(edge, vertex) // end vertex

	// now we can allocate the way corresponding to (edge,offset),
	// but there are three cases to consider:
	// - if the way is bidirectional we have to compute both directions,
	//   if forward == true the from the offset two both endpoints,
	//   and the reverse otherwise
	// - if the way is unidirectional then we have to compute the way
	//   from the StartPoint to offset if forward == false
	// - otherwise we have to compute the way from offset to the EndPoint
	// Strictly speaking only the second case needs an additional binary
	// search in the form of edge.StartPoint, but let's keep this simple
	// for now.
	steps := g.EdgeSteps(edge, vertex)
	b1 := make([]geo.Coordinate, len(steps[:offset]))
	b2 := make([]geo.Coordinate, len(steps[offset+1:]))
	copy(b1, steps[:offset])
	copy(b2, steps[offset+1:])
	l1 := geo.StepLength(steps[:offset+1])
	l2 := geo.StepLength(steps[offset:])
	t1Coord := g.VertexCoordinate(t1)
	t2Coord := g.VertexCoordinate(t2)
	d1, _ := e.To(t1Coord.Lat, t1Coord.Lng, steps[0].Lat, steps[0].Lng)
	d2, _ := e.To(t2Coord.Lat, t2Coord.Lng, steps[len(steps)-1].Lat, steps[len(steps)-1].Lng)
	l1 += d1
	l2 += d2
	target := steps[offset]

	if !forward {
		reverse(b2)
	} else {
		reverse(b1)
	}

	// TODO check oneway based on transport
	oneway := true

	var w []graph.Way
	if !oneway {
		w = make([]graph.Way, 2) // bidirectional
		w[0] = graph.Way{Length: l1, Vertex: t1, Steps: b1, Forward: forward, Target: target}
		w[1] = graph.Way{Length: l2, Vertex: t2, Steps: b2, Forward: forward, Target: target}
	} else {
		w = make([]graph.Way, 1) // one way
		if forward {
			w[0] = graph.Way{Length: l2, Vertex: t2, Steps: b2, Forward: forward, Target: target}
		} else {
			w[0] = graph.Way{Length: l1, Vertex: t1, Steps: b1, Forward: forward, Target: target}
		}
	}
	return w
}

func reverse(steps []geo.Coordinate) {
	for i, j := 0, len(steps)-1; i < j; i, j = i+1, j-1 {
		steps[i], steps[j] = steps[j], steps[i]
	}
}
<commit_msg>completed nearest neighbor search<commit_after>package kdtree

import (
	"ellipsoid"
	"errors"
	"fmt"
	"geo"
	"graph"
	"mm"
	"path"
)

var (
	e             ellipsoid.Ellipsoid
	clusterKdTree ClusterKdTree
)

func init() {
	e = ellipsoid.Init("WGS84", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
}

func LoadKdTree(clusterGraph *graph.ClusterGraph, base string) error {
	// TODO precompute coordinates for faster live queries?
	dummyCoordinates := make([]geo.Coordinate, 0)

	clusterKdTrees := make([]*KdTree, len(clusterGraph.Cluster))
	for i, g := range clusterGraph.Cluster {
		clusterDir := fmt.Sprintf("cluster%d/kdtree.ftf", i+1)
		var encodedSteps []uint32
		err := mm.Open(path.Join(base, clusterDir), &encodedSteps)
		if err != nil {
			return err
		}
		clusterKdTrees[i] = &KdTree{Graph: g, EncodedSteps: encodedSteps, Coordinates: dummyCoordinates}
	}

	var encodedSteps []uint32
	err := mm.Open(path.Join(base, "/overlay/kdtree.ftf"), &encodedSteps)
	if err != nil {
		return err
	}
	overlayKdTree := &KdTree{Graph: clusterGraph.Overlay, EncodedSteps: encodedSteps, Coordinates: dummyCoordinates}

	var bboxesFile []int32
	err = mm.Open(path.Join(base, "bboxes.ftf"), &bboxesFile)
	if err != nil {
		return err
	}
	if len(bboxesFile)/4 != clusterGraph.Overlay.ClusterCount() {
		return errors.New("size of bboxes file does not match cluster count")
	}
	bboxes := make([]geo.BBox, len(bboxesFile)/4)
	for i, _ := range bboxes {
		bboxes[i] = geo.DecodeBBox(bboxesFile[4*i : 4*i+4])
	}

	clusterKdTree = ClusterKdTree{Overlay: overlayKdTree, Cluster: clusterKdTrees, BBoxes: bboxes}
	return nil
}

// NearestNeighbor returns -1 if the way is on the overlay graph
// No fail strategy: a nearest point on the overlay graph is always returned if no point
// is found in the clusters.
func NearestNeighbor(x geo.Coordinate, forward bool, trans graph.Transport) (int, []graph.Way) {
	edges := []graph.Edge(nil)

	t := clusterKdTree.Overlay
	bestEncodedStep := binarySearch(t, t.EncodedSteps, x, true /* compareLat */, trans, &edges)
	coordOverlay := decodeCoordinate(t.Graph, bestEncodedStep, trans, &edges)
	minDistance, _ := e.To(x.Lat, x.Lng, coordOverlay.Lat, coordOverlay.Lng)

	clusterIndex := -1
	for i, b := range clusterKdTree.BBoxes {
		if b.Contains(x) {
			kdTree := clusterKdTree.Cluster[i]
			encodedStep := binarySearch(kdTree, kdTree.EncodedSteps, x, true /* compareLat */, trans, &edges)
			coord := decodeCoordinate(kdTree.Graph, encodedStep, trans, &edges)
			dist, _ := e.To(x.Lat, x.Lng, coord.Lat, coord.Lng)
			if dist < minDistance {
				minDistance = dist
				bestEncodedStep = encodedStep
				clusterIndex = i
			}
		}
	}

	g := clusterKdTree.Cluster[clusterIndex].Graph
	return clusterIndex, decodeWays(g, bestEncodedStep, forward, trans, &edges)
}

func binarySearch(kdTree *KdTree, nodes []uint32, x geo.Coordinate, compareLat bool,
	trans graph.Transport, edges *[]graph.Edge) uint32 {
	g := kdTree.Graph
	if len(nodes) == 0 {
		panic("nearestNeighbor: recursion to dead end")
	} else if len(nodes) == 1 {
		return nodes[0]
	}
	middle := len(nodes) / 2

	// exact hit
	middleCoord := decodeCoordinate(g, nodes[middle], trans, edges)
	if x.Lat == middleCoord.Lat && x.Lng == middleCoord.Lng {
		return nodes[middle]
	}

	// corner case where the nearest point can be on both sides of the middle
	if (compareLat && x.Lat == middleCoord.Lat) || (!compareLat && x.Lng == middleCoord.Lng) {
		// recursion on both halfs
		leftRecEnc := binarySearch(kdTree, nodes[:middle], x, !compareLat, trans, edges)
		rightRecEnc := binarySearch(kdTree, nodes[middle+1:], x, !compareLat, trans, edges)
		leftCoord := decodeCoordinate(g, leftRecEnc, trans, edges)
		rightCoord := decodeCoordinate(g, rightRecEnc, trans, edges)

		// TODO exact distance on Coordinates?
		distMiddle, _ := e.To(x.Lat, x.Lng, middleCoord.Lat, middleCoord.Lng)
		distRecursionLeft, _ := e.To(x.Lat, x.Lng, leftCoord.Lat, leftCoord.Lng)
		distRecursionRight, _ := e.To(x.Lat, x.Lng, rightCoord.Lat, rightCoord.Lng)
		if distRecursionLeft < distRecursionRight {
			if distRecursionLeft < distMiddle {
				return leftRecEnc
			}
			return nodes[middle]
		}
		if distRecursionRight < distMiddle {
			return rightRecEnc
		}
		return nodes[middle]
	}

	var left bool
	if compareLat {
		left = x.Lat < middleCoord.Lat
	} else {
		left = x.Lng < middleCoord.Lng
	}
	if left {
		// stop if there is nothing left of the middle
		if middle == 0 {
			return nodes[middle]
		}
		// recursion on the left half
		recEnc := binarySearch(kdTree, nodes[:middle], x, !compareLat, trans, edges)
		recCoord := decodeCoordinate(g, recEnc, trans, edges)

		// compare middle and result from the left
		distMiddle, _ := e.To(x.Lat, x.Lng, middleCoord.Lat, middleCoord.Lng)
		distRecursion, _ := e.To(x.Lat, x.Lng, recCoord.Lat, recCoord.Lng)
		if distMiddle < distRecursion {
			return nodes[middle]
		}
		return recEnc
	}
	// stop if there is nothing right of the middle
	if middle == len(nodes)-1 {
		return nodes[middle]
	}
	// recursion on the right half
	recEnc := binarySearch(kdTree, nodes[middle+1:], x, !compareLat, trans, edges)
	recCoord := decodeCoordinate(g, recEnc, trans, edges)

	// compare middle and result from the right
	distMiddle, _ := e.To(x.Lat, x.Lng, middleCoord.Lat, middleCoord.Lng)
	distRecursion, _ := e.To(x.Lat, x.Lng, recCoord.Lat, recCoord.Lng)
	if distMiddle < distRecursion {
		return nodes[middle]
	}
	return recEnc
}

func decodeCoordinate(g graph.Graph, ec uint32, trans graph.Transport, edges *[]graph.Edge) geo.Coordinate {
	vertexIndex := ec >> (EdgeOffsetBits + StepOffsetBits)
	edgeOffset := (ec >> StepOffsetBits) & MaxEdgeOffset
	stepOffset := ec & MaxStepOffset
	vertex := graph.Vertex(vertexIndex)
	if edgeOffset == MaxEdgeOffset && stepOffset == MaxStepOffset {
		// it is a vertex and not a step
		return g.VertexCoordinate(vertex)
	}

	(*edges) = g.VertexEdges(vertex, true /* out */, trans, *edges)
	for i, e := range *edges {
		if i == int(edgeOffset) {
			steps := g.EdgeSteps(e, vertex)
			return steps[stepOffset]
		}
		i++
	}
	panic("incorrect encoding: no matching edge found")
}

func decodeWays(g graph.Graph, ec uint32, forward bool, trans graph.Transport, edges *[]graph.Edge) []graph.Way {
	vertexIndex := ec >> (EdgeOffsetBits + StepOffsetBits)
	edgeOffset := (ec >> StepOffsetBits) & MaxEdgeOffset
	offset := ec & MaxStepOffset
	vertex := graph.Vertex(vertexIndex)

	if edgeOffset == MaxEdgeOffset && offset == MaxStepOffset {
		// The easy case, where we hit some vertex exactly.
		w := make([]graph.Way, 1)
		target := g.VertexCoordinate(vertex)
		w[0] = graph.Way{Length: 0, Vertex: vertex, Steps: nil, Target: target}
		return w
	}

	var edge graph.Edge
	(*edges) = g.VertexEdges(vertex, true /* out */, trans, *edges)
	for i, e := range *edges {
		if i == int(edgeOffset) {
			edge = e
			break
		}
		i++
	}
	t1 := vertex                       // start vertex
	t2 := g.EdgeOpposite(edge, vertex) // end vertex

	// now we can allocate the way corresponding to (edge,offset),
	// but there are three cases to consider:
	// - if the way is bidirectional we have to compute both directions,
	//   if forward == true the from the offset two both endpoints,
	//   and the reverse otherwise
	// - if the way is unidirectional then we have to compute the way
	//   from the StartPoint to offset if forward == false
	// - otherwise we have to compute the way from offset to the EndPoint
	// Strictly speaking only the second case needs an additional binary
	// search in the form of edge.StartPoint, but let's keep this simple
	// for now.
	steps := g.EdgeSteps(edge, vertex)
	b1 := make([]geo.Coordinate, len(steps[:offset]))
	b2 := make([]geo.Coordinate, len(steps[offset+1:]))
	copy(b1, steps[:offset])
	copy(b2, steps[offset+1:])
	l1 := geo.StepLength(steps[:offset+1])
	l2 := geo.StepLength(steps[offset:])
	t1Coord := g.VertexCoordinate(t1)
	t2Coord := g.VertexCoordinate(t2)
	d1, _ := e.To(t1Coord.Lat, t1Coord.Lng, steps[0].Lat, steps[0].Lng)
	d2, _ := e.To(t2Coord.Lat, t2Coord.Lng, steps[len(steps)-1].Lat, steps[len(steps)-1].Lng)
	l1 += d1
	l2 += d2
	target := steps[offset]

	if !forward {
		reverse(b2)
	} else {
		reverse(b1)
	}

	// TODO check oneway based on transport
	oneway := true

	var w []graph.Way
	if !oneway {
		w = make([]graph.Way, 2) // bidirectional
		w[0] = graph.Way{Length: l1, Vertex: t1, Steps: b1, Forward: forward, Target: target}
		w[1] = graph.Way{Length: l2, Vertex: t2, Steps: b2, Forward: forward, Target: target}
	} else {
		w = make([]graph.Way, 1) // one way
		if forward {
			w[0] = graph.Way{Length: l2, Vertex: t2, Steps: b2, Forward: forward, Target: target}
		} else {
			w[0] = graph.Way{Length: l1, Vertex: t1, Steps: b1, Forward: forward, Target: target}
		}
	}
	return w
}

func reverse(steps []geo.Coordinate) {
	for i, j := 0, len(steps)-1; i < j; i, j = i+1, j-1 {
		steps[i], steps[j] = steps[j], steps[i]
	}
}
<|endoftext|>


<commit_before><commit_msg>Build assets<commit_after><|endoftext|>


<commit_before>package data

import (
	"crypto/sha256"
	"encoding/base64"
	"strings"
)

type Classroom struct {
	Name     string   `json: "name"`
	Hash     string   `json: "hash"`
	Source   []string `json: "source"`
	Firmware []string `json: "firmware"`
}

func NewClassroom(name string) *Classroom {
	classroom := &Classroom{
		Name:     name,
		Source:   strings.Split(defaultSource, "\n"),
		Firmware: strings.Split(defaultFirmware, "\n"),
	}
	classroom.UpdateHash()
	return classroom
}

func (c *Classroom) UpdateHash() {
	digester := sha256.New()

	for _, line := range c.Source {
		digester.Write([]byte(line))
	}
	for _, line := range c.Firmware {
		digester.Write([]byte(line))
	}
	c.Hash = base64.StdEncoding.EncodeToString(digester.Sum(nil))
}
<commit_msg>Fix vet issues<commit_after>package data

import (
	"crypto/sha256"
	"encoding/base64"
	"strings"
)

type Classroom struct {
	Name     string   `json:"name"`
	Hash     string   `json:"hash"`
	Source   []string `json:"source"`
	Firmware []string `json:"firmware"`
}

func NewClassroom(name string) *Classroom {
	classroom := &Classroom{
		Name:     name,
		Source:   strings.Split(defaultSource, "\n"),
		Firmware: strings.Split(defaultFirmware, "\n"),
	}
	classroom.UpdateHash()
	return classroom
}

func (c *Classroom) UpdateHash() {
	digester := sha256.New()

	for _, line := range c.Source {
		digester.Write([]byte(line))
	}
	for _, line := range c.Firmware {
		digester.Write([]byte(line))
	}
	c.Hash = base64.StdEncoding.EncodeToString(digester.Sum(nil))
}
<|endoftext|>


<commit_before>package pipelineserver

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"go.pachyderm.com/pachyderm/src/pfs"
	"go.pachyderm.com/pachyderm/src/pps"
	"go.pachyderm.com/pachyderm/src/pps/persist"
	"go.pedge.io/google-protobuf"
	"go.pedge.io/proto/rpclog"
	"go.pedge.io/protolog"
	"golang.org/x/net/context"
)

const (
	changeEventTypeCreate changeEventType = iota
	changeEventTypeDelete
)

type changeEventType int

type apiServer struct {
	protorpclog.Logger
	pfsAPIClient     pfs.APIClient
	jobAPIClient     pps.JobAPIClient
	persistAPIClient persist.APIClient

	started                          bool
	pipelineNameToPipelineController map[string]*pipelineController
	lock                             *sync.Mutex
}

func newAPIServer(
	pfsAPIClient pfs.APIClient,
	jobAPIClient pps.JobAPIClient,
	persistAPIClient persist.APIClient,
) *apiServer {
	return &apiServer{
		protorpclog.NewLogger("pachyderm.pps.PipelineAPI"),
		pfsAPIClient,
		jobAPIClient,
		persistAPIClient,
		false,
		make(map[string]*pipelineController),
		&sync.Mutex{},
	}
}

func (a *apiServer) Start() error {
	a.lock.Lock()
	defer a.lock.Unlock()
	// TODO(pedge): volatile bool?
	if a.started {
		// TODO(pedge): abstract error to public variable
		return errors.New("pachyderm.pps.pipelineserver: already started")
	}
	a.started = true
	pipelineInfos, err := a.ListPipeline(context.Background(), &pps.ListPipelineRequest{})
	if err != nil {
		return err
	}
	for _, pipelineInfo := range pipelineInfos.PipelineInfo {
		if err := a.addPipelineController(pipelineInfo); err != nil {
			return err
		}
	}
	return nil
}

func (a *apiServer) CreatePipeline(ctx context.Context, request *pps.CreatePipelineRequest) (response *google_protobuf.Empty, err error) {
	defer func(start time.Time) { a.Log(request, response, err, time.Since(start)) }(time.Now())
	if _, err := a.persistAPIClient.CreatePipelineInfo(
		ctx,
		&persist.PipelineInfo{
			PipelineName: request.Pipeline.Name,
			Transform:    request.Transform,
			Input:        request.Input,
			Output:       request.Output,
		},
	); err != nil {
		return nil, err
	}
	if err := a.registerChangeEvent(
		ctx,
		&changeEvent{
			Type:         changeEventTypeCreate,
			PipelineName: request.Pipeline.Name,
		},
	); err != nil {
		// TODO(pedge): need to roll back the db create
		return nil, err
	}
	return google_protobuf.EmptyInstance, nil
}

func (a *apiServer) InspectPipeline(ctx context.Context, request *pps.InspectPipelineRequest) (response *pps.PipelineInfo, err error) {
	defer func(start time.Time) { a.Log(request, response, err, time.Since(start)) }(time.Now())
	persistPipelineInfo, err := a.persistAPIClient.GetPipelineInfo(ctx, request.Pipeline)
	if err != nil {
		return nil, err
	}
	return persistPipelineInfoToPipelineInfo(persistPipelineInfo), nil
}

func (a *apiServer) ListPipeline(ctx context.Context, request *pps.ListPipelineRequest) (response *pps.PipelineInfos, err error) {
	defer func(start time.Time) { a.Log(request, response, err, time.Since(start)) }(time.Now())
	persistPipelineInfos, err := a.persistAPIClient.ListPipelineInfos(ctx, google_protobuf.EmptyInstance)
	if err != nil {
		return nil, err
	}
	pipelineInfos := make([]*pps.PipelineInfo, len(persistPipelineInfos.PipelineInfo))
	for i, persistPipelineInfo := range persistPipelineInfos.PipelineInfo {
		pipelineInfos[i] = persistPipelineInfoToPipelineInfo(persistPipelineInfo)
	}
	return &pps.PipelineInfos{
		PipelineInfo: pipelineInfos,
	}, nil
}

func (a *apiServer) DeletePipeline(ctx context.Context, request *pps.DeletePipelineRequest) (response *google_protobuf.Empty, err error) {
	if _, err := a.persistAPIClient.DeletePipelineInfo(ctx, request.Pipeline); err != nil {
		return nil, err
	}
	if err := a.registerChangeEvent(
		ctx,
		&changeEvent{
			Type:         changeEventTypeDelete,
			PipelineName: request.Pipeline.Name,
		},
	); err != nil {
		return nil, err
	}
	return google_protobuf.EmptyInstance, nil
}

func persistPipelineInfoToPipelineInfo(persistPipelineInfo *persist.PipelineInfo) *pps.PipelineInfo {
	return &pps.PipelineInfo{
		Pipeline: &pps.Pipeline{
			Name: persistPipelineInfo.PipelineName,
		},
		Transform: persistPipelineInfo.Transform,
		Input:     persistPipelineInfo.Input,
		Output:    persistPipelineInfo.Output,
	}
}

type changeEvent struct {
	Type         changeEventType
	PipelineName string
}

// TODO(pedge): this is relateively out of date, we can just do this directly in the functions, and
// with the create at least, we avoid a db read
func (a *apiServer) registerChangeEvent(ctx context.Context, request *changeEvent) error {
	a.lock.Lock()
	defer a.lock.Unlock()
	switch request.Type {
	case changeEventTypeCreate:
		if !a.pipelineRegistered(request.PipelineName) {
			pipelineInfo, err := a.InspectPipeline(
				ctx,
				&pps.InspectPipelineRequest{
					Pipeline: &pps.Pipeline{
						Name: request.PipelineName,
					},
				},
			)
			if err != nil {
				return err
			}
			if err := a.addPipelineController(pipelineInfo); err != nil {
				return err
			}
			// TODO(pedge): what to do?
		} else {
			protolog.Warnf("pachyderm.pps.pipelineserver: had a create change event for an existing pipeline: %v", request)
			if err := a.removePipelineController(request.PipelineName); err != nil {
				return err
			}
			pipelineInfo, err := a.InspectPipeline(
				ctx,
				&pps.InspectPipelineRequest{
					Pipeline: &pps.Pipeline{
						Name: request.PipelineName,
					},
				},
			)
			if err != nil {
				return err
			}
			if err := a.addPipelineController(pipelineInfo); err != nil {
				return err
			}
		}
	case changeEventTypeDelete:
		if !a.pipelineRegistered(request.PipelineName) {
			protolog.Warnf("pachyderm.pps.pipelineserver: had a delete change event for a pipeline that was not registered: %v", request)
		} else {
			if err := a.removePipelineController(request.PipelineName); err != nil {
				return err
			}
		}
	default:
		return fmt.Errorf("pachyderm.pps.pipelineserver: unknown change event type: %v", request.Type)
	}
	return nil
}

func (a *apiServer) pipelineRegistered(name string) bool {
	_, ok := a.pipelineNameToPipelineController[name]
	return ok
}

func (a *apiServer) addPipelineController(pipelineInfo *pps.PipelineInfo) error {
	pipelineController := newPipelineController(
		a.pfsAPIClient,
		a.jobAPIClient,
		pps.NewLocalPipelineAPIClient(a),
		pipelineInfo,
	)
	a.pipelineNameToPipelineController[pipelineInfo.Pipeline.Name] = pipelineController
	return pipelineController.Start()
}

func (a *apiServer) removePipelineController(name string) error {
	pipelineController, ok := a.pipelineNameToPipelineController[name]
	if !ok {
		return fmt.Errorf("pachyderm.pps.pipelineserver: no pipeline registered for name: %s", name)
	}
	pipelineController.Cancel()
	return nil
}
<commit_msg>pps api pipeline server addPipelineControllerByName<commit_after>package pipelineserver

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"go.pachyderm.com/pachyderm/src/pfs"
	"go.pachyderm.com/pachyderm/src/pps"
	"go.pachyderm.com/pachyderm/src/pps/persist"
	"go.pedge.io/google-protobuf"
	"go.pedge.io/proto/rpclog"
	"go.pedge.io/protolog"
	"golang.org/x/net/context"
)

const (
	changeEventTypeCreate changeEventType = iota
	changeEventTypeDelete
)

type changeEventType int

type apiServer struct {
	protorpclog.Logger
	pfsAPIClient     pfs.APIClient
	jobAPIClient     pps.JobAPIClient
	persistAPIClient persist.APIClient

	started                          bool
	pipelineNameToPipelineController map[string]*pipelineController
	lock                             *sync.Mutex
}

func newAPIServer(
	pfsAPIClient pfs.APIClient,
	jobAPIClient pps.JobAPIClient,
	persistAPIClient persist.APIClient,
) *apiServer {
	return &apiServer{
		protorpclog.NewLogger("pachyderm.pps.PipelineAPI"),
		pfsAPIClient,
		jobAPIClient,
		persistAPIClient,
		false,
		make(map[string]*pipelineController),
		&sync.Mutex{},
	}
}

func (a *apiServer) Start() error {
	a.lock.Lock()
	defer a.lock.Unlock()
	// TODO(pedge): volatile bool?
	if a.started {
		// TODO(pedge): abstract error to public variable
		return errors.New("pachyderm.pps.pipelineserver: already started")
	}
	a.started = true
	pipelineInfos, err := a.ListPipeline(context.Background(), &pps.ListPipelineRequest{})
	if err != nil {
		return err
	}
	for _, pipelineInfo := range pipelineInfos.PipelineInfo {
		if err := a.addPipelineController(pipelineInfo); err != nil {
			return err
		}
	}
	return nil
}

func (a *apiServer) CreatePipeline(ctx context.Context, request *pps.CreatePipelineRequest) (response *google_protobuf.Empty, err error) {
	defer func(start time.Time) { a.Log(request, response, err, time.Since(start)) }(time.Now())
	if _, err := a.persistAPIClient.CreatePipelineInfo(
		ctx,
		&persist.PipelineInfo{
			PipelineName: request.Pipeline.Name,
			Transform:    request.Transform,
			Input:        request.Input,
			Output:       request.Output,
		},
	); err != nil {
		return nil, err
	}
	if err := a.registerChangeEvent(
		ctx,
		&changeEvent{
			Type:         changeEventTypeCreate,
			PipelineName: request.Pipeline.Name,
		},
	); err != nil {
		// TODO(pedge): need to roll back the db create
		return nil, err
	}
	return google_protobuf.EmptyInstance, nil
}

func (a *apiServer) InspectPipeline(ctx context.Context, request *pps.InspectPipelineRequest) (response *pps.PipelineInfo, err error) {
	defer func(start time.Time) { a.Log(request, response, err, time.Since(start)) }(time.Now())
	persistPipelineInfo, err := a.persistAPIClient.GetPipelineInfo(ctx, request.Pipeline)
	if err != nil {
		return nil, err
	}
	return persistPipelineInfoToPipelineInfo(persistPipelineInfo), nil
}

func (a *apiServer) ListPipeline(ctx context.Context, request *pps.ListPipelineRequest) (response *pps.PipelineInfos, err error) {
	defer func(start time.Time) { a.Log(request, response, err, time.Since(start)) }(time.Now())
	persistPipelineInfos, err := a.persistAPIClient.ListPipelineInfos(ctx, google_protobuf.EmptyInstance)
	if err != nil {
		return nil, err
	}
	pipelineInfos := make([]*pps.PipelineInfo, len(persistPipelineInfos.PipelineInfo))
	for i, persistPipelineInfo := range persistPipelineInfos.PipelineInfo {
		pipelineInfos[i] = persistPipelineInfoToPipelineInfo(persistPipelineInfo)
	}
	return &pps.PipelineInfos{
		PipelineInfo: pipelineInfos,
	}, nil
}

func (a *apiServer) DeletePipeline(ctx context.Context, request *pps.DeletePipelineRequest) (response *google_protobuf.Empty, err error) {
	if _, err := a.persistAPIClient.DeletePipelineInfo(ctx, request.Pipeline); err != nil {
		return nil, err
	}
	if err := a.registerChangeEvent(
		ctx,
		&changeEvent{
			Type:         changeEventTypeDelete,
			PipelineName: request.Pipeline.Name,
		},
	); err != nil {
		return nil, err
	}
	return google_protobuf.EmptyInstance, nil
}

func persistPipelineInfoToPipelineInfo(persistPipelineInfo *persist.PipelineInfo) *pps.PipelineInfo {
	return &pps.PipelineInfo{
		Pipeline: &pps.Pipeline{
			Name: persistPipelineInfo.PipelineName,
		},
		Transform: persistPipelineInfo.Transform,
		Input:     persistPipelineInfo.Input,
		Output:    persistPipelineInfo.Output,
	}
}

type changeEvent struct {
	Type         changeEventType
	PipelineName string
}

// TODO(pedge): this is relateively out of date, we can just do this directly in the functions, and
// with the create at least, we avoid a db read
func (a *apiServer) registerChangeEvent(ctx context.Context, request *changeEvent) error {
	a.lock.Lock()
	defer a.lock.Unlock()
	switch request.Type {
	case changeEventTypeCreate:
		if !a.pipelineRegistered(request.PipelineName) {
			if err := a.addPipelineControllerByName(ctx, request.PipelineName); err != nil {
				return err
			}
			// TODO(pedge): what to do?
		} else {
			protolog.Warnf("pachyderm.pps.pipelineserver: had a create change event for an existing pipeline: %v", request)
			if err := a.removePipelineController(request.PipelineName); err != nil {
				return err
			}
			if err := a.addPipelineControllerByName(ctx, request.PipelineName); err != nil {
				return err
			}
		}
	case changeEventTypeDelete:
		if !a.pipelineRegistered(request.PipelineName) {
			protolog.Warnf("pachyderm.pps.pipelineserver: had a delete change event for a pipeline that was not registered: %v", request)
		} else {
			if err := a.removePipelineController(request.PipelineName); err != nil {
				return err
			}
		}
	default:
		return fmt.Errorf("pachyderm.pps.pipelineserver: unknown change event type: %v", request.Type)
	}
	return nil
}

func (a *apiServer) pipelineRegistered(name string) bool {
	_, ok := a.pipelineNameToPipelineController[name]
	return ok
}

func (a *apiServer) addPipelineControllerByName(ctx context.Context, name string) error {
	pipelineInfo, err := a.InspectPipeline(
		ctx,
		&pps.InspectPipelineRequest{
			Pipeline: &pps.Pipeline{
				Name: name,
			},
		},
	)
	if err != nil {
		return err
	}
	return a.addPipelineController(pipelineInfo)
}

func (a *apiServer) addPipelineController(pipelineInfo *pps.PipelineInfo) error {
	pipelineController := newPipelineController(
		a.pfsAPIClient,
		a.jobAPIClient,
		pps.NewLocalPipelineAPIClient(a),
		pipelineInfo,
	)
	a.pipelineNameToPipelineController[pipelineInfo.Pipeline.Name] = pipelineController
	return pipelineController.Start()
}

func (a *apiServer) removePipelineController(name string) error {
	pipelineController, ok := a.pipelineNameToPipelineController[name]
	if !ok {
		return fmt.Errorf("pachyderm.pps.pipelineserver: no pipeline registered for name: %s", name)
	}
	pipelineController.Cancel()
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package wordlist

import (
	"bufio"
	"fmt"
	"io"
	"strings"
)

const (
	maskLetters = "abcdefghijklmnopqrstuvwxyz"
	maskDigits  = "0123456789"
	maskSpecial = "-"
)

var (
	// KnownValidTLDs is a list of valid top-level domains that is maintained by the IANA.
	KnownValidTLDs []string
)

func getWordList(reader io.Reader) []string {
	var words []string

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		// Get the next word in the list
		w := strings.TrimSpace(scanner.Text())
		if err := scanner.Err(); err == nil && w != "" && !strings.Contains(w, "-") {
			words = append(words, w)
		}
	}
	return words
}

// ExpandMask will return a slice of words that a "hashcat-style" mask matches.
func ExpandMask(word string) ([]string, error) {
	var expanded []string
	var chars string

	if strings.Count(word, "?") > 3 {
		return expanded, fmt.Errorf("Exceeded maximum mask size (3): %s", word)
	}

	parts := strings.SplitN(word, "?", 2)
	if len(parts) > 1 {
		if len(parts[1]) > 0 {
			switch parts[1][0] {
			case 'a':
				chars = maskLetters + maskDigits + maskSpecial
			case 'd':
				chars = maskDigits
			case 'u':
				fallthrough
			case 'l':
				chars = maskLetters
			case 's':
				chars = maskSpecial
			default:
				return expanded, fmt.Errorf("Improper mask used: %s", word)
			}
			for _, ch := range chars {
				newWord := parts[0] + string(ch) + parts[1][1:]
				nextRound, err := ExpandMask(newWord)
				if err != nil {
					return expanded, err
				}
				expanded = append(expanded, nextRound...)
			}
		}
	} else {
		expanded = append(expanded, word)
	}
	return expanded, nil
}

// ExpandMaskWordlist performs ExpandMask on a slice of words.
func ExpandMaskWordlist(wordlist []string) ([]string, error) {
	var newWordlist []string
	var newWords []string
	var err error

	for _, word := range wordlist {
		newWords, err = ExpandMask(word)
		if err != nil {
			break
		}

		newWordlist = append(newWordlist, newWords...)
	}

	return newWordlist, err
}
<commit_msg>removed unused variable<commit_after>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package wordlist

import (
	"bufio"
	"fmt"
	"io"
	"strings"
)

const (
	maskLetters = "abcdefghijklmnopqrstuvwxyz"
	maskDigits  = "0123456789"
	maskSpecial = "-"
)

func getWordList(reader io.Reader) []string {
	var words []string

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		// Get the next word in the list
		w := strings.TrimSpace(scanner.Text())
		if err := scanner.Err(); err == nil && w != "" && !strings.Contains(w, "-") {
			words = append(words, w)
		}
	}
	return words
}

// ExpandMask will return a slice of words that a "hashcat-style" mask matches.
func ExpandMask(word string) ([]string, error) {
	var expanded []string
	var chars string

	if strings.Count(word, "?") > 3 {
		return expanded, fmt.Errorf("Exceeded maximum mask size (3): %s", word)
	}

	parts := strings.SplitN(word, "?", 2)
	if len(parts) > 1 {
		if len(parts[1]) > 0 {
			switch parts[1][0] {
			case 'a':
				chars = maskLetters + maskDigits + maskSpecial
			case 'd':
				chars = maskDigits
			case 'u':
				fallthrough
			case 'l':
				chars = maskLetters
			case 's':
				chars = maskSpecial
			default:
				return expanded, fmt.Errorf("Improper mask used: %s", word)
			}
			for _, ch := range chars {
				newWord := parts[0] + string(ch) + parts[1][1:]
				nextRound, err := ExpandMask(newWord)
				if err != nil {
					return expanded, err
				}
				expanded = append(expanded, nextRound...)
			}
		}
	} else {
		expanded = append(expanded, word)
	}
	return expanded, nil
}

// ExpandMaskWordlist performs ExpandMask on a slice of words.
func ExpandMaskWordlist(wordlist []string) ([]string, error) {
	var newWordlist []string
	var newWords []string
	var err error

	for _, word := range wordlist {
		newWords, err = ExpandMask(word)
		if err != nil {
			break
		}

		newWordlist = append(newWordlist, newWords...)
	}

	return newWordlist, err
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ecdsa implements the Elliptic Curve Digital Signature Algorithm, as
// defined in FIPS 186-3.
package ecdsa

// References:
//   [NSA]: Suite B implementer's guide to FIPS 186-3,
//     http://www.nsa.gov/ia/_files/ecdsa.pdf
//   [SECG]: SECG, SEC1
//     http://www.secg.org/download/aid-780/sec1-v2.pdf

import (
	"crypto/elliptic"
	"io"
	"math/big"
)

// PublicKey represents an ECDSA public key.
type PublicKey struct {
	elliptic.Curve
	X, Y *big.Int
}

// PrivateKey represents a ECDSA private key.
type PrivateKey struct {
	PublicKey
	D *big.Int
}

var one = new(big.Int).SetInt64(1)

// randFieldElement returns a random element of the field underlying the given
// curve using the procedure given in [NSA] A.2.1.
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
	params := c.Params()
	b := make([]byte, params.BitSize/8+8)
	_, err = io.ReadFull(rand, b)
	if err != nil {
		return
	}

	k = new(big.Int).SetBytes(b)
	n := new(big.Int).Sub(params.N, one)
	k.Mod(k, n)
	k.Add(k, one)
	return
}

// GenerateKey generates a public&private key pair.
func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error) {
	k, err := randFieldElement(c, rand)
	if err != nil {
		return
	}

	priv = new(PrivateKey)
	priv.PublicKey.Curve = c
	priv.D = k
	priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
	return
}

// hashToInt converts a hash value to an integer. There is some disagreement
// about how this is done. [NSA] suggests that this is done in the obvious
// manner, but [SECG] truncates the hash to the bit-length of the curve order
// first. We follow [SECG] because that's what OpenSSL does.
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
	orderBits := c.Params().N.BitLen()
	orderBytes := (orderBits + 7) / 8
	if len(hash) > orderBytes {
		hash = hash[:orderBytes]
	}

	ret := new(big.Int).SetBytes(hash)
	excess := orderBytes*8 - orderBits
	if excess > 0 {
		ret.Rsh(ret, uint(excess))
	}
	return ret
}

// Sign signs an arbitrary length hash (which should be the result of hashing a
// larger message) using the private key, priv. It returns the signature as a
// pair of integers. The security of the private key depends on the entropy of
// rand.
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
	// See [NSA] 3.4.1
	c := priv.PublicKey.Curve
	N := c.Params().N

	var k, kInv *big.Int
	for {
		for {
			k, err = randFieldElement(c, rand)
			if err != nil {
				r = nil
				return
			}

			kInv = new(big.Int).ModInverse(k, N)
			r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
			r.Mod(r, N)
			if r.Sign() != 0 {
				break
			}
		}

		e := hashToInt(hash, c)
		s = new(big.Int).Mul(priv.D, r)
		s.Add(s, e)
		s.Mul(s, kInv)
		s.Mod(s, N)
		if s.Sign() != 0 {
			break
		}
	}

	return
}

// Verify verifies the signature in r, s of hash using the public key, pub. It
// returns true iff the signature is valid.
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
	// See [NSA] 3.4.2
	c := pub.Curve
	N := c.Params().N

	if r.Sign() == 0 || s.Sign() == 0 {
		return false
	}
	if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 {
		return false
	}
	e := hashToInt(hash, c)
	w := new(big.Int).ModInverse(s, N)

	u1 := e.Mul(e, w)
	u2 := w.Mul(r, w)

	x1, y1 := c.ScalarBaseMult(u1.Bytes())
	x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
	if x1.Cmp(x2) == 0 {
		return false
	}
	x, _ := c.Add(x1, y1, x2, y2)
	x.Mod(x, N)
	return x.Cmp(r) == 0
}
<commit_msg>crypto/ecdsa: fix case where p != 0 mod 8 and the hash length < p.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ecdsa implements the Elliptic Curve Digital Signature Algorithm, as
// defined in FIPS 186-3.
package ecdsa

// References:
//   [NSA]: Suite B implementer's guide to FIPS 186-3,
//     http://www.nsa.gov/ia/_files/ecdsa.pdf
//   [SECG]: SECG, SEC1
//     http://www.secg.org/download/aid-780/sec1-v2.pdf

import (
	"crypto/elliptic"
	"io"
	"math/big"
)

// PublicKey represents an ECDSA public key.
type PublicKey struct {
	elliptic.Curve
	X, Y *big.Int
}

// PrivateKey represents a ECDSA private key.
type PrivateKey struct {
	PublicKey
	D *big.Int
}

var one = new(big.Int).SetInt64(1)

// randFieldElement returns a random element of the field underlying the given
// curve using the procedure given in [NSA] A.2.1.
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
	params := c.Params()
	b := make([]byte, params.BitSize/8+8)
	_, err = io.ReadFull(rand, b)
	if err != nil {
		return
	}

	k = new(big.Int).SetBytes(b)
	n := new(big.Int).Sub(params.N, one)
	k.Mod(k, n)
	k.Add(k, one)
	return
}

// GenerateKey generates a public&private key pair.
func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error) {
	k, err := randFieldElement(c, rand)
	if err != nil {
		return
	}

	priv = new(PrivateKey)
	priv.PublicKey.Curve = c
	priv.D = k
	priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
	return
}

// hashToInt converts a hash value to an integer. There is some disagreement
// about how this is done. [NSA] suggests that this is done in the obvious
// manner, but [SECG] truncates the hash to the bit-length of the curve order
// first. We follow [SECG] because that's what OpenSSL does. Additionally,
// OpenSSL right shifts excess bits from the number if the hash is too large
// and we mirror that too.
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
	orderBits := c.Params().N.BitLen()
	orderBytes := (orderBits + 7) / 8
	if len(hash) > orderBytes {
		hash = hash[:orderBytes]
	}

	ret := new(big.Int).SetBytes(hash)
	excess := len(hash)*8 - orderBits
	if excess > 0 {
		ret.Rsh(ret, uint(excess))
	}
	return ret
}

// Sign signs an arbitrary length hash (which should be the result of hashing a
// larger message) using the private key, priv. It returns the signature as a
// pair of integers. The security of the private key depends on the entropy of
// rand.
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
	// See [NSA] 3.4.1
	c := priv.PublicKey.Curve
	N := c.Params().N

	var k, kInv *big.Int
	for {
		for {
			k, err = randFieldElement(c, rand)
			if err != nil {
				r = nil
				return
			}

			kInv = new(big.Int).ModInverse(k, N)
			r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
			r.Mod(r, N)
			if r.Sign() != 0 {
				break
			}
		}

		e := hashToInt(hash, c)
		s = new(big.Int).Mul(priv.D, r)
		s.Add(s, e)
		s.Mul(s, kInv)
		s.Mod(s, N)
		if s.Sign() != 0 {
			break
		}
	}

	return
}

// Verify verifies the signature in r, s of hash using the public key, pub. It
// returns true iff the signature is valid.
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
	// See [NSA] 3.4.2
	c := pub.Curve
	N := c.Params().N

	if r.Sign() == 0 || s.Sign() == 0 {
		return false
	}
	if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 {
		return false
	}
	e := hashToInt(hash, c)
	w := new(big.Int).ModInverse(s, N)

	u1 := e.Mul(e, w)
	u2 := w.Mul(r, w)

	x1, y1 := c.ScalarBaseMult(u1.Bytes())
	x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
	if x1.Cmp(x2) == 0 {
		return false
	}
	x, _ := c.Add(x1, y1, x2, y2)
	x.Mod(x, N)
	return x.Cmp(r) == 0
}
<|endoftext|>


<commit_before>package slack

import (
	"bytes"
	"log"
	"strconv"

	"github.com/dustin/go-humanize"
	"github.com/eveopsec/zk2s/zk2s/config"
	"github.com/eveopsec/zk2s/zk2s/filter"
	"github.com/eveopsec/zk2s/zk2s/tmpl"
	slacklib "github.com/nlopes/slack"
	"github.com/vivace-io/evelib/crest"
	"github.com/vivace-io/evelib/zkill"
)

// data is passed to templates for defining how a slacklib post appears.
type data struct {
	Killmail       crest.Killmail
	TotalValue     string
	IsLoss         bool
	IsSolo         bool
	InAlli         bool
	LosingCorp     string
	LosingAlli     string
	CorpsInvolved  []string
	AlliInvolved   []string
	PilotInvolved  []string
	FinalBlowPilot []string
	FinalBlowCorp  []string
	FinalBlowAlli  []string
	TotalCorp      []string
	TotalAlli      []string
}

// format loads the formatting template and applies formatting
// rules from the Configuration object.
func format(kill zkill.Kill, channel config.Channel) (messageParams slacklib.PostMessageParameters) {
	title := new(bytes.Buffer)
	body := new(bytes.Buffer)
	var err error

	// define post data for templates
	d := new(data)
	d.Killmail = *kill.Killmail
	d.TotalValue = humanize.Comma(int64(kill.Zkb.TotalValue))
	d.IsLoss = filter.IsLoss(kill, channel)
	//Solo kill testing
	if len(kill.Killmail.Attackers) == 1 {
		d.IsSolo = true
	} else {
		d.IsLoss = false
	}
	//Testing to see if the victim is in an alliance

	if kill.Killmail.Victim.Alliance.Name != "" {
		d.InAlli = true
		d.LosingAlli = kill.Killmail.Victim.Alliance.Name
	}
	d.LosingCorp = kill.Killmail.Victim.Corporation.Name

	// Compile list of pilots involved, if not final blow
	for a := range kill.Killmail.Attackers {
		okToAdd := true
		if kill.Killmail.Attackers[a].FinalBlow == true {
			okToAdd = false
		}
		if kill.Killmail.Attackers[a].Character.Name == "" {
			okToAdd = false
		}
		if okToAdd {
			d.PilotInvolved = append(d.PilotInvolved, kill.Killmail.Attackers[a].Character.Name)
		}
	}

	//Compile the list for the final blow pilot, mainly use for formatting commas on the post
	for a := range kill.Killmail.Attackers {
		if kill.Killmail.Attackers[a].FinalBlow == true {
			okToAdd := true
			if kill.Killmail.Attackers[a].Character.Name == "" {
				okToAdd = false
			}
			if okToAdd {
				d.FinalBlowPilot = append(d.FinalBlowPilot, kill.Killmail.Attackers[a].Character.Name)
				d.FinalBlowCorp = append(d.FinalBlowCorp, kill.Killmail.Attackers[a].Corporation.Name)
				d.FinalBlowAlli = append(d.FinalBlowAlli, kill.Killmail.Attackers[a].Alliance.Name)
				d.TotalCorp = append(d.TotalCorp, kill.Killmail.Attackers[a].Corporation.Name)
				d.TotalAlli = append(d.TotalAlli, kill.Killmail.Attackers[a].Alliance.Name)
			}

		}
	}
	// Compile list of corporations involved from attackers, ignoring duplicates
	for a := range kill.Killmail.Attackers {
		okToAdd := true
		for c := range d.CorpsInvolved {
			if kill.Killmail.Attackers[a].Corporation.Name == d.CorpsInvolved[c] {
				okToAdd = false
				break
			}
			if kill.Killmail.Attackers[a].Corporation.Name == d.FinalBlowCorp[c] {
				okToAdd = false
				break
			}
			if okToAdd {
				d.CorpsInvolved = append(d.CorpsInvolved, kill.Killmail.Attackers[a].Corporation.Name)
				d.TotalCorp = append(d.TotalCorp, kill.Killmail.Attackers[a].Corporation.Name)
			}
		}
	}
	// Compile list of alliances involved from attackers, ignoring duplicates
	for a := range kill.Killmail.Attackers {

		okToAdd := true

		for c := range d.AlliInvolved {

			// Do not add blank alliances (corp is not in an alliance)
			if kill.Killmail.Attackers[a].Alliance.Name == "" {
				okToAdd = false
				break
			}
			if kill.Killmail.Attackers[a].Alliance.Name == d.AlliInvolved[c] {
				okToAdd = false
				d.InAlli = true
				break
			}
			if kill.Killmail.Attackers[a].Alliance.Name == d.FinalBlowAlli[c] {
				okToAdd = false
				d.InAlli = true
				break
			}
			if okToAdd {
				d.AlliInvolved = append(d.AlliInvolved, kill.Killmail.Attackers[a].Alliance.Name)
				d.TotalAlli = append(d.TotalAlli, kill.Killmail.Attackers[a].Alliance.Name)
				d.InAlli = true
			}
		}
	}

	// Execute templates
	err = tmpl.T.ExecuteTemplate(title, "kill-title", d)
	if err != nil {
		log.Println(err)
	}
	err = tmpl.T.ExecuteTemplate(body, "kill-body", d)
	if err != nil {
		log.Println(err)
	}

	attch := slacklib.Attachment{}
	attch.MarkdownIn = []string{"pretext", "text"}
	attch.Title = title.String()
	attch.TitleLink = "https://zkillboard.com/kill/" + strconv.Itoa(kill.KillID) + "/"
	attch.ThumbURL = "http://image.eveonline.com/render/" + strconv.Itoa(kill.Killmail.Victim.ShipType.ID) + "_64.png"
	attch.Text = body.String()
	//Color Coding
	if filter.IsLoss(kill, channel) {
		attch.Color = "danger"
	} else {
		attch.Color = "good"
	}
	messageParams.Attachments = []slacklib.Attachment{attch}
	return
}
<commit_msg>post as bot user now works<commit_after>package slack

import (
	"bytes"
	"log"
	"strconv"

	"github.com/dustin/go-humanize"
	"github.com/eveopsec/zk2s/zk2s/config"
	"github.com/eveopsec/zk2s/zk2s/filter"
	"github.com/eveopsec/zk2s/zk2s/tmpl"
	slacklib "github.com/nlopes/slack"
	"github.com/vivace-io/evelib/crest"
	"github.com/vivace-io/evelib/zkill"
)

// data is passed to templates for defining how a slacklib post appears.
type data struct {
	Killmail       crest.Killmail
	TotalValue     string
	IsLoss         bool
	IsSolo         bool
	InAlli         bool
	LosingCorp     string
	LosingAlli     string
	CorpsInvolved  []string
	AlliInvolved   []string
	PilotInvolved  []string
	FinalBlowPilot []string
	FinalBlowCorp  []string
	FinalBlowAlli  []string
	TotalCorp      []string
	TotalAlli      []string
}

// format loads the formatting template and applies formatting
// rules from the Configuration object.
func format(kill zkill.Kill, channel config.Channel) (messageParams slacklib.PostMessageParameters) {
	title := new(bytes.Buffer)
	body := new(bytes.Buffer)
	var err error

	// define post data for templates
	d := new(data)
	d.Killmail = *kill.Killmail
	d.TotalValue = humanize.Comma(int64(kill.Zkb.TotalValue))
	d.IsLoss = filter.IsLoss(kill, channel)
	//Solo kill testing
	if len(kill.Killmail.Attackers) == 1 {
		d.IsSolo = true
	} else {
		d.IsLoss = false
	}
	//Testing to see if the victim is in an alliance

	if kill.Killmail.Victim.Alliance.Name != "" {
		d.InAlli = true
		d.LosingAlli = kill.Killmail.Victim.Alliance.Name
	}
	d.LosingCorp = kill.Killmail.Victim.Corporation.Name

	// Compile list of pilots involved, if not final blow
	for a := range kill.Killmail.Attackers {
		okToAdd := true
		if kill.Killmail.Attackers[a].FinalBlow == true {
			okToAdd = false
		}
		if kill.Killmail.Attackers[a].Character.Name == "" {
			okToAdd = false
		}
		if okToAdd {
			d.PilotInvolved = append(d.PilotInvolved, kill.Killmail.Attackers[a].Character.Name)
		}
	}

	//Compile the list for the final blow pilot, mainly use for formatting commas on the post
	for a := range kill.Killmail.Attackers {
		if kill.Killmail.Attackers[a].FinalBlow == true {
			okToAdd := true
			if kill.Killmail.Attackers[a].Character.Name == "" {
				okToAdd = false
			}
			if okToAdd {
				d.FinalBlowPilot = append(d.FinalBlowPilot, kill.Killmail.Attackers[a].Character.Name)
				d.FinalBlowCorp = append(d.FinalBlowCorp, kill.Killmail.Attackers[a].Corporation.Name)
				d.FinalBlowAlli = append(d.FinalBlowAlli, kill.Killmail.Attackers[a].Alliance.Name)
				d.TotalCorp = append(d.TotalCorp, kill.Killmail.Attackers[a].Corporation.Name)
				d.TotalAlli = append(d.TotalAlli, kill.Killmail.Attackers[a].Alliance.Name)
			}

		}
	}
	// Compile list of corporations involved from attackers, ignoring duplicates
	for a := range kill.Killmail.Attackers {
		okToAdd := true
		for c := range d.CorpsInvolved {
			if kill.Killmail.Attackers[a].Corporation.Name == d.CorpsInvolved[c] {
				okToAdd = false
				break
			}
			if kill.Killmail.Attackers[a].Corporation.Name == d.FinalBlowCorp[c] {
				okToAdd = false
				break
			}
			if okToAdd {
				d.CorpsInvolved = append(d.CorpsInvolved, kill.Killmail.Attackers[a].Corporation.Name)
				d.TotalCorp = append(d.TotalCorp, kill.Killmail.Attackers[a].Corporation.Name)
			}
		}
	}
	// Compile list of alliances involved from attackers, ignoring duplicates
	for a := range kill.Killmail.Attackers {

		okToAdd := true

		for c := range d.AlliInvolved {

			// Do not add blank alliances (corp is not in an alliance)
			if kill.Killmail.Attackers[a].Alliance.Name == "" {
				okToAdd = false
				break
			}
			if kill.Killmail.Attackers[a].Alliance.Name == d.AlliInvolved[c] {
				okToAdd = false
				d.InAlli = true
				break
			}
			if kill.Killmail.Attackers[a].Alliance.Name == d.FinalBlowAlli[c] {
				okToAdd = false
				d.InAlli = true
				break
			}
			if okToAdd {
				d.AlliInvolved = append(d.AlliInvolved, kill.Killmail.Attackers[a].Alliance.Name)
				d.TotalAlli = append(d.TotalAlli, kill.Killmail.Attackers[a].Alliance.Name)
				d.InAlli = true
			}
		}
	}

	// Execute templates
	err = tmpl.T.ExecuteTemplate(title, "kill-title", d)
	if err != nil {
		log.Println(err)
	}
	err = tmpl.T.ExecuteTemplate(body, "kill-body", d)
	if err != nil {
		log.Println(err)
	}

	attch := slacklib.Attachment{}
	attch.MarkdownIn = []string{"pretext", "text"}
	attch.Title = title.String()
	attch.TitleLink = "https://zkillboard.com/kill/" + strconv.Itoa(kill.KillID) + "/"
	attch.ThumbURL = "http://image.eveonline.com/render/" + strconv.Itoa(kill.Killmail.Victim.ShipType.ID) + "_64.png"
	attch.Text = body.String()
	//Color Coding
	if filter.IsLoss(kill, channel) {
		attch.Color = "danger"
	} else {
		attch.Color = "good"
	}
	messageParams.Attachments = []slacklib.Attachment{attch}
	messageParams.AsUser = true
	return
}
<|endoftext|>


<commit_before>package tls

import (
	"context"

	"v2ray.com/core/common"
	"v2ray.com/core/common/dice"
)

// DTLS writes header as DTLS. See https://tools.ietf.org/html/rfc6347
type DTLS struct {
	epoch    uint16
	sequence uint32
}

// Size implements PacketHeader.
func (*DTLS) Size() int32 {
	return 1 + 2 + 2 + 3 + 2
}

// Write implements PacketHeader.
func (d *DTLS) Write(b []byte) (int, error) {
	b[0] = 23 // application data
	b[1] = 254
	b[2] = 253
	b[3] = byte(d.epoch >> 8)
	b[4] = byte(d.epoch)
	b[5] = byte(d.sequence >> 16)
	b[6] = byte(d.sequence >> 8)
	b[7] = byte(d.sequence)
	d.sequence++
	l := dice.RollUint16()
	b[8] = byte(l >> 8)
	b[9] = byte(l)
	return 10, nil
}

// New creates a new UTP header for the given config.
func New(ctx context.Context, config interface{}) (interface{}, error) {
	return &DTLS{
		epoch:    dice.RollUint16(),
		sequence: 0,
	}, nil
}

func init() {
	common.Must(common.RegisterConfig((*PacketConfig)(nil), New))
}
<commit_msg>update dtls.length logic<commit_after>package tls

import (
	"context"

	"v2ray.com/core/common"
	"v2ray.com/core/common/dice"
)

// DTLS writes header as DTLS. See https://tools.ietf.org/html/rfc6347
type DTLS struct {
	epoch    uint16
	sequence uint32
	length   uint16
}

// Size implements PacketHeader.
func (*DTLS) Size() int32 {
	return 1 + 2 + 2 + 3 + 2
}

// Write implements PacketHeader.
func (d *DTLS) Write(b []byte) (int, error) {
	b[0] = 23 // application data
	b[1] = 254
	b[2] = 253
	b[3] = byte(d.epoch >> 8)
	b[4] = byte(d.epoch)
	b[5] = byte(d.sequence >> 16)
	b[6] = byte(d.sequence >> 8)
	b[7] = byte(d.sequence)
	d.sequence++
	b[8] = byte(d.length >> 8)
	b[9] = byte(d.length)
	d.length += 17
	if d.length > 1024 {
		d.length -= 1024
	}
	return 10, nil
}

// New creates a new UTP header for the given config.
func New(ctx context.Context, config interface{}) (interface{}, error) {
	return &DTLS{
		epoch:    dice.RollUint16(),
		sequence: 0,
		length:   uint16(dice.Roll(1024) + 100),
	}, nil
}

func init() {
	common.Must(common.RegisterConfig((*PacketConfig)(nil), New))
}
<|endoftext|>


<commit_before>package resource

import (
	"fmt"
	"io"
	"os"
	"os/user"
	"path/filepath"

	"github.com/dnaeon/gru/utils"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
	"github.com/imdario/mergo"
)

// Name and description of the resource
const fileResourceType = "file"
const fileResourceDesc = "manages files"

// File types
const fileTypeRegular = "file"
const fileTypeDirectory = "directory"

// BaseFileResource is the base resource for managing files
type BaseFileResource struct {
	// Path to the file
	Path string `hcl:"path"`

	// Permission bits to set on the file
	Mode int `hcl:"mode"`

	// Owner of the file
	Owner string `hcl:"owner"`

	// Group of the file
	Group string `hcl:"group"`

	// Source file to use when creating/updating the file
	Source string `hcl:"source"`

	// The file type we manage
	FileType string `hcl:"type"`
}

// FileResource is a resource which manages files
type FileResource struct {
	BaseResource     `hcl:",squash"`
	BaseFileResource `hcl:",squash"`
}

// NewFileResource creates a new resource for managing files
func NewFileResource(title string, obj *ast.ObjectItem) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	defaultOwner := currentUser.Username
	defaultGroup := currentGroup.Name

	// Resource defaults
	defaults := FileResource{
		BaseResource: BaseResource{
			Title: title,
			Type:  fileResourceType,
			State: StatePresent,
		},
		BaseFileResource: BaseFileResource{
			Path:     title,
			Mode:     0644,
			Owner:    defaultOwner,
			Group:    defaultGroup,
			FileType: fileTypeRegular,
		},
	}

	var fr FileResource
	err = hcl.DecodeObject(&fr, obj)
	if err != nil {
		return nil, err
	}

	// Merge the decoded object with the resource defaults
	err = mergo.Merge(&fr, defaults)
	if err != nil {
		return nil, err
	}

	if fr.FileType != fileTypeRegular || fr.FileType != fileTypeDirectory {
		return nil, fmt.Errorf("Unknown file type '%s'", fr.FileType)
	}

	return &fr, nil
}

// Evaluate evaluates the file resource
func (fr *FileResource) Evaluate(w io.Writer, opts *Options) (State, error) {
	rs := State{
		Current: StateUnknown,
		Want:    fr.State,
		Update:  false,
	}

	// The file we manage
	dst := utils.NewFileUtil(fr.Path)

	// File does not exist
	if !dst.Exists() {
		rs.Current = StateAbsent
		return rs, nil
	} else {
		rs.Current = StatePresent
	}

	fi, err := os.Stat(fr.Path)
	if err != nil {
		return rs, err
	}

	// Check the target file we manage
	switch fr.FileType {
	case fileTypeRegular:
		if !fi.Mode().IsRegular() {
			return rs, fmt.Errorf("%s exists, but is not a regular file", fr.Path)
		}
	case fileTypeDirectory:
		if !fi.IsDir() {
			return rs, fmt.Errorf("%s exists, but is not a directory", fr.Path)
		}
	}

	// Check file content
	if fr.Source != "" {
		srcPath := filepath.Join(opts.SiteDir, "data", fr.Source)
		same, err := dst.SameContentWith(srcPath)
		if err != nil {
			return rs, err
		}
		if !same {
			fr.Printf(w, "content is out of date\n")
			rs.Update = true
		}
	}

	// Check file permissions
	mode, err := dst.Mode()
	if err != nil {
		return rs, err
	}

	if mode.Perm() != os.FileMode(fr.Mode) {
		fr.Printf(w, "permissions are out of date\n")
		rs.Update = true
	}

	// Check ownership
	owner, err := dst.Owner()
	if err != nil {
		return rs, err
	}

	if fr.Owner != owner.User.Username || fr.Group != owner.Group.Name {
		fr.Printf(w, "owner is out of date\n")
		rs.Update = true
	}

	return rs, nil
}

// createRegularFile creates the file and content managed by the resource
func (fr *FileResource) createRegularFile(opts *Options) error {
	dst := utils.NewFileUtil(fr.Path)

	switch {
	case fr.Source == "" && dst.Exists():
		// We have no source, do nothing
		break
	case fr.Source == "" && !dst.Exists():
		// Create an empty file
		if _, err := os.Create(fr.Path); err != nil {
			return err
		}
	case fr.Source != "" && dst.Exists():
		// File exists and we have a source file
		srcPath := filepath.Join(opts.SiteDir, "data", fr.Source)
		if err := dst.CopyFrom(srcPath); err != nil {
			return err
		}
	}

	return nil
}

// createDirectory creates the directory and content managed by the resource
func (fr *FileResource) createDirectory(opts *Options) error {
	if err := os.Mkdir(fr.Path, os.FileMode(fr.Mode)); err != nil {
		return err
	}

	return nil
}

// Create creates the file managed by the resource
func (fr *FileResource) Create(w io.Writer, opts *Options) error {
	dst := utils.NewFileUtil(fr.Path)
	fr.Printf(w, "creating resource\n")

	// Set content
	switch fr.FileType {
	case fileTypeRegular:
		if err := fr.createRegularFile(opts); err != nil {
			return err
		}
	case fileTypeDirectory:
		if err := fr.createDirectory(opts); err != nil {
			return err
		}
	}

	// Set file owner
	if err := dst.SetOwner(fr.Owner, fr.Group); err != nil {
		return err
	}

	// Set file permissions
	return dst.Chmod(os.FileMode(fr.Mode))
}

// Delete deletes the file
func (fr *FileResource) Delete(w io.Writer, opts *Options) error {
	fr.Printf(w, "removing file\n")
	dst := utils.NewFileUtil(fr.Path)

	return dst.Remove()
}

// Update updates the file managed by the resource
func (fr *FileResource) Update(w io.Writer, opts *Options) error {
	dst := utils.NewFileUtil(fr.Path)

	// Update file content if needed
	if fr.Source != "" {
		srcPath := filepath.Join(opts.SiteDir, "data", fr.Source)
		same, err := dst.SameContentWith(srcPath)
		if err != nil {
			return err
		}

		if !same {
			srcFile := utils.NewFileUtil(srcPath)
			srcMd5, err := srcFile.Md5()
			if err != nil {
				return err
			}

			fr.Printf(w, "updating content to md5:%s\n", srcMd5)
			if err := dst.CopyFrom(srcPath); err != nil {
				return err
			}
		}
	}

	// Fix permissions if needed
	mode, err := dst.Mode()
	if err != nil {
		return err
	}

	if mode.Perm() != os.FileMode(fr.Mode) {
		fr.Printf(w, "setting permissions to %#o\n", fr.Mode)
		dst.Chmod(os.FileMode(fr.Mode))
	}

	// Fix ownership if needed
	owner, err := dst.Owner()
	if err != nil {
		return err
	}

	if fr.Owner != owner.User.Username || fr.Group != owner.Group.Name {
		fr.Printf(w, "setting owner to %s:%s\n", fr.Owner, fr.Group)
		if err := dst.SetOwner(fr.Owner, fr.Group); err != nil {
			return err
		}
	}

	return nil
}

func init() {
	item := RegistryItem{
		Name:        fileResourceType,
		Description: fileResourceDesc,
		Provider:    NewFileResource,
	}

	Register(item)
}
<commit_msg>resource: FileResource manages regular files and directories<commit_after>package resource

import (
	"fmt"
	"io"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/dnaeon/gru/utils"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
	"github.com/imdario/mergo"
)

// Name and description of the resource
const fileResourceType = "file"
const fileResourceDesc = "manages files and directories"

// The file types we manage
const fileTypeRegular = "regular"
const fileTypeDirectory = "directory"

// BaseFileResource is the base resource for managing files
type BaseFileResource struct {
	// Path to the file
	Path string `hcl:"path"`

	// Permission bits to set on the file
	Mode int `hcl:"mode"`

	// Owner of the file
	Owner string `hcl:"owner"`

	// Group of the file
	Group string `hcl:"group"`

	// Source file to use when creating/updating the file
	Source string `hcl:"source"`

	// The file type we manage
	FileType string `hcl:"filetype"`

	// Recursively manage the directory
	Recursive bool `hcl:"recursive"`
}

// outdatedFile type is used to describe a file which
// has been identified as being out of date
type outdatedFile struct {
	// Source file to use when reconstructing the content
	src string

	// The destination file which is identified as being out of date
	dst string

	// Flag to indicate that the content is out of date
	isContentOutdated bool

	// Flag to indicate that the permissions are out of date
	isPermissionsOutdated bool

	// Flag to indicate that the owner is out of date
	isOwnerOutdated bool

	// Flag to indicate that file exists in destination, but not in source
	isExtra bool
}

// FileResource is a resource which manages files and directories
type FileResource struct {
	BaseResource     `hcl:",squash"`
	BaseFileResource `hcl:",squash"`

	// Files identified as being out of date
	outdated []*outdatedFile
}

// NewFileResource creates a new resource for managing files
func NewFileResource(title string, obj *ast.ObjectItem) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	// Resource defaults
	defaults := FileResource{
		BaseResource: BaseResource{
			Title: title,
			Type:  fileResourceType,
			State: StatePresent,
		},
		BaseFileResource: BaseFileResource{
			Path:      title,
			Mode:      0644,
			Owner:     currentUser.Username,
			Group:     currentGroup.Name,
			FileType:  fileTypeRegular,
			Recursive: false,
		},
	}

	var fr FileResource
	err = hcl.DecodeObject(&fr, obj)
	if err != nil {
		return nil, err
	}

	// Merge the decoded object with the resource defaults
	err = mergo.Merge(&fr, defaults)
	if err != nil {
		return nil, err
	}

	// Check that the given file type is a valid one
	if fr.FileType != fileTypeRegular && fr.FileType != fileTypeDirectory {
		return nil, fmt.Errorf("Unknown file type '%s'", fr.FileType)
	}

	return &fr, nil
}

// Evaluate evaluates the file resource
func (fr *FileResource) Evaluate(w io.Writer, opts *Options) (State, error) {
	rs := State{
		Current: StateUnknown,
		Want:    fr.State,
		Update:  false,
	}

	// Check for file presence first
	fi, err := os.Stat(fr.Path)
	if os.IsNotExist(err) {
		rs.Current = StateAbsent
		return rs, nil
	} else {
		rs.Current = StatePresent
	}

	// Check the file(s) content, permissions and ownership
	switch fr.FileType {
	case fileTypeRegular:
		if !fi.Mode().IsRegular() {
			return rs, fmt.Errorf("%s exists, but is not a regular file", fr.Path)
		}

		outdated, err := fr.isRegularFileContentOutdated(opts)
		if err != nil {
			return rs, err
		}

		if outdated {
			rs.Update = true
		}
	case fileTypeDirectory:
		if !fi.IsDir() {
			return rs, fmt.Errorf("%s exists, but is not a directory", fr.Path)
		}

		outdated, err := fr.isDirectoryContentOutdated(opts)
		if err != nil {
			return rs, err
		}

		if outdated {
			rs.Update = true
		}
	}

	outdated, err := fr.isPermissionsOutdated()
	if err != nil {
		return rs, err
	}

	if outdated {
		rs.Update = true
	}

	outdated, err = fr.isOwnerOutdated()
	if err != nil {
		return rs, err
	}

	if outdated {
		rs.Update = true
	}

	// Report on what has been identified as being out of date
	for _, item := range fr.outdated {
		// TODO: Report extra files

		if item.isContentOutdated && !item.isExtra {
			fr.Printf(w, "content of %s is out of date\n", item.dst)
		}
		if item.isPermissionsOutdated {
			fr.Printf(w, "permissions of %s are out of date\n", item.dst)
		}
		if item.isOwnerOutdated {
			fr.Printf(w, "owner of %s is out of date\n", item.dst)
		}
	}

	return rs, nil
}

// Create creates the file managed by the resource
func (fr *FileResource) Create(w io.Writer, opts *Options) error {
	fr.Printf(w, "creating resource\n")

	switch fr.FileType {
	case fileTypeRegular:
		if err := fr.createRegularFile(opts); err != nil {
			return err
		}

		dst := utils.NewFileUtil(fr.Path)
		if err := dst.Chmod(os.FileMode(fr.Mode)); err != nil {
			return err
		}
		if err := dst.SetOwner(fr.Owner, fr.Group); err != nil {
			return err
		}
	case fileTypeDirectory:
		if err := fr.createDirectory(opts); err != nil {
			return err
		}
		dstRegistry, err := directoryFileRegistry(fr.Path, []string{})
		if err != nil {
			return err
		}

		for _, path := range dstRegistry {
			dst := utils.NewFileUtil(path)
			if err := dst.Chmod(os.FileMode(fr.Mode)); err != nil {
				return err
			}
			if err := dst.SetOwner(fr.Owner, fr.Group); err != nil {
				return err
			}
		}
	}

	return nil
}

// Delete deletes the file managed by the resource
func (fr *FileResource) Delete(w io.Writer, opts *Options) error {
	fr.Printf(w, "removing resource\n")

	if fr.Recursive {
		return os.RemoveAll(fr.Path)
	}

	return os.Remove(fr.Path)
}

// Update updates the file managed by the resource
func (fr *FileResource) Update(w io.Writer, opts *Options) error {
	for _, item := range fr.outdated {
		dstFile := utils.NewFileUtil(item.dst)

		// TODO: Purge extra files

		// Update file content if needed
		if item.isContentOutdated {
			// Create parent directory for file if missing
			dstDir := filepath.Dir(item.dst)
			_, err := os.Stat(dstDir)
			if os.IsNotExist(err) {
				if err := os.MkdirAll(dstDir, 0755); err != nil {
					return err
				}
			}

			srcFile := utils.NewFileUtil(item.src)
			srcMd5, err := srcFile.Md5()
			if err != nil {
				return err
			}

			fr.Printf(w, "setting content of %s to md5:%s\n", item.dst, srcMd5)
			if err := dstFile.CopyFrom(item.src, true); err != nil {
				return err
			}
		}

		// Update permissions if needed
		if item.isPermissionsOutdated {
			fr.Printf(w, "setting permissions of %s to %#o\n", item.dst, fr.Mode)
			if err := dstFile.Chmod(os.FileMode(fr.Mode)); err != nil {
				return err
			}
		}

		// Update ownership if needed
		if item.isOwnerOutdated {
			fr.Printf(w, "setting owner of %s to %s:%s\n", item.dst, fr.Owner, fr.Group)
			if err := dstFile.SetOwner(fr.Owner, fr.Group); err != nil {
				return err
			}
		}
	}

	return nil
}

// directoryFileRegistry creates a map of all files found in a
// given directory. The keys of the map are the file names with the
// leading source path trimmed and the values are the
// full path to the discovered files.
func directoryFileRegistry(path string, skip []string) (map[string]string, error) {
	registry := make(map[string]string)

	found, err := utils.WalkPath(path, skip)
	if err != nil {
		return registry, err
	}

	for _, name := range found {
		fi, err := os.Stat(name)
		if err != nil {
			return registry, err
		}

		if fi.Mode().IsRegular() {
			trimmed := strings.TrimPrefix(name, path+"/")
			registry[trimmed] = name
		}
	}

	return registry, nil
}

// createRegularFile creates the file and content managed by the resource
func (fr *FileResource) createRegularFile(opts *Options) error {
	dst := utils.NewFileUtil(fr.Path)

	switch {
	case fr.Source == "" && dst.Exists():
		// We have no source, do nothing
		break
	case fr.Source == "" && !dst.Exists():
		// Create an empty file
		if _, err := os.Create(fr.Path); err != nil {
			return err
		}
	case fr.Source != "" && dst.Exists():
		// File exists and we have a source file
		srcPath := filepath.Join(opts.SiteDir, fr.Source)
		if err := dst.CopyFrom(srcPath, false); err != nil {
			return err
		}
	}

	return nil
}

// createDirectory creates the directory and content managed by the resource
func (fr *FileResource) createDirectory(opts *Options) error {
	switch {
	case !fr.Recursive:
		return os.Mkdir(fr.Path, 0755)
	case fr.Recursive && fr.Source != "":
		srcPath := filepath.Join(opts.SiteDir, fr.Source)
		return utils.CopyDir(srcPath, fr.Path)
	case fr.Recursive && fr.Source == "":
		return os.MkdirAll(fr.Path, 0755)
	}

	// Not reached
	return nil
}

// isRegularFileContentOutdated returns a boolean indicating whether the
// content managed by the resource is outdated compared to the source
// file defined by the resource.
// If the file is identified as being out of date it will be appended to the
// list of outdated files for the resource, so it can be further
// processed if needed.
func (fr *FileResource) isRegularFileContentOutdated(opts *Options) (bool, error) {
	if fr.Source != "" {
		srcPath := filepath.Join(opts.SiteDir, fr.Source)
		same, err := utils.SameContent(srcPath, fr.Path)
		if err != nil {
			return false, err
		}

		if !same {
			item := &outdatedFile{
				src:               srcPath,
				dst:               fr.Path,
				isContentOutdated: true,
			}
			fr.outdated = append(fr.outdated, item)
			return true, nil
		}
	}

	return false, nil
}

// isDirectoryContentOutdated returns a boolean indicating whether the
// content of the directory managed by the resource is outdated
// compared to the source directory defined by the resource.
// The files identified as being out of date will be appended to the
// list of outdated files for the resource, so they can be further
// processed if needed.
func (fr *FileResource) isDirectoryContentOutdated(opts *Options) (bool, error) {
	isOutdated := false
	if fr.Source != "" && fr.Recursive {
		srcPath := filepath.Join(opts.SiteDir, fr.Source)

		// Exclude the ".git" repo directory from the source path,
		// since our source files reside in a git repo
		srcRegistry, err := directoryFileRegistry(srcPath, []string{".git"})
		if err != nil {
			return false, err
		}

		dstRegistry, err := directoryFileRegistry(fr.Path, []string{})
		if err != nil {
			return false, err
		}

		// Check source and destination files' content
		for name := range srcRegistry {
			item := &outdatedFile{
				src:               srcRegistry[name],
				dst:               dstRegistry[name],
				isContentOutdated: true,
			}

			// File is missing
			if _, ok := dstRegistry[name]; !ok {
				item.dst = filepath.Join(fr.Path, name)
				fr.outdated = append(fr.outdated, item)
				isOutdated = true
				continue
			}

			// Check if content has changed
			same, err := utils.SameContent(srcRegistry[name], dstRegistry[name])
			if err != nil {
				return false, err
			}

			if !same {
				fr.outdated = append(fr.outdated, item)
				isOutdated = true
			}
		}

		// Check for extra files in the managed directory
		for name := range dstRegistry {
			item := &outdatedFile{
				dst:     dstRegistry[name],
				isExtra: true,
			}
			if _, ok := srcRegistry[name]; !ok {
				fr.outdated = append(fr.outdated, item)
				isOutdated = true
			}
		}
	}

	return isOutdated, nil
}

// isPermissionsOutdated returns a boolean indicating whether the
// file's permissions managed by the resource are outdated compared
// to the ones defined by the resource.
// Each file identified as being out of date will be appended to the
// list of outdated files for the resource, so they can be further
// processed if needed.
func (fr *FileResource) isPermissionsOutdated() (bool, error) {
	dstRegistry, err := directoryFileRegistry(fr.Path, []string{})
	if err != nil {
		return false, err
	}

	isOutdated := false
	for name := range dstRegistry {
		item := &outdatedFile{
			dst: dstRegistry[name],
			isPermissionsOutdated: true,
		}

		dst := utils.NewFileUtil(dstRegistry[name])
		mode, err := dst.Mode()
		if err != nil {
			return false, err
		}

		if mode.Perm() != os.FileMode(fr.Mode) {
			fr.outdated = append(fr.outdated, item)
			isOutdated = true
		}
	}

	return isOutdated, nil
}

// isOwnerOutdated returns a boolean indicating whether the
// file's owner managed by the resource is outdated compared to the
// ones defined by the resource.
// Each file identified as being out of date will be appended to the
// list of outdated files for the resource, so they can be further
// processed if needed.
func (fr *FileResource) isOwnerOutdated() (bool, error) {
	dstRegistry, err := directoryFileRegistry(fr.Path, []string{})
	if err != nil {
		return false, err
	}

	isOutdated := false
	for name := range dstRegistry {
		item := &outdatedFile{
			dst:             dstRegistry[name],
			isOwnerOutdated: true,
		}

		dst := utils.NewFileUtil(dstRegistry[name])
		owner, err := dst.Owner()
		if err != nil {
			return false, err
		}

		if fr.Owner != owner.User.Username || fr.Group != owner.Group.Name {
			fr.outdated = append(fr.outdated, item)
			isOutdated = true
		}
	}

	return isOutdated, nil
}

func init() {
	item := RegistryItem{
		Name:        fileResourceType,
		Description: fileResourceDesc,
		Provider:    NewFileResource,
	}

	Register(item)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/whitepages/terraform-provider-stingray/Godeps/_workspace/src/github.com/hashicorp/terraform/helper/hashcode"
	"github.com/whitepages/terraform-provider-stingray/Godeps/_workspace/src/github.com/hashicorp/terraform/helper/schema"
	"github.com/whitepages/terraform-provider-stingray/Godeps/_workspace/src/github.com/whitepages/go-stingray"
)

func resourcePool() *schema.Resource {
	return &schema.Resource{
		Create: resourcePoolCreate,
		Read:   resourcePoolRead,
		Update: resourcePoolUpdate,
		Delete: resourcePoolDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"connection_max_connect_time": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"connection_max_connections_per_node": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"connection_max_queue_size": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"connection_max_reply_time": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"connection_queue_timeout": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"load_balancing_priority_enabled": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},

			"monitors": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"nodes": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"note": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"passive_monitoring": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},

			"tcp_nagle": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourcePoolCreate(d *schema.ResourceData, meta interface{}) error {
	err := resourcePoolSet(d, meta)
	if err != nil {
		return err
	}

	return resourcePoolRead(d, meta)
}

func resourcePoolRead(d *schema.ResourceData, meta interface{}) error {
	c := meta.(*stingray.Client)

	r, resp, err := c.GetPool(d.Get("name").(string))
	if err != nil {
		if resp != nil && resp.StatusCode == 404 {
			// The resource doesn't exist anymore
			d.SetId("")

			return nil
		}

		return fmt.Errorf("Error reading resource: %s", err)
	}

	d.Set("connection_max_connect_time", int(*r.Connection.MaxConnectTime))
	d.Set("connection_max_connections_per_node", int(*r.Connection.MaxConnectionsPerNode))
	d.Set("connection_max_queue_size", int(*r.Connection.MaxQueueSize))
	d.Set("connection_max_reply_time", int(*r.Connection.MaxReplyTime))
	d.Set("connection_queue_timeout", int(*r.Connection.QueueTimeout))
	d.Set("load_balancing_priority_enabled", bool(*r.LoadBalancing.PriorityEnabled))
	d.Set("monitors", []string(*r.Basic.Monitors))
	d.Set("nodes", nodesTableToNodes(*r.Basic.NodesTable))
	d.Set("note", string(*r.Basic.Note))
	d.Set("passive_monitoring", bool(*r.Basic.PassiveMonitoring))
	d.Set("tcp_nagle", bool(*r.TCP.Nagle))

	return nil
}

func resourcePoolUpdate(d *schema.ResourceData, meta interface{}) error {
	err := resourcePoolSet(d, meta)
	if err != nil {
		return err
	}

	return resourcePoolRead(d, meta)
}

func resourcePoolDelete(d *schema.ResourceData, meta interface{}) error {
	c := meta.(*stingray.Client)
	r := stingray.NewPool(d.Id())

	_, err := c.Delete(r)
	if err != nil {
		return err
	}

	return nil
}

func resourcePoolSet(d *schema.ResourceData, meta interface{}) error {
	c := meta.(*stingray.Client)
	r := stingray.NewPool(d.Get("name").(string))

	setInt(&r.Connection.MaxConnectTime, d, "connection_max_connect_time")
	setInt(&r.Connection.MaxConnectionsPerNode, d, "connection_max_connections_per_node")
	setInt(&r.Connection.MaxQueueSize, d, "connection_max_queue_size")
	setInt(&r.Connection.MaxReplyTime, d, "connection_max_reply_time")
	setInt(&r.Connection.QueueTimeout, d, "connection_queue_timeout")
	setBool(&r.LoadBalancing.PriorityEnabled, d, "load_balancing_priority_enabled")
	setStringSet(&r.Basic.Monitors, d, "monitors")
	setNodesTable(&r.Basic.NodesTable, d, "nodes")
	setString(&r.Basic.Note, d, "note")
	setBool(&r.Basic.PassiveMonitoring, d, "passive_monitoring")
	setBool(&r.TCP.Nagle, d, "tcp_nagle")

	_, err := c.Set(r)
	if err != nil {
		return err
	}

	d.SetId(d.Get("name").(string))

	return nil
}

func setNodesTable(target **stingray.NodesTable, d *schema.ResourceData, key string) {
	if _, ok := d.GetOk(key); ok {
		var nodes []string
		if v := d.Get(key).(*schema.Set); v.Len() > 0 {
			nodes = make([]string, v.Len())
			for i, v := range v.List() {
				nodes[i] = v.(string)
			}
		}
		nodesTable := nodesToNodesTable(nodes)
		*target = &nodesTable
	}
}

func nodesToNodesTable(nodes []string) stingray.NodesTable {
	t := []stingray.Node{}

	for _, v := range nodes {
		t = append(t, stingray.Node{Node: stingray.String(v)})
	}

	return t
}

func nodesTableToNodes(t []stingray.Node) []string {
	nodes := []string{}

	for _, v := range t {
		// A node deleted from the web UI will still exist in
		// the nodes_table, but state and weight will not
		// exist
		if v.State != nil {
			nodes = append(nodes, *v.Node)
		}
	}

	return nodes
}
<commit_msg>pool: Support load_balancing_algorithm<commit_after>package main

import (
	"fmt"

	"github.com/whitepages/terraform-provider-stingray/Godeps/_workspace/src/github.com/hashicorp/terraform/helper/hashcode"
	"github.com/whitepages/terraform-provider-stingray/Godeps/_workspace/src/github.com/hashicorp/terraform/helper/schema"
	"github.com/whitepages/terraform-provider-stingray/Godeps/_workspace/src/github.com/whitepages/go-stingray"
)

func resourcePool() *schema.Resource {
	return &schema.Resource{
		Create: resourcePoolCreate,
		Read:   resourcePoolRead,
		Update: resourcePoolUpdate,
		Delete: resourcePoolDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"connection_max_connect_time": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"connection_max_connections_per_node": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"connection_max_queue_size": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"connection_max_reply_time": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"connection_queue_timeout": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"load_balancing_algorithm": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"load_balancing_priority_enabled": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},

			"monitors": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"nodes": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"note": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"passive_monitoring": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},

			"tcp_nagle": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourcePoolCreate(d *schema.ResourceData, meta interface{}) error {
	err := resourcePoolSet(d, meta)
	if err != nil {
		return err
	}

	return resourcePoolRead(d, meta)
}

func resourcePoolRead(d *schema.ResourceData, meta interface{}) error {
	c := meta.(*stingray.Client)

	r, resp, err := c.GetPool(d.Get("name").(string))
	if err != nil {
		if resp != nil && resp.StatusCode == 404 {
			// The resource doesn't exist anymore
			d.SetId("")

			return nil
		}

		return fmt.Errorf("Error reading resource: %s", err)
	}

	d.Set("connection_max_connect_time", int(*r.Connection.MaxConnectTime))
	d.Set("connection_max_connections_per_node", int(*r.Connection.MaxConnectionsPerNode))
	d.Set("connection_max_queue_size", int(*r.Connection.MaxQueueSize))
	d.Set("connection_max_reply_time", int(*r.Connection.MaxReplyTime))
	d.Set("connection_queue_timeout", int(*r.Connection.QueueTimeout))
	d.Set("load_balancing_algorithm", string(*r.LoadBalancing.Algorithm))
	d.Set("load_balancing_priority_enabled", bool(*r.LoadBalancing.PriorityEnabled))
	d.Set("monitors", []string(*r.Basic.Monitors))
	d.Set("nodes", nodesTableToNodes(*r.Basic.NodesTable))
	d.Set("note", string(*r.Basic.Note))
	d.Set("passive_monitoring", bool(*r.Basic.PassiveMonitoring))
	d.Set("tcp_nagle", bool(*r.TCP.Nagle))

	return nil
}

func resourcePoolUpdate(d *schema.ResourceData, meta interface{}) error {
	err := resourcePoolSet(d, meta)
	if err != nil {
		return err
	}

	return resourcePoolRead(d, meta)
}

func resourcePoolDelete(d *schema.ResourceData, meta interface{}) error {
	c := meta.(*stingray.Client)
	r := stingray.NewPool(d.Id())

	_, err := c.Delete(r)
	if err != nil {
		return err
	}

	return nil
}

func resourcePoolSet(d *schema.ResourceData, meta interface{}) error {
	c := meta.(*stingray.Client)
	r := stingray.NewPool(d.Get("name").(string))

	setInt(&r.Connection.MaxConnectTime, d, "connection_max_connect_time")
	setInt(&r.Connection.MaxConnectionsPerNode, d, "connection_max_connections_per_node")
	setInt(&r.Connection.MaxQueueSize, d, "connection_max_queue_size")
	setInt(&r.Connection.MaxReplyTime, d, "connection_max_reply_time")
	setInt(&r.Connection.QueueTimeout, d, "connection_queue_timeout")
	setString(&r.LoadBalancing.Algorithm, d, "load_balancing_algorithm")
	setBool(&r.LoadBalancing.PriorityEnabled, d, "load_balancing_priority_enabled")
	setStringSet(&r.Basic.Monitors, d, "monitors")
	setNodesTable(&r.Basic.NodesTable, d, "nodes")
	setString(&r.Basic.Note, d, "note")
	setBool(&r.Basic.PassiveMonitoring, d, "passive_monitoring")
	setBool(&r.TCP.Nagle, d, "tcp_nagle")

	_, err := c.Set(r)
	if err != nil {
		return err
	}

	d.SetId(d.Get("name").(string))

	return nil
}

func setNodesTable(target **stingray.NodesTable, d *schema.ResourceData, key string) {
	if _, ok := d.GetOk(key); ok {
		var nodes []string
		if v := d.Get(key).(*schema.Set); v.Len() > 0 {
			nodes = make([]string, v.Len())
			for i, v := range v.List() {
				nodes[i] = v.(string)
			}
		}
		nodesTable := nodesToNodesTable(nodes)
		*target = &nodesTable
	}
}

func nodesToNodesTable(nodes []string) stingray.NodesTable {
	t := []stingray.Node{}

	for _, v := range nodes {
		t = append(t, stingray.Node{Node: stingray.String(v)})
	}

	return t
}

func nodesTableToNodes(t []stingray.Node) []string {
	nodes := []string{}

	for _, v := range t {
		// A node deleted from the web UI will still exist in
		// the nodes_table, but state and weight will not
		// exist
		if v.State != nil {
			nodes = append(nodes, *v.Node)
		}
	}

	return nodes
}
<|endoftext|>


<commit_before>package osin

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

// OutputJSON encodes the Response to JSON and writes to the http.ResponseWriter
func OutputJSON(rs *Response, w http.ResponseWriter, r *http.Request) error {
	// Add headers
	for i, k := range rs.Headers {
		for _, v := range k {
			w.Header().Add(i, v)
		}
	}

	if rs.Type == REDIRECT {
		// Output redirect with parameters
		u, err := rs.GetRedirectUrl()
		if err != nil {
			return err
		}
		w.Header().Add("Location", u)
		w.WriteHeader(302)
	} else {
		// Output json
		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(rs.StatusCode)

		encoder := json.NewEncoder(w)
		err := encoder.Encode(rs.Output)
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>only set the Content-Type header when one hasn't already been set<commit_after>package osin

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

// OutputJSON encodes the Response to JSON and writes to the http.ResponseWriter
func OutputJSON(rs *Response, w http.ResponseWriter, r *http.Request) error {
	// Add headers
	for i, k := range rs.Headers {
		for _, v := range k {
			w.Header().Add(i, v)
		}
	}

	if rs.Type == REDIRECT {
		// Output redirect with parameters
		u, err := rs.GetRedirectUrl()
		if err != nil {
			return err
		}
		w.Header().Add("Location", u)
		w.WriteHeader(302)
	} else {
		// set content type if the response doesn't already have one associated with it
		if w.Header().Get("Content-Type") == "" {
			w.Header().Set("Content-Type", "application/json")
		}
		w.WriteHeader(rs.StatusCode)

		encoder := json.NewEncoder(w)
		err := encoder.Encode(rs.Output)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>