<commit_before>/*
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package exif // import "jonathanpittman.com/tiff/exif"

import (
	"math/big"
	"strings"

	"jonathanpittman.com/tiff"
)

/*
http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html
http://www.exiv2.org/tags.html
http://www.cipa.jp/exifprint/index_e.html
http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
http://www.jeita.or.jp/cgi-bin/standard_e/list.cgi?cateid=1&subcateid=4
*/

const ExifIFDTagID = 34665

var (
	exifTags     = tiff.NewTagSet("Exif", 0, 65535)
	ExifTagSpace = tiff.NewTagSpace("Exif")
	exifIFDTag   = tiff.NewTag(ExifIFDTagID, "ExifIFD", nil)
)

/*
TODO: Break up these exif tags into sets based on the exif version.  They
still all likely belong in the same space though.  For an example, take
a look at the way DNG was broken up.  Tags introduced in newer versions
are added to a set named for the version.  They still all get put into
the same space, just the sets are identified separately.
*/

/*
TODO: Break up tags into categories
From TIFF:
  A. Tags relating to image data structure
  B. Tags relating to recording offset
  C. Tags relating to image data characteristics
  D. Other tags
From EXIF:
  A. Tags Relating to Version
  B. Tag Relating to Image Data Characteristics
  C. Tags Relating to Image Configuration
  D. Tags Relating to User Information
  E. Tag Relating to Related File Information
  F. Tags Relating to Date and Time
  G. Tags Relating to Picture-Taking Conditions
  G2. Tags Relating to shooting situation
  H. Other Tags
From GPS:
  A. Tags Relating to GPS
From Interoperability:
  A. Attached Information Related to Interoperability
*/

// fiRat displays rationals (fractions) in n/d notation.  It assumes the
// underlying Go type is a *big.Rat.  This can be better.
func fiRat(f tiff.Field) string {
	return f.Type().Valuer()(f.Value().Bytes(), f.Value().Order()).Interface().(*big.Rat).String()
}

// fiRatAsFloat displays a rational as a float with 2 decimal points.
func fiRatAsFloat(f tiff.Field) string {
	return f.Type().Valuer()(f.Value().Bytes(), f.Value().Order()).Interface().(*big.Rat).FloatString(2)
}

/* The purpose of this is to do:
"0.95" -> "f/0.95"
"2.80" -> "f/2.8"
"4.00" -> "f/4"
*/
// fiFNumber displays an ƒ/stop value in the form f/n.nn, f/n.n, or f/n.
func fiFNumber(f tiff.Field) string {
	return "f/" + strings.TrimSuffix(strings.TrimRight(fiRatAsFloat(f), "0"), ".")
}

func init() {
	tiff.PrivateTags.Register(exifIFDTag)

	exifTags.Register(tiff.NewTag(33434, "ExposureTime", fiRat))
	exifTags.Register(tiff.NewTag(33437, "FNumber", fiFNumber))
	exifTags.Register(tiff.NewTag(34850, "ExposureProgram", nil))
	exifTags.Register(tiff.NewTag(34852, "SpectralSensitivity", nil))
	exifTags.Register(tiff.NewTag(34855, "ISOSpeedRatings", nil))
	exifTags.Register(tiff.NewTag(34856, "OECF", nil))
	exifTags.Register(tiff.NewTag(34864, "SensitivityType", nil))
	exifTags.Register(tiff.NewTag(34866, "RecommendedExposureIndex", nil))
	exifTags.Register(tiff.NewTag(36864, "ExifVersion", nil))
	exifTags.Register(tiff.NewTag(36867, "DateTimeOriginal", nil))
	exifTags.Register(tiff.NewTag(36868, "DateTimeDigitized", nil))
	exifTags.Register(tiff.NewTag(37121, "ComponentsConfiguration", nil))
	exifTags.Register(tiff.NewTag(37122, "CompressedBitsPerPixel", nil))
	exifTags.Register(tiff.NewTag(37377, "ShutterSpeedValue", nil))
	exifTags.Register(tiff.NewTag(37378, "ApertureValue", nil))
	exifTags.Register(tiff.NewTag(37379, "BrightnessValue", nil))
	exifTags.Register(tiff.NewTag(37380, "ExposureBiasValue", nil))
	exifTags.Register(tiff.NewTag(37381, "MaxApertureValue", nil))
	exifTags.Register(tiff.NewTag(37382, "SubjectDistance", nil))
	exifTags.Register(tiff.NewTag(37383, "MeteringMode", nil))
	exifTags.Register(tiff.NewTag(37384, "LightSource", nil))
	exifTags.Register(tiff.NewTag(37385, "Flash", nil))
	exifTags.Register(tiff.NewTag(37386, "FocalLength", nil))
	exifTags.Register(tiff.NewTag(37396, "SubjectArea", nil))
	exifTags.Register(tiff.NewTag(37500, "MakerNote", nil))
	exifTags.Register(tiff.NewTag(37510, "UserComment", nil))
	exifTags.Register(tiff.NewTag(37520, "SubsecTime", nil))
	exifTags.Register(tiff.NewTag(37521, "SubsecTimeOriginal", nil))
	exifTags.Register(tiff.NewTag(37522, "SubsecTimeDigitized", nil))
	exifTags.Register(tiff.NewTag(40960, "FlashpixVersion", nil))
	exifTags.Register(tiff.NewTag(40961, "ColorSpace", nil))
	exifTags.Register(tiff.NewTag(40962, "PixelXDimension", nil))
	exifTags.Register(tiff.NewTag(40963, "PixelYDimension", nil))
	exifTags.Register(tiff.NewTag(40964, "RelatedSoundFile", nil))
	exifTags.Register(tiff.NewTag(41483, "FlashEnergy", nil))
	exifTags.Register(tiff.NewTag(41484, "SpatialFrequencyResponse", nil))
	exifTags.Register(tiff.NewTag(41486, "FocalPlaneXResolution", nil))
	exifTags.Register(tiff.NewTag(41487, "FocalPlaneYResolution", nil))
	exifTags.Register(tiff.NewTag(41488, "FocalPlaneResolutionUnit", nil))
	exifTags.Register(tiff.NewTag(41492, "SubjectLocation", nil))
	exifTags.Register(tiff.NewTag(41493, "ExposureIndex", nil))
	exifTags.Register(tiff.NewTag(41495, "SensingMethod", nil))
	exifTags.Register(tiff.NewTag(41728, "FileSource", nil))
	exifTags.Register(tiff.NewTag(41729, "SceneType", nil))
	exifTags.Register(tiff.NewTag(41730, "CFAPattern", nil))
	exifTags.Register(tiff.NewTag(41985, "CustomRendered", nil))
	exifTags.Register(tiff.NewTag(41986, "ExposureMode", nil))
	exifTags.Register(tiff.NewTag(41987, "WhiteBalance", nil))
	exifTags.Register(tiff.NewTag(41988, "DigitalZoomRatio", nil))
	exifTags.Register(tiff.NewTag(41989, "FocalLengthIn35mmFilm", nil))
	exifTags.Register(tiff.NewTag(41990, "SceneCaptureType", nil))
	exifTags.Register(tiff.NewTag(41991, "GainControl", nil))
	exifTags.Register(tiff.NewTag(41992, "Contrast", nil))
	exifTags.Register(tiff.NewTag(41993, "Saturation", nil))
	exifTags.Register(tiff.NewTag(41994, "Sharpness", nil))
	exifTags.Register(tiff.NewTag(41995, "DeviceSettingDescription", nil))
	exifTags.Register(tiff.NewTag(41996, "SubjectDistanceRange", nil))
	exifTags.Register(tiff.NewTag(42016, "ImageUniqueID", nil))
	exifTags.Register(tiff.NewTag(42032, "CameraOwnerName", nil))
	exifTags.Register(tiff.NewTag(42033, "BodySerialNumber", nil))
	exifTags.Register(tiff.NewTag(42034, "LensSpecification", nil))
	exifTags.Register(tiff.NewTag(42035, "LensMake", nil))
	exifTags.Register(tiff.NewTag(42036, "LensModel", nil))
	exifTags.Register(tiff.NewTag(42037, "LensSerialNumber", nil))

	// Tags that indicate the offsets to the respective IFDs.
	exifTags.Register(exifIFDTag)
	exifTags.Register(gpsIFDTag)
	exifTags.Register(iopIFDTag)

	// Not sure if this actually belongs in Exif, but it has shown up in an ExifIFD.
	exifTags.Register(tiff.NewTag(18246, "Rating", nil))

	// Prevent further registration in exif.  If tags are missing, they
	// should be added here instead of added from the outside.
	exifTags.Lock()

	tiff.DefaultTagSpace.RegisterTagSet(exifTags)

	ExifTagSpace.RegisterTagSet(tiff.BaselineTags)
	ExifTagSpace.RegisterTagSet(tiff.ExtendedTags)
	ExifTagSpace.RegisterTagSet(exifTags)

	tiff.RegisterTagSpace(ExifTagSpace)
}
<commit_msg>tiff/exif: Add FieldInterpreter for ExposureProgram.<commit_after>/*
Copyright 2016 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package exif // import "jonathanpittman.com/tiff/exif"

import (
	"math/big"
	"strings"

	"jonathanpittman.com/tiff"
)

/*
http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html
http://www.exiv2.org/tags.html
http://www.cipa.jp/exifprint/index_e.html
http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
http://www.jeita.or.jp/cgi-bin/standard_e/list.cgi?cateid=1&subcateid=4
*/

const ExifIFDTagID = 34665

var (
	exifTags     = tiff.NewTagSet("Exif", 0, 65535)
	ExifTagSpace = tiff.NewTagSpace("Exif")
	exifIFDTag   = tiff.NewTag(ExifIFDTagID, "ExifIFD", nil)
)

/*
TODO: Break up these exif tags into sets based on the exif version.  They
still all likely belong in the same space though.  For an example, take
a look at the way DNG was broken up.  Tags introduced in newer versions
are added to a set named for the version.  They still all get put into
the same space, just the sets are identified separately.
*/

/*
TODO: Break up tags into categories
From TIFF:
  A. Tags relating to image data structure
  B. Tags relating to recording offset
  C. Tags relating to image data characteristics
  D. Other tags
From EXIF:
  A. Tags Relating to Version
  B. Tag Relating to Image Data Characteristics
  C. Tags Relating to Image Configuration
  D. Tags Relating to User Information
  E. Tag Relating to Related File Information
  F. Tags Relating to Date and Time
  G. Tags Relating to Picture-Taking Conditions
  G2. Tags Relating to shooting situation
  H. Other Tags
From GPS:
  A. Tags Relating to GPS
From Interoperability:
  A. Attached Information Related to Interoperability
*/

// fiRat displays rationals (fractions) in n/d notation.  It assumes the
// underlying Go type is a *big.Rat.  This can be better.
func fiRat(f tiff.Field) string {
	return f.Type().Valuer()(f.Value().Bytes(), f.Value().Order()).Interface().(*big.Rat).String()
}

// fiRatAsFloat displays a rational as a float with 2 decimal points.
func fiRatAsFloat(f tiff.Field) string {
	return f.Type().Valuer()(f.Value().Bytes(), f.Value().Order()).Interface().(*big.Rat).FloatString(2)
}

/* f/Stop formatting:
The purpose of this is to do:
  "0.95" -> "f/0.95"
  "2.80" -> "f/2.8"
  "4.00" -> "f/4"
*/
// fiFNumber displays an ƒ/stop value in the form f/n.nn, f/n.n, or f/n.
func fiFNumber(f tiff.Field) string {
	return "f/" + strings.TrimSuffix(strings.TrimRight(fiRatAsFloat(f), "0"), ".")
}

// ExposureProgram value names.  In the future, this may need to be a map if
// index values are skipped.
var exposureProgramVals = [...]string{
	"Not defined",       // 0
	"Manual",            // 1
	"Normal program",    // 2
	"Aperture priority", // 3
	"Shutter priority",  // 4
	"Creative program",  // 5 (biased toward depth of field)
	"Action program",    // 6 (biased toward fast shutter speed)
	"Portrait mode",     // 7 (for closeup photos with the background out of focus)
	"Landscape mode",    // 8 (for landscape photos with the background in focus)
}

func fiExposureProgram(f tiff.Field) string {
	prog := f.Type().Valuer()(f.Value().Bytes(), f.Value().Order()).Uint()
	if prog >= uint64(len(exposureProgramVals)) {
		return ""
	}
	return exposureProgramVals[prog]
}

func init() {
	tiff.PrivateTags.Register(exifIFDTag)

	exifTags.Register(tiff.NewTag(33434, "ExposureTime", fiRat))
	exifTags.Register(tiff.NewTag(33437, "FNumber", fiFNumber))
	exifTags.Register(tiff.NewTag(34850, "ExposureProgram", fiExposureProgram))
	exifTags.Register(tiff.NewTag(34852, "SpectralSensitivity", nil))
	exifTags.Register(tiff.NewTag(34855, "ISOSpeedRatings", nil))
	exifTags.Register(tiff.NewTag(34856, "OECF", nil))
	exifTags.Register(tiff.NewTag(34864, "SensitivityType", nil))
	exifTags.Register(tiff.NewTag(34866, "RecommendedExposureIndex", nil))
	exifTags.Register(tiff.NewTag(36864, "ExifVersion", nil))
	exifTags.Register(tiff.NewTag(36867, "DateTimeOriginal", nil))
	exifTags.Register(tiff.NewTag(36868, "DateTimeDigitized", nil))
	exifTags.Register(tiff.NewTag(37121, "ComponentsConfiguration", nil))
	exifTags.Register(tiff.NewTag(37122, "CompressedBitsPerPixel", nil))
	exifTags.Register(tiff.NewTag(37377, "ShutterSpeedValue", nil))
	exifTags.Register(tiff.NewTag(37378, "ApertureValue", nil))
	exifTags.Register(tiff.NewTag(37379, "BrightnessValue", nil))
	exifTags.Register(tiff.NewTag(37380, "ExposureBiasValue", nil))
	exifTags.Register(tiff.NewTag(37381, "MaxApertureValue", nil))
	exifTags.Register(tiff.NewTag(37382, "SubjectDistance", nil))
	exifTags.Register(tiff.NewTag(37383, "MeteringMode", nil))
	exifTags.Register(tiff.NewTag(37384, "LightSource", nil))
	exifTags.Register(tiff.NewTag(37385, "Flash", nil))
	exifTags.Register(tiff.NewTag(37386, "FocalLength", nil))
	exifTags.Register(tiff.NewTag(37396, "SubjectArea", nil))
	exifTags.Register(tiff.NewTag(37500, "MakerNote", nil))
	exifTags.Register(tiff.NewTag(37510, "UserComment", nil))
	exifTags.Register(tiff.NewTag(37520, "SubsecTime", nil))
	exifTags.Register(tiff.NewTag(37521, "SubsecTimeOriginal", nil))
	exifTags.Register(tiff.NewTag(37522, "SubsecTimeDigitized", nil))
	exifTags.Register(tiff.NewTag(40960, "FlashpixVersion", nil))
	exifTags.Register(tiff.NewTag(40961, "ColorSpace", nil))
	exifTags.Register(tiff.NewTag(40962, "PixelXDimension", nil))
	exifTags.Register(tiff.NewTag(40963, "PixelYDimension", nil))
	exifTags.Register(tiff.NewTag(40964, "RelatedSoundFile", nil))
	exifTags.Register(tiff.NewTag(41483, "FlashEnergy", nil))
	exifTags.Register(tiff.NewTag(41484, "SpatialFrequencyResponse", nil))
	exifTags.Register(tiff.NewTag(41486, "FocalPlaneXResolution", nil))
	exifTags.Register(tiff.NewTag(41487, "FocalPlaneYResolution", nil))
	exifTags.Register(tiff.NewTag(41488, "FocalPlaneResolutionUnit", nil))
	exifTags.Register(tiff.NewTag(41492, "SubjectLocation", nil))
	exifTags.Register(tiff.NewTag(41493, "ExposureIndex", nil))
	exifTags.Register(tiff.NewTag(41495, "SensingMethod", nil))
	exifTags.Register(tiff.NewTag(41728, "FileSource", nil))
	exifTags.Register(tiff.NewTag(41729, "SceneType", nil))
	exifTags.Register(tiff.NewTag(41730, "CFAPattern", nil))
	exifTags.Register(tiff.NewTag(41985, "CustomRendered", nil))
	exifTags.Register(tiff.NewTag(41986, "ExposureMode", nil))
	exifTags.Register(tiff.NewTag(41987, "WhiteBalance", nil))
	exifTags.Register(tiff.NewTag(41988, "DigitalZoomRatio", nil))
	exifTags.Register(tiff.NewTag(41989, "FocalLengthIn35mmFilm", nil))
	exifTags.Register(tiff.NewTag(41990, "SceneCaptureType", nil))
	exifTags.Register(tiff.NewTag(41991, "GainControl", nil))
	exifTags.Register(tiff.NewTag(41992, "Contrast", nil))
	exifTags.Register(tiff.NewTag(41993, "Saturation", nil))
	exifTags.Register(tiff.NewTag(41994, "Sharpness", nil))
	exifTags.Register(tiff.NewTag(41995, "DeviceSettingDescription", nil))
	exifTags.Register(tiff.NewTag(41996, "SubjectDistanceRange", nil))
	exifTags.Register(tiff.NewTag(42016, "ImageUniqueID", nil))
	exifTags.Register(tiff.NewTag(42032, "CameraOwnerName", nil))
	exifTags.Register(tiff.NewTag(42033, "BodySerialNumber", nil))
	exifTags.Register(tiff.NewTag(42034, "LensSpecification", nil))
	exifTags.Register(tiff.NewTag(42035, "LensMake", nil))
	exifTags.Register(tiff.NewTag(42036, "LensModel", nil))
	exifTags.Register(tiff.NewTag(42037, "LensSerialNumber", nil))

	// Tags that indicate the offsets to the respective IFDs.
	exifTags.Register(exifIFDTag)
	exifTags.Register(gpsIFDTag)
	exifTags.Register(iopIFDTag)

	// Not sure if this actually belongs in Exif, but it has shown up in an ExifIFD.
	exifTags.Register(tiff.NewTag(18246, "Rating", nil))

	// Prevent further registration in exif.  If tags are missing, they
	// should be added here instead of added from the outside.
	exifTags.Lock()

	tiff.DefaultTagSpace.RegisterTagSet(exifTags)

	ExifTagSpace.RegisterTagSet(tiff.BaselineTags)
	ExifTagSpace.RegisterTagSet(tiff.ExtendedTags)
	ExifTagSpace.RegisterTagSet(exifTags)

	tiff.RegisterTagSpace(ExifTagSpace)
}
<|endoftext|>


<commit_before>package expect

import (
	"fmt"
	"path"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

func New(t *testing.T) func(target interface{}) *expectation {
	return (&expector{T: t}).expect
}

type expector struct {
	T *testing.T
}

func (e *expector) expect(target interface{}) *expectation {
	return &expectation{T: e.T, target: target}
}

type expectation struct {
	T      *testing.T
	target interface{}
}

func (e *expectation) ToHaveLength(l int) {
	realLen, ok := haveLength(e.target, l)
	e.expect(ok, "expected %#v to have length %d but was %d", e.target, l, realLen)
}

func (e *expectation) ToContain(element interface{}) {
	e.expect(contains(e.target, element), "expected %#v to contain %#v", e.target, element)
}
func (e *expectation) ToHavePrefix(prefix string) {
	s := e.expectString()
	e.expect(strings.HasPrefix(s, prefix), "expected %q to have prefix %q", s, prefix)
}

func (e *expectation) ToHaveSuffix(prefix string) {
	s := e.expectString()
	e.expect(strings.HasSuffix(s, prefix), "expected %q to have suffix %q", s, prefix)
}

func (e *expectation) ToNotEqual(i interface{}) {
	e.expect(e.target != i, "expected %#v to equal %#v", e.target, i)
}

func (e *expectation) ToEqual(i interface{}) {
	e.expect(equal(e.target, i), "expected %#v to equal %#v", e.target, i)
}

func (e *expectation) ToNotBeNil() {
	e.expect(e.target != nil, "expected %q to not be nil", e.target)
}

func (e *expectation) ToBeNil() {
	e.expect(e.target == nil, "expected %q to be nil", e.target)
}

// helpers

func equal(target interface{}, other interface{}) bool {
	return fmt.Sprint(target) == fmt.Sprint(other)
}

func contains(in interface{}, target interface{}) bool {
	switch v := valueOf(in).(type) {
	case string:
		if s, ok := target.(string); ok {
			return strings.Contains(v, s)
		}
		return false
	case []interface{}:
		for _, el := range v {
			if el == target {
				return true
			}
		}
	}
	return false
}

func haveLength(in interface{}, l int) (int, bool) {
	switch v := valueOf(in).(type) {
	case string:
		return len(v), len(v) == l
	case []interface{}:
		return len(v), len(v) == l
	}
	return 0, false
}

func valueOf(list interface{}) interface{} {
	out := []interface{}{}
	v := reflect.ValueOf(list)
	switch v.Kind() {
	case reflect.String:
		return v.String()
	case reflect.Slice:
		for i := 0; i < v.Len(); i++ {
			out = append(out, v.Index(i).Interface())
		}
		return out
	}
	return nil
}

func (e *expectation) expect(check bool, format string, args ...interface{}) {
	if check {
		e.pass()
	} else {
		e.fail(format, args...)
	}
}

func (e *expectation) pass() {
	fmt.Print(".")
}

func (e *expectation) fail(format string, i ...interface{}) {
	_, file, line, _ := runtime.Caller(3)
	fmt.Printf("\n\t%s:%d: %s\n", path.Base(file), line, fmt.Sprintf(format, i...))
	e.T.FailNow()
}

func (e *expectation) expectString() string {
	s, ok := e.target.(string)
	if !ok {
		e.fail("expected target to be string but was %T", e.target)
	}
	return s
}
<commit_msg>fix error message<commit_after>package expect

import (
	"fmt"
	"path"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

func New(t *testing.T) func(target interface{}) *expectation {
	return (&expector{T: t}).expect
}

type expector struct {
	T *testing.T
}

func (e *expector) expect(target interface{}) *expectation {
	return &expectation{T: e.T, target: target}
}

type expectation struct {
	T      *testing.T
	target interface{}
}

func (e *expectation) ToHaveLength(l int) {
	realLen, ok := haveLength(e.target, l)
	e.expect(ok, "expected %#v to have length %d but was %d", e.target, l, realLen)
}

func (e *expectation) ToContain(element interface{}) {
	e.expect(contains(e.target, element), "expected %#v to contain %#v", e.target, element)
}
func (e *expectation) ToHavePrefix(prefix string) {
	s := e.expectString()
	e.expect(strings.HasPrefix(s, prefix), "expected %q to have prefix %q", s, prefix)
}

func (e *expectation) ToHaveSuffix(prefix string) {
	s := e.expectString()
	e.expect(strings.HasSuffix(s, prefix), "expected %q to have suffix %q", s, prefix)
}

func (e *expectation) ToNotEqual(i interface{}) {
	e.expect(e.target != i, "expected %#v to not equal %#v", e.target, i)
}

func (e *expectation) ToEqual(i interface{}) {
	e.expect(equal(e.target, i), "expected %#v to equal %#v", e.target, i)
}

func (e *expectation) ToNotBeNil() {
	e.expect(e.target != nil, "expected %q to not be nil", e.target)
}

func (e *expectation) ToBeNil() {
	e.expect(e.target == nil, "expected %q to be nil", e.target)
}

// helpers

func equal(target interface{}, other interface{}) bool {
	return fmt.Sprint(target) == fmt.Sprint(other)
}

func contains(in interface{}, target interface{}) bool {
	switch v := valueOf(in).(type) {
	case string:
		if s, ok := target.(string); ok {
			return strings.Contains(v, s)
		}
		return false
	case []interface{}:
		for _, el := range v {
			if el == target {
				return true
			}
		}
	}
	return false
}

func haveLength(in interface{}, l int) (int, bool) {
	switch v := valueOf(in).(type) {
	case string:
		return len(v), len(v) == l
	case []interface{}:
		return len(v), len(v) == l
	}
	return 0, false
}

func valueOf(list interface{}) interface{} {
	out := []interface{}{}
	v := reflect.ValueOf(list)
	switch v.Kind() {
	case reflect.String:
		return v.String()
	case reflect.Slice:
		for i := 0; i < v.Len(); i++ {
			out = append(out, v.Index(i).Interface())
		}
		return out
	}
	return nil
}

func (e *expectation) expect(check bool, format string, args ...interface{}) {
	if check {
		e.pass()
	} else {
		e.fail(format, args...)
	}
}

func (e *expectation) pass() {
	fmt.Print(".")
}

func (e *expectation) fail(format string, i ...interface{}) {
	_, file, line, _ := runtime.Caller(3)
	fmt.Printf("\n\t%s:%d: %s\n", path.Base(file), line, fmt.Sprintf(format, i...))
	e.T.FailNow()
}

func (e *expectation) expectString() string {
	s, ok := e.target.(string)
	if !ok {
		e.fail("expected target to be string but was %T", e.target)
	}
	return s
}
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"strings"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	"golang.org/x/net/context"
)

type CmdTeamEdit struct {
	libkb.Contextified
	Team      keybase1.TeamName
	PrintOnly bool
	Settings  keybase1.TeamSettings
}

func newCmdTeamEdit(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "edit",
		ArgumentHelp: "<team name>",
		Usage:        "Edit team settings.",
		Action: func(c *cli.Context) {
			cmd := NewCmdTeamEditRunner(g)
			cl.ChooseCommand(cmd, "edit", c)
		},
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "open",
				Usage: "Change team to be open or closed",
			},
			cli.StringFlag{
				Name:  "join-as",
				Usage: "team role (writer, reader) [required when changing team to open]",
			},
		},
	}
}

func NewCmdTeamEditRunner(g *libkb.GlobalContext) *CmdTeamEdit {
	return &CmdTeamEdit{Contextified: libkb.NewContextified(g)}
}

func (c *CmdTeamEdit) ParseArgv(ctx *cli.Context) error {
	var err error
	c.Team, err = ParseOneTeamNameK1(ctx)
	if err != nil {
		return err
	}

	if ctx.NumFlags() == 0 {
		// Just a team name and no other flags - print current team settings.
		c.PrintOnly = true
		return nil
	}

	sopen := ctx.String("open")
	switch sopen {
	case "true":
		c.Settings.Open = true
	case "false":
		c.Settings.Open = false
	default:
		return errors.New("invalid --open flag, please use true or false")
	}

	if c.Settings.Open {
		srole := ctx.String("join-as")
		if srole == "" {
			return errors.New("team role required via --join-as flag")
		}

		role, ok := keybase1.TeamRoleMap[strings.ToUpper(srole)]
		if !ok {
			return errors.New("invalid team role, please use writer, or reader")
		}

		switch role {
		case keybase1.TeamRole_READER, keybase1.TeamRole_WRITER:
			break
		default:
			return errors.New("invalid team role, please use writer, or reader")
		}

		c.Settings.JoinAs = role
	}

	return nil
}

func (c *CmdTeamEdit) applySettings(cli keybase1.TeamsClient) error {
	dui := c.G().UI.GetTerminalUI()

	details, err := cli.TeamGet(context.Background(), keybase1.TeamGetArg{Name: c.Team.String(), ForceRepoll: false})
	if err != nil {
		return err
	}

	if !details.Settings.Open && !c.Settings.Open {
		dui.Printf("The team is already closed.\n")
		return nil
	}

	if details.Settings.Open && c.Settings.Open && details.Settings.JoinAs == c.Settings.JoinAs {
		dui.Printf("The team is already open with default role: %s\n", strings.ToLower(details.Settings.JoinAs.String()))
		return nil
	}

	arg := keybase1.TeamSetSettingsArg{
		Name:     c.Team.String(),
		Settings: c.Settings,
	}

	err = cli.TeamSetSettings(context.Background(), arg)
	if err != nil {
		return err
	}

	dui.Printf("Team settings were changed.\n")
	return nil
}

func (c *CmdTeamEdit) Run() error {
	protocols := []rpc.Protocol{
		NewTeamsUIProtocol(c.G()),
	}

	if err := RegisterProtocolsWithContext(protocols, c.G()); err != nil {
		return err
	}

	cli, err := GetTeamsClient(c.G())
	if err != nil {
		return err
	}

	if !c.PrintOnly {
		if err := c.applySettings(cli); err != nil {
			return err
		}
	}

	details, err := cli.TeamGet(context.Background(), keybase1.TeamGetArg{Name: c.Team.String(), ForceRepoll: !c.PrintOnly})
	if err != nil {
		return err
	}

	dui := c.G().UI.GetTerminalUI()
	dui.Printf("Current settings of team %q:\n", c.Team.String())
	if details.Settings.Open {
		dui.Printf("  Open:\t\t\ttrue\n")
		dui.Printf("  New member role:\t%s\n", strings.ToLower(details.Settings.JoinAs.String()))
	} else {
		dui.Printf("  Open:\tfalse\n")
	}

	return nil
}

func (c *CmdTeamEdit) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		API:       true,
		KbKeyring: true,
	}
}
<commit_msg>Use Bool flag, remove protocols<commit_after>package client

import (
	"errors"
	"strings"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type CmdTeamEdit struct {
	libkb.Contextified
	Team      keybase1.TeamName
	PrintOnly bool
	Settings  keybase1.TeamSettings
}

func newCmdTeamEdit(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "edit",
		ArgumentHelp: "<team name>",
		Usage:        "Edit team settings.",
		Action: func(c *cli.Context) {
			cmd := NewCmdTeamEditRunner(g)
			cl.ChooseCommand(cmd, "edit", c)
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "open",
				Usage: "Change team to be open or closed",
			},
			cli.StringFlag{
				Name:  "join-as",
				Usage: "team role (writer, reader) [required when changing team to open]",
			},
		},
	}
}

func NewCmdTeamEditRunner(g *libkb.GlobalContext) *CmdTeamEdit {
	return &CmdTeamEdit{Contextified: libkb.NewContextified(g)}
}

func (c *CmdTeamEdit) ParseArgv(ctx *cli.Context) error {
	var err error
	c.Team, err = ParseOneTeamNameK1(ctx)
	if err != nil {
		return err
	}

	if ctx.NumFlags() == 0 {
		// Just a team name and no other flags - print current team settings.
		c.PrintOnly = true
		return nil
	}

	if ctx.Generic("open") == nil {
		return errors.New("--open flag is required")
	}

	c.Settings.Open = ctx.Bool("open")
	if c.Settings.Open {
		srole := ctx.String("join-as")
		if srole == "" {
			return errors.New("team role required via --join-as flag")
		}

		role, ok := keybase1.TeamRoleMap[strings.ToUpper(srole)]
		if !ok {
			return errors.New("invalid team role, please use writer, or reader")
		}

		switch role {
		case keybase1.TeamRole_READER, keybase1.TeamRole_WRITER:
			break
		default:
			return errors.New("invalid team role, please use writer, or reader")
		}

		c.Settings.JoinAs = role
	}

	return nil
}

func (c *CmdTeamEdit) applySettings(cli keybase1.TeamsClient) error {
	dui := c.G().UI.GetTerminalUI()

	details, err := cli.TeamGet(context.Background(), keybase1.TeamGetArg{Name: c.Team.String(), ForceRepoll: false})
	if err != nil {
		return err
	}

	if !details.Settings.Open && !c.Settings.Open {
		dui.Printf("The team is already closed.\n")
		return nil
	}

	if details.Settings.Open && c.Settings.Open && details.Settings.JoinAs == c.Settings.JoinAs {
		dui.Printf("The team is already open with default role: %s\n", strings.ToLower(details.Settings.JoinAs.String()))
		return nil
	}

	arg := keybase1.TeamSetSettingsArg{
		Name:     c.Team.String(),
		Settings: c.Settings,
	}

	err = cli.TeamSetSettings(context.Background(), arg)
	if err != nil {
		return err
	}

	dui.Printf("Team settings were changed.\n")
	return nil
}

func (c *CmdTeamEdit) Run() error {
	cli, err := GetTeamsClient(c.G())
	if err != nil {
		return err
	}

	if !c.PrintOnly {
		if err := c.applySettings(cli); err != nil {
			return err
		}
	}

	details, err := cli.TeamGet(context.Background(), keybase1.TeamGetArg{Name: c.Team.String(), ForceRepoll: !c.PrintOnly})
	if err != nil {
		return err
	}

	dui := c.G().UI.GetTerminalUI()
	dui.Printf("Current settings of team %q:\n", c.Team.String())
	if details.Settings.Open {
		dui.Printf("  Open:\t\t\ttrue\n")
		dui.Printf("  New member role:\t%s\n", strings.ToLower(details.Settings.JoinAs.String()))
	} else {
		dui.Printf("  Open:\tfalse\n")
	}

	return nil
}

func (c *CmdTeamEdit) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		API:       true,
		KbKeyring: true,
	}
}
<|endoftext|>


<commit_before>package agent

// You can overridden buildVersion at compile time by using:
//
//  go run -ldflags "-X github.com/buildkite/agent/agent.buildVersion abc" *.go --version
//
// On CI, the binaries are always build with the buildVersion variable set.

var baseVersion string = "3.0-beta.1"
var buildVersion string = ""

func Version() string {
	return baseVersion
}

func BuildVersion() string {
	if buildVersion != "" {
		return buildVersion
	} else {
		return "x"
	}
}
<commit_msg>Bump master to 3.0-beta.2<commit_after>package agent

// You can overridden buildVersion at compile time by using:
//
//  go run -ldflags "-X github.com/buildkite/agent/agent.buildVersion abc" *.go --version
//
// On CI, the binaries are always build with the buildVersion variable set.

var baseVersion string = "3.0-beta.2"
var buildVersion string = ""

func Version() string {
	return baseVersion
}

func BuildVersion() string {
	if buildVersion != "" {
		return buildVersion
	} else {
		return "x"
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package execution

import (
	"strconv"
	"time"

	"github.com/getgauge/gauge/api"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/execution/result"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/manifest"
	"github.com/getgauge/gauge/plugin"
	"github.com/getgauge/gauge/plugin/install"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/runner"
	"github.com/getgauge/gauge/validation"
)

var NumberOfExecutionStreams int
var InParallel bool
var checkUpdatesDuringExecution = false

type execution interface {
	run()
	result() *result.SuiteResult
}

type executionInfo struct {
	manifest        *manifest.Manifest
	specs           *gauge.SpecCollection
	runner          *runner.TestRunner
	pluginHandler   *plugin.Handler
	consoleReporter reporter.Reporter
	errMaps         *validation.ValidationErrMaps
	inParallel      bool
	numberOfStreams int
}

func newExecutionInfo(s *gauge.SpecCollection, r *runner.TestRunner, ph *plugin.Handler, rep reporter.Reporter, e *validation.ValidationErrMaps, p bool) *executionInfo {
	m, err := manifest.ProjectManifest()
	if err != nil {
		logger.Fatalf(err.Error())
	}
	return &executionInfo{
		manifest:        m,
		specs:           s,
		runner:          r,
		pluginHandler:   ph,
		consoleReporter: rep,
		errMaps:         e,
		inParallel:      p,
		numberOfStreams: NumberOfExecutionStreams,
	}
}

func ExecuteSpecs(specDirs []string) int {
	validateFlags()
	if checkUpdatesDuringExecution && config.CheckUpdates() {
		i := &install.UpdateFacade{}
		i.BufferUpdateDetails()
		defer i.PrintUpdateBuffer()
	}

	runner := startAPI()
	specs, errMap := validation.ValidateSpecs(specDirs, runner)
	ei := newExecutionInfo(specs, runner, nil, reporter.Current(), errMap, InParallel)
	e := newExecution(ei)
	e.run()
	return printExecutionStatus(e.result(), errMap)
}

func newExecution(executionInfo *executionInfo) execution {
	if executionInfo.inParallel {
		return newParallelExecution(executionInfo)
	}
	return newSimpleExecution(executionInfo)
}

func startAPI() *runner.TestRunner {
	sc := api.StartAPI()
	select {
	case runner := <-sc.RunnerChan:
		return runner
	case err := <-sc.ErrorChan:
		logger.Fatalf("Failed to start gauge API: %s", err.Error())
	}
	return nil
}

func printExecutionStatus(suiteResult *result.SuiteResult, errMap *validation.ValidationErrMaps) int {
	nSkippedScenarios := len(errMap.ScenarioErrs)
	nSkippedSpecs := len(errMap.SpecErrs)
	nExecutedSpecs := len(suiteResult.SpecResults) - nSkippedSpecs
	nFailedSpecs := suiteResult.SpecsFailedCount
	nPassedSpecs := nExecutedSpecs - nFailedSpecs

	nExecutedScenarios := 0
	nFailedScenarios := 0
	nPassedScenarios := 0
	for _, specResult := range suiteResult.SpecResults {
		nExecutedScenarios += specResult.ScenarioCount
		nFailedScenarios += specResult.ScenarioFailedCount
	}
	nExecutedScenarios -= nSkippedScenarios
	nPassedScenarios = nExecutedScenarios - nFailedScenarios

	logger.Info("Specifications:\t%d executed\t%d passed\t%d failed\t%d skipped", nExecutedSpecs, nPassedSpecs, nFailedSpecs, nSkippedSpecs)
	logger.Info("Scenarios:\t%d executed\t%d passed\t%d failed\t%d skipped", nExecutedScenarios, nPassedScenarios, nFailedScenarios, nSkippedScenarios)
	logger.Info("\nTotal time taken: %s", time.Millisecond*time.Duration(suiteResult.ExecutionTime))

	for _, unhandledErr := range suiteResult.UnhandledErrors {
		logger.Errorf(unhandledErr.Error())
	}
	exitCode := 0
	if suiteResult.IsFailed || (nSkippedSpecs+nSkippedScenarios) > 0 {
		exitCode = 1
	}
	return exitCode
}

func validateFlags() {
	if !InParallel {
		return
	}
	if NumberOfExecutionStreams < 1 {
		logger.Fatalf("Invalid input(%s) to --n flag.", strconv.Itoa(NumberOfExecutionStreams))
	}
	if !isValidStrategy(Strategy) {
		logger.Fatalf("Invalid input(%s) to --strategy flag.", Strategy)
	}
}
<commit_msg>Fixing scenario counts in exec summary | fixes #303<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package execution

import (
	"strconv"
	"time"

	"github.com/getgauge/gauge/api"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/execution/result"
	"github.com/getgauge/gauge/gauge"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/manifest"
	"github.com/getgauge/gauge/plugin"
	"github.com/getgauge/gauge/plugin/install"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/runner"
	"github.com/getgauge/gauge/validation"
)

var NumberOfExecutionStreams int
var InParallel bool
var checkUpdatesDuringExecution = false

type execution interface {
	run()
	result() *result.SuiteResult
}

type executionInfo struct {
	manifest        *manifest.Manifest
	specs           *gauge.SpecCollection
	runner          *runner.TestRunner
	pluginHandler   *plugin.Handler
	consoleReporter reporter.Reporter
	errMaps         *validation.ValidationErrMaps
	inParallel      bool
	numberOfStreams int
}

func newExecutionInfo(s *gauge.SpecCollection, r *runner.TestRunner, ph *plugin.Handler, rep reporter.Reporter, e *validation.ValidationErrMaps, p bool) *executionInfo {
	m, err := manifest.ProjectManifest()
	if err != nil {
		logger.Fatalf(err.Error())
	}
	return &executionInfo{
		manifest:        m,
		specs:           s,
		runner:          r,
		pluginHandler:   ph,
		consoleReporter: rep,
		errMaps:         e,
		inParallel:      p,
		numberOfStreams: NumberOfExecutionStreams,
	}
}

func ExecuteSpecs(specDirs []string) int {
	validateFlags()
	if checkUpdatesDuringExecution && config.CheckUpdates() {
		i := &install.UpdateFacade{}
		i.BufferUpdateDetails()
		defer i.PrintUpdateBuffer()
	}

	runner := startAPI()
	specs, errMap := validation.ValidateSpecs(specDirs, runner)
	ei := newExecutionInfo(specs, runner, nil, reporter.Current(), errMap, InParallel)
	e := newExecution(ei)
	e.run()
	return printExecutionStatus(e.result(), errMap)
}

func newExecution(executionInfo *executionInfo) execution {
	if executionInfo.inParallel {
		return newParallelExecution(executionInfo)
	}
	return newSimpleExecution(executionInfo)
}

func startAPI() *runner.TestRunner {
	sc := api.StartAPI()
	select {
	case runner := <-sc.RunnerChan:
		return runner
	case err := <-sc.ErrorChan:
		logger.Fatalf("Failed to start gauge API: %s", err.Error())
	}
	return nil
}

func printExecutionStatus(suiteResult *result.SuiteResult, errMap *validation.ValidationErrMaps) int {
	nSkippedScenarios := len(errMap.ScenarioErrs)
	nSkippedSpecs := len(errMap.SpecErrs)
	nExecutedSpecs := len(suiteResult.SpecResults) - nSkippedSpecs
	nFailedSpecs := suiteResult.SpecsFailedCount
	nPassedSpecs := nExecutedSpecs - nFailedSpecs

	nExecutedScenarios := 0
	nFailedScenarios := 0
	nPassedScenarios := 0
	for _, specResult := range suiteResult.SpecResults {
		nExecutedScenarios += specResult.ScenarioCount
		nFailedScenarios += specResult.ScenarioFailedCount
	}
	nExecutedScenarios -= nSkippedScenarios
	nPassedScenarios = nExecutedScenarios - nFailedScenarios

	if nExecutedScenarios < 0 {
		nExecutedScenarios = 0
	}

	if nPassedScenarios < 0 {
		nPassedScenarios = 0
	}

	logger.Info("Specifications:\t%d executed\t%d passed\t%d failed\t%d skipped", nExecutedSpecs, nPassedSpecs, nFailedSpecs, nSkippedSpecs)
	logger.Info("Scenarios:\t%d executed\t%d passed\t%d failed\t%d skipped", nExecutedScenarios, nPassedScenarios, nFailedScenarios, nSkippedScenarios)
	logger.Info("\nTotal time taken: %s", time.Millisecond*time.Duration(suiteResult.ExecutionTime))

	for _, unhandledErr := range suiteResult.UnhandledErrors {
		logger.Errorf(unhandledErr.Error())
	}
	exitCode := 0
	if suiteResult.IsFailed || (nSkippedSpecs+nSkippedScenarios) > 0 {
		exitCode = 1
	}
	return exitCode
}

func validateFlags() {
	if !InParallel {
		return
	}
	if NumberOfExecutionStreams < 1 {
		logger.Fatalf("Invalid input(%s) to --n flag.", strconv.Itoa(NumberOfExecutionStreams))
	}
	if !isValidStrategy(Strategy) {
		logger.Fatalf("Invalid input(%s) to --strategy flag.", Strategy)
	}
}
<|endoftext|>


<commit_before>package input

import (
	"runtime"
	"time"

	"github.com/funkygao/dbus/engine"
	"github.com/funkygao/dbus/pkg/model"
	conf "github.com/funkygao/jsconf"
	log "github.com/funkygao/log4go"
)

var (
	_ engine.Input     = &MockInput{}
	_ engine.Pauser    = &MockInput{}
	_ engine.Restarter = &MockInput{}
)

type MockInput struct {
	inChan <-chan *engine.Packet

	payload engine.Payloader
	sleep   time.Duration
}

func (this *MockInput) Init(config *conf.Conf) {
	this.sleep = config.Duration("sleep", 0)
	switch config.String("payload", "Bytes") {
	case "RowsEvent":
		this.payload = &model.RowsEvent{
			Log:       "mysql-bin.0001",
			Position:  498876,
			Schema:    "mydabase",
			Table:     "user_account",
			Action:    "I",
			Timestamp: 1486554654,
			Rows:      [][]interface{}{{"user", 15, "hello world"}},
		}

	default:
		this.payload = model.Bytes(`{"log":"6633343-bin.006419","pos":795931083,"db":"owl_t_prod_cd","tbl":"owl_mi","dml":"U","ts":1488934500,"rows":[[132332,"expired_keys","过期的key的个数","mock-monitor|member-mock|10.1.1.1|10489|expired_keys","mock-monitor",244526,"10489",null,"2015-12-25 17:12:00",null,null,"28571284","Ok","TypeLong","2017-03-08 08:54:01",null,null,null,null,null,null],[132332,"expired_keys","过期的key的个数","mock-monitor|member-mock|10.1.1.6|10489|expired_keys","mock-monitor",244526,"10489",null,"2015-12-25 17:12:00",null,null,"28571320","Ok","TypeLong","2017-03-08 08:55:00",null,null,null,null,null,null]]}`)
	}
}

func (this *MockInput) SampleConfig() string {
	return `
	sleep: 1s
	payload: "Bytes" // Bytes|RowsEvent
	`
}

func (this *MockInput) OnAck(pack *engine.Packet) error {
	return nil
}

func (this *MockInput) StopAcker(r engine.InputRunner) {}

func (this *MockInput) CleanupForRestart() bool {
	return true
}

func (this *MockInput) Pause(r engine.InputRunner) error {
	log.Info("[%s] paused", r.Name())
	this.inChan = nil
	return nil
}

func (this *MockInput) Resume(r engine.InputRunner) error {
	log.Info("[%s] resumed", r.Name())
	this.inChan = r.Exchange().InChan()
	return nil
}

func (this *MockInput) Run(r engine.InputRunner, h engine.PluginHelper) error {
	this.inChan = r.Exchange().InChan()
	var n int64
	for {
		select {
		case <-r.Stopper():
			log.Info("[%s] %d packets injected", r.Name(), n)
			return nil

		case pack := <-this.inChan:
			pack.Payload = this.payload
			r.Exchange().Inject(pack)
			n++

			if this.sleep > 0 {
				time.Sleep(this.sleep)
			}

		default:
			runtime.Gosched()
		}
	}

	return nil
}

func init() {
	engine.RegisterPlugin("MockInput", func() engine.Plugin {
		return new(MockInput)
	})
}
<commit_msg>rename<commit_after>package input

import (
	"runtime"
	"time"

	"github.com/funkygao/dbus/engine"
	"github.com/funkygao/dbus/pkg/model"
	conf "github.com/funkygao/jsconf"
	log "github.com/funkygao/log4go"
)

var (
	_ engine.Input     = &MockInput{}
	_ engine.Pauser    = &MockInput{}
	_ engine.Restarter = &MockInput{}
)

type MockInput struct {
	inChan <-chan *engine.Packet

	payload engine.Payloader
	sleep   time.Duration
}

func (this *MockInput) Init(config *conf.Conf) {
	this.sleep = config.Duration("sleep", 0)
	switch config.String("payload", "Bytes") {
	case "RowsEvent":
		this.payload = &model.RowsEvent{
			Log:       "mysql-bin.0001",
			Position:  498876,
			Schema:    "mydabase",
			Table:     "user_account",
			Action:    "I",
			Timestamp: 1486554654,
			Rows:      [][]interface{}{{"user", 15, "hello world"}},
		}

	default:
		this.payload = model.Bytes(`{"log":"6633343-bin.006419","pos":795931083,"db":"owl_t_prod_cd","tbl":"owl_mi","dml":"U","ts":1488934500,"rows":[[132332,"expired_keys","过期的key的个数","mock-monitor|member-mock|10.1.1.1|10489|expired_keys","mock-monitor",244526,"10489",null,"2015-12-25 17:12:00",null,null,"28571284","Ok","TypeLong","2017-03-08 08:54:01",null,null,null,null,null,null],[132332,"expired_keys","过期的key的个数","mock-monitor|member-mock|10.1.1.6|10489|expired_keys","mock-monitor",244526,"10489",null,"2015-12-25 17:12:00",null,null,"28571320","Ok","TypeLong","2017-03-08 08:55:00",null,null,null,null,null,null]]}`)
	}
}

func (this *MockInput) SampleConfig() string {
	return `
	sleep: 1s
	payload: "Bytes" // Bytes|RowsEvent
	`
}

func (this *MockInput) Ack(pack *engine.Packet) error {
	return nil
}

func (this *MockInput) End(r engine.InputRunner) {}

func (this *MockInput) CleanupForRestart() bool {
	return true
}

func (this *MockInput) Pause(r engine.InputRunner) error {
	log.Info("[%s] paused", r.Name())
	this.inChan = nil
	return nil
}

func (this *MockInput) Resume(r engine.InputRunner) error {
	log.Info("[%s] resumed", r.Name())
	this.inChan = r.Exchange().InChan()
	return nil
}

func (this *MockInput) Run(r engine.InputRunner, h engine.PluginHelper) error {
	this.inChan = r.Exchange().InChan()
	var n int64
	for {
		select {
		case <-r.Stopper():
			log.Info("[%s] %d packets injected", r.Name(), n)
			return nil

		case pack := <-this.inChan:
			pack.Payload = this.payload
			r.Exchange().Inject(pack)
			n++

			if this.sleep > 0 {
				time.Sleep(this.sleep)
			}

		default:
			runtime.Gosched()
		}
	}

	return nil
}

func init() {
	engine.RegisterPlugin("MockInput", func() engine.Plugin {
		return new(MockInput)
	})
}
<|endoftext|>


<commit_before>package main

func main() {
	println("Hello world!")
}
<commit_msg>Sketch up a simple runner in main<commit_after>package main

import "github.com/synapse-garden/iq/web"

func main() {
	iqr := web.CreateRunner(25000, nil)
	iqr.StartRun()
	println("Runner running on port 25000")
	err := <-iqr.Errors()
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package lib

import (
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/nightlyone/lockfile"
	"github.com/pivotal-golang/archiver/extractor"

	"github.com/subutai-io/base/agent/config"
	"github.com/subutai-io/base/agent/lib/container"
	"github.com/subutai-io/base/agent/lib/gpg"
	"github.com/subutai-io/base/agent/lib/template"
	"github.com/subutai-io/base/agent/log"
)

var (
	lock lockfile.Lockfile
)

func templId(templ, arch, version string, kurjun *http.Client) string {
	url := config.Management.Kurjun + "/template/info?name=" + templ + "&version=" + version + "&type=text"
	if version == "stable" || len(version) == 0 {
		url = config.Management.Kurjun + "/template/info?name=" + templ + "&type=text"
	}
	response, err := kurjun.Get(url)
	log.Debug(config.Management.Kurjun + "/template/info?name=" + templ + "&type=text")
	if log.Check(log.WarnLevel, "Getting kurjun response", err) || response.StatusCode != 200 {
		return ""
	}

	hash, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if log.Check(log.WarnLevel, "Reading response body", err) {
		return ""
	}

	log.Debug("Template id: " + string(hash))
	return string(hash)
}

func md5sum(filePath string) string {
	file, err := os.Open(filePath)
	if err != nil {
		return ""
	}
	defer file.Close()

	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return ""
	}
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func checkLocal(templ, md5, arch string) string {
	var response string
	files, _ := ioutil.ReadDir(config.Agent.LxcPrefix + "tmpdir")
	for _, f := range files {
		file := strings.Split(f.Name(), "-subutai-template_")
		if len(file) == 2 && file[0] == templ && strings.Contains(file[1], arch) {
			if len(md5) == 0 {
				fmt.Print("Cannot check md5 of local template. Trust anyway? (y/n)")
				_, err := fmt.Scanln(&response)
				log.Check(log.FatalLevel, "Reading input", err)
				if response == "y" {
					return config.Agent.LxcPrefix + "tmpdir/" + f.Name()
				}
			}
			if md5 == md5sum(config.Agent.LxcPrefix+"tmpdir/"+f.Name()) {
				return config.Agent.LxcPrefix + "tmpdir/" + f.Name()
			}
		}
	}
	return ""
}

func download(file, id string, kurjun *http.Client) string {
	out, err := os.Create(config.Agent.LxcPrefix + "tmpdir/" + file)
	log.Check(log.FatalLevel, "Creating file "+file, err)
	defer out.Close()
	response, err := kurjun.Get(config.Management.Kurjun + "/template/get?id=" + id)
	log.Check(log.FatalLevel, "Getting "+config.Management.Kurjun+"/template/get?id="+id, err)
	defer response.Body.Close()
	_, err = io.Copy(out, response.Body)
	log.Check(log.FatalLevel, "Writing file "+file, err)
	if strings.Split(id, ".")[1] == md5sum(config.Agent.LxcPrefix+"tmpdir/"+file) {
		return config.Agent.LxcPrefix + "tmpdir/" + file
	}
	log.Error("Failed to check MD5 after download. Please check your connection and try again.")
	return ""
}
func lockSubutai(file string) bool {
	lock, err := lockfile.New("/var/run/lock/subutai." + file)
	if log.Check(log.DebugLevel, "Init lock "+file, err) {
		return false
	}

	err = lock.TryLock()
	if log.Check(log.DebugLevel, "Locking file "+file, err) {
		return false
	}

	return true
}

func unlockSubutai() {
	lock.Unlock()
}

func LxcImport(templ, version, token string) {
	log.Info("Importing " + templ)
	for !lockSubutai(templ + ".import") {
		time.Sleep(time.Second * 1)
	}
	defer unlockSubutai()

	if container.IsContainer(templ) && templ == "management" && len(token) > 1 {
		gpg.ExchageAndEncrypt("management", token)
		return
	}

	if container.IsContainer(templ) {
		log.Info(templ + " container exist")
		return
	}

	fullname := templ + "-subutai-template_" + config.Template.Version + "_" + config.Template.Arch + ".tar.gz"
	// if len(token) == 0 {
	// token = gpg.GetToken()
	// }
	if len(version) == 0 && templ == "management" {
		version = config.Management.Version
	}
	kurjun := config.CheckKurjun()
	id := templId(templ, runtime.GOARCH, version, kurjun)
	md5 := ""
	if len(strings.Split(id, ".")) > 1 {
		md5 = strings.Split(id, ".")[1]
	}

	archive := checkLocal(templ, md5, runtime.GOARCH)
	if len(archive) == 0 && len(md5) != 0 {
		log.Info("Downloading " + templ)
		archive = download(fullname, id, kurjun)
	} else if len(archive) == 0 && len(md5) == 0 {
		log.Error(templ + " " + version + " template not found")
	}

	log.Info("Unpacking template " + templ)
	tgz := extractor.NewTgz()
	tgz.Extract(archive, config.Agent.LxcPrefix+"tmpdir/"+templ)
	templdir := config.Agent.LxcPrefix + "tmpdir/" + templ
	parent := container.GetConfigItem(templdir+"/config", "subutai.parent")
	if parent != "" && parent != templ && !container.IsTemplate(parent) {
		log.Info("Parent template required: " + parent)
		LxcImport(parent, "stable", token)
	}

	log.Info("Installing template " + templ)
	template.Install(parent, templ)
	// TODO following lines kept for back compatibility with old templates, should be deleted when all templates will be replaced.
	os.Rename(config.Agent.LxcPrefix+templ+"/"+templ+"-home", config.Agent.LxcPrefix+templ+"/home")
	os.Rename(config.Agent.LxcPrefix+templ+"/"+templ+"-var", config.Agent.LxcPrefix+templ+"/var")
	os.Rename(config.Agent.LxcPrefix+templ+"/"+templ+"-opt", config.Agent.LxcPrefix+templ+"/opt")

	if templ == "management" {
		template.MngInit()
	}

	container.SetContainerConf(templ, [][]string{
		{"lxc.rootfs", config.Agent.LxcPrefix + templ + "/rootfs"},
		{"lxc.mount", config.Agent.LxcPrefix + templ + "/fstab"},
		{"lxc.hook.pre-start", ""},
		{"lxc.include", config.Agent.AppPrefix + "share/lxc/config/ubuntu.common.conf"},
		{"lxc.include", config.Agent.AppPrefix + "share/lxc/config/ubuntu.userns.conf"},
		{"subutai.config.path", config.Agent.AppPrefix + "etc"},
		{"lxc.network.script.up", config.Agent.AppPrefix + "bin/create_ovs_interface"},
		{"lxc.mount.entry", config.Agent.LxcPrefix + templ + "/home home none bind,rw 0 0"},
		{"lxc.mount.entry", config.Agent.LxcPrefix + templ + "/opt opt none bind,rw 0 0"},
		{"lxc.mount.entry", config.Agent.LxcPrefix + templ + "/var var none bind,rw 0 0"},
	})
	log.Check(log.FatalLevel, "Removing temp dir "+templdir, os.RemoveAll(templdir))
}
<commit_msg>Added progress bar for import. #388<commit_after>package lib

import (
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/cheggaaa/pb"
	"github.com/nightlyone/lockfile"
	"github.com/pivotal-golang/archiver/extractor"

	"github.com/subutai-io/base/agent/config"
	"github.com/subutai-io/base/agent/lib/container"
	"github.com/subutai-io/base/agent/lib/gpg"
	"github.com/subutai-io/base/agent/lib/template"
	"github.com/subutai-io/base/agent/log"
)

var (
	lock lockfile.Lockfile
)

func templId(templ, arch, version string, kurjun *http.Client) string {
	url := config.Management.Kurjun + "/template/info?name=" + templ + "&version=" + version + "&type=text"
	if version == "stable" || len(version) == 0 {
		url = config.Management.Kurjun + "/template/info?name=" + templ + "&type=text"
	}
	response, err := kurjun.Get(url)
	log.Debug(config.Management.Kurjun + "/template/info?name=" + templ + "&type=text")
	if log.Check(log.WarnLevel, "Getting kurjun response", err) || response.StatusCode != 200 {
		return ""
	}

	hash, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if log.Check(log.WarnLevel, "Reading response body", err) {
		return ""
	}

	log.Debug("Template id: " + string(hash))
	return string(hash)
}

func md5sum(filePath string) string {
	file, err := os.Open(filePath)
	if err != nil {
		return ""
	}
	defer file.Close()

	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return ""
	}
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func checkLocal(templ, md5, arch string) string {
	var response string
	files, _ := ioutil.ReadDir(config.Agent.LxcPrefix + "tmpdir")
	for _, f := range files {
		file := strings.Split(f.Name(), "-subutai-template_")
		if len(file) == 2 && file[0] == templ && strings.Contains(file[1], arch) {
			if len(md5) == 0 {
				fmt.Print("Cannot check md5 of local template. Trust anyway? (y/n)")
				_, err := fmt.Scanln(&response)
				log.Check(log.FatalLevel, "Reading input", err)
				if response == "y" {
					return config.Agent.LxcPrefix + "tmpdir/" + f.Name()
				}
			}
			if md5 == md5sum(config.Agent.LxcPrefix+"tmpdir/"+f.Name()) {
				return config.Agent.LxcPrefix + "tmpdir/" + f.Name()
			}
		}
	}
	return ""
}

func download(file, id string, kurjun *http.Client) string {
	out, err := os.Create(config.Agent.LxcPrefix + "tmpdir/" + file)
	log.Check(log.FatalLevel, "Creating file "+file, err)
	defer out.Close()
	response, err := kurjun.Get(config.Management.Kurjun + "/template/get?id=" + id)
	log.Check(log.FatalLevel, "Getting "+config.Management.Kurjun+"/template/get?id="+id, err)
	defer response.Body.Close()

	bar := pb.New(int(response.ContentLength)).SetUnits(pb.U_BYTES)
	bar.Start()
	rd := bar.NewProxyReader(response.Body)

	_, err = io.Copy(out, rd)
	log.Check(log.FatalLevel, "Writing file "+file, err)
	if strings.Split(id, ".")[1] == md5sum(config.Agent.LxcPrefix+"tmpdir/"+file) {
		return config.Agent.LxcPrefix + "tmpdir/" + file
	}
	log.Error("Failed to check MD5 after download. Please check your connection and try again.")
	return ""
}
func lockSubutai(file string) bool {
	lock, err := lockfile.New("/var/run/lock/subutai." + file)
	if log.Check(log.DebugLevel, "Init lock "+file, err) {
		return false
	}

	err = lock.TryLock()
	if log.Check(log.DebugLevel, "Locking file "+file, err) {
		return false
	}

	return true
}

func unlockSubutai() {
	lock.Unlock()
}

func LxcImport(templ, version, token string) {
	log.Info("Importing " + templ)
	for !lockSubutai(templ + ".import") {
		time.Sleep(time.Second * 1)
	}
	defer unlockSubutai()

	if container.IsContainer(templ) && templ == "management" && len(token) > 1 {
		gpg.ExchageAndEncrypt("management", token)
		return
	}

	if container.IsContainer(templ) {
		log.Info(templ + " container exist")
		return
	}

	fullname := templ + "-subutai-template_" + config.Template.Version + "_" + config.Template.Arch + ".tar.gz"
	// if len(token) == 0 {
	// token = gpg.GetToken()
	// }
	if len(version) == 0 && templ == "management" {
		version = config.Management.Version
	}
	kurjun := config.CheckKurjun()
	id := templId(templ, runtime.GOARCH, version, kurjun)
	md5 := ""
	if len(strings.Split(id, ".")) > 1 {
		md5 = strings.Split(id, ".")[1]
	}

	archive := checkLocal(templ, md5, runtime.GOARCH)
	if len(archive) == 0 && len(md5) != 0 {
		log.Info("Downloading " + templ)
		archive = download(fullname, id, kurjun)
	} else if len(archive) == 0 && len(md5) == 0 {
		log.Error(templ + " " + version + " template not found")
	}

	log.Info("Unpacking template " + templ)
	tgz := extractor.NewTgz()
	tgz.Extract(archive, config.Agent.LxcPrefix+"tmpdir/"+templ)
	templdir := config.Agent.LxcPrefix + "tmpdir/" + templ
	parent := container.GetConfigItem(templdir+"/config", "subutai.parent")
	if parent != "" && parent != templ && !container.IsTemplate(parent) {
		log.Info("Parent template required: " + parent)
		LxcImport(parent, "stable", token)
	}

	log.Info("Installing template " + templ)
	template.Install(parent, templ)
	// TODO following lines kept for back compatibility with old templates, should be deleted when all templates will be replaced.
	os.Rename(config.Agent.LxcPrefix+templ+"/"+templ+"-home", config.Agent.LxcPrefix+templ+"/home")
	os.Rename(config.Agent.LxcPrefix+templ+"/"+templ+"-var", config.Agent.LxcPrefix+templ+"/var")
	os.Rename(config.Agent.LxcPrefix+templ+"/"+templ+"-opt", config.Agent.LxcPrefix+templ+"/opt")

	if templ == "management" {
		template.MngInit()
	}

	container.SetContainerConf(templ, [][]string{
		{"lxc.rootfs", config.Agent.LxcPrefix + templ + "/rootfs"},
		{"lxc.mount", config.Agent.LxcPrefix + templ + "/fstab"},
		{"lxc.hook.pre-start", ""},
		{"lxc.include", config.Agent.AppPrefix + "share/lxc/config/ubuntu.common.conf"},
		{"lxc.include", config.Agent.AppPrefix + "share/lxc/config/ubuntu.userns.conf"},
		{"subutai.config.path", config.Agent.AppPrefix + "etc"},
		{"lxc.network.script.up", config.Agent.AppPrefix + "bin/create_ovs_interface"},
		{"lxc.mount.entry", config.Agent.LxcPrefix + templ + "/home home none bind,rw 0 0"},
		{"lxc.mount.entry", config.Agent.LxcPrefix + templ + "/opt opt none bind,rw 0 0"},
		{"lxc.mount.entry", config.Agent.LxcPrefix + templ + "/var var none bind,rw 0 0"},
	})
	log.Check(log.FatalLevel, "Removing temp dir "+templdir, os.RemoveAll(templdir))
}
<|endoftext|>


<commit_before>package agent

import (
	"reflect"
	"sync"
	"time"

	log "github.com/coreos/fleet/Godeps/_workspace/src/github.com/golang/glog"

	"github.com/coreos/fleet/machine"
	"github.com/coreos/fleet/registry"
	"github.com/coreos/fleet/unit"
)

func NewUnitStatePublisher(mgr unit.UnitManager, reg registry.Registry, mach machine.Machine) *UnitStatePublisher {
	cache := make(map[string]*unit.UnitState)
	return &UnitStatePublisher{mgr, reg, mach, sync.RWMutex{}, cache}
}

type UnitStatePublisher struct {
	mgr  unit.UnitManager
	reg  registry.Registry
	mach machine.Machine

	mutex sync.RWMutex
	cache map[string]*unit.UnitState
}

// Run caches all of the heartbeat objects from the provided channel, publishing
// them to the Registry every 5s. Heartbeat objects are also published as they
// are received on the channel.
func (p *UnitStatePublisher) Run(beatchan <-chan *unit.UnitStateHeartbeat, stop chan bool) {
	go func() {
		tick := time.Tick(5 * time.Second)
		for {
			select {
			case <-stop:
				return
			case <-tick:
				p.PublishAll()
			}
		}
	}()

	machID := p.mach.State().ID

	for {
		select {
		case <-stop:
			return
		case bt := <-beatchan:
			if bt.State != nil {
				bt.State.MachineID = machID
			}

			p.addToCache(bt)
		}
	}
}

func (p *UnitStatePublisher) PublishAll() {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	cache := make(map[string]*unit.UnitState)
	for name, us := range p.cache {
		p.publishOne(name, us)
		if us != nil {
			cache[name] = us
		}
	}

	p.cache = cache
}

func (p *UnitStatePublisher) publishOne(name string, us *unit.UnitState) {
	if us == nil {
		log.Infof("Destroying UnitState(%s) in Registry", name)
		err := p.reg.RemoveUnitState(name)
		if err != nil {
			log.Errorf("Failed to destroy UnitState(%s) in Registry: %v", name, err)
		}
	} else {
		log.Infof("Pushing UnitState(%s) to Registry: loadState=%s, activeState=%s, subState=%s", name, us.LoadState, us.ActiveState, us.SubState)
		p.reg.SaveUnitState(name, us)
	}
}

func (p *UnitStatePublisher) addToCache(update *unit.UnitStateHeartbeat) {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	last := p.cache[update.Name]
	p.cache[update.Name] = update.State

	// As an optimization, publish changes as they flow in
	if !reflect.DeepEqual(last, update.State) {
		go p.publishOne(update.Name, update.State)
	}
}
<commit_msg>agent: unexport UnitStatePublisher.PublishAll<commit_after>package agent

import (
	"reflect"
	"sync"
	"time"

	log "github.com/coreos/fleet/Godeps/_workspace/src/github.com/golang/glog"

	"github.com/coreos/fleet/machine"
	"github.com/coreos/fleet/registry"
	"github.com/coreos/fleet/unit"
)

func NewUnitStatePublisher(mgr unit.UnitManager, reg registry.Registry, mach machine.Machine) *UnitStatePublisher {
	cache := make(map[string]*unit.UnitState)
	return &UnitStatePublisher{mgr, reg, mach, sync.RWMutex{}, cache}
}

type UnitStatePublisher struct {
	mgr  unit.UnitManager
	reg  registry.Registry
	mach machine.Machine

	mutex sync.RWMutex
	cache map[string]*unit.UnitState
}

// Run caches all of the heartbeat objects from the provided channel, publishing
// them to the Registry every 5s. Heartbeat objects are also published as they
// are received on the channel.
func (p *UnitStatePublisher) Run(beatchan <-chan *unit.UnitStateHeartbeat, stop chan bool) {
	go func() {
		tick := time.Tick(5 * time.Second)
		for {
			select {
			case <-stop:
				return
			case <-tick:
				p.publishAll()
			}
		}
	}()

	machID := p.mach.State().ID

	for {
		select {
		case <-stop:
			return
		case bt := <-beatchan:
			if bt.State != nil {
				bt.State.MachineID = machID
			}

			p.addToCache(bt)
		}
	}
}

func (p *UnitStatePublisher) publishAll() {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	cache := make(map[string]*unit.UnitState)
	for name, us := range p.cache {
		p.publishOne(name, us)
		if us != nil {
			cache[name] = us
		}
	}

	p.cache = cache
}

func (p *UnitStatePublisher) publishOne(name string, us *unit.UnitState) {
	if us == nil {
		log.Infof("Destroying UnitState(%s) in Registry", name)
		err := p.reg.RemoveUnitState(name)
		if err != nil {
			log.Errorf("Failed to destroy UnitState(%s) in Registry: %v", name, err)
		}
	} else {
		log.Infof("Pushing UnitState(%s) to Registry: loadState=%s, activeState=%s, subState=%s", name, us.LoadState, us.ActiveState, us.SubState)
		p.reg.SaveUnitState(name, us)
	}
}

func (p *UnitStatePublisher) addToCache(update *unit.UnitStateHeartbeat) {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	last := p.cache[update.Name]
	p.cache[update.Name] = update.State

	// As an optimization, publish changes as they flow in
	if !reflect.DeepEqual(last, update.State) {
		go p.publishOne(update.Name, update.State)
	}
}
<|endoftext|>


<commit_before>package quad_test

import (
	"github.com/ghthor/engine/rpg2d/coord"
	"github.com/ghthor/engine/rpg2d/quad"

	"github.com/ghthor/gospec"
	. "github.com/ghthor/gospec"
)

func DescribeQuad(c gospec.Context) {
	c.Specify("a quad tree", func() {
		q, err := quad.New(coord.Bounds{
			TopL: coord.Cell{-1000, 1000},
			BotR: coord.Cell{1000, -1000},
		}, 1, nil)
		c.Assume(err, IsNil)

		c.Specify("will subdivide", func() {
			c.Assume(len(q.Children()), Equals, 0)
			q = q.Insert(MockEntity{0, coord.Cell{0, 0}})
			q = q.Insert(MockEntity{1, coord.Cell{5, 5}})
			c.Expect(len(q.Children()), Equals, 4)
		})

		c.Specify("can remove an entity", func() {
			e := MockEntity{0, coord.Cell{0, 0}}

			c.Assume(len(q.QueryCell(e.Cell())), Equals, 0)

			q = q.Insert(e)
			c.Assume(len(q.QueryCell(e.Cell())), Equals, 1)

			q = q.Remove(e)
			c.Expect(len(q.QueryCell(e.Cell())), Equals, 0)
		})

		c.Specify("can be queried by cell", func() {
			for maxSize := 2; maxSize < 10*10; maxSize++ {
				q, err := quad.New(coord.Bounds{
					coord.Cell{-5, 5},
					coord.Cell{4, -4},
				}, maxSize, nil)
				c.Assume(err, IsNil)
				c.Assume(q.Bounds().Width(), Equals, 10)
				c.Assume(q.Bounds().Height(), Equals, 10)

				id := int64(0)
				for j := 5; j > -5; j-- {
					for i := -5; i < 5; i++ {
						q = q.Insert(MockEntity{id, coord.Cell{i, j}})
						id++
					}
				}

				id = 0
				for j := 5; j > -5; j-- {
					for i := -5; i < 5; i++ {
						c.Expect(q.QueryCell(coord.Cell{i, j})[0].Id(), Equals, id)
						id++
					}
				}

				c.Assume(len(q.QueryBounds(q.Bounds())), Equals, 10*10)

				var fn func(q quad.Quad)
				fn = func(q quad.Quad) {
					children := q.Children()
					if len(children) == 0 {
						size := len(q.QueryBounds(q.Bounds()))

						c.Expect(size, Satisfies, size <= maxSize)
					} else {
						for _, child := range children {
							fn(child)
						}
					}
				}

				fn(q)
			}
		})
	})
}
<commit_msg>Verify that my test setup was operating as expected<commit_after>package quad_test

import (
	"github.com/ghthor/engine/rpg2d/coord"
	"github.com/ghthor/engine/rpg2d/quad"

	"github.com/ghthor/gospec"
	. "github.com/ghthor/gospec"
)

func DescribeQuad(c gospec.Context) {
	c.Specify("a quad tree", func() {
		q, err := quad.New(coord.Bounds{
			TopL: coord.Cell{-1000, 1000},
			BotR: coord.Cell{1000, -1000},
		}, 1, nil)
		c.Assume(err, IsNil)

		c.Specify("will subdivide", func() {
			c.Assume(len(q.Children()), Equals, 0)
			q = q.Insert(MockEntity{0, coord.Cell{0, 0}})
			q = q.Insert(MockEntity{1, coord.Cell{5, 5}})
			c.Expect(len(q.Children()), Equals, 4)
		})

		c.Specify("can remove an entity", func() {
			e := MockEntity{0, coord.Cell{0, 0}}

			c.Assume(len(q.QueryCell(e.Cell())), Equals, 0)

			q = q.Insert(e)
			c.Assume(len(q.QueryCell(e.Cell())), Equals, 1)

			q = q.Remove(e)
			c.Expect(len(q.QueryCell(e.Cell())), Equals, 0)
		})

		c.Specify("can be queried by cell", func() {
			for maxSize := 2; maxSize < 10*10; maxSize++ {
				q, err := quad.New(coord.Bounds{
					coord.Cell{-5, 5},
					coord.Cell{4, -4},
				}, maxSize, nil)
				c.Assume(err, IsNil)
				c.Assume(q.Bounds().Width(), Equals, 10)
				c.Assume(q.Bounds().Height(), Equals, 10)

				id := int64(0)
				for j := 5; j > -5; j-- {
					for i := -5; i < 5; i++ {
						q = q.Insert(MockEntity{id, coord.Cell{i, j}})
						id++
					}
				}

				id = 0
				for j := 5; j > -5; j-- {
					for i := -5; i < 5; i++ {
						entities := q.QueryCell(coord.Cell{i, j})
						c.Assume(len(entities), Equals, 1)

						c.Expect(entities[0].Id(), Equals, id)
						id++
					}
				}

				c.Assume(len(q.QueryBounds(q.Bounds())), Equals, 10*10)

				var fn func(q quad.Quad)
				fn = func(q quad.Quad) {
					children := q.Children()
					if len(children) == 0 {
						size := len(q.QueryBounds(q.Bounds()))

						c.Expect(size, Satisfies, size <= maxSize)
					} else {
						for _, child := range children {
							fn(child)
						}
					}
				}

				fn(q)
			}
		})
	})
}
<|endoftext|>


<commit_before>package automation

import (
	"errors"
	"testing"

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

type mockAllCompletedService struct {
	GoogleService
}

func (s *mockAllCompletedService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	var result []*Requirement
	for _, api := range apis {
		result = append(result, &Requirement{Name: api, Status: RequirementCompleted})
	}
	return result, nil
}

func (s *mockAllCompletedService) ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error) {
	var result []*Requirement
	for _, perm := range permissions {
		result = append(result, &Requirement{Name: perm[0], Status: RequirementCompleted})
	}
	return result, nil
}

func TestAllCompleted(t *testing.T) {
	mock := &mockAllCompletedService{}
	reqs, err := ListProjectRequirements(mock, "")
	if assert.NoError(t, err, "No error from ListProjectrequierements expected") {
		var actualNames, expectedNames []string
		for _, req := range reqs {
			assert.Equal(t, RequirementCompleted, req.Status, "Requirements should be compeleted")
			actualNames = append(actualNames, req.Name)
		}
		for _, api := range requiredAPIs {
			expectedNames = append(expectedNames, api)
		}
		for _, perm := range requiredPermissions {
			expectedNames = append(expectedNames, perm[0])
		}
		assert.EqualValues(t, expectedNames, actualNames, "Requirements should contain all APIs and permissions")
	}
}

type mockService struct {
	GoogleService
	apiReqs               []*Requirement
	permissionReqs        []*Requirement
	listPermissionsCalled bool
}

func (s *mockService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	return s.apiReqs, nil
}

func (s *mockService) ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error) {
	s.listPermissionsCalled = true
	return s.permissionReqs, nil
}

var failedRequirements = []*Requirement{&Requirement{Status: RequirementFailed}}

func TestFailAPIRequirements(t *testing.T) {
	mock := &mockService{apiReqs: failedRequirements}

	reqs, err := ListProjectRequirements(mock, "")
	if assert.NoError(t, err, "ListProjectRequirements should not return error") {
		assert.EqualValues(t, mock.apiReqs, reqs, "ListProjectRequirements should return api requirements")
		assert.False(t, mock.listPermissionsCalled, "ListPermissionRequirements should not be called if api reqs are failed")
	}
}

func TestFailPermissionRequirements(t *testing.T) {
	mock := &mockService{apiReqs: []*Requirement{&Requirement{Status: RequirementCompleted}},
		permissionReqs: failedRequirements}

	reqs, err := ListProjectRequirements(mock, "")
	if assert.NoError(t, err, "ListProjectRequirements should not return error") {
		assert.EqualValues(t, append(mock.apiReqs, mock.permissionReqs...), reqs,
			"ListProjectRequirements should return api & permission requirements, if api requirements are completed")
	}
}

type errorAPIService struct {
	GoogleService
	err error
}

func (s *errorAPIService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	return nil, s.err
}

type errorPermissionService struct {
	GoogleService
	err error
}

func (s *errorPermissionService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	return nil, nil
}

func (s *errorPermissionService) ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error) {
	return nil, s.err
}

func TestErrorAPIRequirements(t *testing.T) {
	for _, mock := range []GoogleService{&errorAPIService{err: errors.New("hi! i'm error")},
		&errorPermissionService{err: errors.New("another error")}} {
		reqs, err := ListProjectRequirements(mock, "")
		if assert.Error(t, err, "ListProjectRequirements should result in error") {
			assert.Nil(t, reqs, "Only one of returned values should be non-nil")
		}
	}
}
<commit_msg>add tests for listRequirements<commit_after>package automation

import (
	"errors"
	"fmt"
	"math/rand"
	"testing"

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

type mockAllCompletedService struct {
	GoogleService
}

func (s *mockAllCompletedService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	var result []*Requirement
	for _, api := range apis {
		result = append(result, &Requirement{Name: api, Status: RequirementCompleted})
	}
	return result, nil
}

func (s *mockAllCompletedService) ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error) {
	var result []*Requirement
	for _, perm := range permissions {
		result = append(result, &Requirement{Name: perm[0], Status: RequirementCompleted})
	}
	return result, nil
}

func checkAllRequirementsCompleted(t *testing.T, reqs []*Requirement) {
	var actualNames, expectedNames []string
	for _, req := range reqs {
		assert.Equal(t, RequirementCompleted, req.Status, "Requirements should be compeleted")
		actualNames = append(actualNames, req.Name)
	}
	for _, api := range requiredAPIs {
		expectedNames = append(expectedNames, api)
	}
	for _, perm := range requiredPermissions {
		expectedNames = append(expectedNames, perm[0])
	}
	assert.EqualValues(t, expectedNames, actualNames, "Requirements should contain all APIs and permissions")
}

func TestAllCompleted(t *testing.T) {
	mock := &mockAllCompletedService{}
	reqs, err := ListProjectRequirements(mock, "")
	if assert.NoError(t, err, "No error from ListProjectrequierements expected") {
		checkAllRequirementsCompleted(t, reqs)
	}
}

type mockService struct {
	GoogleService
	apiReqs               []*Requirement
	permissionReqs        []*Requirement
	listPermissionsCalled bool
}

func (s *mockService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	return s.apiReqs, nil
}

func (s *mockService) ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error) {
	s.listPermissionsCalled = true
	return s.permissionReqs, nil
}

var failedRequirements = []*Requirement{&Requirement{Status: RequirementFailed}}

func TestFailAPIRequirements(t *testing.T) {
	mock := &mockService{apiReqs: failedRequirements}

	reqs, err := ListProjectRequirements(mock, "")
	if assert.NoError(t, err, "ListProjectRequirements should not return error") {
		assert.EqualValues(t, mock.apiReqs, reqs, "ListProjectRequirements should return api requirements")
		assert.False(t, mock.listPermissionsCalled, "ListPermissionRequirements should not be called if api reqs are failed")
	}
}

func TestFailPermissionRequirements(t *testing.T) {
	mock := &mockService{apiReqs: []*Requirement{&Requirement{Status: RequirementCompleted}},
		permissionReqs: failedRequirements}

	reqs, err := ListProjectRequirements(mock, "")
	if assert.NoError(t, err, "ListProjectRequirements should not return error") {
		assert.EqualValues(t, append(mock.apiReqs, mock.permissionReqs...), reqs,
			"ListProjectRequirements should return api & permission requirements, if api requirements are completed")
	}
}

type errorAPIService struct {
	GoogleService
	err error
}

func (s *errorAPIService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	return nil, s.err
}

type errorPermissionService struct {
	GoogleService
	err error
}

func (s *errorPermissionService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	return nil, nil
}

func (s *errorPermissionService) ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error) {
	return nil, s.err
}

func TestErrorAPIRequirements(t *testing.T) {
	for _, mock := range []GoogleService{&errorAPIService{err: errors.New("hi! i'm error")},
		&errorPermissionService{err: errors.New("another error")}} {
		reqs, err := ListProjectRequirements(mock, "")
		if assert.Error(t, err, "ListProjectRequirements should result in error") {
			assert.Nil(t, reqs, "Only one of returned values should be non-nil")
		}
	}
}

const (
	errorProject  = "err"
	failedProject = "fail"
)

type mockProjectService struct {
	GoogleService
}

func getService(project string) GoogleService {
	var service GoogleService
	switch project {
	case errorProject:
		service = &errorAPIService{err: errors.New("")}
	case failedProject:
		service = &mockService{apiReqs: failedRequirements}
	default:
		service = &mockAllCompletedService{}
	}
	return service
}

func (s *mockProjectService) ListAPIRequirements(project string, apis []string) ([]*Requirement, error) {
	rec, err := getService(project).ListAPIRequirements(project, apis)
	return rec, err
}

func (s *mockProjectService) ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error) {
	rec, err := getService(project).ListPermissionRequirements(project, permissions)
	return rec, err
}

func TestListRequirements(t *testing.T) {
	rand.Seed(42)
	for numProjects := 0; numProjects < 5; numProjects++ {
		for i := 0; i < 100; i++ {
			var projects []string
			for ind := 0; ind < numProjects; ind++ {
				project := fmt.Sprintf("project %d", ind)
				if rand.Int()%2 == 0 {
					project = failedProject
				}
				projects = append(projects, project)
			}

			task := &Task{}
			reqs, err := ListRequirements(&mockProjectService{}, projects, task)
			if assert.NoError(t, err, "No error expected from ListRequirements") {
				var actualProjects []string
				for _, req := range reqs {
					actualProjects = append(actualProjects, req.Project)
					if req.Project == failedProject {
						assert.EqualValues(t, failedRequirements, req.Requirements, "Should be equal to failed requirements for this project")
					} else {
						checkAllRequirementsCompleted(t, req.Requirements)
					}
				}
				assert.EqualValues(t, projects, actualProjects, "Should contain the same projects")
				done, all := task.GetProgress()
				assert.True(t, done == all, "ListRequirements should be done now")
			}
		}
	}
}

func TestErrorListRequirements(t *testing.T) {
	projects := []string{"ok", errorProject, failedProject}
	task := &Task{}
	reqs, err := ListRequirements(&mockProjectService{}, projects, task)
	if assert.Error(t, err) {
		assert.Nil(t, reqs, "Only one value should be non-nil")
		done, all := task.GetProgress()
		assert.True(t, done < all, "ListRequirements should not be done because of error")
	}
}
<|endoftext|>


<commit_before>package types

import (
	"github.com/atlassian/smith/pkg/cleanup"
	"github.com/atlassian/smith/pkg/util"

	sc_v1b1 "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1"
	"github.com/pkg/errors"
	apps_v1 "k8s.io/api/apps/v1"
	core_v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

var (
	MainKnownTypes = map[schema.GroupKind]cleanup.SpecCleanup{
		{Group: apps_v1.GroupName, Kind: "Deployment"}: deploymentCleanup,
		{Group: core_v1.GroupName, Kind: "Service"}:    serviceCleanup,
		{Group: core_v1.GroupName, Kind: "Secret"}:     secretCleanup,
	}

	ServiceCatalogKnownTypes = map[schema.GroupKind]cleanup.SpecCleanup{
		{Group: sc_v1b1.GroupName, Kind: "ServiceBinding"}:  scServiceBindingCleanup,
		{Group: sc_v1b1.GroupName, Kind: "ServiceInstance"}: scServiceInstanceCleanup,
	}

	apps_v1_scheme = runtime.NewScheme()
	sc_v1b1_scheme = runtime.NewScheme()
	core_v1_scheme = runtime.NewScheme()
)

func init() {
	err := apps_v1.SchemeBuilder.AddToScheme(apps_v1_scheme)
	if err != nil {
		panic(err)
	}
	err = sc_v1b1.SchemeBuilder.AddToScheme(sc_v1b1_scheme)
	if err != nil {
		panic(err)
	}
	err = core_v1.SchemeBuilder.AddToScheme(core_v1_scheme)
	if err != nil {
		panic(err)
	}
}

func deploymentCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var deploymentSpec apps_v1.Deployment
	if err := util.ConvertType(apps_v1_scheme, spec, &deploymentSpec); err != nil {
		return nil, err
	}

	deploymentSpec.Spec.Template.Spec.DeprecatedServiceAccount = deploymentSpec.Spec.Template.Spec.ServiceAccountName

	return util.RuntimeToUnstructured(&deploymentSpec)
}

func serviceCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var serviceSpec core_v1.Service
	if err := util.ConvertType(core_v1_scheme, spec, &serviceSpec); err != nil {
		return nil, err
	}
	var serviceActual core_v1.Service
	if err := util.ConvertType(core_v1_scheme, actual, &serviceActual); err != nil {
		return nil, err
	}

	serviceSpec.Spec.ClusterIP = serviceActual.Spec.ClusterIP
	serviceSpec.Status = serviceActual.Status

	if len(serviceActual.Spec.Ports) == len(serviceSpec.Spec.Ports) {
		for i, port := range serviceSpec.Spec.Ports {
			if port.NodePort == 0 {
				actualPort := serviceActual.Spec.Ports[i]
				port.NodePort = actualPort.NodePort
				if port == actualPort { // NodePort field is the only difference, other fields are the same
					// Copy port from actual if port is not specified in spec
					serviceSpec.Spec.Ports[i].NodePort = actualPort.NodePort
				}
			}
		}
	}

	return &serviceSpec, nil
}

func secretCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var secretSpec core_v1.Secret
	if err := util.ConvertType(core_v1_scheme, spec, &secretSpec); err != nil {
		return nil, err
	}

	// StringData overwrites Data
	if len(secretSpec.StringData) > 0 {
		if secretSpec.Data == nil {
			secretSpec.Data = make(map[string][]byte, len(secretSpec.StringData))
		}
		for k, v := range secretSpec.StringData {
			secretSpec.Data[k] = []byte(v)
		}
		secretSpec.StringData = nil
	}

	return &secretSpec, nil
}

func scServiceBindingCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var sbSpec sc_v1b1.ServiceBinding
	if err := util.ConvertType(sc_v1b1_scheme, spec, &sbSpec); err != nil {
		return nil, err
	}
	var sbActual sc_v1b1.ServiceBinding
	if err := util.ConvertType(sc_v1b1_scheme, actual, &sbActual); err != nil {
		return nil, err
	}

	sbSpec.Spec.ExternalID = sbActual.Spec.ExternalID
	if sbActual.Spec.UserInfo != nil {
		sbSpec.Spec.UserInfo = sbActual.Spec.UserInfo.DeepCopy()
	}
	sbSpec.Status = sbActual.Status

	return &sbSpec, nil
}

func scServiceInstanceCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var instanceSpec sc_v1b1.ServiceInstance
	if err := util.ConvertType(sc_v1b1_scheme, spec, &instanceSpec); err != nil {
		return nil, err
	}
	var instanceActual sc_v1b1.ServiceInstance
	if err := util.ConvertType(sc_v1b1_scheme, actual, &instanceActual); err != nil {
		return nil, err
	}

	if instanceSpec.Spec.ClusterServiceClassName != "" &&
		instanceSpec.Spec.ClusterServiceClassName != instanceActual.Spec.ClusterServiceClassName {
		return nil, errors.New("clusterServiceClassName has changed when it should be immutable")
	}

	if instanceSpec.Spec.ClusterServicePlanName != "" &&
		instanceSpec.Spec.ClusterServicePlanName != instanceActual.Spec.ClusterServicePlanName {
		return nil, errors.New("clusterServicePlanName has changed when it should be immutable")
	}

	if instanceActual.Spec.ClusterServiceClassExternalName == instanceSpec.Spec.ClusterServiceClassExternalName {
		instanceSpec.Spec.ClusterServiceClassRef = instanceActual.Spec.ClusterServiceClassRef
		instanceSpec.Spec.ClusterServiceClassName = instanceActual.Spec.ClusterServiceClassName
	}

	if instanceActual.Spec.ClusterServicePlanExternalName == instanceSpec.Spec.ClusterServicePlanExternalName {
		instanceSpec.Spec.ClusterServicePlanRef = instanceActual.Spec.ClusterServicePlanRef
		instanceSpec.Spec.ClusterServicePlanName = instanceActual.Spec.ClusterServicePlanName
	}

	instanceSpec.ObjectMeta.Finalizers = instanceActual.ObjectMeta.Finalizers

	instanceSpec.Spec.ExternalID = instanceActual.Spec.ExternalID
	if instanceActual.Spec.UserInfo != nil {
		instanceSpec.Spec.UserInfo = instanceActual.Spec.UserInfo.DeepCopy()
	}

	return &instanceSpec, nil
}
<commit_msg>No need for extra deep copy<commit_after>package types

import (
	"github.com/atlassian/smith/pkg/cleanup"
	"github.com/atlassian/smith/pkg/util"

	sc_v1b1 "github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1"
	"github.com/pkg/errors"
	apps_v1 "k8s.io/api/apps/v1"
	core_v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

var (
	MainKnownTypes = map[schema.GroupKind]cleanup.SpecCleanup{
		{Group: apps_v1.GroupName, Kind: "Deployment"}: deploymentCleanup,
		{Group: core_v1.GroupName, Kind: "Service"}:    serviceCleanup,
		{Group: core_v1.GroupName, Kind: "Secret"}:     secretCleanup,
	}

	ServiceCatalogKnownTypes = map[schema.GroupKind]cleanup.SpecCleanup{
		{Group: sc_v1b1.GroupName, Kind: "ServiceBinding"}:  scServiceBindingCleanup,
		{Group: sc_v1b1.GroupName, Kind: "ServiceInstance"}: scServiceInstanceCleanup,
	}

	apps_v1_scheme = runtime.NewScheme()
	sc_v1b1_scheme = runtime.NewScheme()
	core_v1_scheme = runtime.NewScheme()
)

func init() {
	err := apps_v1.SchemeBuilder.AddToScheme(apps_v1_scheme)
	if err != nil {
		panic(err)
	}
	err = sc_v1b1.SchemeBuilder.AddToScheme(sc_v1b1_scheme)
	if err != nil {
		panic(err)
	}
	err = core_v1.SchemeBuilder.AddToScheme(core_v1_scheme)
	if err != nil {
		panic(err)
	}
}

func deploymentCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var deploymentSpec apps_v1.Deployment
	if err := util.ConvertType(apps_v1_scheme, spec, &deploymentSpec); err != nil {
		return nil, err
	}

	deploymentSpec.Spec.Template.Spec.DeprecatedServiceAccount = deploymentSpec.Spec.Template.Spec.ServiceAccountName

	return util.RuntimeToUnstructured(&deploymentSpec)
}

func serviceCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var serviceSpec core_v1.Service
	if err := util.ConvertType(core_v1_scheme, spec, &serviceSpec); err != nil {
		return nil, err
	}
	var serviceActual core_v1.Service
	if err := util.ConvertType(core_v1_scheme, actual, &serviceActual); err != nil {
		return nil, err
	}

	serviceSpec.Spec.ClusterIP = serviceActual.Spec.ClusterIP
	serviceSpec.Status = serviceActual.Status

	if len(serviceActual.Spec.Ports) == len(serviceSpec.Spec.Ports) {
		for i, port := range serviceSpec.Spec.Ports {
			if port.NodePort == 0 {
				actualPort := serviceActual.Spec.Ports[i]
				port.NodePort = actualPort.NodePort
				if port == actualPort { // NodePort field is the only difference, other fields are the same
					// Copy port from actual if port is not specified in spec
					serviceSpec.Spec.Ports[i].NodePort = actualPort.NodePort
				}
			}
		}
	}

	return &serviceSpec, nil
}

func secretCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var secretSpec core_v1.Secret
	if err := util.ConvertType(core_v1_scheme, spec, &secretSpec); err != nil {
		return nil, err
	}

	// StringData overwrites Data
	if len(secretSpec.StringData) > 0 {
		if secretSpec.Data == nil {
			secretSpec.Data = make(map[string][]byte, len(secretSpec.StringData))
		}
		for k, v := range secretSpec.StringData {
			secretSpec.Data[k] = []byte(v)
		}
		secretSpec.StringData = nil
	}

	return &secretSpec, nil
}

func scServiceBindingCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var sbSpec sc_v1b1.ServiceBinding
	if err := util.ConvertType(sc_v1b1_scheme, spec, &sbSpec); err != nil {
		return nil, err
	}
	var sbActual sc_v1b1.ServiceBinding
	if err := util.ConvertType(sc_v1b1_scheme, actual, &sbActual); err != nil {
		return nil, err
	}

	sbSpec.Spec.ExternalID = sbActual.Spec.ExternalID
	if sbActual.Spec.UserInfo != nil {
		sbSpec.Spec.UserInfo = sbActual.Spec.UserInfo.DeepCopy()
	}
	sbSpec.Status = sbActual.Status

	return &sbSpec, nil
}

func scServiceInstanceCleanup(spec, actual *unstructured.Unstructured) (runtime.Object, error) {
	var instanceSpec sc_v1b1.ServiceInstance
	if err := util.ConvertType(sc_v1b1_scheme, spec, &instanceSpec); err != nil {
		return nil, err
	}
	var instanceActual sc_v1b1.ServiceInstance
	if err := util.ConvertType(sc_v1b1_scheme, actual, &instanceActual); err != nil {
		return nil, err
	}

	if instanceSpec.Spec.ClusterServiceClassName != "" &&
		instanceSpec.Spec.ClusterServiceClassName != instanceActual.Spec.ClusterServiceClassName {
		return nil, errors.New("clusterServiceClassName has changed when it should be immutable")
	}

	if instanceSpec.Spec.ClusterServicePlanName != "" &&
		instanceSpec.Spec.ClusterServicePlanName != instanceActual.Spec.ClusterServicePlanName {
		return nil, errors.New("clusterServicePlanName has changed when it should be immutable")
	}

	if instanceActual.Spec.ClusterServiceClassExternalName == instanceSpec.Spec.ClusterServiceClassExternalName {
		instanceSpec.Spec.ClusterServiceClassRef = instanceActual.Spec.ClusterServiceClassRef
		instanceSpec.Spec.ClusterServiceClassName = instanceActual.Spec.ClusterServiceClassName
	}

	if instanceActual.Spec.ClusterServicePlanExternalName == instanceSpec.Spec.ClusterServicePlanExternalName {
		instanceSpec.Spec.ClusterServicePlanRef = instanceActual.Spec.ClusterServicePlanRef
		instanceSpec.Spec.ClusterServicePlanName = instanceActual.Spec.ClusterServicePlanName
	}

	instanceSpec.ObjectMeta.Finalizers = instanceActual.ObjectMeta.Finalizers

	instanceSpec.Spec.ExternalID = instanceActual.Spec.ExternalID
	if instanceActual.Spec.UserInfo != nil {
		instanceSpec.Spec.UserInfo = instanceActual.Spec.UserInfo
	}

	return &instanceSpec, nil
}
<|endoftext|>