<commit_before>package assertions

import (
	"fmt"
	"strconv"

	"ci.guzzler.io/guzzler/corcel/core"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type ATC struct {
	Actual               interface{}
	Instance             interface{}
	ActualStringNumber   bool
	InstanceStringNumber bool
}

func NewATC(actual interface{}, instance interface{}) (newInstance ATC) {
	newInstance.Actual = actual
	newInstance.Instance = instance
	switch actualType := actual.(type) {
	case string:
		_, err := strconv.ParseFloat(actualType, 64)
		if err == nil {
			newInstance.ActualStringNumber = true
		}
	}
	switch instanceType := instance.(type) {
	case string:
		_, err := strconv.ParseFloat(instanceType, 64)
		if err == nil {
			newInstance.InstanceStringNumber = true
		}
	}
	return
}

var _ = FDescribe("Assertions", func() {

	key := "some:key"

	Context("Greater Than Assertion", func() {

		/*
					   Test Required
					                            INSTANCE

			                                 nil      float64    int    string-number   string
					             ACTUAL

					             float64         x          x          x          x           √

					             int             x          x          x          x           √

					             string-number   x          x          x          x           √

					             string          x          x          x          x           x

					             nil             x          x          x          x           x

		*/

		//To set further context I am making the following assumption
		//Something is greater than nil
		//nil is NOT greater than nil
		//nil is NOT greater than Something
		//string which is not a number is NOT greater than any number
		//number is NOT greater than a string which is not a number
		//Attempts will first be made to parse strings into a float64
		Context("Succeeds", func() {

			key := "some:key"

			assertTrueResult := func(actualValue interface{}, instanceValue interface{}) {
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(true))
				Expect(result["message"]).To(BeNil())
			}

			var nilValue interface{}
			var successfulAssertionTestCases = []ATC{
				NewATC(float64(1.1), nilValue),
				NewATC(int(1), nilValue),
				NewATC("1", nilValue),
				NewATC("a", nilValue),
				NewATC(float64(5), float64(1)),
				NewATC(int(5), float64(1)),
				NewATC("2.2", float64(1)),
			}

			for _, successCase := range successfulAssertionTestCases {
				actualValue := successCase.Actual
				instanceValue := successCase.Instance
				testName := fmt.Sprintf("ACTUAL > INSTANCE when Actual is of type %T and Instance is of type %T", actualValue, instanceValue)
				if successCase.ActualStringNumber {
					testName = fmt.Sprintf("%s. Actual value is a STRING NUMBER", testName)
				}
				if successCase.InstanceStringNumber {
					testName = fmt.Sprintf("%s. Instance value is a STRING NUMBER", testName)
				}
				FIt(testName, func() {
					assertTrueResult(actualValue, instanceValue)
				})
			}

			PIt("When Actual is string-number and Instance is float64", func() {

			})

			PIt("When Actual is float64 and Instance is int", func() {

			})

			PIt("When Actual is int and Instance is int", func() {

			})

			PIt("When Actual is string-number and Instance is int", func() {

			})

			PIt("When Actual is string and Instance is string", func() {

			})

			PIt("When Actual is float64 and Instance is string-number", func() {

			})

			PIt("When Actual is int and Instance is string-number", func() {

			})

			PIt("When Actual is string-number and Instance is string-number", func() {

			})
		})

		Context("Fails", func() {
			PIt("When Actual is nil and Instance is nil", func() {

			})

			PIt("When Actual is nil and Instance is int", func() {

			})

			PIt("When Actual is nil and Instance is string-number", func() {

			})

			PIt("When Actual is nil and Instance is string", func() {

			})

			PIt("When Actual is nil and Instance is float64", func() {

			})

			PIt("When Actual is float64 and Instance is float64", func() {

			})

			PIt("When Actual is int and Instance is float64", func() {

			})

			PIt("When Actual is string-number and Instance is float64", func() {

			})

			PIt("When Actual is float64 and Instance is int", func() {

			})

			PIt("When Actual is float64 and Instance is int", func() {

			})

			PIt("When Actual is int and Instance is int", func() {

			})

			PIt("When Actual is string-number and Instance is int", func() {

			})

			PIt("When Actual is string and Instance is int", func() {

			})

			PIt("When Actual is string and Instance is float64", func() {

			})

			PIt("When Actual is string and Instance is string-number", func() {

			})

			PIt("When Actual is string and Instance is string", func() {

			})

			PIt("When Actual is float64 and Instance is string", func() {

			})

			PIt("When Actual is int and Instance is string", func() {

			})

			PIt("When Actual string-number int and Instance is string", func() {

			})

		})

	})

	Context("Not Equal Assertion", func() {

		It("Succeeds", func() {
			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Fails", func() {
			executionResult := core.ExecutionResult{
				key: 7,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 7 does match 7"))
		})
	})

	Context("Exact Assertion", func() {
		It("Exact Assertion Succeeds", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: expectedValue,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Exact Assertion Fails", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 8 does not match 7"))
		})

		//NOTHING is currently using the message when an assertion fails but we will need
		//it for when we put the errors into the report.  One of the edge cases with the message
		//is that say the actual value was a string "7" and the expected is an int 7.  The message
		//will not include the quotes so the message would read 7 does not equal 7 as opposed
		//to "7" does not equal 7.  Notice this is a type mismatch
		PIt("Exact Assertion Fails when actual and expected are different types", func() {
			key := "some:key"
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: "7",
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: \"7\" does not match 7"))
		})
	})

})
<commit_msg>Succeeds when Actual is float64 and Instance is int<commit_after>package assertions

import (
	"fmt"
	"strconv"

	"ci.guzzler.io/guzzler/corcel/core"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type ATC struct {
	Actual               interface{}
	Instance             interface{}
	ActualStringNumber   bool
	InstanceStringNumber bool
}

func NewATC(actual interface{}, instance interface{}) (newInstance ATC) {
	newInstance.Actual = actual
	newInstance.Instance = instance
	switch actualType := actual.(type) {
	case string:
		_, err := strconv.ParseFloat(actualType, 64)
		if err == nil {
			newInstance.ActualStringNumber = true
		}
	}
	switch instanceType := instance.(type) {
	case string:
		_, err := strconv.ParseFloat(instanceType, 64)
		if err == nil {
			newInstance.InstanceStringNumber = true
		}
	}
	return
}

var _ = FDescribe("Assertions", func() {

	key := "some:key"

	Context("Greater Than Assertion", func() {

		/*
					   Test Required
					                            INSTANCE

			                                 nil      float64    int    string-number   string
					             ACTUAL

					             float64         x          x          x          x           √

					             int             x          x          x          x           √

					             string-number   x          x          x          x           √

					             string          x          x          x          x           x

					             nil             x          x          x          x           x

		*/

		//To set further context I am making the following assumption
		//Something is greater than nil
		//nil is NOT greater than nil
		//nil is NOT greater than Something
		//string which is not a number is NOT greater than any number
		//number is NOT greater than a string which is not a number
		//Attempts will first be made to parse strings into a float64
		Context("Succeeds", func() {

			key := "some:key"

			assertTrueResult := func(actualValue interface{}, instanceValue interface{}) {
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(true))
				Expect(result["message"]).To(BeNil())
			}

			var nilValue interface{}
			var successfulAssertionTestCases = []ATC{
				NewATC(float64(1.1), nilValue),
				NewATC(int(1), nilValue),
				NewATC("1", nilValue),
				NewATC("a", nilValue),
				NewATC(float64(5), float64(1)),
				NewATC(int(5), float64(1)),
				NewATC("2.2", float64(1)),
				NewATC(float64(5), int(1)),
			}

			for _, successCase := range successfulAssertionTestCases {
				actualValue := successCase.Actual
				instanceValue := successCase.Instance
				testName := fmt.Sprintf("ACTUAL > INSTANCE when Actual is of type %T and Instance is of type %T", actualValue, instanceValue)
				if successCase.ActualStringNumber {
					testName = fmt.Sprintf("%s. Actual value is a STRING NUMBER", testName)
				}
				if successCase.InstanceStringNumber {
					testName = fmt.Sprintf("%s. Instance value is a STRING NUMBER", testName)
				}
				FIt(testName, func() {
					assertTrueResult(actualValue, instanceValue)
				})
			}

			PIt("When Actual is float64 and Instance is int", func() {

			})

			PIt("When Actual is int and Instance is int", func() {

			})

			PIt("When Actual is string-number and Instance is int", func() {

			})

			PIt("When Actual is string and Instance is string", func() {

			})

			PIt("When Actual is float64 and Instance is string-number", func() {

			})

			PIt("When Actual is int and Instance is string-number", func() {

			})

			PIt("When Actual is string-number and Instance is string-number", func() {

			})
		})

		Context("Fails", func() {
			PIt("When Actual is nil and Instance is nil", func() {

			})

			PIt("When Actual is nil and Instance is int", func() {

			})

			PIt("When Actual is nil and Instance is string-number", func() {

			})

			PIt("When Actual is nil and Instance is string", func() {

			})

			PIt("When Actual is nil and Instance is float64", func() {

			})

			PIt("When Actual is float64 and Instance is float64", func() {

			})

			PIt("When Actual is int and Instance is float64", func() {

			})

			PIt("When Actual is string-number and Instance is float64", func() {

			})

			PIt("When Actual is float64 and Instance is int", func() {

			})

			PIt("When Actual is float64 and Instance is int", func() {

			})

			PIt("When Actual is int and Instance is int", func() {

			})

			PIt("When Actual is string-number and Instance is int", func() {

			})

			PIt("When Actual is string and Instance is int", func() {

			})

			PIt("When Actual is string and Instance is float64", func() {

			})

			PIt("When Actual is string and Instance is string-number", func() {

			})

			PIt("When Actual is string and Instance is string", func() {

			})

			PIt("When Actual is float64 and Instance is string", func() {

			})

			PIt("When Actual is int and Instance is string", func() {

			})

			PIt("When Actual string-number int and Instance is string", func() {

			})

		})

	})

	Context("Not Equal Assertion", func() {

		It("Succeeds", func() {
			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Fails", func() {
			executionResult := core.ExecutionResult{
				key: 7,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 7 does match 7"))
		})
	})

	Context("Exact Assertion", func() {
		It("Exact Assertion Succeeds", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: expectedValue,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Exact Assertion Fails", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 8 does not match 7"))
		})

		//NOTHING is currently using the message when an assertion fails but we will need
		//it for when we put the errors into the report.  One of the edge cases with the message
		//is that say the actual value was a string "7" and the expected is an int 7.  The message
		//will not include the quotes so the message would read 7 does not equal 7 as opposed
		//to "7" does not equal 7.  Notice this is a type mismatch
		PIt("Exact Assertion Fails when actual and expected are different types", func() {
			key := "some:key"
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: "7",
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: \"7\" does not match 7"))
		})
	})

})
<|endoftext|>


<commit_before>package stardb

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"path"
	"time"

	"gopkg.in/mgo.v2"

	"gopkg.in/mgo.v2/bson"
)

type Farm struct {

	// Fields with  `json:"-"` are ommited for web display

	ID        bson.ObjectId `bson:"_id,omitempty" json:"-"`
	UniqueID  int           `json:"-"`
	Name      string
	Farmer    string
	Likes     int
	SaveTime  time.Time `json:"-"`
	Thumbnail string
}

func (f *Farm) ScreenshotPath() string {
	return fmt.Sprintf("/screenshot/%v/%d.xml", f.ID.Hex(), f.SaveTime.Unix())
}

func FarmsJSON() ([]byte, error) {
	var result []*Farm

	if err := FarmCollection.Find(nil).Limit(20).All(&result); err != nil {
		return nil, err
	}
	for _, farm := range result {
		farm.Thumbnail = farm.ScreenshotPath()
	}
	return json.Marshal(result)
}

func UpdateFarmTime(c *mgo.Collection, id bson.ObjectId, ts time.Time) error {
	return c.Update(bson.M{"_id": id}, bson.M{"savetime": ts})
}

func FindFarm(c *mgo.Collection, uniqueIDForThisGame int, playerName, farmName string) (ret *Farm, existing bool, err error) {
	ret = &Farm{}
	q := c.Find(bson.M{
		"name":     farmName,
		"farmer":   playerName,
		"uniqueid": uniqueIDForThisGame,
	})
	if err := q.One(&ret); err != nil {
		log.Println("not found", err)

		farm := &Farm{
			Name:     farmName,
			Farmer:   playerName,
			UniqueID: uniqueIDForThisGame,
			ID:       bson.NewObjectId(),
			SaveTime: time.Now(),
		}
		if err := c.Insert(farm); err != nil {
			log.Println("could not insert", err)
			return nil, false, err
		}
		log.Println("insert ok", farm.ID.String())
		return farm, false, nil
	}
	log.Printf("found ok %v, %v, %v", ret.Name, ret.Farmer, ret.SaveTime)

	return ret, true, nil
}

func WriteSaveFile(farm *Farm, body []byte, ts time.Time) error {
	if farm.SaveTime.IsZero() {
		return fmt.Errorf("error writing save file: unexpected zero save time")
	}

	saveFile := path.Join("saveGames", farm.ScreenshotPath())
	g, err := GFS.Create(saveFile)
	if err != nil {
		return fmt.Errorf("Error opening grid saveGames %v: %v", saveFile, err)

	}
	g.SetUploadDate(ts)
	defer g.Close()
	if _, err := g.Write(body); err != nil {
		return fmt.Errorf("Failed to write grid save file at %v: %v", saveFile, err)
	}

	log.Printf("Wrote grid saveGame file %v", saveFile)
	return nil
}

// NewScreenshotWriter saves a screenshot in GFS at screenshots/<hexid>.png
func NewScreenshotWriter(farm *Farm, ts time.Time) (io.WriteCloser, error) {
	if farm.SaveTime.IsZero() {
		return nil, fmt.Errorf("error writing screenshot: unexpected zero save time")
	}
	g, err := GFS.Create(farm.ScreenshotPath())
	if err != nil {
		return nil, err
	}
	g.SetUploadDate(ts)
	return g, nil
}
<commit_msg>close stuff<commit_after>package stardb

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"path"
	"time"

	"gopkg.in/mgo.v2"

	"gopkg.in/mgo.v2/bson"
)

type Farm struct {

	// Fields with  `json:"-"` are ommited for web display

	ID        bson.ObjectId `bson:"_id,omitempty" json:"-"`
	UniqueID  int           `json:"-"`
	Name      string
	Farmer    string
	Likes     int
	SaveTime  time.Time `json:"-"`
	Thumbnail string
}

func (f *Farm) ScreenshotPath() string {
	return fmt.Sprintf("/screenshot/%v/%d.xml", f.ID.Hex(), f.SaveTime.Unix())
}

func FarmsJSON() ([]byte, error) {
	var result []*Farm

	if err := FarmCollection.Find(nil).Limit(20).All(&result); err != nil {
		return nil, err
	}
	for _, farm := range result {
		farm.Thumbnail = farm.ScreenshotPath()
	}
	return json.Marshal(result)
}

func UpdateFarmTime(c *mgo.Collection, id bson.ObjectId, ts time.Time) error {
	return c.Update(bson.M{"_id": id}, bson.M{"savetime": ts})
}

func FindFarm(c *mgo.Collection, uniqueIDForThisGame int, playerName, farmName string) (ret *Farm, existing bool, err error) {
	ret = &Farm{}
	q := c.Find(bson.M{
		"name":     farmName,
		"farmer":   playerName,
		"uniqueid": uniqueIDForThisGame,
	})
	if err := q.One(&ret); err != nil {
		log.Println("not found", err)

		farm := &Farm{
			Name:     farmName,
			Farmer:   playerName,
			UniqueID: uniqueIDForThisGame,
			ID:       bson.NewObjectId(),
			SaveTime: time.Now(),
		}
		if err := c.Insert(farm); err != nil {
			log.Println("could not insert", err)
			return nil, false, err
		}
		log.Println("insert ok", farm.ID.String())
		return farm, false, nil
	}
	log.Printf("found ok %v, %v, %v", ret.Name, ret.Farmer, ret.SaveTime)

	return ret, true, nil
}

func WriteSaveFile(farm *Farm, body []byte, ts time.Time) error {
	if farm.SaveTime.IsZero() {
		return fmt.Errorf("error writing save file: unexpected zero save time")
	}

	saveFile := path.Join("saveGames", farm.ScreenshotPath())
	g, err := GFS.Create(saveFile)
	if err != nil {
		return fmt.Errorf("Error opening grid saveGames %v: %v", saveFile, err)

	}
	g.SetUploadDate(ts)
	defer g.Close()
	if _, err := g.Write(body); err != nil {
		return fmt.Errorf("Failed to write grid save file at %v: %v", saveFile, err)
	}

	log.Printf("Wrote grid saveGame file %v", saveFile)
	return nil
}

// NewScreenshotWriter saves a screenshot in GFS at screenshots/<hexid>.png
func NewScreenshotWriter(farm *Farm, ts time.Time) (io.WriteCloser, error) {
	if farm.SaveTime.IsZero() {
		return nil, fmt.Errorf("error writing screenshot: unexpected zero save time")
	}
	g, err := GFS.Create(farm.ScreenshotPath())
	if err != nil {
		return nil, err
	}
	g.SetUploadDate(ts)
	defer g.Close()
	return g, nil
}
<|endoftext|>


<commit_before>package rekognition

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"

	SDK "github.com/aws/aws-sdk-go/service/rekognition"

	"github.com/evalphobia/aws-sdk-go-wrapper/private/pointers"
)

// DetectFacesFromLocalFile gets label info from local image file.
func (svc *Rekognition) DetectFacesFromLocalFile(filepath string) (*FaceDetailResponse, error) {
	byt, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}
	return svc.DetectFacesByBytes(byt)
}

// DetectFacesFromURL gets label info from URL.
func (svc *Rekognition) DetectFacesFromURL(url string) (*FaceDetailResponse, error) {
	byt, err := svc.getImageFromURL(url)
	if err != nil {
		return nil, err
	}
	return svc.DetectFacesByBytes(byt)
}

// DetectFacesByBytes gets label info from image of bytes.
func (svc *Rekognition) DetectFacesByBytes(byt []byte) (*FaceDetailResponse, error) {
	input := &SDK.DetectFacesInput{
		Image: &SDK.Image{
			Bytes: byt,
		},
	}
	return svc.detectFaces(input)
}

// DetectFacesByS3Object gets label info from image of S3 object.
func (svc *Rekognition) DetectFacesByS3Object(bucket, name string, version ...string) (*FaceDetailResponse, error) {
	input := &SDK.DetectFacesInput{
		Image: &SDK.Image{
			S3Object: createS3Object(bucket, name, version...),
		},
	}
	return svc.detectFaces(input)
}

// detectFaces executes `detectFace` operation and gets face info.
func (svc *Rekognition) detectFaces(input *SDK.DetectFacesInput) (*FaceDetailResponse, error) {
	op, err := svc.client.DetectFaces(input)
	if err != nil {
		svc.Errorf("error on `DetectFaces` operation; error=%s;", err.Error())
		return nil, err
	}

	list := make([]*FaceDetail, len(op.FaceDetails))
	for i, f := range op.FaceDetails {
		list[i] = NewFaceDetailFromAWSFaceDetail(f)
	}

	result := &FaceDetailResponse{
		List: list,
	}
	if op.OrientationCorrection != nil {
		result.Orientation = *op.OrientationCorrection
	}
	return result, nil
}

// DetectLabelsFromLocalFile gets label info from local image file.
func (svc *Rekognition) DetectLabelsFromLocalFile(filepath string) (*LabelResponse, error) {
	byt, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}
	return svc.DetectLabelsByBytes(byt)
}

// DetectLabelsFromURL gets label info from URL.
func (svc *Rekognition) DetectLabelsFromURL(url string) (*LabelResponse, error) {
	byt, err := svc.getImageFromURL(url)
	if err != nil {
		return nil, err
	}
	return svc.DetectLabelsByBytes(byt)
}

// DetectLabelsByBytes gets label info from image of bytes.
func (svc *Rekognition) DetectLabelsByBytes(byt []byte) (*LabelResponse, error) {
	input := &SDK.DetectLabelsInput{
		Image: &SDK.Image{
			Bytes: byt,
		},
	}
	return svc.detectLabels(input)
}

// DetectLabelsByS3Object gets label info from image of S3 object.
func (svc *Rekognition) DetectLabelsByS3Object(bucket, name string, version ...string) (*LabelResponse, error) {
	input := &SDK.DetectLabelsInput{
		Image: &SDK.Image{
			S3Object: createS3Object(bucket, name, version...),
		},
	}
	return svc.detectLabels(input)
}

// detectLabels executes `DetectLabels` operation and gets face info.
func (svc *Rekognition) detectLabels(input *SDK.DetectLabelsInput) (*LabelResponse, error) {
	op, err := svc.client.DetectLabels(input)
	if err != nil {
		svc.Errorf("error on `DetectLabels` operation; error=%s;", err.Error())
		return nil, err
	}

	list := make([]*Label, len(op.Labels))
	for i, l := range op.Labels {
		list[i] = NewLabelFromAWSLabel(l)
	}

	result := &LabelResponse{
		List: list,
	}
	if op.OrientationCorrection != nil {
		result.Orientation = *op.OrientationCorrection
	}
	return result, nil
}

// DetectModerationLabelsFromLocalFile gets moderation info from local image file.
func (svc *Rekognition) DetectModerationLabelsFromLocalFile(filepath string) (*ModerationLabelResponse, error) {
	byt, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}
	return svc.DetectModerationLabelsByBytes(byt)
}

// DetectModerationLabelsFromURL gets moderation info from URL.
func (svc *Rekognition) DetectModerationLabelsFromURL(url string) (*ModerationLabelResponse, error) {
	byt, err := svc.getImageFromURL(url)
	if err != nil {
		return nil, err
	}
	return svc.DetectModerationLabelsByBytes(byt)
}

// DetectModerationLabelsByBytes gets moderation info from image of bytes.
func (svc *Rekognition) DetectModerationLabelsByBytes(byt []byte) (*ModerationLabelResponse, error) {
	input := &SDK.DetectModerationLabelsInput{
		Image: &SDK.Image{
			Bytes: byt,
		},
	}
	return svc.detectModerationLabels(input)
}

// DetectModerationLabelsByS3Object gets moderation info from image of S3 object.
func (svc *Rekognition) DetectModerationLabelsByS3Object(bucket, name string, version ...string) (*ModerationLabelResponse, error) {
	input := &SDK.DetectModerationLabelsInput{
		Image: &SDK.Image{
			S3Object: createS3Object(bucket, name, version...),
		},
	}
	return svc.detectModerationLabels(input)
}

// detectModerationLabels executes `DetectModerationLabels` operation and gets face info.
func (svc *Rekognition) detectModerationLabels(input *SDK.DetectModerationLabelsInput) (*ModerationLabelResponse, error) {
	op, err := svc.client.DetectModerationLabels(input)
	if err != nil {
		svc.Errorf("error on `DetectModerationLabels` operation; error=%s;", err.Error())
		return nil, err
	}

	list := make([]*ModerationLabel, len(op.ModerationLabels))
	for i, l := range op.ModerationLabels {
		list[i] = NewModerationLabelFromAWSModerationLabel(l)
	}

	return &ModerationLabelResponse{
		List: list,
	}, nil
}

// RecognizeCelebritiesFromLocalFile gets celebrities info from local image file.
func (svc *Rekognition) RecognizeCelebritiesFromLocalFile(filepath string) (*CelebrityResponse, error) {
	byt, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}
	return svc.RecognizeCelebritiesByBytes(byt)
}

// RecognizeCelebritiesFromURL gets celebrities info from URL.
func (svc *Rekognition) RecognizeCelebritiesFromURL(url string) (*CelebrityResponse, error) {
	byt, err := svc.getImageFromURL(url)
	if err != nil {
		return nil, err
	}
	return svc.RecognizeCelebritiesByBytes(byt)
}

// RecognizeCelebritiesByBytes gets celebrities info from image of bytes.
func (svc *Rekognition) RecognizeCelebritiesByBytes(byt []byte) (*CelebrityResponse, error) {
	input := &SDK.RecognizeCelebritiesInput{
		Image: &SDK.Image{
			Bytes: byt,
		},
	}
	return svc.recognizeCelebrities(input)
}

// RecognizeCelebritiesByS3Object gets celebrities info from image of S3 object.
func (svc *Rekognition) RecognizeCelebritiesByS3Object(bucket, name string, version ...string) (*CelebrityResponse, error) {
	input := &SDK.RecognizeCelebritiesInput{
		Image: &SDK.Image{
			S3Object: createS3Object(bucket, name, version...),
		},
	}
	return svc.recognizeCelebrities(input)
}

// recognizeCelebrities executes `RecognizeCelebrities` operation and gets celebrities info.
func (svc *Rekognition) recognizeCelebrities(input *SDK.RecognizeCelebritiesInput) (*CelebrityResponse, error) {
	op, err := svc.client.RecognizeCelebrities(input)
	if err != nil {
		svc.Errorf("error on `RecognizeCelebrities` operation; error=%s;", err.Error())
		return &CelebrityResponse{}, err
	}

	list := make([]*Celebrity, len(op.CelebrityFaces))
	for i, c := range op.CelebrityFaces {
		var urls []string
		if len(c.Urls) != 0 {
			urls := make([]string, len(c.Urls))
			for j, url := range c.Urls {
				urls[j] = *url
			}
		}

		list[i] = &Celebrity{
			Name:            *c.Name,
			MatchConfidence: *c.MatchConfidence,
			Urls:            urls,
		}
	}

	return &CelebrityResponse{
		List: list,
	}, nil
}

// GetCelebrityInfoByID gets celebrities info by id.
func (svc *Rekognition) GetCelebrityInfoByID(id string) (*CelebrityInfoResponse, error) {
	return svc.getCelebrityInfo(&SDK.GetCelebrityInfoInput{
		Id: pointers.String(id),
	})
}

// getCelebrityInfo executes `GetCelebrityInfo` operation and gets celebrities info.
func (svc *Rekognition) getCelebrityInfo(input *SDK.GetCelebrityInfoInput) (*CelebrityInfoResponse, error) {
	op, err := svc.client.GetCelebrityInfo(input)
	if err != nil {
		svc.Errorf("error on `GetCelebrityInfo` operation; error=%s;", err.Error())
		return &CelebrityInfoResponse{}, err
	}

	return NewCelebrityInfoResponseFromAWSOutput(op), nil
}

// CompareFacesFromLocalFile compares faces from local image files.
func (svc *Rekognition) CompareFacesFromLocalFile(source, target string) (*CompareFaceResponse, error) {
	sourceByte, err := ioutil.ReadFile(source)
	if err != nil {
		return nil, err
	}
	targetByte, err := ioutil.ReadFile(target)
	if err != nil {
		return nil, err
	}

	return svc.CompareFacesByBytes(sourceByte, targetByte)
}

// CompareFacesFromURL compares faces from URL.
func (svc *Rekognition) CompareFacesFromURL(source, target string) (*CompareFaceResponse, error) {
	sourceByte, err := svc.getImageFromURL(source)
	if err != nil {
		return nil, err
	}
	targetByte, err := svc.getImageFromURL(target)
	if err != nil {
		return nil, err
	}

	return svc.CompareFacesByBytes(sourceByte, targetByte)
}

// CompareFacesByBytes compares faces from image of bytes.
func (svc *Rekognition) CompareFacesByBytes(source, target []byte) (*CompareFaceResponse, error) {
	sourceImage := &SDK.Image{
		Bytes: source,
	}
	targetImage := &SDK.Image{
		Bytes: target,
	}

	input := &SDK.CompareFacesInput{
		SourceImage: sourceImage,
		TargetImage: targetImage,
	}
	return svc.compareFaces(input)
}

// CompareFaces executes `CompareFaces` operation and gets celebrities info.
func (svc *Rekognition) CompareFaces(input *SDK.CompareFacesInput) (*CompareFaceResponse, error) {
	return svc.CompareFaces(input)
}

// compareFaces executes `CompareFaces` operation and gets celebrities info.
func (svc *Rekognition) compareFaces(input *SDK.CompareFacesInput) (*CompareFaceResponse, error) {
	op, err := svc.client.CompareFaces(input)
	if err != nil {
		svc.Errorf("error on `CompareFaces` operation; error=%s;", err.Error())
		return &CompareFaceResponse{}, err
	}

	fmt.Printf("op=%#v\n", op)
	return NewCompareFaceResponseFromAWSOutput(op), nil
}

// getImageFromURL gets image data from url.
func (svc *Rekognition) getImageFromURL(url string) ([]byte, error) {
	cli := svc.httpClient
	if cli == nil {
		err := errors.New("error on `getImageFromURL`; error=`svc.httpClient is nil`;")
		svc.Errorf(err.Error())
		return nil, err
	}

	resp, err := cli.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, resp.Body)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// createS3Object creates *SDK.S3Object from bucket and name.
func createS3Object(bucket, name string, version ...string) *SDK.S3Object {
	s3object := &SDK.S3Object{
		Bucket: pointers.String(bucket),
		Name:   pointers.String(name),
	}
	if len(version) == 1 {
		s3object.Version = pointers.String(version[0])
	}
	return s3object
}
<commit_msg>[rekognition] Fix CompareFaces (#38)<commit_after>package rekognition

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"

	SDK "github.com/aws/aws-sdk-go/service/rekognition"

	"github.com/evalphobia/aws-sdk-go-wrapper/private/pointers"
)

// DetectFacesFromLocalFile gets label info from local image file.
func (svc *Rekognition) DetectFacesFromLocalFile(filepath string) (*FaceDetailResponse, error) {
	byt, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}
	return svc.DetectFacesByBytes(byt)
}

// DetectFacesFromURL gets label info from URL.
func (svc *Rekognition) DetectFacesFromURL(url string) (*FaceDetailResponse, error) {
	byt, err := svc.getImageFromURL(url)
	if err != nil {
		return nil, err
	}
	return svc.DetectFacesByBytes(byt)
}

// DetectFacesByBytes gets label info from image of bytes.
func (svc *Rekognition) DetectFacesByBytes(byt []byte) (*FaceDetailResponse, error) {
	input := &SDK.DetectFacesInput{
		Image: &SDK.Image{
			Bytes: byt,
		},
	}
	return svc.detectFaces(input)
}

// DetectFacesByS3Object gets label info from image of S3 object.
func (svc *Rekognition) DetectFacesByS3Object(bucket, name string, version ...string) (*FaceDetailResponse, error) {
	input := &SDK.DetectFacesInput{
		Image: &SDK.Image{
			S3Object: createS3Object(bucket, name, version...),
		},
	}
	return svc.detectFaces(input)
}

// detectFaces executes `detectFace` operation and gets face info.
func (svc *Rekognition) detectFaces(input *SDK.DetectFacesInput) (*FaceDetailResponse, error) {
	op, err := svc.client.DetectFaces(input)
	if err != nil {
		svc.Errorf("error on `DetectFaces` operation; error=%s;", err.Error())
		return nil, err
	}

	list := make([]*FaceDetail, len(op.FaceDetails))
	for i, f := range op.FaceDetails {
		list[i] = NewFaceDetailFromAWSFaceDetail(f)
	}

	result := &FaceDetailResponse{
		List: list,
	}
	if op.OrientationCorrection != nil {
		result.Orientation = *op.OrientationCorrection
	}
	return result, nil
}

// DetectLabelsFromLocalFile gets label info from local image file.
func (svc *Rekognition) DetectLabelsFromLocalFile(filepath string) (*LabelResponse, error) {
	byt, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}
	return svc.DetectLabelsByBytes(byt)
}

// DetectLabelsFromURL gets label info from URL.
func (svc *Rekognition) DetectLabelsFromURL(url string) (*LabelResponse, error) {
	byt, err := svc.getImageFromURL(url)
	if err != nil {
		return nil, err
	}
	return svc.DetectLabelsByBytes(byt)
}

// DetectLabelsByBytes gets label info from image of bytes.
func (svc *Rekognition) DetectLabelsByBytes(byt []byte) (*LabelResponse, error) {
	input := &SDK.DetectLabelsInput{
		Image: &SDK.Image{
			Bytes: byt,
		},
	}
	return svc.detectLabels(input)
}

// DetectLabelsByS3Object gets label info from image of S3 object.
func (svc *Rekognition) DetectLabelsByS3Object(bucket, name string, version ...string) (*LabelResponse, error) {
	input := &SDK.DetectLabelsInput{
		Image: &SDK.Image{
			S3Object: createS3Object(bucket, name, version...),
		},
	}
	return svc.detectLabels(input)
}

// detectLabels executes `DetectLabels` operation and gets face info.
func (svc *Rekognition) detectLabels(input *SDK.DetectLabelsInput) (*LabelResponse, error) {
	op, err := svc.client.DetectLabels(input)
	if err != nil {
		svc.Errorf("error on `DetectLabels` operation; error=%s;", err.Error())
		return nil, err
	}

	list := make([]*Label, len(op.Labels))
	for i, l := range op.Labels {
		list[i] = NewLabelFromAWSLabel(l)
	}

	result := &LabelResponse{
		List: list,
	}
	if op.OrientationCorrection != nil {
		result.Orientation = *op.OrientationCorrection
	}
	return result, nil
}

// DetectModerationLabelsFromLocalFile gets moderation info from local image file.
func (svc *Rekognition) DetectModerationLabelsFromLocalFile(filepath string) (*ModerationLabelResponse, error) {
	byt, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}
	return svc.DetectModerationLabelsByBytes(byt)
}

// DetectModerationLabelsFromURL gets moderation info from URL.
func (svc *Rekognition) DetectModerationLabelsFromURL(url string) (*ModerationLabelResponse, error) {
	byt, err := svc.getImageFromURL(url)
	if err != nil {
		return nil, err
	}
	return svc.DetectModerationLabelsByBytes(byt)
}

// DetectModerationLabelsByBytes gets moderation info from image of bytes.
func (svc *Rekognition) DetectModerationLabelsByBytes(byt []byte) (*ModerationLabelResponse, error) {
	input := &SDK.DetectModerationLabelsInput{
		Image: &SDK.Image{
			Bytes: byt,
		},
	}
	return svc.detectModerationLabels(input)
}

// DetectModerationLabelsByS3Object gets moderation info from image of S3 object.
func (svc *Rekognition) DetectModerationLabelsByS3Object(bucket, name string, version ...string) (*ModerationLabelResponse, error) {
	input := &SDK.DetectModerationLabelsInput{
		Image: &SDK.Image{
			S3Object: createS3Object(bucket, name, version...),
		},
	}
	return svc.detectModerationLabels(input)
}

// detectModerationLabels executes `DetectModerationLabels` operation and gets face info.
func (svc *Rekognition) detectModerationLabels(input *SDK.DetectModerationLabelsInput) (*ModerationLabelResponse, error) {
	op, err := svc.client.DetectModerationLabels(input)
	if err != nil {
		svc.Errorf("error on `DetectModerationLabels` operation; error=%s;", err.Error())
		return nil, err
	}

	list := make([]*ModerationLabel, len(op.ModerationLabels))
	for i, l := range op.ModerationLabels {
		list[i] = NewModerationLabelFromAWSModerationLabel(l)
	}

	return &ModerationLabelResponse{
		List: list,
	}, nil
}

// RecognizeCelebritiesFromLocalFile gets celebrities info from local image file.
func (svc *Rekognition) RecognizeCelebritiesFromLocalFile(filepath string) (*CelebrityResponse, error) {
	byt, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}
	return svc.RecognizeCelebritiesByBytes(byt)
}

// RecognizeCelebritiesFromURL gets celebrities info from URL.
func (svc *Rekognition) RecognizeCelebritiesFromURL(url string) (*CelebrityResponse, error) {
	byt, err := svc.getImageFromURL(url)
	if err != nil {
		return nil, err
	}
	return svc.RecognizeCelebritiesByBytes(byt)
}

// RecognizeCelebritiesByBytes gets celebrities info from image of bytes.
func (svc *Rekognition) RecognizeCelebritiesByBytes(byt []byte) (*CelebrityResponse, error) {
	input := &SDK.RecognizeCelebritiesInput{
		Image: &SDK.Image{
			Bytes: byt,
		},
	}
	return svc.recognizeCelebrities(input)
}

// RecognizeCelebritiesByS3Object gets celebrities info from image of S3 object.
func (svc *Rekognition) RecognizeCelebritiesByS3Object(bucket, name string, version ...string) (*CelebrityResponse, error) {
	input := &SDK.RecognizeCelebritiesInput{
		Image: &SDK.Image{
			S3Object: createS3Object(bucket, name, version...),
		},
	}
	return svc.recognizeCelebrities(input)
}

// recognizeCelebrities executes `RecognizeCelebrities` operation and gets celebrities info.
func (svc *Rekognition) recognizeCelebrities(input *SDK.RecognizeCelebritiesInput) (*CelebrityResponse, error) {
	op, err := svc.client.RecognizeCelebrities(input)
	if err != nil {
		svc.Errorf("error on `RecognizeCelebrities` operation; error=%s;", err.Error())
		return &CelebrityResponse{}, err
	}

	list := make([]*Celebrity, len(op.CelebrityFaces))
	for i, c := range op.CelebrityFaces {
		var urls []string
		if len(c.Urls) != 0 {
			urls := make([]string, len(c.Urls))
			for j, url := range c.Urls {
				urls[j] = *url
			}
		}

		list[i] = &Celebrity{
			Name:            *c.Name,
			MatchConfidence: *c.MatchConfidence,
			Urls:            urls,
		}
	}

	return &CelebrityResponse{
		List: list,
	}, nil
}

// GetCelebrityInfoByID gets celebrities info by id.
func (svc *Rekognition) GetCelebrityInfoByID(id string) (*CelebrityInfoResponse, error) {
	return svc.getCelebrityInfo(&SDK.GetCelebrityInfoInput{
		Id: pointers.String(id),
	})
}

// getCelebrityInfo executes `GetCelebrityInfo` operation and gets celebrities info.
func (svc *Rekognition) getCelebrityInfo(input *SDK.GetCelebrityInfoInput) (*CelebrityInfoResponse, error) {
	op, err := svc.client.GetCelebrityInfo(input)
	if err != nil {
		svc.Errorf("error on `GetCelebrityInfo` operation; error=%s;", err.Error())
		return &CelebrityInfoResponse{}, err
	}

	return NewCelebrityInfoResponseFromAWSOutput(op), nil
}

// CompareFacesFromLocalFile compares faces from local image files.
func (svc *Rekognition) CompareFacesFromLocalFile(source, target string) (*CompareFaceResponse, error) {
	sourceByte, err := ioutil.ReadFile(source)
	if err != nil {
		return nil, err
	}
	targetByte, err := ioutil.ReadFile(target)
	if err != nil {
		return nil, err
	}

	return svc.CompareFacesByBytes(sourceByte, targetByte)
}

// CompareFacesFromURL compares faces from URL.
func (svc *Rekognition) CompareFacesFromURL(source, target string) (*CompareFaceResponse, error) {
	sourceByte, err := svc.getImageFromURL(source)
	if err != nil {
		return nil, err
	}
	targetByte, err := svc.getImageFromURL(target)
	if err != nil {
		return nil, err
	}

	return svc.CompareFacesByBytes(sourceByte, targetByte)
}

// CompareFacesByBytes compares faces from image of bytes.
func (svc *Rekognition) CompareFacesByBytes(source, target []byte) (*CompareFaceResponse, error) {
	sourceImage := &SDK.Image{
		Bytes: source,
	}
	targetImage := &SDK.Image{
		Bytes: target,
	}

	input := &SDK.CompareFacesInput{
		SourceImage: sourceImage,
		TargetImage: targetImage,
	}
	return svc.compareFaces(input)
}

// CompareFaces executes `CompareFaces` operation and gets celebrities info.
func (svc *Rekognition) CompareFaces(input *SDK.CompareFacesInput) (*CompareFaceResponse, error) {
	return svc.compareFaces(input)
}

// compareFaces executes `CompareFaces` operation and gets celebrities info.
func (svc *Rekognition) compareFaces(input *SDK.CompareFacesInput) (*CompareFaceResponse, error) {
	op, err := svc.client.CompareFaces(input)
	if err != nil {
		svc.Errorf("error on `CompareFaces` operation; error=%s;", err.Error())
		return &CompareFaceResponse{}, err
	}

	return NewCompareFaceResponseFromAWSOutput(op), nil
}

// getImageFromURL gets image data from url.
func (svc *Rekognition) getImageFromURL(url string) ([]byte, error) {
	cli := svc.httpClient
	if cli == nil {
		err := errors.New("error on `getImageFromURL`; error=`svc.httpClient is nil`;")
		svc.Errorf(err.Error())
		return nil, err
	}

	resp, err := cli.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, resp.Body)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// createS3Object creates *SDK.S3Object from bucket and name.
func createS3Object(bucket, name string, version ...string) *SDK.S3Object {
	s3object := &SDK.S3Object{
		Bucket: pointers.String(bucket),
		Name:   pointers.String(name),
	}
	if len(version) == 1 {
		s3object.Version = pointers.String(version[0])
	}
	return s3object
}
<|endoftext|>


<commit_before>package rbac

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"path"
	"strconv"
	"sync"
	"time"

	"gopkg.in/macaroon-bakery.v2/bakery"
	"gopkg.in/macaroon-bakery.v2/httpbakery"
	"gopkg.in/macaroon-bakery.v2/httpbakery/agent"

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

type rbacResource struct {
	Identifier string `json:"identifier"`
	Name       string `json:"name"`
}

type rbacResourcePost struct {
	LastSyncID *string        `json:"last-sync-id"`
	Updates    []rbacResource `json:"updates,omitempty"`
	Removals   []string       `json:"removals,omitempty"`
}

type rbacResourcePostResponse struct {
	SyncID string `json:"sync-id"`
}

type rbacStatus struct {
	LastChange string `json:"last-change"`
}

// Errors
var errUnknownUser = fmt.Errorf("Unknown RBAC user")

// UserAccess struct for permission checks.
type UserAccess struct {
	Admin    bool
	Projects map[string][]string
}

// Server represents an RBAC server.
type Server struct {
	apiURL string
	apiKey string

	lastSyncID string
	client     *httpbakery.Client
	lastChange string

	ctx       context.Context
	ctxCancel context.CancelFunc

	resources     map[string]string // Maps name to identifier
	resourcesLock sync.Mutex

	permissions map[string]map[string][]string

	permissionsLock *sync.Mutex

	ProjectsFunc func() (map[int64]string, error)
}

// NewServer returns a new RBAC server instance.
func NewServer(apiURL string, apiKey string, agentAuthURL string, agentUsername string, agentPrivateKey string, agentPublicKey string) (*Server, error) {
	r := Server{
		apiURL:          apiURL,
		apiKey:          apiKey,
		lastSyncID:      "",
		lastChange:      "",
		resources:       make(map[string]string),
		permissions:     make(map[string]map[string][]string),
		permissionsLock: &sync.Mutex{},
	}

	// Setup context
	r.ctx, r.ctxCancel = context.WithCancel(context.Background())

	var keyPair bakery.KeyPair
	keyPair.Private.UnmarshalText([]byte(agentPrivateKey))
	keyPair.Public.UnmarshalText([]byte(agentPublicKey))

	r.client = httpbakery.NewClient()
	authInfo := agent.AuthInfo{
		Key: &keyPair,
		Agents: []agent.Agent{
			{
				URL:      agentAuthURL,
				Username: agentUsername,
			},
		},
	}

	err := agent.SetUpAuth(r.client, &authInfo)
	if err != nil {
		return nil, err
	}

	r.client.Client.Jar, err = cookiejar.New(nil)
	if err != nil {
		return nil, err
	}

	return &r, nil
}

// StartStatusCheck runs a status checking loop.
func (r *Server) StartStatusCheck() {
	var status rbacStatus

	// Figure out the new URL.
	u, err := url.Parse(r.apiURL)
	if err != nil {
		logger.Errorf("Failed to parse RBAC url: %v", err)
		return
	}
	u.Path = path.Join(u.Path, "/api/service/v1/changes")

	go func() {
		for {
			if status.LastChange != "" {
				values := url.Values{}
				values.Set("last-change", status.LastChange)
				u.RawQuery = values.Encode()
			}

			req, err := http.NewRequestWithContext(r.ctx, "GET", u.String(), nil)
			if err != nil {
				if err == context.Canceled {
					return
				}

				logger.Errorf("Failed to prepare RBAC query: %v", err)
				return
			}

			resp, err := r.client.Do(req)
			if err != nil {
				if err == context.Canceled {
					return
				}

				logger.Errorf("Failed to hit new RBAC URL, falling back: %v", err)
				r.oldStatusCheck()
				return
			}

			if resp.StatusCode == 404 {
				resp.Body.Close()
				logger.Debugf("RBAC server doesn't support new monitoring API, falling back.")
				r.oldStatusCheck()
				return
			}

			if resp.StatusCode == 504 {
				// 504 indicates the server timed out the background connection, just re-connect.
				resp.Body.Close()
				continue
			}

			if resp.StatusCode != 200 {
				// For other errors we assume a server restart and give it a few seconds.
				resp.Body.Close()
				logger.Debugf("RBAC server disconnected, re-connecting. (code=%v)", resp.StatusCode)
				time.Sleep(10)
				continue
			}

			err = json.NewDecoder(resp.Body).Decode(&status)
			resp.Body.Close()
			if err != nil {
				logger.Errorf("Failed to parse RBAC response, re-trying: %v", err)
				time.Sleep(10)
				continue
			}

			r.lastChange = status.LastChange
			logger.Debugf("RBAC change detected, flushing cache")
			r.flushCache()
		}
	}()
}

func (r *Server) oldStatusCheck() {
	// NOTE: Can be dropped once new RBAC hits stable.
	r.hasStatusChanged()

	go func() {
		for {
			select {
			case <-r.ctx.Done():
				return
			case <-time.After(time.Minute):
				if r.hasStatusChanged() {
					logger.Debugf("RBAC change detected, flushing cache")
					r.flushCache()
				}
			}
		}
	}()
}

// StopStatusCheck stops the periodic status checker.
func (r *Server) StopStatusCheck() {
	r.ctxCancel()
}

// SyncProjects updates the list of projects in RBAC
func (r *Server) SyncProjects() error {
	if r.ProjectsFunc == nil {
		return fmt.Errorf("ProjectsFunc isn't configured yet, cannot sync")
	}

	resources := []rbacResource{}
	resourcesMap := map[string]string{}

	// Get all projects
	projects, err := r.ProjectsFunc()
	if err != nil {
		return err
	}

	// Convert to RBAC format
	for id, name := range projects {
		resources = append(resources, rbacResource{
			Name:       name,
			Identifier: strconv.FormatInt(id, 10),
		})

		resourcesMap[name] = strconv.FormatInt(id, 10)
	}

	// Update RBAC
	err = r.postResources(resources, nil, true)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	r.resources = resourcesMap
	r.resourcesLock.Unlock()

	return nil
}

// AddProject adds a new project resource to RBAC.
func (r *Server) AddProject(id int64, name string) error {
	resource := rbacResource{
		Name:       name,
		Identifier: strconv.FormatInt(id, 10),
	}

	// Update RBAC
	err := r.postResources([]rbacResource{resource}, nil, false)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	r.resources[name] = strconv.FormatInt(id, 10)
	r.resourcesLock.Unlock()

	return nil
}

// DeleteProject adds a new project resource to RBAC.
func (r *Server) DeleteProject(id int64) error {
	// Update RBAC
	err := r.postResources(nil, []string{strconv.FormatInt(id, 10)}, false)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	for k, v := range r.resources {
		if v == strconv.FormatInt(id, 10) {
			delete(r.resources, k)
			break
		}
	}
	r.resourcesLock.Unlock()

	return nil
}

// RenameProject renames an existing project resource in RBAC.
func (r *Server) RenameProject(id int64, name string) error {
	return r.AddProject(id, name)
}

// UserAccess returns a UserAccess struct for the user.
func (r *Server) UserAccess(username string) (*UserAccess, error) {
	r.permissionsLock.Lock()
	defer r.permissionsLock.Unlock()

	// Check whether the permissions are cached.
	_, cached := r.permissions[username]

	if !cached {
		r.syncPermissions(username)
	}

	// Checked if the user exists.
	permissions, ok := r.permissions[username]
	if !ok {
		return nil, errUnknownUser
	}

	// Prepare the response.
	access := UserAccess{
		Admin:    shared.StringInSlice("admin", permissions[""]),
		Projects: map[string][]string{},
	}

	for k, v := range permissions {
		// Skip the global permissions.
		if k == "" {
			continue
		}

		access.Projects[k] = v
	}

	return &access, nil
}

func (r *Server) hasStatusChanged() bool {
	var status rbacStatus

	u, err := url.Parse(r.apiURL)
	if err != nil {
		return true
	}

	u.Path = path.Join(u.Path, "/api/service/v1/status")

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return true
	}

	resp, err := r.client.Do(req)
	if err != nil {
		return true
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&status)
	if err != nil {
		return true
	}

	if r.lastChange == "" {
		r.lastChange = status.LastChange
		return true
	}

	hasChanged := r.lastChange != status.LastChange
	r.lastChange = status.LastChange

	return hasChanged
}

func (r *Server) flushCache() {
	r.permissionsLock.Lock()
	defer r.permissionsLock.Unlock()

	logger.Info("Flushing RBAC permissions cache")

	for k, v := range r.permissions {
		for k := range v {
			delete(v, k)
		}

		delete(r.permissions, k)
	}

	logger.Info("Flushed RBAC permissions cache")
}

func (r *Server) syncAdmin(username string) bool {
	u, err := url.Parse(r.apiURL)
	if err != nil {
		return false
	}

	values := url.Values{}
	values.Set("u", username)
	u.RawQuery = values.Encode()
	u.Path = path.Join(u.Path, "/api/service/v1/resources/lxd/permissions-for-user")

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return false
	}

	resp, err := r.client.Do(req)
	if err != nil {
		return false
	}
	defer resp.Body.Close()

	var permissions map[string][]string

	err = json.NewDecoder(resp.Body).Decode(&permissions)
	if err != nil {
		return false
	}

	return shared.StringInSlice("admin", permissions[""])
}

func (r *Server) syncPermissions(username string) error {
	u, err := url.Parse(r.apiURL)
	if err != nil {
		return err
	}

	values := url.Values{}
	values.Set("u", username)
	u.RawQuery = values.Encode()
	u.Path = path.Join(u.Path, "/api/service/v1/resources/project/permissions-for-user")

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	resp, err := r.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var permissions map[string][]string

	err = json.NewDecoder(resp.Body).Decode(&permissions)
	if err != nil {
		return err
	}

	if r.syncAdmin(username) {
		permissions[""] = []string{"admin"}
	}

	// No need to acquire the lock since the caller (HasPermission) already has it.
	r.permissions[username] = permissions

	return nil
}

func (r *Server) postResources(updates []rbacResource, removals []string, force bool) error {
	// Make sure that we have a baseline sync in place
	if !force && r.lastSyncID == "" {
		return r.SyncProjects()
	}

	// Generate the URL
	u, err := url.Parse(r.apiURL)
	if err != nil {
		return err
	}

	u.Path = path.Join(u.Path, "/api/service/v1/resources/project")

	// Prepare the request body
	resourcePost := rbacResourcePost{
		Updates:  updates,
		Removals: removals,
	}

	if force {
		resourcePost.LastSyncID = nil
	} else {
		resourcePost.LastSyncID = &r.lastSyncID
	}

	body, err := json.Marshal(&resourcePost)
	if err != nil {
		return err
	}

	// Perform the request
	req, err := http.NewRequest("POST", u.String(), bytes.NewReader(body))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := r.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Handle errors
	if resp.StatusCode == 409 {
		// Sync IDs don't match, force sync
		return r.SyncProjects()
	} else if resp.StatusCode != http.StatusOK {
		// Something went wrong
		return errors.New(resp.Status)
	}

	// Extract the new SyncID
	var postRespose rbacResourcePostResponse
	err = json.NewDecoder(resp.Body).Decode(&postRespose)
	if err != nil {
		return err
	}

	r.lastSyncID = postRespose.SyncID

	return nil
}
<commit_msg>lxd/rbac: Fix checks by matching proper name<commit_after>package rbac

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"path"
	"strconv"
	"sync"
	"time"

	"gopkg.in/macaroon-bakery.v2/bakery"
	"gopkg.in/macaroon-bakery.v2/httpbakery"
	"gopkg.in/macaroon-bakery.v2/httpbakery/agent"

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

type rbacResource struct {
	Identifier string `json:"identifier"`
	Name       string `json:"name"`
}

type rbacResourcePost struct {
	LastSyncID *string        `json:"last-sync-id"`
	Updates    []rbacResource `json:"updates,omitempty"`
	Removals   []string       `json:"removals,omitempty"`
}

type rbacResourcePostResponse struct {
	SyncID string `json:"sync-id"`
}

type rbacStatus struct {
	LastChange string `json:"last-change"`
}

// Errors
var errUnknownUser = fmt.Errorf("Unknown RBAC user")

// UserAccess struct for permission checks.
type UserAccess struct {
	Admin    bool
	Projects map[string][]string
}

// Server represents an RBAC server.
type Server struct {
	apiURL string
	apiKey string

	lastSyncID string
	client     *httpbakery.Client
	lastChange string

	ctx       context.Context
	ctxCancel context.CancelFunc

	resources     map[string]string // Maps name to identifier
	resourcesLock sync.Mutex

	permissions map[string]map[string][]string

	permissionsLock *sync.Mutex

	ProjectsFunc func() (map[int64]string, error)
}

// NewServer returns a new RBAC server instance.
func NewServer(apiURL string, apiKey string, agentAuthURL string, agentUsername string, agentPrivateKey string, agentPublicKey string) (*Server, error) {
	r := Server{
		apiURL:          apiURL,
		apiKey:          apiKey,
		lastSyncID:      "",
		lastChange:      "",
		resources:       make(map[string]string),
		permissions:     make(map[string]map[string][]string),
		permissionsLock: &sync.Mutex{},
	}

	// Setup context
	r.ctx, r.ctxCancel = context.WithCancel(context.Background())

	var keyPair bakery.KeyPair
	keyPair.Private.UnmarshalText([]byte(agentPrivateKey))
	keyPair.Public.UnmarshalText([]byte(agentPublicKey))

	r.client = httpbakery.NewClient()
	authInfo := agent.AuthInfo{
		Key: &keyPair,
		Agents: []agent.Agent{
			{
				URL:      agentAuthURL,
				Username: agentUsername,
			},
		},
	}

	err := agent.SetUpAuth(r.client, &authInfo)
	if err != nil {
		return nil, err
	}

	r.client.Client.Jar, err = cookiejar.New(nil)
	if err != nil {
		return nil, err
	}

	return &r, nil
}

// StartStatusCheck runs a status checking loop.
func (r *Server) StartStatusCheck() {
	var status rbacStatus

	// Figure out the new URL.
	u, err := url.Parse(r.apiURL)
	if err != nil {
		logger.Errorf("Failed to parse RBAC url: %v", err)
		return
	}
	u.Path = path.Join(u.Path, "/api/service/v1/changes")

	go func() {
		for {
			if status.LastChange != "" {
				values := url.Values{}
				values.Set("last-change", status.LastChange)
				u.RawQuery = values.Encode()
			}

			req, err := http.NewRequestWithContext(r.ctx, "GET", u.String(), nil)
			if err != nil {
				if err == context.Canceled {
					return
				}

				logger.Errorf("Failed to prepare RBAC query: %v", err)
				return
			}

			resp, err := r.client.Do(req)
			if err != nil {
				if err == context.Canceled {
					return
				}

				logger.Errorf("Failed to hit new RBAC URL, falling back: %v", err)
				r.oldStatusCheck()
				return
			}

			if resp.StatusCode == 404 {
				resp.Body.Close()
				logger.Debugf("RBAC server doesn't support new monitoring API, falling back.")
				r.oldStatusCheck()
				return
			}

			if resp.StatusCode == 504 {
				// 504 indicates the server timed out the background connection, just re-connect.
				resp.Body.Close()
				continue
			}

			if resp.StatusCode != 200 {
				// For other errors we assume a server restart and give it a few seconds.
				resp.Body.Close()
				logger.Debugf("RBAC server disconnected, re-connecting. (code=%v)", resp.StatusCode)
				time.Sleep(10)
				continue
			}

			err = json.NewDecoder(resp.Body).Decode(&status)
			resp.Body.Close()
			if err != nil {
				logger.Errorf("Failed to parse RBAC response, re-trying: %v", err)
				time.Sleep(10)
				continue
			}

			r.lastChange = status.LastChange
			logger.Debugf("RBAC change detected, flushing cache")
			r.flushCache()
		}
	}()
}

func (r *Server) oldStatusCheck() {
	// NOTE: Can be dropped once new RBAC hits stable.
	r.hasStatusChanged()

	go func() {
		for {
			select {
			case <-r.ctx.Done():
				return
			case <-time.After(time.Minute):
				if r.hasStatusChanged() {
					logger.Debugf("RBAC change detected, flushing cache")
					r.flushCache()
				}
			}
		}
	}()
}

// StopStatusCheck stops the periodic status checker.
func (r *Server) StopStatusCheck() {
	r.ctxCancel()
}

// SyncProjects updates the list of projects in RBAC
func (r *Server) SyncProjects() error {
	if r.ProjectsFunc == nil {
		return fmt.Errorf("ProjectsFunc isn't configured yet, cannot sync")
	}

	resources := []rbacResource{}
	resourcesMap := map[string]string{}

	// Get all projects
	projects, err := r.ProjectsFunc()
	if err != nil {
		return err
	}

	// Convert to RBAC format
	for id, name := range projects {
		resources = append(resources, rbacResource{
			Name:       name,
			Identifier: strconv.FormatInt(id, 10),
		})

		resourcesMap[name] = strconv.FormatInt(id, 10)
	}

	// Update RBAC
	err = r.postResources(resources, nil, true)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	r.resources = resourcesMap
	r.resourcesLock.Unlock()

	return nil
}

// AddProject adds a new project resource to RBAC.
func (r *Server) AddProject(id int64, name string) error {
	resource := rbacResource{
		Name:       name,
		Identifier: strconv.FormatInt(id, 10),
	}

	// Update RBAC
	err := r.postResources([]rbacResource{resource}, nil, false)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	r.resources[name] = strconv.FormatInt(id, 10)
	r.resourcesLock.Unlock()

	return nil
}

// DeleteProject adds a new project resource to RBAC.
func (r *Server) DeleteProject(id int64) error {
	// Update RBAC
	err := r.postResources(nil, []string{strconv.FormatInt(id, 10)}, false)
	if err != nil {
		return err
	}

	// Update project map
	r.resourcesLock.Lock()
	for k, v := range r.resources {
		if v == strconv.FormatInt(id, 10) {
			delete(r.resources, k)
			break
		}
	}
	r.resourcesLock.Unlock()

	return nil
}

// RenameProject renames an existing project resource in RBAC.
func (r *Server) RenameProject(id int64, name string) error {
	return r.AddProject(id, name)
}

// UserAccess returns a UserAccess struct for the user.
func (r *Server) UserAccess(username string) (*UserAccess, error) {
	r.permissionsLock.Lock()
	defer r.permissionsLock.Unlock()

	// Check whether the permissions are cached.
	_, cached := r.permissions[username]

	if !cached {
		r.syncPermissions(username)
	}

	// Checked if the user exists.
	permissions, ok := r.permissions[username]
	if !ok {
		return nil, errUnknownUser
	}

	// Prepare the response.
	access := UserAccess{
		Admin:    shared.StringInSlice("admin", permissions[""]),
		Projects: map[string][]string{},
	}

	for k, v := range permissions {
		// Skip the global permissions.
		if k == "" {
			continue
		}

		// Look for project name.
		for projectName, resourceID := range r.resources {
			if k != resourceID {
				continue
			}

			access.Projects[projectName] = v
			break
		}

		// Ignore unknown projects.
	}

	return &access, nil
}

func (r *Server) hasStatusChanged() bool {
	var status rbacStatus

	u, err := url.Parse(r.apiURL)
	if err != nil {
		return true
	}

	u.Path = path.Join(u.Path, "/api/service/v1/status")

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return true
	}

	resp, err := r.client.Do(req)
	if err != nil {
		return true
	}
	defer resp.Body.Close()

	err = json.NewDecoder(resp.Body).Decode(&status)
	if err != nil {
		return true
	}

	if r.lastChange == "" {
		r.lastChange = status.LastChange
		return true
	}

	hasChanged := r.lastChange != status.LastChange
	r.lastChange = status.LastChange

	return hasChanged
}

func (r *Server) flushCache() {
	r.permissionsLock.Lock()
	defer r.permissionsLock.Unlock()

	logger.Info("Flushing RBAC permissions cache")

	for k, v := range r.permissions {
		for k := range v {
			delete(v, k)
		}

		delete(r.permissions, k)
	}

	logger.Info("Flushed RBAC permissions cache")
}

func (r *Server) syncAdmin(username string) bool {
	u, err := url.Parse(r.apiURL)
	if err != nil {
		return false
	}

	values := url.Values{}
	values.Set("u", username)
	u.RawQuery = values.Encode()
	u.Path = path.Join(u.Path, "/api/service/v1/resources/lxd/permissions-for-user")

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return false
	}

	resp, err := r.client.Do(req)
	if err != nil {
		return false
	}
	defer resp.Body.Close()

	var permissions map[string][]string

	err = json.NewDecoder(resp.Body).Decode(&permissions)
	if err != nil {
		return false
	}

	return shared.StringInSlice("admin", permissions[""])
}

func (r *Server) syncPermissions(username string) error {
	u, err := url.Parse(r.apiURL)
	if err != nil {
		return err
	}

	values := url.Values{}
	values.Set("u", username)
	u.RawQuery = values.Encode()
	u.Path = path.Join(u.Path, "/api/service/v1/resources/project/permissions-for-user")

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	resp, err := r.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var permissions map[string][]string

	err = json.NewDecoder(resp.Body).Decode(&permissions)
	if err != nil {
		return err
	}

	if r.syncAdmin(username) {
		permissions[""] = []string{"admin"}
	}

	// No need to acquire the lock since the caller (HasPermission) already has it.
	r.permissions[username] = permissions

	return nil
}

func (r *Server) postResources(updates []rbacResource, removals []string, force bool) error {
	// Make sure that we have a baseline sync in place
	if !force && r.lastSyncID == "" {
		return r.SyncProjects()
	}

	// Generate the URL
	u, err := url.Parse(r.apiURL)
	if err != nil {
		return err
	}

	u.Path = path.Join(u.Path, "/api/service/v1/resources/project")

	// Prepare the request body
	resourcePost := rbacResourcePost{
		Updates:  updates,
		Removals: removals,
	}

	if force {
		resourcePost.LastSyncID = nil
	} else {
		resourcePost.LastSyncID = &r.lastSyncID
	}

	body, err := json.Marshal(&resourcePost)
	if err != nil {
		return err
	}

	// Perform the request
	req, err := http.NewRequest("POST", u.String(), bytes.NewReader(body))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := r.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Handle errors
	if resp.StatusCode == 409 {
		// Sync IDs don't match, force sync
		return r.SyncProjects()
	} else if resp.StatusCode != http.StatusOK {
		// Something went wrong
		return errors.New(resp.Status)
	}

	// Extract the new SyncID
	var postRespose rbacResourcePostResponse
	err = json.NewDecoder(resp.Body).Decode(&postRespose)
	if err != nil {
		return err
	}

	r.lastSyncID = postRespose.SyncID

	return nil
}
<|endoftext|>


<commit_before>package debugserver_test

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"

	cf_debug_server "code.cloudfoundry.org/debugserver"
	"code.cloudfoundry.org/lager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("CF Debug Server", func() {
	var (
		logBuf *gbytes.Buffer
		sink   *lager.ReconfigurableSink

		process ifrit.Process
	)

	BeforeEach(func() {
		logBuf = gbytes.NewBuffer()
		sink = lager.NewReconfigurableSink(
			lager.NewWriterSink(logBuf, lager.DEBUG),
			// permit no logging by default, for log reconfiguration below
			lager.FATAL+1,
		)
	})

	AfterEach(func() {
		ginkgomon.Interrupt(process)
	})

	Describe("AddFlags", func() {
		It("adds flags to the flagset", func() {
			flags := flag.NewFlagSet("test", flag.ContinueOnError)
			cf_debug_server.AddFlags(flags)

			f := flags.Lookup(cf_debug_server.DebugFlag)
			Expect(f).NotTo(BeNil())
		})
	})

	Describe("DebugAddress", func() {
		Context("when flags are not added", func() {
			It("returns the empty string", func() {
				flags := flag.NewFlagSet("test", flag.ContinueOnError)
				Expect(cf_debug_server.DebugAddress(flags)).To(Equal(""))
			})
		})

		Context("when flags are added", func() {
			var flags *flag.FlagSet
			BeforeEach(func() {
				flags = flag.NewFlagSet("test", flag.ContinueOnError)
				cf_debug_server.AddFlags(flags)
			})

			Context("when set", func() {
				It("returns the address", func() {
					flags.Parse([]string{"-debugAddr", address})

					Expect(cf_debug_server.DebugAddress(flags)).To(Equal(address))
				})
			})

			Context("when not set", func() {
				It("returns the empty string", func() {
					Expect(cf_debug_server.DebugAddress(flags)).To(Equal(""))
				})
			})
		})
	})

	Describe("Run", func() {
		It("serves debug information", func() {
			var err error
			process, err = cf_debug_server.Run(address, sink)
			Expect(err).NotTo(HaveOccurred())

			debugResponse, err := http.Get(fmt.Sprintf("http://%s/debug/pprof/goroutine", address))
			Expect(err).NotTo(HaveOccurred())

			debugInfo, err := ioutil.ReadAll(debugResponse.Body)
			Expect(err).NotTo(HaveOccurred())

			Expect(debugInfo).To(ContainSubstring("goroutine profile: total"))

		})

		Context("when the address is already in use", func() {
			var listener net.Listener

			BeforeEach(func() {
				var err error
				listener, err = net.Listen("tcp", address)
				Expect(err).NotTo(HaveOccurred())
			})

			AfterEach(func() {
				listener.Close()
			})

			It("returns an error", func() {
				var err error
				process, err = cf_debug_server.Run(address, sink)
				Expect(err).To(HaveOccurred())
				Expect(err).To(BeAssignableToTypeOf(&net.OpError{}))
				netErr := err.(*net.OpError)
				Expect(netErr.Op).To(Equal("listen"))
			})
		})

		Context("checking log-level endpoint", func() {
			validForms := map[lager.LogLevel][]string{
				lager.DEBUG: []string{"debug", "DEBUG", "d", strconv.Itoa(int(lager.DEBUG))},
				lager.INFO:  []string{"info", "INFO", "i", strconv.Itoa(int(lager.INFO))},
				lager.ERROR: []string{"error", "ERROR", "e", strconv.Itoa(int(lager.ERROR))},
				lager.FATAL: []string{"fatal", "FATAL", "f", strconv.Itoa(int(lager.FATAL))},
			}

			//This will add another 16 unit tests to the suit
			for level, acceptedForms := range validForms {
				for _, form := range acceptedForms {
					testLevel := level
					testForm := form

					It("can reconfigure the given sink with "+form, func() {
						var err error
						process, err = cf_debug_server.Run(address, sink)
						Expect(err).NotTo(HaveOccurred())

						sink.Log(lager.LogFormat{LogLevel: testLevel, Message: "hello before level change"})
						Eventually(logBuf).ShouldNot(gbytes.Say("hello before level change"))

						request, err := http.NewRequest("PUT", fmt.Sprintf("http://%s/log-level", address), bytes.NewBufferString(testForm))

						Expect(err).NotTo(HaveOccurred())

						response, err := http.DefaultClient.Do(request)
						Expect(err).NotTo(HaveOccurred())

						Expect(response.StatusCode).To(Equal(http.StatusOK))
						response.Body.Close()

						sink.Log(lager.LogFormat{LogLevel: testLevel, Message: "Logs sent with log-level " + testForm})
						Eventually(logBuf).Should(gbytes.Say("Logs sent with log-level " + testForm))
					})
				}
			}
		})

	})
})
<commit_msg>relax debug server assertion<commit_after>package debugserver_test

import (
	"bytes"
	"flag"
	"fmt"
	"net"
	"net/http"
	"strconv"

	cf_debug_server "code.cloudfoundry.org/debugserver"
	"code.cloudfoundry.org/lager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("CF Debug Server", func() {
	var (
		logBuf *gbytes.Buffer
		sink   *lager.ReconfigurableSink

		process ifrit.Process
	)

	BeforeEach(func() {
		logBuf = gbytes.NewBuffer()
		sink = lager.NewReconfigurableSink(
			lager.NewWriterSink(logBuf, lager.DEBUG),
			// permit no logging by default, for log reconfiguration below
			lager.FATAL+1,
		)
	})

	AfterEach(func() {
		ginkgomon.Interrupt(process)
	})

	Describe("AddFlags", func() {
		It("adds flags to the flagset", func() {
			flags := flag.NewFlagSet("test", flag.ContinueOnError)
			cf_debug_server.AddFlags(flags)

			f := flags.Lookup(cf_debug_server.DebugFlag)
			Expect(f).NotTo(BeNil())
		})
	})

	Describe("DebugAddress", func() {
		Context("when flags are not added", func() {
			It("returns the empty string", func() {
				flags := flag.NewFlagSet("test", flag.ContinueOnError)
				Expect(cf_debug_server.DebugAddress(flags)).To(Equal(""))
			})
		})

		Context("when flags are added", func() {
			var flags *flag.FlagSet
			BeforeEach(func() {
				flags = flag.NewFlagSet("test", flag.ContinueOnError)
				cf_debug_server.AddFlags(flags)
			})

			Context("when set", func() {
				It("returns the address", func() {
					flags.Parse([]string{"-debugAddr", address})

					Expect(cf_debug_server.DebugAddress(flags)).To(Equal(address))
				})
			})

			Context("when not set", func() {
				It("returns the empty string", func() {
					Expect(cf_debug_server.DebugAddress(flags)).To(Equal(""))
				})
			})
		})
	})

	Describe("Run", func() {
		It("serves debug information", func() {
			var err error
			process, err = cf_debug_server.Run(address, sink)
			Expect(err).NotTo(HaveOccurred())

			debugResponse, err := http.Get(fmt.Sprintf("http://%s/debug/pprof/goroutine", address))
			Expect(err).NotTo(HaveOccurred())
			defer debugResponse.Body.Close()
		})

		Context("when the address is already in use", func() {
			var listener net.Listener

			BeforeEach(func() {
				var err error
				listener, err = net.Listen("tcp", address)
				Expect(err).NotTo(HaveOccurred())
			})

			AfterEach(func() {
				listener.Close()
			})

			It("returns an error", func() {
				var err error
				process, err = cf_debug_server.Run(address, sink)
				Expect(err).To(HaveOccurred())
				Expect(err).To(BeAssignableToTypeOf(&net.OpError{}))
				netErr := err.(*net.OpError)
				Expect(netErr.Op).To(Equal("listen"))
			})
		})

		Context("checking log-level endpoint", func() {
			validForms := map[lager.LogLevel][]string{
				lager.DEBUG: []string{"debug", "DEBUG", "d", strconv.Itoa(int(lager.DEBUG))},
				lager.INFO:  []string{"info", "INFO", "i", strconv.Itoa(int(lager.INFO))},
				lager.ERROR: []string{"error", "ERROR", "e", strconv.Itoa(int(lager.ERROR))},
				lager.FATAL: []string{"fatal", "FATAL", "f", strconv.Itoa(int(lager.FATAL))},
			}

			//This will add another 16 unit tests to the suit
			for level, acceptedForms := range validForms {
				for _, form := range acceptedForms {
					testLevel := level
					testForm := form

					It("can reconfigure the given sink with "+form, func() {
						var err error
						process, err = cf_debug_server.Run(address, sink)
						Expect(err).NotTo(HaveOccurred())

						sink.Log(lager.LogFormat{LogLevel: testLevel, Message: "hello before level change"})
						Eventually(logBuf).ShouldNot(gbytes.Say("hello before level change"))

						request, err := http.NewRequest("PUT", fmt.Sprintf("http://%s/log-level", address), bytes.NewBufferString(testForm))

						Expect(err).NotTo(HaveOccurred())

						response, err := http.DefaultClient.Do(request)
						Expect(err).NotTo(HaveOccurred())

						Expect(response.StatusCode).To(Equal(http.StatusOK))
						response.Body.Close()

						sink.Log(lager.LogFormat{LogLevel: testLevel, Message: "Logs sent with log-level " + testForm})
						Eventually(logBuf).Should(gbytes.Say("Logs sent with log-level " + testForm))
					})
				}
			}
		})

	})
})
<|endoftext|>


<commit_before>/*******************************************************************************
* Copyright (C) Zenoss, Inc. 2013, all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
*******************************************************************************/

package client

import (
	//"github.com/zenoss/glog"
	"github.com/zenoss/serviced/dao"
	"net/rpc"
)

// A serviced client.
type ControlClient struct {
	addr      string
	rpcClient *rpc.Client
}

// Ensure that ControlClient implements the ControlPlane interface.
var _ dao.ControlPlane = &ControlClient{}

// Create a new ControlClient.
func NewControlClient(addr string) (s *ControlClient, err error) {
	s = new(ControlClient)
	s.addr = addr
	glog.V(4).Infof("Connecting to %s", addr)
	rpcClient, err := rpc.DialHTTP("tcp", s.addr)
	s.rpcClient = rpcClient
	return s, err
}

// Return the matching hosts.
func (s *ControlClient) Close() (err error) {
	return s.rpcClient.Close()
}

func (s *ControlClient) GetServiceEndpoints(serviceId string, response *map[string][]*dao.ApplicationEndpoint) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServiceEndpoints", serviceId, response)
}

// Return the matching hosts.
func (s *ControlClient) GetHosts(request dao.EntityRequest, replyHosts *map[string]*dao.Host) (err error) {
	return s.rpcClient.Call("ControlPlane.GetHosts", request, replyHosts)
}

func (s *ControlClient) AddHost(host dao.Host, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddHost", host, unused)
}

func (s *ControlClient) UpdateHost(host dao.Host, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateHost", host, unused)
}

func (s *ControlClient) RemoveHost(hostId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveHost", hostId, unused)
}

func (s *ControlClient) GetServices(request dao.EntityRequest, replyServices *[]*dao.Service) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServices", request, replyServices)
}

func (s *ControlClient) AddService(service dao.Service, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddService", service, unused)
}

func (s *ControlClient) UpdateService(service dao.Service, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateService", service, unused)
}

func (s *ControlClient) RemoveService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveService", serviceId, unused)
}

func (s *ControlClient) GetServicesForHost(hostId string, servicesForHost *[]*dao.Service) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServicesForHost", hostId, servicesForHost)
}

func (s *ControlClient) GetServiceLogs(serviceId string, logs *string) error {
	return s.rpcClient.Call("ControlPlane.GetServiceLogs", serviceId, logs)
}

func (s *ControlClient) GetServiceStateLogs(serviceStateId string, logs *string) error {
	return s.rpcClient.Call("ControlPlane.GetServiceStateLogs", serviceStateId, logs)
}

func (s *ControlClient) GetRunningServicesForHost(hostId string, runningServices *[]*dao.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServicesForHost", hostId, runningServices)
}

func (s *ControlClient) GetRunningServicesForService(serviceId string, runningServices *[]*dao.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServicesForService", serviceId, runningServices)
}

func (s *ControlClient) StopRunningInstance(serviceStateId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.StopRunningInstance", serviceStateId, unused)
}

func (s *ControlClient) GetRunningServices(request dao.EntityRequest, runningServices *[]*dao.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServices", request, runningServices)
}

func (s *ControlClient) GetServiceStates(serviceId string, states *[]*dao.ServiceState) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServiceStates", serviceId, states)
}

func (s *ControlClient) StartService(serviceId string, hostId *string) (err error) {
	return s.rpcClient.Call("ControlPlane.StartService", serviceId, hostId)
}

func (s *ControlClient) RestartService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RestartService", serviceId, unused)
}

func (s *ControlClient) StopService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.StopService", serviceId, unused)
}

func (s *ControlClient) UpdateServiceState(state dao.ServiceState, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateServiceState", state, unused)
}

func (s *ControlClient) GetResourcePools(request dao.EntityRequest, pools *map[string]*dao.ResourcePool) (err error) {
	return s.rpcClient.Call("ControlPlane.GetResourcePools", request, pools)
}

func (s *ControlClient) AddResourcePool(pool dao.ResourcePool, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddResourcePool", pool, unused)
}

func (s *ControlClient) UpdateResourcePool(pool dao.ResourcePool, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateResourcePool", pool, unused)
}

func (s *ControlClient) RemoveResourcePool(poolId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveResourcePool", poolId, unused)
}

func (s *ControlClient) GetHostsForResourcePool(poolId string, poolHosts *[]*dao.PoolHost) (err error) {
	return s.rpcClient.Call("ControlPlane.GetHostsForResourcePool", poolId, poolHosts)
}

func (s *ControlClient) AddHostToResourcePool(poolHost dao.PoolHost, unused *int) error {
	return s.rpcClient.Call("ControlPlane.AddHostToResourcePool", poolHost, unused)
}

func (s *ControlClient) RemoveHostFromResourcePool(poolHost dao.PoolHost, unused *int) error {
	return s.rpcClient.Call("ControlPlane.RemoveHostFromResourcePool", poolHost, unused)
}

func (s *ControlClient) DeployTemplate(request dao.ServiceTemplateDeploymentRequest, unused *int) error {
	return s.rpcClient.Call("ControlPlane.DeployTemplate", request, unused)
}

func (s *ControlClient) GetServiceTemplates(unused int, serviceTemplates *map[string]*dao.ServiceTemplate) error {
	return s.rpcClient.Call("ControlPlane.GetServiceTemplates", unused, serviceTemplates)
}

func (s *ControlClient) AddServiceTemplate(serviceTemplate dao.ServiceTemplate, unused *int) error {
	return s.rpcClient.Call("ControlPlane.AddServiceTemplate", serviceTemplate, unused)
}

func (s *ControlClient) UpdateServiceTemplate(serviceTemplate dao.ServiceTemplate, unused *int) error {
	return s.rpcClient.Call("ControlPlane.UpdateServiceTemplate", serviceTemplate, unused)
}

func (s *ControlClient) RemoveServiceTemplate(serviceTemplateId string, unused *int) error {
	return s.rpcClient.Call("ControlPlane.RemoveServiceTemplate", serviceTemplateId, unused)
}
<commit_msg>hotfix for merge issue<commit_after>/*******************************************************************************
* Copyright (C) Zenoss, Inc. 2013, all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
*******************************************************************************/

package client

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/dao"
	"net/rpc"
)

// A serviced client.
type ControlClient struct {
	addr      string
	rpcClient *rpc.Client
}

// Ensure that ControlClient implements the ControlPlane interface.
var _ dao.ControlPlane = &ControlClient{}

// Create a new ControlClient.
func NewControlClient(addr string) (s *ControlClient, err error) {
	s = new(ControlClient)
	s.addr = addr
	glog.V(4).Infof("Connecting to %s", addr)
	rpcClient, err := rpc.DialHTTP("tcp", s.addr)
	s.rpcClient = rpcClient
	return s, err
}

// Return the matching hosts.
func (s *ControlClient) Close() (err error) {
	return s.rpcClient.Close()
}

func (s *ControlClient) GetServiceEndpoints(serviceId string, response *map[string][]*dao.ApplicationEndpoint) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServiceEndpoints", serviceId, response)
}

// Return the matching hosts.
func (s *ControlClient) GetHosts(request dao.EntityRequest, replyHosts *map[string]*dao.Host) (err error) {
	return s.rpcClient.Call("ControlPlane.GetHosts", request, replyHosts)
}

func (s *ControlClient) AddHost(host dao.Host, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddHost", host, unused)
}

func (s *ControlClient) UpdateHost(host dao.Host, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateHost", host, unused)
}

func (s *ControlClient) RemoveHost(hostId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveHost", hostId, unused)
}

func (s *ControlClient) GetServices(request dao.EntityRequest, replyServices *[]*dao.Service) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServices", request, replyServices)
}

func (s *ControlClient) AddService(service dao.Service, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddService", service, unused)
}

func (s *ControlClient) UpdateService(service dao.Service, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateService", service, unused)
}

func (s *ControlClient) RemoveService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveService", serviceId, unused)
}

func (s *ControlClient) GetServicesForHost(hostId string, servicesForHost *[]*dao.Service) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServicesForHost", hostId, servicesForHost)
}

func (s *ControlClient) GetServiceLogs(serviceId string, logs *string) error {
	return s.rpcClient.Call("ControlPlane.GetServiceLogs", serviceId, logs)
}

func (s *ControlClient) GetServiceStateLogs(serviceStateId string, logs *string) error {
	return s.rpcClient.Call("ControlPlane.GetServiceStateLogs", serviceStateId, logs)
}

func (s *ControlClient) GetRunningServicesForHost(hostId string, runningServices *[]*dao.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServicesForHost", hostId, runningServices)
}

func (s *ControlClient) GetRunningServicesForService(serviceId string, runningServices *[]*dao.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServicesForService", serviceId, runningServices)
}

func (s *ControlClient) StopRunningInstance(serviceStateId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.StopRunningInstance", serviceStateId, unused)
}

func (s *ControlClient) GetRunningServices(request dao.EntityRequest, runningServices *[]*dao.RunningService) (err error) {
	return s.rpcClient.Call("ControlPlane.GetRunningServices", request, runningServices)
}

func (s *ControlClient) GetServiceStates(serviceId string, states *[]*dao.ServiceState) (err error) {
	return s.rpcClient.Call("ControlPlane.GetServiceStates", serviceId, states)
}

func (s *ControlClient) StartService(serviceId string, hostId *string) (err error) {
	return s.rpcClient.Call("ControlPlane.StartService", serviceId, hostId)
}

func (s *ControlClient) RestartService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RestartService", serviceId, unused)
}

func (s *ControlClient) StopService(serviceId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.StopService", serviceId, unused)
}

func (s *ControlClient) UpdateServiceState(state dao.ServiceState, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateServiceState", state, unused)
}

func (s *ControlClient) GetResourcePools(request dao.EntityRequest, pools *map[string]*dao.ResourcePool) (err error) {
	return s.rpcClient.Call("ControlPlane.GetResourcePools", request, pools)
}

func (s *ControlClient) AddResourcePool(pool dao.ResourcePool, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.AddResourcePool", pool, unused)
}

func (s *ControlClient) UpdateResourcePool(pool dao.ResourcePool, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.UpdateResourcePool", pool, unused)
}

func (s *ControlClient) RemoveResourcePool(poolId string, unused *int) (err error) {
	return s.rpcClient.Call("ControlPlane.RemoveResourcePool", poolId, unused)
}

func (s *ControlClient) GetHostsForResourcePool(poolId string, poolHosts *[]*dao.PoolHost) (err error) {
	return s.rpcClient.Call("ControlPlane.GetHostsForResourcePool", poolId, poolHosts)
}

func (s *ControlClient) AddHostToResourcePool(poolHost dao.PoolHost, unused *int) error {
	return s.rpcClient.Call("ControlPlane.AddHostToResourcePool", poolHost, unused)
}

func (s *ControlClient) RemoveHostFromResourcePool(poolHost dao.PoolHost, unused *int) error {
	return s.rpcClient.Call("ControlPlane.RemoveHostFromResourcePool", poolHost, unused)
}

func (s *ControlClient) DeployTemplate(request dao.ServiceTemplateDeploymentRequest, unused *int) error {
	return s.rpcClient.Call("ControlPlane.DeployTemplate", request, unused)
}

func (s *ControlClient) GetServiceTemplates(unused int, serviceTemplates *map[string]*dao.ServiceTemplate) error {
	return s.rpcClient.Call("ControlPlane.GetServiceTemplates", unused, serviceTemplates)
}

func (s *ControlClient) AddServiceTemplate(serviceTemplate dao.ServiceTemplate, unused *int) error {
	return s.rpcClient.Call("ControlPlane.AddServiceTemplate", serviceTemplate, unused)
}

func (s *ControlClient) UpdateServiceTemplate(serviceTemplate dao.ServiceTemplate, unused *int) error {
	return s.rpcClient.Call("ControlPlane.UpdateServiceTemplate", serviceTemplate, unused)
}

func (s *ControlClient) RemoveServiceTemplate(serviceTemplateId string, unused *int) error {
	return s.rpcClient.Call("ControlPlane.RemoveServiceTemplate", serviceTemplateId, unused)
}
<|endoftext|>