<commit_before>package movie_test

import (
	"reflect"
	"testing"
	"time"

	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/movie"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/google/uuid"
	"github.com/pkg/errors"
)

// Returns a valid User with mocked data
func newValidUser() user.User {
	return user.User{
		Email:        "foo@bar.com",
		LastName:     "Bar",
		FirstName:    "Foo",
		FullName:     "Foo Bar",
		HostedDomain: "example.com",
		PictureURL:   "example.com/profile.png",
		ProfileLink:  "example.com/FooBar",
	}
}

func NewValidUserMovie() (user.User, *movie.Movie) {
	uid := uuid.New()
	externalID := "ExternalID"

	u := newValidUser()

	m, _ := movie.NewMovie(uid, externalID, &u)

	return u, m
}

// Returns an invalid user defined by the method user.IsValid()
func newInvalidUser() user.User {
	return user.User{
		Email:        "",
		LastName:     "",
		FirstName:    "",
		FullName:     "",
		HostedDomain: "example.com",
		PictureURL:   "example.com/profile.png",
		ProfileLink:  "example.com/FooBar",
	}
}

// Testing error when sent a nil uuid
func TestNewMovieErrorUuid(t *testing.T) {
	t.Helper()

	u := newValidUser()
	wantError := errs.E(errs.Validation, errs.Parameter("ID"), errors.New(errs.MissingField("ID").Error()))
	if gotMovie, gotError := movie.NewMovie(uuid.UUID{}, "randomExternalId", &u); !reflect.DeepEqual(wantError.Error(), gotError.Error()) && gotMovie != nil {
		t.Errorf("Want: %v\nGot: %v", wantError, gotError)
	}
}

// Testing error when sent a nil ExtlID
func TestNewMovieErrorExtlID(t *testing.T) {
	t.Helper()

	u := newValidUser()
	uid, _ := uuid.NewUUID()
	wantError := errs.E(errs.Validation, errs.Parameter("ID"), errors.New(errs.MissingField("ID").Error()))
	if gotMovie, gotError := movie.NewMovie(uid, "", &u); !reflect.DeepEqual(wantError.Error(), gotError.Error()) && gotMovie != nil {
		t.Errorf("Want: %v\nGot: %v", wantError, gotError)
	}
}

// Testing error when User invalid
func TestNewMovieErrorInvalidUser(t *testing.T) {
	t.Helper()

	u := newInvalidUser()
	uid, _ := uuid.NewUUID()

	wantError := errs.E(errs.Validation, errs.Parameter("User"), errors.New("User is invalid"))

	if gotMovie, gotError := movie.NewMovie(uid, "externalID", &u); !reflect.DeepEqual(wantError.Error(), gotError.Error()) && gotMovie != nil {
		t.Errorf("Want: %v\nGot: %v", wantError, gotError)
	}
}

// Testing creating NewMovie
func TestNewMovie(t *testing.T) {
	t.Helper()

	u := newValidUser()
	uid, _ := uuid.NewUUID()
	externalID := "externalID"

	wantMovie := movie.Movie{
		ID:         uid,
		ExternalID: externalID,
		CreateUser: u,
		UpdateUser: u,
	}
	gotMovie, gotError := movie.NewMovie(uid, externalID, &u)
	if gotError != nil {

		if gotMovie.ID != uid {
			t.Errorf("Want: %v\nGot: %v\n\n", wantMovie.ID, gotMovie.ID)
		}
		if gotMovie.ExternalID != wantMovie.ExternalID {
			t.Errorf("Want: %v\nGot: %v\n\n", wantMovie.ExternalID, gotMovie.ExternalID)
		}
		if gotMovie.CreateUser != wantMovie.CreateUser {
			t.Errorf("Want: %v\nGot: %v\n\n", wantMovie.CreateUser, gotMovie.CreateUser)
		}
		if gotMovie.UpdateUser != wantMovie.UpdateUser {
			t.Errorf("Want: %v\nGot: %v\n\n", wantMovie.UpdateUser, gotMovie.UpdateUser)
		}
	}
}

func TestSetExternalID(t *testing.T) {
	_, gotMovie := NewValidUserMovie()
	externalID2 := "externalIDUpdated"

	gotMovie.SetExternalID(externalID2)

	if gotMovie.ExternalID != externalID2 {
		t.Errorf("Want: %v\nGot: %v\n\n", gotMovie.ExternalID, externalID2)
	}
}

func TestSetTitle(t *testing.T) {
	_, gotMovie := NewValidUserMovie()
	Title := "Movie Title"

	gotMovie.SetTitle(Title)

	if gotMovie.Title != Title {
		t.Errorf("Want: %v\nGot: %v\n\n", gotMovie.Title, Title)
	}
}

func TestSetRated(t *testing.T) {
	_, gotMovie := NewValidUserMovie()
	Rated := "R"

	gotMovie.SetRated(Rated)

	if gotMovie.Rated != Rated {
		t.Errorf("Want: %v\nGot: %v\n\n", gotMovie.Rated, Rated)
	}
}

func TestSetReleasedOk(t *testing.T) {
	newReleased := "1984-01-02T15:04:05Z"
	r, err := time.Parse(time.RFC3339, newReleased)
	if err != nil {
		t.Fatalf("time.Parse() error = %v", err)
	}

	_, gotMovie := NewValidUserMovie()

	gotMovie, _ = gotMovie.SetReleased(newReleased)

	if gotMovie.Released != r {
		t.Errorf("Want: %v\nGot: %v\n\n", newReleased, gotMovie.Released)
	}
}

func TestSetReleasedWrong(t *testing.T) {
	newRealeased := "wrong-time"

	_, gotMovie := NewValidUserMovie()

	_, e := gotMovie.SetReleased(newRealeased)
	_, err := time.Parse(time.RFC3339, newRealeased)

	want := errs.E(errs.Validation,
		errs.Code("invalid_date_format"),
		errs.Parameter("release_date"),
		errors.WithStack(err))

	if e.Error() != want.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", want, e)
	}
}

func TestSetRunTime(t *testing.T) {
	rt := 1999

	_, gotMovie := NewValidUserMovie()

	gotMovie.SetRunTime(rt)

	if gotMovie.RunTime != rt {
		t.Errorf("\nWant: %v\nGot: %v\n\n", rt, gotMovie.RunTime)
	}
}

func TestSetDirector(t *testing.T) {
	d := "Director Drach"

	_, gotMovie := NewValidUserMovie()

	gotMovie.SetDirector(d)

	if gotMovie.Director != d {
		t.Errorf("\nWant: %v\nGot: %v\n\n", d, gotMovie.Director)
	}
}

func TestSetWriter(t *testing.T) {
	w := "Writer Drach"

	_, gotMovie := NewValidUserMovie()

	gotMovie.SetWriter(w)

	if gotMovie.Writer != w {
		t.Errorf("\nWant: %v\nGot: %v\n\n", w, gotMovie.Writer)
	}
}

func TestSetUpdateUser(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	newUser := user.User{
		Email:        "foo2@bar.com",
		LastName:     "Barw",
		FirstName:    "Foow",
		FullName:     "Foow Barw",
		HostedDomain: "example.com.br",
		PictureURL:   "example.com.br/profile-we.png",
		ProfileLink:  "example.com.br/FoowBar",
	}

	gotMovie.SetUpdateUser(&newUser)

	if gotMovie.UpdateUser != newUser {
		t.Errorf("\nWant: %v\nGot: %v\n\n", newUser, gotMovie.UpdateUser)
	}
}

func TestSetUpdateTime(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	oldTime := gotMovie.UpdateTime

	gotMovie.SetUpdateTime()

	oldTimeSeconds := time.Since(oldTime).Seconds()
	updatedTimeSeconds := time.Since(gotMovie.UpdateTime).Seconds()

	if oldTimeSeconds < updatedTimeSeconds {
		t.Errorf("Previous time '%v' should be lower than '%v'", oldTimeSeconds, updatedTimeSeconds)
	}
}

func TestValidMovie(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	gotMovie, _ = gotMovie.SetReleased("1996-12-19T16:39:57-08:00")

	gotMovie.
		SetTitle("Movie Title").
		SetRated("R").
		SetRunTime(19).
		SetDirector("Movie Director").
		SetWriter("Movie Writer")

	if gotMovie.IsValid() != nil {
		t.Errorf("\nWant: %v\nGot: %v\n\n", nil, gotMovie.IsValid())
	}
}

func TestInvalidMovieTitle(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	wantErr := errs.E(errs.Validation, errs.Parameter("title"), errs.MissingField("title"))

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieRated(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	gotMovie.SetTitle("Movie Title")

	wantErr := errs.E(errs.Validation, errs.Parameter("rated"), errs.MissingField("Rated"))

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieReleased(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	gotMovie.
		SetTitle("Movie Title").
		SetRated("R")

	wantErr := errs.E(errs.Validation, errs.Parameter("release_date"), "Released must have a value")

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieRunTime(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	gotMovie, _ = gotMovie.SetReleased("1996-12-19T16:39:57-08:00")
	gotMovie.
		SetTitle("Movie Title").
		SetRated("R")

	wantErr := errs.E(errs.Validation, errs.Parameter("run_time"), "Run time must be greater than zero")

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieDirector(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	gotMovie, _ = gotMovie.SetReleased("1996-12-19T16:39:57-08:00")
	gotMovie.
		SetTitle("Movie Title").
		SetRated("R").
		SetRunTime(19)

	wantErr := errs.E(errs.Validation, errs.Parameter("director"), errs.MissingField("Director"))

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieWriter(t *testing.T) {
	_, gotMovie := NewValidUserMovie()

	gotMovie, _ = gotMovie.SetReleased("1996-12-19T16:39:57-08:00")
	gotMovie.
		SetTitle("Movie Title").
		SetRated("R").
		SetRunTime(19).
		SetDirector("Movie Director")

	wantErr := errs.E(errs.Validation, errs.Parameter("writer"), errs.MissingField("Writer"))

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}
<commit_msg>Update NewValidUserMovie to NewValidMovie<commit_after>package movie_test

import (
	qt "github.com/frankban/quicktest"
	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/movie"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/google/uuid"
	"github.com/pkg/errors"
	"reflect"
	"testing"
	"time"
)

// Returns a valid User with mocked data
func newValidUser() user.User {
	return user.User{
		Email:        "foo@bar.com",
		LastName:     "Bar",
		FirstName:    "Foo",
		FullName:     "Foo Bar",
		HostedDomain: "example.com",
		PictureURL:   "example.com/profile.png",
		ProfileLink:  "example.com/FooBar",
	}
}

func NewValidMovie() *movie.Movie {
	uid := uuid.New()
	externalID := "ExternalID"

	u := newValidUser()

	m, _ := movie.NewMovie(uid, externalID, &u)

	return m
}

// Returns an invalid user defined by the method user.IsValid()
func newInvalidUser() user.User {
	return user.User{
		Email:        "",
		LastName:     "",
		FirstName:    "",
		FullName:     "",
		HostedDomain: "example.com",
		PictureURL:   "example.com/profile.png",
		ProfileLink:  "example.com/FooBar",
	}
}

// Testing error when sent a nil uuid
func TestNewMovieErrorUuid(t *testing.T) {
	t.Helper()

	u := newValidUser()
	wantError := errs.E(errs.Validation, errs.Parameter("ID"), errors.New(errs.MissingField("ID").Error()))
	if gotMovie, gotError := movie.NewMovie(uuid.UUID{}, "randomExternalId", &u); !reflect.DeepEqual(wantError.Error(), gotError.Error()) && gotMovie != nil {
		t.Errorf("Want: %v\nGot: %v", wantError, gotError)
	}
}

// Testing error when sent a nil ExtlID
func TestNewMovieErrorExtlID(t *testing.T) {
	t.Helper()

	u := newValidUser()
	uid, _ := uuid.NewUUID()
	wantError := errs.E(errs.Validation, errs.Parameter("ID"), errors.New(errs.MissingField("ID").Error()))
	if gotMovie, gotError := movie.NewMovie(uid, "", &u); !reflect.DeepEqual(wantError.Error(), gotError.Error()) && gotMovie != nil {
		t.Errorf("Want: %v\nGot: %v", wantError, gotError)
	}
}

// Testing error when User invalid
func TestNewMovieErrorInvalidUser(t *testing.T) {
	t.Helper()

	u := newInvalidUser()
	uid, _ := uuid.NewUUID()

	wantError := errs.E(errs.Validation, errs.Parameter("User"), errors.New("User is invalid"))

	if gotMovie, gotError := movie.NewMovie(uid, "externalID", &u); !reflect.DeepEqual(wantError.Error(), gotError.Error()) && gotMovie != nil {
		t.Errorf("Want: %v\nGot: %v", wantError, gotError)
	}
}

// Testing creating NewMovie
func TestNewMovie(t *testing.T) {
	t.Helper()

	u := newValidUser()
	uid, _ := uuid.NewUUID()
	externalID := "externalID"

	wantMovie := movie.Movie{
		ID:         uid,
		ExternalID: externalID,
		CreateUser: u,
		UpdateUser: u,
	}
	gotMovie, gotError := movie.NewMovie(uid, externalID, &u)
	if gotError != nil {

		if gotMovie.ID != uid {
			t.Errorf("Want: %v\nGot: %v\n\n", wantMovie.ID, gotMovie.ID)
		}
		if gotMovie.ExternalID != wantMovie.ExternalID {
			t.Errorf("Want: %v\nGot: %v\n\n", wantMovie.ExternalID, gotMovie.ExternalID)
		}
		if gotMovie.CreateUser != wantMovie.CreateUser {
			t.Errorf("Want: %v\nGot: %v\n\n", wantMovie.CreateUser, gotMovie.CreateUser)
		}
		if gotMovie.UpdateUser != wantMovie.UpdateUser {
			t.Errorf("Want: %v\nGot: %v\n\n", wantMovie.UpdateUser, gotMovie.UpdateUser)
		}
	}
}

func TestSetExternalID(t *testing.T) {
	gotMovie := NewValidMovie()
	externalID2 := "externalIDUpdated"

	gotMovie.SetExternalID(externalID2)

	if gotMovie.ExternalID != externalID2 {
		t.Errorf("Want: %v\nGot: %v\n\n", gotMovie.ExternalID, externalID2)
	}
}

func TestSetTitle(t *testing.T) {
	gotMovie := NewValidMovie()
	Title := "Movie Title"

	gotMovie.SetTitle(Title)

	if gotMovie.Title != Title {
		t.Errorf("Want: %v\nGot: %v\n\n", gotMovie.Title, Title)
	}
}

func TestSetRated(t *testing.T) {
	gotMovie := NewValidMovie()
	Rated := "R"

	gotMovie.SetRated(Rated)

	if gotMovie.Rated != Rated {
		t.Errorf("Want: %v\nGot: %v\n\n", gotMovie.Rated, Rated)
	}
}

func TestSetReleasedOk(t *testing.T) {
	newReleased := "1984-01-02T15:04:05Z"
	r, err := time.Parse(time.RFC3339, newReleased)
	if err != nil {
		t.Fatalf("time.Parse() error = %v", err)
	}

	gotMovie := NewValidMovie()

	gotMovie, _ = gotMovie.SetReleased(newReleased)

	if gotMovie.Released != r {
		t.Errorf("Want: %v\nGot: %v\n\n", newReleased, gotMovie.Released)
	}
}

func TestSetReleasedWrong(t *testing.T) {
	newRealeased := "wrong-time"

	gotMovie := NewValidMovie()

	_, e := gotMovie.SetReleased(newRealeased)
	_, err := time.Parse(time.RFC3339, newRealeased)

	want := errs.E(errs.Validation,
		errs.Code("invalid_date_format"),
		errs.Parameter("release_date"),
		errors.WithStack(err))

	if e.Error() != want.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", want, e)
	}
}

func TestSetRunTime(t *testing.T) {
	rt := 1999

	gotMovie := NewValidMovie()

	gotMovie.SetRunTime(rt)

	if gotMovie.RunTime != rt {
		t.Errorf("\nWant: %v\nGot: %v\n\n", rt, gotMovie.RunTime)
	}
}

func TestSetDirector(t *testing.T) {
	d := "Director Drach"

	gotMovie := NewValidMovie()

	gotMovie.SetDirector(d)

	if gotMovie.Director != d {
		t.Errorf("\nWant: %v\nGot: %v\n\n", d, gotMovie.Director)
	}
}

func TestSetWriter(t *testing.T) {
	w := "Writer Drach"

	gotMovie := NewValidMovie()

	gotMovie.SetWriter(w)

	if gotMovie.Writer != w {
		t.Errorf("\nWant: %v\nGot: %v\n\n", w, gotMovie.Writer)
	}
}

func TestSetUpdateUser(t *testing.T) {
	gotMovie := NewValidMovie()

	newUser := user.User{
		Email:        "foo2@bar.com",
		LastName:     "Barw",
		FirstName:    "Foow",
		FullName:     "Foow Barw",
		HostedDomain: "example.com.br",
		PictureURL:   "example.com.br/profile-we.png",
		ProfileLink:  "example.com.br/FoowBar",
	}

	gotMovie.SetUpdateUser(&newUser)

	if gotMovie.UpdateUser != newUser {
		t.Errorf("\nWant: %v\nGot: %v\n\n", newUser, gotMovie.UpdateUser)
	}
}

func TestSetUpdateTime(t *testing.T) {
	gotMovie := NewValidMovie()

	oldTime := gotMovie.UpdateTime

	gotMovie.SetUpdateTime()

	oldTimeSeconds := time.Since(oldTime).Seconds()
	updatedTimeSeconds := time.Since(gotMovie.UpdateTime).Seconds()

	if oldTimeSeconds < updatedTimeSeconds {
		t.Errorf("Previous time '%v' should be lower than '%v'", oldTimeSeconds, updatedTimeSeconds)
	}
}

func TestValidMovie(t *testing.T) {
	gotMovie := NewValidMovie()

	gotMovie, _ = gotMovie.SetReleased("1996-12-19T16:39:57-08:00")

	gotMovie.
		SetTitle("Movie Title").
		SetRated("R").
		SetRunTime(19).
		SetDirector("Movie Director").
		SetWriter("Movie Writer")

	if gotMovie.IsValid() != nil {
		t.Errorf("\nWant: %v\nGot: %v\n\n", nil, gotMovie.IsValid())
	}
}

func TestInvalidMovieTitle(t *testing.T) {
	gotMovie := NewValidMovie()

	wantErr := errs.E(errs.Validation, errs.Parameter("title"), errs.MissingField("title"))

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieRated(t *testing.T) {
	gotMovie := NewValidMovie()

	gotMovie.SetTitle("Movie Title")

	wantErr := errs.E(errs.Validation, errs.Parameter("rated"), errs.MissingField("Rated"))

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieReleased(t *testing.T) {
	gotMovie := NewValidMovie()

	gotMovie.
		SetTitle("Movie Title").
		SetRated("R")

	wantErr := errs.E(errs.Validation, errs.Parameter("release_date"), "Released must have a value")

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieRunTime(t *testing.T) {
	gotMovie := NewValidMovie()

	gotMovie, _ = gotMovie.SetReleased("1996-12-19T16:39:57-08:00")
	gotMovie.
		SetTitle("Movie Title").
		SetRated("R")

	wantErr := errs.E(errs.Validation, errs.Parameter("run_time"), "Run time must be greater than zero")

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieDirector(t *testing.T) {
	gotMovie := NewValidMovie()

	gotMovie, _ = gotMovie.SetReleased("1996-12-19T16:39:57-08:00")
	gotMovie.
		SetTitle("Movie Title").
		SetRated("R").
		SetRunTime(19)

	wantErr := errs.E(errs.Validation, errs.Parameter("director"), errs.MissingField("Director"))

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}

func TestInvalidMovieWriter(t *testing.T) {
	gotMovie := NewValidMovie()

	gotMovie, _ = gotMovie.SetReleased("1996-12-19T16:39:57-08:00")
	gotMovie.
		SetTitle("Movie Title").
		SetRated("R").
		SetRunTime(19).
		SetDirector("Movie Director")

	wantErr := errs.E(errs.Validation, errs.Parameter("writer"), errs.MissingField("Writer"))

	if err := gotMovie.IsValid(); err.Error() != wantErr.Error() {
		t.Errorf("\nWant: %v\nGot: %v\n\n", wantErr.Error(), err.Error())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"log"
	"os"
	"strings"

	"fmt"
	"time"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

const (
	owner = "StackExchange"
	repo  = "dnscontrol"
	tag   = "latest"
)

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

var bg = context.Background

var files = []string{"dnscontrol.exe", "dnscontrol-Linux", "dnscontrol-Darwin"}

func main() {

	tok := os.Getenv("GITHUB_ACCESS_TOKEN")
	if tok == "" {
		log.Fatal("$GITHUB_ACCESS_TOKEN required")
	}
	c := github.NewClient(oauth2.NewClient(bg(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: tok})))

	log.Println("Getting release info")
	rel, _, err := c.Repositories.GetReleaseByTag(bg(), owner, repo, tag)
	check(err)

	for _, f := range files {
		log.Printf("--- %s", f)

		var found *github.ReleaseAsset
		var foundOld *github.ReleaseAsset
		for _, ass := range rel.Assets {
			if ass.GetName() == f {
				found = &ass
			}
			if ass.GetName() == f+".old" {
				foundOld = &ass
			}
		}
		if foundOld != nil {
			log.Fatalf("%s.old was already found. Previous deploy likely failed. Please check and manually delete.", f)
		}
		if found != nil {
			n := found.GetName() + ".old"
			found.Name = &n
			log.Printf("Renaming old asset %s(%d) to .old", found.GetName(), found.GetID())
			_, _, err = c.Repositories.EditReleaseAsset(bg(), owner, repo, found.GetID(), found)
			check(err)
		}

		log.Printf("Uploading new file %s", f)
		upOpts := &github.UploadOptions{}
		upOpts.Name = f
		f, err := os.Open(f)
		check(err)
		_, _, err = c.Repositories.UploadReleaseAsset(bg(), owner, repo, rel.GetID(), upOpts, f)
		check(err)

		if found != nil {
			log.Println("Deleting old asset")
			_, err = c.Repositories.DeleteReleaseAsset(bg(), owner, repo, found.GetID())
			check(err)
		}
	}

	log.Println("Editing release body")
	body := strings.TrimSpace(rel.GetBody())
	lines := strings.Split(body, "\n")
	last := lines[len(lines)-1]
	if !strings.HasPrefix(last, "Last updated:") {
		log.Fatal("Release body is not what I expected. Abort!")
	}
	last = fmt.Sprintf("Last updated: %s", time.Now().Format("Mon Jan 2 2006 @15:04 MST"))
	lines[len(lines)-1] = last
	body = strings.Join(lines, "\n")
	rel.Body = &body
	c.Repositories.EditRelease(bg(), owner, repo, rel.GetID(), rel)

	log.Println("DONE")
}
<commit_msg>pointers are hard<commit_after>package main

import (
	"context"
	"log"
	"os"
	"strings"

	"fmt"
	"time"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

const (
	owner = "StackExchange"
	repo  = "dnscontrol"
	tag   = "latest"
)

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

var bg = context.Background

var files = []string{"dnscontrol.exe", "dnscontrol-Linux", "dnscontrol-Darwin"}

func main() {

	tok := os.Getenv("GITHUB_ACCESS_TOKEN")
	if tok == "" {
		log.Fatal("$GITHUB_ACCESS_TOKEN required")
	}
	c := github.NewClient(oauth2.NewClient(bg(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: tok})))

	log.Println("Getting release info")
	rel, _, err := c.Repositories.GetReleaseByTag(bg(), owner, repo, tag)
	check(err)

	for _, f := range files {
		log.Printf("--- %s", f)

		var found github.ReleaseAsset
		var exists bool
		var foundOld bool
		for _, ass := range rel.Assets {
			if ass.GetName() == f {
				exists = true
				found = ass
			}
			if ass.GetName() == f+".old" {
				foundOld = true
			}
		}

		if foundOld {
			log.Fatalf("%s.old was already found. Previous deploy likely failed. Please check and manually delete.", f)
		}
		if exists {
			oldN := found.GetName()
			n := oldN + ".old"
			found.Name = &n
			log.Printf("Renaming old asset %s(%d) to %s", oldN, found.GetID(), found.GetName())
			_, _, err = c.Repositories.EditReleaseAsset(bg(), owner, repo, found.GetID(), &found)
			check(err)
		}

		log.Printf("Uploading new file %s", f)
		upOpts := &github.UploadOptions{}
		upOpts.Name = f
		f, err := os.Open(f)
		check(err)
		_, _, err = c.Repositories.UploadReleaseAsset(bg(), owner, repo, rel.GetID(), upOpts, f)
		check(err)

		if exists {
			log.Println("Deleting old asset")
			_, err = c.Repositories.DeleteReleaseAsset(bg(), owner, repo, found.GetID())
			check(err)
		}
	}

	log.Println("Editing release body")
	body := strings.TrimSpace(rel.GetBody())
	lines := strings.Split(body, "\n")
	last := lines[len(lines)-1]
	if !strings.HasPrefix(last, "Last updated:") {
		log.Fatal("Release body is not what I expected. Abort!")
	}
	last = fmt.Sprintf("Last updated: %s", time.Now().Format("Mon Jan 2 2006 @15:04 MST"))
	lines[len(lines)-1] = last
	body = strings.Join(lines, "\n")
	rel.Body = &body
	c.Repositories.EditRelease(bg(), owner, repo, rel.GetID(), rel)

	log.Println("DONE")
}
<|endoftext|>


<commit_before>package perceptive

import (
	"errors"
	"image"
	"image/color"

	"github.com/disintegration/imaging"
	"github.com/umahmood/iter"
)

// Errors
var (
	ErrNilImage    = errors.New("perceptive: nil image")
	ErrInvalidHash = errors.New("perceptive: invalid perceptual hash")
)

// PerceptualHash perceptual hash algorithms
type PerceptualHash uint8

// Valid perceptual hashes
const (
	Average    PerceptualHash = iota // Average hash (Ahash)
	Difference                       // Difference hash (Dhash)
)

// grayscale produces the grayscale version of the image
func grayscale(img image.Image) image.Image {
	return imaging.Grayscale(img)
}

// resize resizes an image to the specified width and height using the
// Lanczos filter.
func resize(img image.Image, width, height int) image.Image {
	return imaging.Resize(img, width, height, imaging.Lanczos)
}

// Ahash implements the average hash algorithm.
func Ahash(img image.Image) (uint64, error) {
	if img == nil {
		return 0, ErrNilImage
	}

	const hashSize = 8

	pImg := grayscale(img)
	pImg = resize(pImg, hashSize, hashSize)

	// calc. average of the colors.
	var total int
	for row := range iter.N(hashSize) {
		for col := range iter.N(hashSize) {
			pixelVal := pImg.At(col, row).(color.NRGBA)
			total = total + int(pixelVal.R+pixelVal.G+pixelVal.B)
		}
	}

	avg := (total / (hashSize * hashSize))

	var hash uint64
	var pos uint64

	for row := range iter.N(hashSize) {
		for col := range iter.N(hashSize) {
			pixelVal := pImg.At(col, row).(color.NRGBA)
			sum := int(pixelVal.R + pixelVal.G + pixelVal.B)
			if sum > avg {
				hash |= (1 << pos)
			}
			pos++
		}
	}

	return hash, nil
}

// Dhash implements the difference hash algorithm.
func Dhash(img image.Image) (uint64, error) {
	if img == nil {
		return 0, ErrNilImage
	}

	const hashSize = 8

	pImg := grayscale(img)
	pImg = resize(pImg, hashSize+1, hashSize)

	// comparePixels compares the values of two pixels, returns true if px1 is
	// greater than px2.
	comparePixels := func(px1, px2 color.NRGBA) bool {
		var x uint8
		var y uint8
		x = px1.R + px1.G + px1.B
		y = px2.R + px2.G + px2.B
		if x > y {
			return true
		}
		return false
	}

	var hash uint64
	var pos uint64

	for row := range iter.N(hashSize) {
		for col := range iter.N(hashSize) {
			pixelLeft := pImg.At(col, row).(color.NRGBA)
			pixelRight := pImg.At(col+1, row).(color.NRGBA)
			if comparePixels(pixelLeft, pixelRight) {
				hash |= (1 << pos)
			}
			pos++
		}
	}

	return hash, nil
}

// HammingDistance computes the Hamming distance of two integers.
func HammingDistance(x, y uint64) int {
	dist := 0
	val := x ^ y
	for val != 0 {
		dist++
		val = val & (val - 1)
	}
	return dist
}

// CompareImages compares two images using the given perceptual hash algorithm.
// The function returns a distance value indicating how similar the two images
// are. The distance value differs depending on the perceptual hash algorithm
// used. If perceptual hash type is Average or Difference:
//
// - A distance of 0 indicates the same hash and likely a similar image.
// - A distance between 1 and 10 the image is potentially a variation.
// - A distance greater than 10 the image is likely a different image.
func CompareImages(img1, img2 image.Image, hash PerceptualHash) (int, error) {
	var a uint64
	var b uint64
	var err error

	switch hash {
	case Average:
		a, err = Ahash(img1)
		b, err = Ahash(img2)
	case Difference:
		a, err = Dhash(img1)
		b, err = Dhash(img2)
	default:
		return -1, ErrInvalidHash
	}

	if err != nil {
		return -1, err
	}

	return HammingDistance(a, b), nil
}
<commit_msg>fixed formatting of comment for function CompareImages<commit_after>package perceptive

import (
	"errors"
	"image"
	"image/color"

	"github.com/disintegration/imaging"
	"github.com/umahmood/iter"
)

// Errors
var (
	ErrNilImage    = errors.New("perceptive: nil image")
	ErrInvalidHash = errors.New("perceptive: invalid perceptual hash")
)

// PerceptualHash perceptual hash algorithms
type PerceptualHash uint8

// Valid perceptual hashes
const (
	Average    PerceptualHash = iota // Average hash (Ahash)
	Difference                       // Difference hash (Dhash)
)

// grayscale produces the grayscale version of the image
func grayscale(img image.Image) image.Image {
	return imaging.Grayscale(img)
}

// resize resizes an image to the specified width and height using the
// Lanczos filter.
func resize(img image.Image, width, height int) image.Image {
	return imaging.Resize(img, width, height, imaging.Lanczos)
}

// Ahash implements the average hash algorithm.
func Ahash(img image.Image) (uint64, error) {
	if img == nil {
		return 0, ErrNilImage
	}

	const hashSize = 8

	pImg := grayscale(img)
	pImg = resize(pImg, hashSize, hashSize)

	// calc. average of the colors.
	var total int
	for row := range iter.N(hashSize) {
		for col := range iter.N(hashSize) {
			pixelVal := pImg.At(col, row).(color.NRGBA)
			total = total + int(pixelVal.R+pixelVal.G+pixelVal.B)
		}
	}

	avg := (total / (hashSize * hashSize))

	var hash uint64
	var pos uint64

	for row := range iter.N(hashSize) {
		for col := range iter.N(hashSize) {
			pixelVal := pImg.At(col, row).(color.NRGBA)
			sum := int(pixelVal.R + pixelVal.G + pixelVal.B)
			if sum > avg {
				hash |= (1 << pos)
			}
			pos++
		}
	}

	return hash, nil
}

// Dhash implements the difference hash algorithm.
func Dhash(img image.Image) (uint64, error) {
	if img == nil {
		return 0, ErrNilImage
	}

	const hashSize = 8

	pImg := grayscale(img)
	pImg = resize(pImg, hashSize+1, hashSize)

	// comparePixels compares the values of two pixels, returns true if px1 is
	// greater than px2.
	comparePixels := func(px1, px2 color.NRGBA) bool {
		var x uint8
		var y uint8
		x = px1.R + px1.G + px1.B
		y = px2.R + px2.G + px2.B
		if x > y {
			return true
		}
		return false
	}

	var hash uint64
	var pos uint64

	for row := range iter.N(hashSize) {
		for col := range iter.N(hashSize) {
			pixelLeft := pImg.At(col, row).(color.NRGBA)
			pixelRight := pImg.At(col+1, row).(color.NRGBA)
			if comparePixels(pixelLeft, pixelRight) {
				hash |= (1 << pos)
			}
			pos++
		}
	}

	return hash, nil
}

// HammingDistance computes the Hamming distance of two integers.
func HammingDistance(x, y uint64) int {
	dist := 0
	val := x ^ y
	for val != 0 {
		dist++
		val = val & (val - 1)
	}
	return dist
}

// CompareImages compares two images using the given perceptual hash algorithm.
// The function returns a distance value indicating how similar the two images
// are. The distance value differs depending on the perceptual hash algorithm
// used. If perceptual hash type is Average or Difference:
//
// - A distance of 0 indicates the same hash and likely a similar image.
//
// - A distance between 1 and 10 the image is potentially a variation.
//
// - A distance greater than 10 the image is likely a different image.
func CompareImages(img1, img2 image.Image, hash PerceptualHash) (int, error) {
	var a uint64
	var b uint64
	var err error

	switch hash {
	case Average:
		a, err = Ahash(img1)
		b, err = Ahash(img2)
	case Difference:
		a, err = Dhash(img1)
		b, err = Dhash(img2)
	default:
		return -1, ErrInvalidHash
	}

	if err != nil {
		return -1, err
	}

	return HammingDistance(a, b), nil
}
<|endoftext|>


<commit_before>// Package overlay creates a simple 'window' overlay system for gopherjs
package overlay

import (
	"github.com/MJKWoolnough/gopherjs/style"
	"github.com/MJKWoolnough/gopherjs/xjs"
	"honnef.co/go/js/dom"
)

const css = `
window, body {
	width : 100%;
	margin : 0;
	overflow-x : hidden;
}

.mw-overlay {
	position : fixed;
	top : 0;
	left : 0;
	right : 0;
	bottom : 0;
	background-color : rgba(0, 0, 0, 0.25);
	overflow-y : scroll;
	overflow-x : hidden;
}

.mw-overlay > *:not(.closer) {
	position : absolute;
	top : 10px;
	left : 10px;
	right : 10px;
	background-color : #fff;
}

.mw-overlay > .closer {
	color : #fff;
	font-family : serif;
	right : 12px;
	position : absolute;
	text-shadow : #000 -1px -1px 0, #000 1px -1px 0, #000 1px 1px 0, #000 -1px 1px 0, #000 -1px 0 0, #000 1px 0 0;
	top : 10px;
	transition : color 0.25s;
	transition-timing-function : ease;
	user-select : none;
	z-index : 1;
	-moz-user-select : none;
	-webkit-user-select : none;
}

.mw-overlay > .closer:hover {
	color : #f00;
	cursor : pointer;
}

* > .mw-overlay:not(:last-of-type) {
	background-color : transparent;
}
`

func init() {
	style.Add(css)
}

// Overlay is a dom.Element which
type Overlay struct {
	dom.Element
	onClose []func()
}

// OnClose adds an onClose handler for the overlay
func (o *Overlay) OnClose(f func()) {
	o.onClose = append(o.onClose, f)
}

// Close closes the overlay, and runs any given onClose handle funcs
func (o *Overlay) Close() error {
	p := o.ParentNode()
	if p != nil {
		p.RemoveChild(o)
		for _, f := range o.onClose {
			f()
		}
	}
	return nil
}

// New wraps a dom.Element in an Overlay
func New(e dom.Node) *Overlay {
	o := &Overlay{Element: xjs.CreateElement("div")}
	o.SetAttribute("class", "mw-overlay")
	c := xjs.CreateElement("div")
	xjs.SetInnerText(c, "X")
	c.SetAttribute("class", "closer")
	c.AddEventListener("click", false, func(dom.Event) {
		o.Close()
	})
	o.AppendChild(c)
	o.AppendChild(e)
	return o
}
<commit_msg>Replaced xjs useage with xdom<commit_after>// Package overlay creates a simple 'window' overlay system for gopherjs
package overlay

import (
	"github.com/MJKWoolnough/gopherjs/style"
	"github.com/MJKWoolnough/gopherjs/xdom"
	"honnef.co/go/js/dom"
)

const css = `
window, body {
	width : 100%;
	margin : 0;
	overflow-x : hidden;
}

.mw-overlay {
	position : fixed;
	top : 0;
	left : 0;
	right : 0;
	bottom : 0;
	background-color : rgba(0, 0, 0, 0.25);
	overflow-y : scroll;
	overflow-x : hidden;
}

.mw-overlay > *:not(.closer) {
	position : absolute;
	top : 10px;
	left : 10px;
	right : 10px;
	background-color : #fff;
}

.mw-overlay > .closer {
	color : #fff;
	font-family : serif;
	right : 12px;
	position : absolute;
	text-shadow : #000 -1px -1px 0, #000 1px -1px 0, #000 1px 1px 0, #000 -1px 1px 0, #000 -1px 0 0, #000 1px 0 0;
	top : 10px;
	transition : color 0.25s;
	transition-timing-function : ease;
	user-select : none;
	z-index : 1;
	-moz-user-select : none;
	-webkit-user-select : none;
}

.mw-overlay > .closer:hover {
	color : #f00;
	cursor : pointer;
}

* > .mw-overlay:not(:last-of-type) {
	background-color : transparent;
}
`

func init() {
	style.Add(css)
}

// Overlay is a dom.Element which
type Overlay struct {
	dom.Element
	onClose []func()
}

// OnClose adds an onClose handler for the overlay
func (o *Overlay) OnClose(f func()) {
	o.onClose = append(o.onClose, f)
}

// Close closes the overlay, and runs any given onClose handle funcs
func (o *Overlay) Close() error {
	p := o.ParentNode()
	if p != nil {
		p.RemoveChild(o)
		for _, f := range o.onClose {
			f()
		}
	}
	return nil
}

// New wraps a dom.Element in an Overlay
func New(e dom.Node) *Overlay {
	o := &Overlay{Element: xdom.Div()}
	o.SetAttribute("class", "mw-overlay")
	c := xdom.Div()
	c.SetTextContent("X")
	c.SetAttribute("class", "closer")
	c.AddEventListener("click", false, func(dom.Event) {
		o.Close()
	})
	o.AppendChild(c)
	o.AppendChild(e)
	return o
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
)

var (
	newLine = ""
)

func main() {
	files, err := ioutil.ReadDir("./")
	if err != nil {
		log.Fatal(err)
	}

	for _, file := range files {
		if strings.HasSuffix(file.Name(), ".spec.js") {
			ExtractJasmine(file.Name())
		}
	}
}

func ExtractJasmine(fileName string) {
	file, err := os.Open(fileName)
	if err != nil {
		log.Fatal(err)
	}
	reader := bufio.NewReader(file)

	re := regexp.MustCompile("^(\\s*)(\\w+)\\((.+),(\\s*)function(.*)$")

	fmt.Printf("%s::: %s :::\n", newLine, fileName)
	newLine = "\n\n"

	for line, err := Readln(reader); err == nil; line, err = Readln(reader) {
		matches := re.FindStringSubmatch(line)
		if len(matches) > 0 {
			if matches[2] == "it" {
				fmt.Printf("    ")
			} else {
				fmt.Printf("\n  ")
			}
			fmt.Printf("%s: %s\n", matches[2], matches[3])
		}
	}
}

// Readln returns a single line (without the ending \n)
// from the input buffered reader.
// An error is returned if there is an error with the
// buffered reader.
func Readln(r *bufio.Reader) (string, error) {
	var (
		isPrefix bool  = true
		err      error = nil
		line, ln []byte
	)
	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		ln = append(ln, line...)
	}
	return string(ln), err
}
<commit_msg>Move the jasmine extraction in a go routine for each file<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
	"sync"
)

var (
	newLine = ""
)

func main() {
	files, err := ioutil.ReadDir("./")
	if err != nil {
		log.Fatal(err)
	}

	var waitGroup sync.WaitGroup

	for _, file := range files {
		if strings.HasSuffix(file.Name(), ".spec.js") {
			waitGroup.Add(1)
			go func(fileName string) {
				ExtractJasmine(fileName)
				waitGroup.Done()
			}(file.Name())
		}
	}

	waitGroup.Wait()
}

func ExtractJasmine(fileName string) {
	file, err := os.Open(fileName)
	if err != nil {
		log.Fatal(err)
	}
	reader := bufio.NewReader(file)

	re := regexp.MustCompile("^(\\s*)(\\w+)\\((.+),(\\s*)function(.*)$")

	fmt.Printf("%s::: %s :::\n", newLine, fileName)
	newLine = "\n\n"

	for line, err := Readln(reader); err == nil; line, err = Readln(reader) {
		matches := re.FindStringSubmatch(line)
		if len(matches) > 0 {
			if matches[2] == "it" {
				fmt.Printf("    ")
			} else {
				fmt.Printf("\n  ")
			}
			fmt.Printf("%s: %s\n", matches[2], matches[3])
		}
	}
}

// Readln returns a single line (without the ending \n)
// from the input buffered reader.
// An error is returned if there is an error with the
// buffered reader.
func Readln(r *bufio.Reader) (string, error) {
	var (
		isPrefix bool  = true
		err      error = nil
		line, ln []byte
	)
	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		ln = append(ln, line...)
	}
	return string(ln), err
}
<|endoftext|>


<commit_before>// Package pinata is a utility to beat data out of interface{}, []interface{}
// and map[string]interface{}.
package pinata

import (
	"fmt"
	"strings"
)

// Pinata holds a value and offers methods for extracting data from it.
type Pinata interface {
	Contents() interface{}
	Error() error
	ClearError()
	StringAtPath(string, ...string) string
	String() string
	StringAtIndex(int) string
	PinataAtPath(string, ...string) Pinata
	PinataAtIndex(int) Pinata
}

// New creates a new Pinata. Instances returned are not thread safe.
func New(contents interface{}) Pinata {
	switch t := contents.(type) {
	default:
		return &otherPinata{contents: t}
	case map[string]interface{}:
		return &mapPinata{contents: t}
	case []interface{}:
		return &slicePinata{}
	}
}

type basePinata struct {
	err error
}

func (p *basePinata) Error() error {
	return p.err
}

func (p *basePinata) ClearError() {
	p.err = nil
}

func (p *basePinata) String() string {
	if p.err != nil {
		return ""
	}
	p.err = fmt.Errorf("String(): not a string")
	return ""
}

func (p *basePinata) PinataAtIndex(index int) Pinata {
	p.indexUnsupported("PinataAtIndex", index)
	return nil
}

func (p *basePinata) PinataAtPath(pathStart string, path ...string) Pinata {
	p.pathUnsupported("PinataAtPath", pathStart, path)
	return nil
}

func (p *basePinata) StringAtPath(pathStart string, path ...string) string {
	p.pathUnsupported("StringAtPath", pathStart, path)
	return ""
}

func (p *basePinata) StringAtIndex(index int) string {
	p.indexUnsupported("StringAtIndex", index)
	return ""
}

func (p *basePinata) Contents() interface{} {
	return nil
}

func (p *basePinata) indexErrorf(method string, index int, msg string) error {
	return fmt.Errorf("%s(%d): %s", method, index, msg)
}

func (p *basePinata) indexUnsupported(method string, index int) {
	if p.err != nil {
		return
	}
	p.err = p.indexErrorf(method, index, "not a slice so can't access by index")
}

func (p *basePinata) setIndexOutOfRange(method string, index int, contents []interface{}) bool {
	if index < 0 || index >= len(contents) {
		p.err = p.indexErrorf(method, index, fmt.Sprintf("index out of range: %d", index))
		return true
	}
	return false
}

func (p *basePinata) pathErrorf(method, pathStart string, path []string, msg string) error {
	return fmt.Errorf(`%s("%s"): %s`, method, strings.Join(toSlice(pathStart, path), `", "`), msg)
}

func (p *basePinata) pathUnsupported(method, pathStart string, path []string) {
	if p.err != nil {
		return
	}
	p.err = p.pathErrorf(method, pathStart, path, "not a map so can't access by path")
}

type otherPinata struct {
	basePinata
	contents interface{}
}

func (p *otherPinata) String() string {
	if p.err != nil {
		return ""
	}
	if v, ok := p.contents.(string); ok {
		return v
	}
	p.err = fmt.Errorf("String(): not a string")
	return ""
}

func (p *otherPinata) Contents() interface{} {
	return p.contents
}

type slicePinata struct {
	basePinata
	contents []interface{}
}

func (p *slicePinata) pinataAtIndex(method string, index int) Pinata {
	if p.err != nil {
		return nil
	}
	if p.setIndexOutOfRange(method, index, p.contents) {
		return nil
	}
	return New(p.contents[index])
}

func (p *slicePinata) PinataAtIndex(index int) Pinata {
	return p.pinataAtIndex("PinataAtIndex", index)
}

func (p *slicePinata) StringAtIndex(index int) string {
	const method = "StringAtIndex"
	pinata := p.pinataAtIndex(method, index)
	if p.err != nil {
		return ""
	}
	s := pinata.String()
	if pinata.Error() != nil {
		p.err = p.indexErrorf(method, index, "not a string")
	}
	return s
}

func (p *slicePinata) Contents() interface{} {
	return p.contents
}

type mapPinata struct {
	basePinata
	contents map[string]interface{}
}

func (p *mapPinata) pinataAtPath(method, pathStart string, path ...string) Pinata {
	if p.err != nil {
		return nil
	}
	if v, ok := p.contents[pathStart]; ok {
		currentPinata := New(v)
		rest := path
		for len(rest) > 0 {
			tmp := currentPinata.PinataAtPath(rest[0])
			rest = rest[1:len(rest)]
			if currentPinata.Error() != nil {
				sofar := path[:len(path)-len(rest)]
				msg := fmt.Sprintf(`path ("%s", "%s") not found`, pathStart, strings.Join(sofar, `", "`))
				p.err = p.pathErrorf(method, pathStart, path, msg)
				return nil
			}
			currentPinata = tmp
		}
		return currentPinata
	}
	p.err = p.pathErrorf(method, pathStart, path, fmt.Sprintf(`path ("%s") not found`, pathStart))
	return nil
}

func (p *mapPinata) PinataAtPath(pathStart string, path ...string) Pinata {
	return p.pinataAtPath("PinataAtPath", pathStart, path...)
}

func (p *mapPinata) StringAtPath(pathStart string, path ...string) string {
	const method = "StringAtPath"
	pinata := p.pinataAtPath(method, pathStart, path...)
	if p.err != nil {
		return ""
	}
	s := pinata.String()
	if pinata.Error() != nil {
		p.err = p.pathErrorf(method, pathStart, path, "not a string")
	}
	return s
}

func (p *mapPinata) Contents() interface{} {
	return p.contents
}

func toSlice(first string, rest []string) []string {
	slice := make([]string, len(rest)+1)
	i := 0
	slice[i] = first
	for _, v := range rest {
		i++
		slice[i] = v
	}
	return slice
}
<commit_msg>allow for easier error inspection<commit_after>// Package pinata is a utility to beat data out of interface{}, []interface{}
// and map[string]interface{}.
package pinata

import (
	"bytes"
	"fmt"
)

// Pinata holds a value and offers methods for extracting data from it.
type Pinata interface {
	Contents() interface{}
	Error() error
	ClearError()
	StringAtPath(string, ...string) string
	String() string
	StringAtIndex(int) string
	PinataAtPath(string, ...string) Pinata
	PinataAtIndex(int) Pinata
}

// New creates a new Pinata. Instances returned are not thread safe.
func New(contents interface{}) Pinata {
	switch t := contents.(type) {
	default:
		return &otherPinata{contents: t}
	case map[string]interface{}:
		return &mapPinata{contents: t}
	case []interface{}:
		return &slicePinata{}
	}
}

var _ = error(PinataError{})

type ErrorReason string

const (
	ErrorReasonUnknown         ErrorReason = "unknown"
	ErrorReasonIncompatbleType             = "incompatible type"
	ErrorReasonNotFound                    = "not found"
	ErrorReasonInvalidInput                = "invalid input"
)

type PinataError struct {
	Reason ErrorReason
	Method string
	Input  []interface{}
	Advice string
}

func (p PinataError) Error() string {
	var input string
	if len(p.Input) > 0 {
		var buf bytes.Buffer
		for i := range p.Input {
			buf.WriteString("%#v")
			if i < len(p.Input)-1 {
				buf.WriteString(", ")
			}
		}
		input = fmt.Sprintf(buf.String(), p.Input...)
	}
	return fmt.Sprintf("pinata: %s(%s): %s - %s", p.Method, input, p.Reason, p.Advice)
}

type basePinata struct {
	err error
}

func (p *basePinata) Error() error {
	return p.err
}

func (p *basePinata) ClearError() {
	p.err = nil
}

func (p *basePinata) String() string {
	if p.err != nil {
		return ""
	}
	p.err = &PinataError{
		Method: "String",
		Reason: ErrorReasonIncompatbleType,
		Input:  nil,
		Advice: "call this method on a string pinata",
	}
	return ""
}

func (p *basePinata) PinataAtIndex(index int) Pinata {
	p.indexUnsupported("PinataAtIndex", index)
	return nil
}

func (p *basePinata) PinataAtPath(pathStart string, path ...string) Pinata {
	p.pathUnsupported("PinataAtPath", pathStart, path)
	return nil
}

func (p *basePinata) StringAtPath(pathStart string, path ...string) string {
	p.pathUnsupported("StringAtPath", pathStart, path)
	return ""
}

func (p *basePinata) StringAtIndex(index int) string {
	p.indexUnsupported("StringAtIndex", index)
	return ""
}

func (p *basePinata) Contents() interface{} {
	return nil
}

func (p *basePinata) indexErrorf(method string, index int, reason ErrorReason, advice string) error {
	return &PinataError{
		Method: method,
		Reason: reason,
		Input:  []interface{}{index},
		Advice: advice,
	}

	//return fmt.Errorf("%s(%d): %s", method, index, msg)
}

func (p *basePinata) indexUnsupported(method string, index int) {
	if p.err != nil {
		return
	}
	p.err = p.indexErrorf(method, index, ErrorReasonIncompatbleType, "call this method on a slice pinata")
}

func (p *basePinata) setIndexOutOfRange(method string, index int, contents []interface{}) bool {
	if index < 0 || index >= len(contents) {
		p.err = p.indexErrorf(method, index, ErrorReasonInvalidInput,
			fmt.Sprintf("specify an index from 0 to %d", len(contents)-1))
		return true
	}
	return false
}

func (p *basePinata) pathErrorf(method, pathStart string, path []string, reason ErrorReason, advice string) error {
	return &PinataError{
		Method: method,
		Reason: reason,
		Input:  toSlice(pathStart, path),
		Advice: advice,
	}
}

func (p *basePinata) pathUnsupported(method, pathStart string, path []string) {
	if p.err != nil {
		return
	}
	p.err = p.pathErrorf(method, pathStart, path, ErrorReasonIncompatbleType, "call this method on a map pinata")
}

type otherPinata struct {
	basePinata
	contents interface{}
}

func (p *otherPinata) String() string {
	if p.err != nil {
		return ""
	}
	if v, ok := p.contents.(string); ok {
		return v
	}
	return p.basePinata.String()
}

func (p *otherPinata) Contents() interface{} {
	return p.contents
}

type slicePinata struct {
	basePinata
	contents []interface{}
}

func (p *slicePinata) pinataAtIndex(method string, index int) Pinata {
	if p.err != nil {
		return nil
	}
	if p.setIndexOutOfRange(method, index, p.contents) {
		return nil
	}
	return New(p.contents[index])
}

func (p *slicePinata) PinataAtIndex(index int) Pinata {
	return p.pinataAtIndex("PinataAtIndex", index)
}

func (p *slicePinata) StringAtIndex(index int) string {
	const method = "StringAtIndex"
	pinata := p.pinataAtIndex(method, index)
	if p.err != nil {
		return ""
	}
	s := pinata.String()
	if pinata.Error() != nil {
		p.err = p.indexErrorf(method, index, ErrorReasonIncompatbleType, "not a string, try another type")
	}
	return s
}

func (p *slicePinata) Contents() interface{} {
	return p.contents
}

type mapPinata struct {
	basePinata
	contents map[string]interface{}
}

func (p *mapPinata) pinataAtPath(method, pathStart string, path ...string) Pinata {
	if p.err != nil {
		return nil
	}
	if v, ok := p.contents[pathStart]; ok {
		//if v, ok := v.(map[string]interface{})
		currentPinata := New(v)
		rest := path
		for len(rest) > 0 {
			tmp := currentPinata.PinataAtPath(rest[0])
			rest = rest[1:len(rest)]
			if currentPinata.Error() != nil {
				// TODO need to customise the message based on the returned error
				//sofar := path[:len(path)-len(rest)]
				p.err = p.pathErrorf(method, pathStart, path, ErrorReasonNotFound, "can't find that, sorry")
				return nil
			}
			currentPinata = tmp
		}
		return currentPinata
	}
	p.err = p.pathErrorf(method, pathStart, path, ErrorReasonNotFound, fmt.Sprintf(`no "%s" in this pinata`, pathStart))
	return nil
}

func (p *mapPinata) PinataAtPath(pathStart string, path ...string) Pinata {
	return p.pinataAtPath("PinataAtPath", pathStart, path...)
}

func (p *mapPinata) StringAtPath(pathStart string, path ...string) string {
	const method = "StringAtPath"
	pinata := p.pinataAtPath(method, pathStart, path...)
	if p.err != nil {
		return ""
	}
	s := pinata.String()
	if pinata.Error() != nil {
		p.err = p.pathErrorf(method, pathStart, path, ErrorReasonIncompatbleType, "not a string, try another type")
	}
	return s
}

func (p *mapPinata) Contents() interface{} {
	return p.contents
}

func toSlice(first string, rest []string) []interface{} {
	slice := make([]interface{}, len(rest)+1)
	i := 0
	slice[i] = first
	for _, v := range rest {
		i++
		slice[i] = v
	}
	return slice
}
<|endoftext|>


<commit_before>package sudoku

import (
	"math/rand"
)

type instructionType int

const (
	INSERT = iota
	GET
	DECREMENT_ACTIVE
	DISPOSE
)

type instruction struct {
	result          chan interface{}
	instructionType instructionType
	item            interface{}
	probability     float32
}

type stackItem struct {
	item interface{}
	next *stackItem
}

type ChanSyncedStack struct {
	*SyncedStack
	DefaultProbability float32
	Output             chan interface{}
	doneChan           chan bool
}

type SyncedStack struct {
	closed         bool
	instructions   chan instruction
	numItems       int
	numActiveItems int
	firstItem      *stackItem
}

func NewChanSyncedStack(doneChan chan bool) *ChanSyncedStack {
	//WARNING: doneChan should be a buffered channel or else you cloud get deadlock!
	result := &ChanSyncedStack{&SyncedStack{false, make(chan instruction), 0, 0, nil}, 1.0, make(chan interface{}, 1), doneChan}
	go result.workLoop()
	return result
}

func NewSyncedStack() *SyncedStack {
	stack := &SyncedStack{false, make(chan instruction), 0, 0, nil}
	go stack.workLoop()
	return stack
}

func (self *SyncedStack) Length() int {
	return self.numItems
}

func (self *SyncedStack) NumActiveItems() int {
	return self.numActiveItems
}

func (self *SyncedStack) IsDone() bool {
	return self.numItems == 0 && self.numActiveItems == 0
}

func (self *SyncedStack) Dispose() {
	self.instructions <- instruction{nil, DISPOSE, nil, 0.0}
	//Purposefully don't block.
}

func (self *SyncedStack) workLoop() {
	for {
		instruction, ok := <-self.instructions
		if !ok {
			return
		}
		self.processInstruction(instruction)
	}
}

func (self *ChanSyncedStack) workLoop() {

	//This workloop is complicated.
	//If we ahve an item and there's room in the Output channel, we ALWAYS want to do that.
	// But if there's not room in the channel, we don't want to just wait for an instruction--
	// because it's possible that the Output is emptied before we get another instruction.
	// So try first to fill output, then try either.

	var instruction instruction
	var ok bool
	for {
		if self.closed {
			return
		}
		if self.numItems > 0 {
			wrappedItem, previous := self.doSelect(self.DefaultProbability)
			select {
			case self.Output <- wrappedItem.item:
				self.doExtract(wrappedItem, previous)
			default:
				select {
				case self.Output <- wrappedItem.item:
					self.doExtract(wrappedItem, previous)
				case instruction, ok = <-self.instructions:
					if !ok {
						return
					}
					self.processInstruction(instruction)
				}
			}
		} else {
			instruction, ok = <-self.instructions
			if !ok {
				return
			}
			self.processInstruction(instruction)
		}
	}
}

func (self *ChanSyncedStack) processInstruction(instruction instruction) {
	if instruction.instructionType == DISPOSE {
		self.doDispose()
	} else {
		self.SyncedStack.processInstruction(instruction)
	}
}

func (self *SyncedStack) processInstruction(instruction instruction) {
	switch instruction.instructionType {
	case INSERT:
		self.doInsert(instruction.item)
		instruction.result <- nil
	case GET:
		wrappedItem, previous := self.doSelect(instruction.probability)
		instruction.result <- self.doExtract(wrappedItem, previous)
	case DECREMENT_ACTIVE:
		self.doDecrementActive()
		instruction.result <- nil
	case DISPOSE:
		//disposes don't have a channel result.
		self.doDispose()
	}
	//Drop other instructions on the floor for now.
}

func (self *ChanSyncedStack) ItemDone() {
	self.SyncedStack.ItemDone()
	if self.IsDone() {
		self.Dispose()
	}
}

func (self *SyncedStack) ItemDone() {
	if self.closed {
		return
	}
	result := make(chan interface{})
	self.instructions <- instruction{result, DECREMENT_ACTIVE, nil, 0.0}
	<-result
	return
}

func (self *SyncedStack) Insert(item interface{}) {
	if self.closed {
		return
	}
	result := make(chan interface{})
	self.instructions <- instruction{result, INSERT, item, 0.0}
	<-result
	return
}

func (self *ChanSyncedStack) Pop() interface{} {
	//You must use output for a ChanSyncedStack
	return nil
}

func (self *SyncedStack) Pop() interface{} {
	//Gets the last item on the stack.
	return self.Get(1.0)
}

func (self *ChanSyncedStack) Get(probability float32) interface{} {
	//You must use output for a ChanSyncedStack
	return nil
}

func (self *SyncedStack) Get(probability float32) interface{} {
	if self.closed {
		return nil
	}
	//Working from the back, will take each item with probability probability, else move to the next item in the stack.
	result := make(chan interface{})
	self.instructions <- instruction{result, GET, nil, probability}
	return <-result
}

func (self *ChanSyncedStack) doDispose() {
	self.SyncedStack.doDispose()
	close(self.Output)
	self.doneChan <- true
}

func (self *SyncedStack) doDispose() {
	//TODO: do we need to close out anything else here?
	//TODO: the work loops, when they notice the channel is closed, should probably exit.
	self.closed = true
	close(self.instructions)
}

func (self *SyncedStack) doDecrementActive() {
	//May only be called from workLoop.
	if self.numActiveItems > 0 {
		self.numActiveItems--
	}
}

func (self *SyncedStack) doInsert(item interface{}) {
	//May only be called from workLoop
	wrappedItem := &stackItem{item, self.firstItem}
	self.firstItem = wrappedItem
	self.numItems++
}

func (self *SyncedStack) doSelect(probability float32) (item *stackItem, previous *stackItem) {

	var lastItem *stackItem
	var lastLastItem *stackItem

	wrappedItem := self.firstItem

	for wrappedItem != nil {
		if rand.Float32() < probability {
			return wrappedItem, lastItem
		}
		lastLastItem = lastItem
		lastItem = wrappedItem
		wrappedItem = wrappedItem.next
	}

	return lastItem, lastLastItem
}

func (self *SyncedStack) doExtract(item *stackItem, previous *stackItem) interface{} {
	//may only be called from within workLoop
	//Called when we've decided we ware going to take the item.
	if item == nil {
		return nil
	}
	self.numActiveItems++
	self.numItems--
	if previous != nil {
		previous.next = item.next
	} else {
		self.firstItem = item.next
	}
	return item.item
}
<commit_msg>TESTS FAIL. Dispose() should also short-circuit if instructions queue is already closed.<commit_after>package sudoku

import (
	"math/rand"
)

type instructionType int

const (
	INSERT = iota
	GET
	DECREMENT_ACTIVE
	DISPOSE
)

type instruction struct {
	result          chan interface{}
	instructionType instructionType
	item            interface{}
	probability     float32
}

type stackItem struct {
	item interface{}
	next *stackItem
}

type ChanSyncedStack struct {
	*SyncedStack
	DefaultProbability float32
	Output             chan interface{}
	doneChan           chan bool
}

type SyncedStack struct {
	closed         bool
	instructions   chan instruction
	numItems       int
	numActiveItems int
	firstItem      *stackItem
}

func NewChanSyncedStack(doneChan chan bool) *ChanSyncedStack {
	//WARNING: doneChan should be a buffered channel or else you cloud get deadlock!
	result := &ChanSyncedStack{&SyncedStack{false, make(chan instruction), 0, 0, nil}, 1.0, make(chan interface{}, 1), doneChan}
	go result.workLoop()
	return result
}

func NewSyncedStack() *SyncedStack {
	stack := &SyncedStack{false, make(chan instruction), 0, 0, nil}
	go stack.workLoop()
	return stack
}

func (self *SyncedStack) Length() int {
	return self.numItems
}

func (self *SyncedStack) NumActiveItems() int {
	return self.numActiveItems
}

func (self *SyncedStack) IsDone() bool {
	return self.numItems == 0 && self.numActiveItems == 0
}

func (self *SyncedStack) Dispose() {
	if self.closed {
		return
	}
	self.instructions <- instruction{nil, DISPOSE, nil, 0.0}
	//Purposefully don't block.
}

func (self *SyncedStack) workLoop() {
	for {
		instruction, ok := <-self.instructions
		if !ok {
			return
		}
		self.processInstruction(instruction)
	}
}

func (self *ChanSyncedStack) workLoop() {

	//This workloop is complicated.
	//If we ahve an item and there's room in the Output channel, we ALWAYS want to do that.
	// But if there's not room in the channel, we don't want to just wait for an instruction--
	// because it's possible that the Output is emptied before we get another instruction.
	// So try first to fill output, then try either.

	var instruction instruction
	var ok bool
	for {
		if self.closed {
			return
		}
		if self.numItems > 0 {
			wrappedItem, previous := self.doSelect(self.DefaultProbability)
			select {
			case self.Output <- wrappedItem.item:
				self.doExtract(wrappedItem, previous)
			default:
				select {
				case self.Output <- wrappedItem.item:
					self.doExtract(wrappedItem, previous)
				case instruction, ok = <-self.instructions:
					if !ok {
						return
					}
					self.processInstruction(instruction)
				}
			}
		} else {
			instruction, ok = <-self.instructions
			if !ok {
				return
			}
			self.processInstruction(instruction)
		}
	}
}

func (self *ChanSyncedStack) processInstruction(instruction instruction) {
	if instruction.instructionType == DISPOSE {
		self.doDispose()
	} else {
		self.SyncedStack.processInstruction(instruction)
	}
}

func (self *SyncedStack) processInstruction(instruction instruction) {
	switch instruction.instructionType {
	case INSERT:
		self.doInsert(instruction.item)
		instruction.result <- nil
	case GET:
		wrappedItem, previous := self.doSelect(instruction.probability)
		instruction.result <- self.doExtract(wrappedItem, previous)
	case DECREMENT_ACTIVE:
		self.doDecrementActive()
		instruction.result <- nil
	case DISPOSE:
		//disposes don't have a channel result.
		self.doDispose()
	}
	//Drop other instructions on the floor for now.
}

func (self *ChanSyncedStack) ItemDone() {
	self.SyncedStack.ItemDone()
	if self.IsDone() {
		self.Dispose()
	}
}

func (self *SyncedStack) ItemDone() {
	if self.closed {
		return
	}
	result := make(chan interface{})
	self.instructions <- instruction{result, DECREMENT_ACTIVE, nil, 0.0}
	<-result
	return
}

func (self *SyncedStack) Insert(item interface{}) {
	if self.closed {
		return
	}
	result := make(chan interface{})
	self.instructions <- instruction{result, INSERT, item, 0.0}
	<-result
	return
}

func (self *ChanSyncedStack) Pop() interface{} {
	//You must use output for a ChanSyncedStack
	return nil
}

func (self *SyncedStack) Pop() interface{} {
	//Gets the last item on the stack.
	return self.Get(1.0)
}

func (self *ChanSyncedStack) Get(probability float32) interface{} {
	//You must use output for a ChanSyncedStack
	return nil
}

func (self *SyncedStack) Get(probability float32) interface{} {
	if self.closed {
		return nil
	}
	//Working from the back, will take each item with probability probability, else move to the next item in the stack.
	result := make(chan interface{})
	self.instructions <- instruction{result, GET, nil, probability}
	return <-result
}

func (self *ChanSyncedStack) doDispose() {
	self.SyncedStack.doDispose()
	close(self.Output)
	self.doneChan <- true
}

func (self *SyncedStack) doDispose() {
	//TODO: do we need to close out anything else here?
	//TODO: the work loops, when they notice the channel is closed, should probably exit.
	self.closed = true
	close(self.instructions)
}

func (self *SyncedStack) doDecrementActive() {
	//May only be called from workLoop.
	if self.numActiveItems > 0 {
		self.numActiveItems--
	}
}

func (self *SyncedStack) doInsert(item interface{}) {
	//May only be called from workLoop
	wrappedItem := &stackItem{item, self.firstItem}
	self.firstItem = wrappedItem
	self.numItems++
}

func (self *SyncedStack) doSelect(probability float32) (item *stackItem, previous *stackItem) {

	var lastItem *stackItem
	var lastLastItem *stackItem

	wrappedItem := self.firstItem

	for wrappedItem != nil {
		if rand.Float32() < probability {
			return wrappedItem, lastItem
		}
		lastLastItem = lastItem
		lastItem = wrappedItem
		wrappedItem = wrappedItem.next
	}

	return lastItem, lastLastItem
}

func (self *SyncedStack) doExtract(item *stackItem, previous *stackItem) interface{} {
	//may only be called from within workLoop
	//Called when we've decided we ware going to take the item.
	if item == nil {
		return nil
	}
	self.numActiveItems++
	self.numItems--
	if previous != nil {
		previous.next = item.next
	} else {
		self.firstItem = item.next
	}
	return item.item
}
<|endoftext|>


<commit_before>package provider_fm

import (
	"context"
	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)

type resourceStringType struct{}

func (r resourceStringType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) {
	description := "The resource `random_string` generates a random permutation of alphanumeric " +
		"characters and optionally special characters.\n" +
		"\n" +
		"This resource *does* use a cryptographic random number generator.\n" +
		"\n" +
		"Historically this resource's intended usage has been ambiguous as the original example used " +
		"it in a password. For backwards compatibility it will continue to exist. For unique ids please " +
		"use [random_id](id.html), for sensitive random values please use [random_password](password.html)."

	schema := getStringSchemaV1(false, description)
	schema.Version = 1

	return schema, nil
}

func (r resourceStringType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
	return resourceString{
		p: *(p.(*provider)),
	}, nil
}

type resourceString struct {
	p provider
}

func (r resourceString) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
	createString(ctx, req, resp, false)
}

func (r resourceString) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
	// Intentionally left blank.
}

func (r resourceString) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
	// Intentionally left blank.
}

func (r resourceString) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
	resp.State.RemoveResource(ctx)
}

func (r resourceString) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
	importString(ctx, req, resp, false)
}

func (r resourceString) ValidateConfig(ctx context.Context, req tfsdk.ValidateResourceConfigRequest, resp *tfsdk.ValidateResourceConfigResponse) {
	validateLength(ctx, req, resp)
}

func (r resourceString) UpgradeState(context.Context) map[int64]tfsdk.ResourceStateUpgrader {
	return map[int64]tfsdk.ResourceStateUpgrader{
		0: tfsdk.ResourceStateUpgrader{
			StateUpgrader: migrateStringStateV0toV1,
		},
	}
}
<commit_msg>go fmt (#177)<commit_after>package provider_fm

import (
	"context"
	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)

type resourceStringType struct{}

func (r resourceStringType) GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics) {
	description := "The resource `random_string` generates a random permutation of alphanumeric " +
		"characters and optionally special characters.\n" +
		"\n" +
		"This resource *does* use a cryptographic random number generator.\n" +
		"\n" +
		"Historically this resource's intended usage has been ambiguous as the original example used " +
		"it in a password. For backwards compatibility it will continue to exist. For unique ids please " +
		"use [random_id](id.html), for sensitive random values please use [random_password](password.html)."

	schema := getStringSchemaV1(false, description)
	schema.Version = 1

	return schema, nil
}

func (r resourceStringType) NewResource(_ context.Context, p tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
	return resourceString{
		p: *(p.(*provider)),
	}, nil
}

type resourceString struct {
	p provider
}

func (r resourceString) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
	createString(ctx, req, resp, false)
}

func (r resourceString) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
	// Intentionally left blank.
}

func (r resourceString) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) {
	// Intentionally left blank.
}

func (r resourceString) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) {
	resp.State.RemoveResource(ctx)
}

func (r resourceString) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) {
	importString(ctx, req, resp, false)
}

func (r resourceString) ValidateConfig(ctx context.Context, req tfsdk.ValidateResourceConfigRequest, resp *tfsdk.ValidateResourceConfigResponse) {
	validateLength(ctx, req, resp)
}

func (r resourceString) UpgradeState(context.Context) map[int64]tfsdk.ResourceStateUpgrader {
	return map[int64]tfsdk.ResourceStateUpgrader{
		0: {
			StateUpgrader: migrateStringStateV0toV1,
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Hajime Hoshi
//
// 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 oto

import (
	"time"
)

type Player struct {
	player         *player
	sampleRate     int
	channelNum     int
	bytesPerSample int
	bufferSize     int
}

func NewPlayer(sampleRate, channelNum, bytesPerSample, bufferSizeInBytes int) (*Player, error) {
	p, err := newPlayer(sampleRate, channelNum, bytesPerSample, bufferSizeInBytes)
	if err != nil {
		return nil, err
	}
	return &Player{
		player:         p,
		sampleRate:     sampleRate,
		channelNum:     channelNum,
		bytesPerSample: bytesPerSample,
		bufferSize:     bufferSizeInBytes,
	}, nil
}

func (p *Player) samplesPerOneSec() int {
	return p.sampleRate * p.channelNum * p.bytesPerSample
}

// Write is io.Writer's Write.
func (p *Player) Write(data []uint8) (int, error) {
	written := 0
	total := len(data)
	// TODO: Fix player's Write to satisfy io.Writer.
	// Now player's Write doesn't satisfy io.Writer's requirements since
	// the current Write might return without processing all given data.
	for written < total {
		n, err := p.player.Write(data)
		written += n
		if err != nil {
			return written, err
		}
		data = data[n:]
		// Mitigate the busy loop (#10).
		if n == 0 {
			t := time.Second * time.Duration(p.bufferSize) / time.Duration(p.samplesPerOneSec()) / 4
			time.Sleep(t)
		}
	}
	return written, nil
}

// Close is io.Closer's Close.
func (p *Player) Close() error {
	return p.player.Close()
}

func max(a, b int) int {
	if a < b {
		return b
	}
	return a
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
<commit_msg>Bug fix: Sleep whenever the buffer is full (#10)<commit_after>// Copyright 2017 Hajime Hoshi
//
// 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 oto

import (
	"time"
)

type Player struct {
	player         *player
	sampleRate     int
	channelNum     int
	bytesPerSample int
	bufferSize     int
}

func NewPlayer(sampleRate, channelNum, bytesPerSample, bufferSizeInBytes int) (*Player, error) {
	p, err := newPlayer(sampleRate, channelNum, bytesPerSample, bufferSizeInBytes)
	if err != nil {
		return nil, err
	}
	return &Player{
		player:         p,
		sampleRate:     sampleRate,
		channelNum:     channelNum,
		bytesPerSample: bytesPerSample,
		bufferSize:     bufferSizeInBytes,
	}, nil
}

func (p *Player) samplesPerOneSec() int {
	return p.sampleRate * p.channelNum * p.bytesPerSample
}

// Write is io.Writer's Write.
func (p *Player) Write(data []uint8) (int, error) {
	written := 0
	total := len(data)
	// TODO: Fix player's Write to satisfy io.Writer.
	// Now player's Write doesn't satisfy io.Writer's requirements since
	// the current Write might return without processing all given data.
	for written < total {
		n, err := p.player.Write(data)
		written += n
		if err != nil {
			return written, err
		}
		data = data[n:]
		// When not all data is written, the underlying buffer is full.
		// Mitigate the busy loop by sleeping (#10).
		if len(data) > 0 {
			t := time.Second * time.Duration(p.bufferSize) / time.Duration(p.samplesPerOneSec()) / 4
			time.Sleep(t)
		}
	}
	return written, nil
}

// Close is io.Closer's Close.
func (p *Player) Close() error {
	return p.player.Close()
}

func max(a, b int) int {
	if a < b {
		return b
	}
	return a
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>package overlay

import (
	"fmt"
	"net"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
)

const ovPeerTable = "overlay_peer_table"

type peerKey struct {
	peerIP  net.IP
	peerMac net.HardwareAddr
}

type peerEntry struct {
	eid        string
	vtep       net.IP
	peerIPMask net.IPMask
	inSandbox  bool
	isLocal    bool
}

type peerMap struct {
	mp map[string]peerEntry
	sync.Mutex
}

type peerNetworkMap struct {
	mp map[string]*peerMap
	sync.Mutex
}

func (pKey peerKey) String() string {
	return fmt.Sprintf("%s %s", pKey.peerIP, pKey.peerMac)
}

func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
	ipB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerIP = net.ParseIP(string(ipB))

	macB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerMac, err = net.ParseMAC(string(macB))
	if err != nil {
		return err
	}

	return nil
}

var peerDbWg sync.WaitGroup

func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	nids := []string{}
	for nid := range d.peerDb.mp {
		nids = append(nids, nid)
	}
	d.peerDb.Unlock()

	for _, nid := range nids {
		d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
			return f(nid, pKey, pEntry)
		})
	}
	return nil
}

func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	d.peerDb.Unlock()

	if !ok {
		return nil
	}

	mp := map[string]peerEntry{}

	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		mp[pKeyStr] = pEntry
	}
	pMap.Unlock()

	for pKeyStr, pEntry := range mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			logrus.Warnf("Peer key scan on network %s failed: %v", nid, err)
		}
		if f(&pKey, &pEntry) {
			return nil
		}
	}

	return nil
}

func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
	var (
		peerMac    net.HardwareAddr
		vtep       net.IP
		peerIPMask net.IPMask
		found      bool
	)

	err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
		if pKey.peerIP.Equal(peerIP) {
			peerMac = pKey.peerMac
			peerIPMask = pEntry.peerIPMask
			vtep = pEntry.vtep
			found = true
			return found
		}

		return found
	})

	if err != nil {
		return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
	}

	if !found {
		return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
	}

	return peerMac, peerIPMask, vtep, nil
}

func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {

	peerDbWg.Wait()

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.mp[nid] = &peerMap{
			mp: make(map[string]peerEntry),
		}

		pMap = d.peerDb.mp[nid]
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pEntry := peerEntry{
		eid:        eid,
		vtep:       vtep,
		peerIPMask: peerIPMask,
		isLocal:    isLocal,
	}

	pMap.Lock()
	pMap.mp[pKey.String()] = pEntry
	pMap.Unlock()
}

func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP) peerEntry {
	peerDbWg.Wait()

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return peerEntry{}
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pMap.Lock()

	pEntry, ok := pMap.mp[pKey.String()]
	if ok {
		// Mismatched endpoint ID(possibly outdated). Do not
		// delete peerdb
		if pEntry.eid != eid {
			pMap.Unlock()
			return pEntry
		}
	}

	delete(pMap.mp, pKey.String())
	pMap.Unlock()

	return pEntry
}

func (d *driver) peerDbUpdateSandbox(nid string) {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return
	}
	d.peerDb.Unlock()

	peerDbWg.Add(1)

	var peerOps []func()
	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			fmt.Printf("peer key scan failed: %v", err)
		}

		if pEntry.isLocal {
			continue
		}

		// Go captures variables by reference. The pEntry could be
		// pointing to the same memory location for every iteration. Make
		// a copy of pEntry before capturing it in the following closure.
		entry := pEntry
		op := func() {
			if err := d.peerAdd(nid, entry.eid, pKey.peerIP, entry.peerIPMask,
				pKey.peerMac, entry.vtep,
				false, false, false); err != nil {
				fmt.Printf("peerdbupdate in sandbox failed for ip %s and mac %s: %v",
					pKey.peerIP, pKey.peerMac, err)
			}
		}

		peerOps = append(peerOps, op)
	}
	pMap.Unlock()

	for _, op := range peerOps {
		op()
	}

	peerDbWg.Done()
}

func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb, l2Miss, l3Miss bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	if updateDb {
		d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	IP := &net.IPNet{
		IP:   peerIP,
		Mask: peerIPMask,
	}

	s := n.getSubnetforIP(IP)
	if s == nil {
		return fmt.Errorf("couldn't find the subnet %q in network %q", IP.String(), n.id)
	}

	if err := n.obtainVxlanID(s); err != nil {
		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
	}

	if err := n.joinSubnetSandbox(s, false); err != nil {
		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
	}

	if err := d.checkEncryption(nid, vtep, n.vxlanID(s), false, true); err != nil {
		logrus.Warn(err)
	}

	// Add neighbor entry for the peer IP
	if err := sbox.AddNeighbor(peerIP, peerMac, l3Miss, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
		return fmt.Errorf("could not add neighbor entry into the sandbox: %v", err)
	}

	// Add fdb entry to the bridge for the peer mac
	if err := sbox.AddNeighbor(vtep, peerMac, l2Miss, sbox.NeighborOptions().LinkName(s.vxlanName),
		sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
		return fmt.Errorf("could not add fdb entry into the sandbox: %v", err)
	}

	return nil
}

func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	var pEntry peerEntry
	if updateDb {
		pEntry = d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	// Delete fdb entry to the bridge for the peer mac only if the
	// entry existed in local peerdb. If it is a stale delete
	// request, still call DeleteNeighbor but only to cleanup any
	// leftover sandbox neighbor cache and not actually delete the
	// kernel state.
	if (eid == pEntry.eid && vtep.Equal(pEntry.vtep)) ||
		(eid != pEntry.eid && !vtep.Equal(pEntry.vtep)) {
		if err := sbox.DeleteNeighbor(vtep, peerMac,
			eid == pEntry.eid && vtep.Equal(pEntry.vtep)); err != nil {
			return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
		}
	}

	// Delete neighbor entry for the peer IP
	if eid == pEntry.eid {
		if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
			return fmt.Errorf("could not delete neighbor entry into the sandbox: %v", err)
		}
	}

	if err := d.checkEncryption(nid, vtep, 0, false, false); err != nil {
		logrus.Warn(err)
	}

	return nil
}

func (d *driver) pushLocalDb() {
	d.peerDbWalk(func(nid string, pKey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			d.pushLocalEndpointEvent("join", nid, pEntry.eid)
		}
		return false
	})
}

func (d *driver) peerDBUpdateSelf() {
	d.peerDbWalk(func(nid string, pkey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			pEntry.vtep = net.ParseIP(d.advertiseAddress)
		}
		return false
	})
}
<commit_msg>Removed printfs<commit_after>package overlay

import (
	"fmt"
	"net"
	"sync"
	"syscall"

	"github.com/Sirupsen/logrus"
)

const ovPeerTable = "overlay_peer_table"

type peerKey struct {
	peerIP  net.IP
	peerMac net.HardwareAddr
}

type peerEntry struct {
	eid        string
	vtep       net.IP
	peerIPMask net.IPMask
	inSandbox  bool
	isLocal    bool
}

type peerMap struct {
	mp map[string]peerEntry
	sync.Mutex
}

type peerNetworkMap struct {
	mp map[string]*peerMap
	sync.Mutex
}

func (pKey peerKey) String() string {
	return fmt.Sprintf("%s %s", pKey.peerIP, pKey.peerMac)
}

func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error {
	ipB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerIP = net.ParseIP(string(ipB))

	macB, err := state.Token(true, nil)
	if err != nil {
		return err
	}

	pKey.peerMac, err = net.ParseMAC(string(macB))
	if err != nil {
		return err
	}

	return nil
}

var peerDbWg sync.WaitGroup

func (d *driver) peerDbWalk(f func(string, *peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	nids := []string{}
	for nid := range d.peerDb.mp {
		nids = append(nids, nid)
	}
	d.peerDb.Unlock()

	for _, nid := range nids {
		d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
			return f(nid, pKey, pEntry)
		})
	}
	return nil
}

func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool) error {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	d.peerDb.Unlock()

	if !ok {
		return nil
	}

	mp := map[string]peerEntry{}

	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		mp[pKeyStr] = pEntry
	}
	pMap.Unlock()

	for pKeyStr, pEntry := range mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			logrus.Warnf("Peer key scan on network %s failed: %v", nid, err)
		}
		if f(&pKey, &pEntry) {
			return nil
		}
	}

	return nil
}

func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
	var (
		peerMac    net.HardwareAddr
		vtep       net.IP
		peerIPMask net.IPMask
		found      bool
	)

	err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
		if pKey.peerIP.Equal(peerIP) {
			peerMac = pKey.peerMac
			peerIPMask = pEntry.peerIPMask
			vtep = pEntry.vtep
			found = true
			return found
		}

		return found
	})

	if err != nil {
		return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
	}

	if !found {
		return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
	}

	return peerMac, peerIPMask, vtep, nil
}

func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {

	peerDbWg.Wait()

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.mp[nid] = &peerMap{
			mp: make(map[string]peerEntry),
		}

		pMap = d.peerDb.mp[nid]
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pEntry := peerEntry{
		eid:        eid,
		vtep:       vtep,
		peerIPMask: peerIPMask,
		isLocal:    isLocal,
	}

	pMap.Lock()
	pMap.mp[pKey.String()] = pEntry
	pMap.Unlock()
}

func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP) peerEntry {
	peerDbWg.Wait()

	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return peerEntry{}
	}
	d.peerDb.Unlock()

	pKey := peerKey{
		peerIP:  peerIP,
		peerMac: peerMac,
	}

	pMap.Lock()

	pEntry, ok := pMap.mp[pKey.String()]
	if ok {
		// Mismatched endpoint ID(possibly outdated). Do not
		// delete peerdb
		if pEntry.eid != eid {
			pMap.Unlock()
			return pEntry
		}
	}

	delete(pMap.mp, pKey.String())
	pMap.Unlock()

	return pEntry
}

func (d *driver) peerDbUpdateSandbox(nid string) {
	d.peerDb.Lock()
	pMap, ok := d.peerDb.mp[nid]
	if !ok {
		d.peerDb.Unlock()
		return
	}
	d.peerDb.Unlock()

	peerDbWg.Add(1)

	var peerOps []func()
	pMap.Lock()
	for pKeyStr, pEntry := range pMap.mp {
		var pKey peerKey
		if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil {
			logrus.Errorf("peer key scan failed: %v", err)
		}

		if pEntry.isLocal {
			continue
		}

		// Go captures variables by reference. The pEntry could be
		// pointing to the same memory location for every iteration. Make
		// a copy of pEntry before capturing it in the following closure.
		entry := pEntry
		op := func() {
			if err := d.peerAdd(nid, entry.eid, pKey.peerIP, entry.peerIPMask,
				pKey.peerMac, entry.vtep,
				false, false, false); err != nil {
				logrus.Errorf("peerdbupdate in sandbox failed for ip %s and mac %s: %v",
					pKey.peerIP, pKey.peerMac, err)
			}
		}

		peerOps = append(peerOps, op)
	}
	pMap.Unlock()

	for _, op := range peerOps {
		op()
	}

	peerDbWg.Done()
}

func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb, l2Miss, l3Miss bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	if updateDb {
		d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	IP := &net.IPNet{
		IP:   peerIP,
		Mask: peerIPMask,
	}

	s := n.getSubnetforIP(IP)
	if s == nil {
		return fmt.Errorf("couldn't find the subnet %q in network %q", IP.String(), n.id)
	}

	if err := n.obtainVxlanID(s); err != nil {
		return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
	}

	if err := n.joinSubnetSandbox(s, false); err != nil {
		return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
	}

	if err := d.checkEncryption(nid, vtep, n.vxlanID(s), false, true); err != nil {
		logrus.Warn(err)
	}

	// Add neighbor entry for the peer IP
	if err := sbox.AddNeighbor(peerIP, peerMac, l3Miss, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
		return fmt.Errorf("could not add neighbor entry into the sandbox: %v", err)
	}

	// Add fdb entry to the bridge for the peer mac
	if err := sbox.AddNeighbor(vtep, peerMac, l2Miss, sbox.NeighborOptions().LinkName(s.vxlanName),
		sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
		return fmt.Errorf("could not add fdb entry into the sandbox: %v", err)
	}

	return nil
}

func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
	peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {

	if err := validateID(nid, eid); err != nil {
		return err
	}

	var pEntry peerEntry
	if updateDb {
		pEntry = d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep)
	}

	n := d.network(nid)
	if n == nil {
		return nil
	}

	sbox := n.sandbox()
	if sbox == nil {
		return nil
	}

	// Delete fdb entry to the bridge for the peer mac only if the
	// entry existed in local peerdb. If it is a stale delete
	// request, still call DeleteNeighbor but only to cleanup any
	// leftover sandbox neighbor cache and not actually delete the
	// kernel state.
	if (eid == pEntry.eid && vtep.Equal(pEntry.vtep)) ||
		(eid != pEntry.eid && !vtep.Equal(pEntry.vtep)) {
		if err := sbox.DeleteNeighbor(vtep, peerMac,
			eid == pEntry.eid && vtep.Equal(pEntry.vtep)); err != nil {
			return fmt.Errorf("could not delete fdb entry into the sandbox: %v", err)
		}
	}

	// Delete neighbor entry for the peer IP
	if eid == pEntry.eid {
		if err := sbox.DeleteNeighbor(peerIP, peerMac, true); err != nil {
			return fmt.Errorf("could not delete neighbor entry into the sandbox: %v", err)
		}
	}

	if err := d.checkEncryption(nid, vtep, 0, false, false); err != nil {
		logrus.Warn(err)
	}

	return nil
}

func (d *driver) pushLocalDb() {
	d.peerDbWalk(func(nid string, pKey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			d.pushLocalEndpointEvent("join", nid, pEntry.eid)
		}
		return false
	})
}

func (d *driver) peerDBUpdateSelf() {
	d.peerDbWalk(func(nid string, pkey *peerKey, pEntry *peerEntry) bool {
		if pEntry.isLocal {
			pEntry.vtep = net.ParseIP(d.advertiseAddress)
		}
		return false
	})
}
<|endoftext|>


<commit_before>package controller

// Internal Run Guard Module

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/fsnotify/fsnotify"
	"github.com/pkg/errors"
	"github.com/shirou/gopsutil/process"
)

func (w *Worker) runProtect(ctx context.Context, insp *types.ContainerJSON, pid int, timelim uint64, outputlim int64, outputfile string) (info runinfo, err error) {
	starttime := time.Now().UnixNano()
	curtime := time.Now().UnixNano()

	// Use run protect to protect the running instance
	// It will start right after the execmd =w=
	info.outputexceed = false
	info.usedmem = 0
	info.usedtime = 0
	info.timeexceed = false
	p, err := process.NewProcess(int32(pid))
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot attach process")
		return
	}
	m, err := p.MemoryInfoEx()
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot get memory info")
	}
	var f os.FileInfo
	if outputfile != "" {
		f, err = os.Stat(outputfile)
		if err != nil && !os.IsNotExist(err) {
			err = errors.Wrap(err, "run protect error: cannot get output file")
			return
		}
	}
	wt, er := fsnotify.NewWatcher()
	if er != nil {
		err = errors.Wrap(er, "run protect error: cannot create watcher")
		return
	}
	defer wt.Close()
	err = wt.Add(w.WorkDir)
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot add watchpoint")
		return
	}
	log.Debugf("Add watch to %s", w.WorkDir)
Loop:
	for {
		select {
		case ev := <-wt.Events:
			log.Debugf("%s", ev.String())
			println(ev.String())
			if ev.Op == fsnotify.Create && strings.HasSuffix(ev.Name, "done.lck") {
				curtime = time.Now().UnixNano()
				break Loop
			}
		default:
			curtime = time.Now().UnixNano()
			// Only when output file is not empty, check the file size
			if outputfile != "" {
				f, err = os.Stat(outputfile)
				if err != nil && !os.IsNotExist(err) {
					err = errors.Wrap(err, "run protect error: cannot stat outputfile")
					break Loop
				}
				// Output Limit exceed
				if err == nil && f.Size() > outputlim {
					info.outputexceed = true
					break Loop
				}
			}
			// Collect memory used
			if info.usedmem < m.Dirty {
				info.usedmem = m.Dirty
			}
			// Time limit exceed
			if curtime-starttime > int64(timelim*1000000000) {
				info.timeexceed = true
				log.Debugf("Program exceed hard time limit(used %d, hardlim %d), terminated now", curtime-starttime, timelim*1000000000)
				// Killed the program
				err = p.Terminate()
				if err != nil {
					p.Kill()
				}
				break Loop
			}
			// done.lck create too quick, then just get it and exit
			_, err = os.Stat(filepath.Join(w.WorkDir, "done.lck"))
			if err == nil {
				break Loop
			}

		}
	}
	info.usedtime = curtime - starttime
	info.memexceed = insp.State.OOMKilled
	err = os.RemoveAll(filepath.Join(w.WorkDir, "done.lck"))
	if err != nil {
		err = errors.Wrap(err, "cannot remove done.lck [ABORT!]")
		return
	}
	println("DONE.LCK SHOULD BE REOMVED")

	return
}

func (w *Worker) execcmd(ctx context.Context, cli *client.Client, user string, cmd string) (info types.ContainerExecInspect, err error) {
	ec := types.ExecConfig{}
	ec.Detach = false
	ec.Tty = false
	ec.Cmd = make([]string, 3)
	ec.Cmd[0] = "/bin/bash"
	ec.Cmd[1] = "-c"
	ec.Cmd[2] = cmd
	//ec.Cmd = strings.Split(cmd, " ")
	//excmd := strings.Join(ec.Cmd[2:], " ")
	//ec.Cmd[2] = excmd
	ec.User = user
	//log.Infof("ONLY FOR NOW DEBUG ec %v", ec.Cmd)
	eresp, er := cli.ContainerExecCreate(ctx, w.containerID, ec)
	if er != nil {
		err = errors.Wrap(er, "exec command in container error")
		return
	}
	sc := types.ExecStartCheck{}
	log.Infof("ExecStartCheck %+v", sc)
	sc.Tty = true
	sc.Detach = false
	//log.Infof("exec ID = %s", eresp.ID)
	err = cli.ContainerExecStart(ctx, eresp.ID, sc)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
		return
	}
	//insp, err := cli.ContainerExecAttach(ctx, eresp.ID, ec)
	//if err != nil {
	//	err = errors.Wrap(err, "exec command in container error")
	//}
	//defer insp.Close()
	log.Debugf("Executing exec ID = %s", eresp.ID)
	//time.Sleep(1 * time.Second)
	info, err = cli.ContainerExecInspect(ctx, eresp.ID)
	ins, er := cli.ContainerInspect(ctx, w.containerID)
	log.Infof("ONLY FOR CURRENT DEBUG ins.State.ExitCode = %d", ins.State.ExitCode)
	log.Infof("ONLY FOR CURRENT DEBUG info.ExitCode = %d", info.ExitCode)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
	}
	//buf := bytes.Buffer{}
	//buf.ReadFrom(insp.Reader)
	return
}
<commit_msg>Add attach & detach mode exec<commit_after>package controller

// Internal Run Guard Module

import (
	"context"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/fsnotify/fsnotify"
	"github.com/pkg/errors"
	"github.com/shirou/gopsutil/process"
)

func (w *Worker) runProtect(ctx context.Context, insp *types.ContainerJSON, pid int, timelim uint64, outputlim int64, outputfile string) (info runinfo, err error) {
	starttime := time.Now().UnixNano()
	curtime := time.Now().UnixNano()

	// Use run protect to protect the running instance
	// It will start right after the execmd =w=
	info.outputexceed = false
	info.usedmem = 0
	info.usedtime = 0
	info.timeexceed = false
	p, err := process.NewProcess(int32(pid))
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot attach process")
		return
	}
	m, err := p.MemoryInfoEx()
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot get memory info")
	}
	var f os.FileInfo
	if outputfile != "" {
		f, err = os.Stat(outputfile)
		if err != nil && !os.IsNotExist(err) {
			err = errors.Wrap(err, "run protect error: cannot get output file")
			return
		}
	}
	wt, er := fsnotify.NewWatcher()
	if er != nil {
		err = errors.Wrap(er, "run protect error: cannot create watcher")
		return
	}
	defer wt.Close()
	err = wt.Add(w.WorkDir)
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot add watchpoint")
		return
	}
	log.Debugf("Add watch to %s", w.WorkDir)
Loop:
	for {
		select {
		case ev := <-wt.Events:
			log.Debugf("%s", ev.String())
			if ev.Op == fsnotify.Create && strings.HasSuffix(ev.Name, "done.lck") {
				curtime = time.Now().UnixNano()
				break Loop
			}
		default:
			curtime = time.Now().UnixNano()
			// Only when output file is not empty, check the file size
			if outputfile != "" {
				f, err = os.Stat(outputfile)
				if err != nil && !os.IsNotExist(err) {
					err = errors.Wrap(err, "run protect error: cannot stat outputfile")
					break Loop
				}
				// Output Limit exceed
				if err == nil && f.Size() > outputlim {
					info.outputexceed = true
					break Loop
				}
			}
			// Collect memory used
			if info.usedmem < m.Dirty {
				info.usedmem = m.Dirty
			}
			// Time limit exceed
			if curtime-starttime > int64(timelim*1000000000) {
				info.timeexceed = true
				log.Debugf("Program exceed hard time limit(used %d, hardlim %d), terminated now", curtime-starttime, timelim*1000000000)
				// Killed the program
				err = p.Terminate()
				if err != nil {
					p.Kill()
				}
				break Loop
			}
			// done.lck create too quick, then just get it and exit
			_, err = os.Stat(filepath.Join(w.WorkDir, "done.lck"))
			if err == nil {
				break Loop
			}

		}
	}
	info.usedtime = curtime - starttime
	info.memexceed = insp.State.OOMKilled
	err = os.RemoveAll(filepath.Join(w.WorkDir, "done.lck"))
	if err != nil {
		err = errors.Wrap(err, "cannot remove done.lck [ABORT!]")
		return
	}
	return
}

func (w *Worker) execcmd(ctx context.Context, cli *client.Client, user string, cmd string) (info types.ContainerExecInspect, err error) {
	ec := types.ExecConfig{}
	ec.Detach = true
	ec.Tty = false
	ec.AttachStdout = true // Set this to true will make the command block until finish
	ec.Cmd = make([]string, 3)
	ec.Cmd[0] = "/bin/bash"
	ec.Cmd[1] = "-c"
	ec.Cmd[2] = cmd
	log.Infof("%+v", ec)
	ec.User = user
	eresp, er := cli.ContainerExecCreate(ctx, w.containerID, ec)
	if er != nil {
		err = errors.Wrap(er, "exec command in container error")
		return
	}
	sc := types.ExecStartCheck{}
	sc.Tty = ec.Tty
	sc.Detach = ec.Detach
	log.Infof("ExecStartCheck %+v", sc)
	err = cli.ContainerExecStart(ctx, eresp.ID, sc)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
		return
	}
	log.Debugf("Executing exec ID = %s", eresp.ID)
	info, err = cli.ContainerExecInspect(ctx, eresp.ID)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
		return
	}
	log.Infof("ONLY FOR CURRENT DEBUG info.ExitCode = %d", info.ExitCode)
	return
}

func (w *Worker) execcmdAttach(ctx context.Context, cli *client.Client, user string, cmd string) (info types.ContainerExecInspect, err error) {
	ec := types.ExecConfig{}
	ec.Detach = false
	ec.Tty = false
	ec.AttachStdout = true // Set this to true will make the command block until finish
	ec.Cmd = make([]string, 3)
	ec.Cmd[0] = "/bin/bash"
	ec.Cmd[1] = "-c"
	ec.Cmd[2] = cmd
	log.Infof("%+v", ec)
	ec.User = user
	eresp, er := cli.ContainerExecCreate(ctx, w.containerID, ec)
	if er != nil {
		err = errors.Wrap(er, "exec command in container error")
		return
	}
	sc := types.ExecStartCheck{}
	sc.Tty = ec.Tty
	sc.Detach = ec.Detach
	log.Infof("ExecStartCheck %+v", sc)
	err = cli.ContainerExecStart(ctx, eresp.ID, sc)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
		return
	}
	insp, err := cli.ContainerExecAttach(ctx, eresp.ID, ec)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
	}
	c := insp.Conn
	defer insp.Close()

	log.Debugf("Executing exec ID = %s", eresp.ID)
	one := make([]byte, 1)
	_, err = c.Read(one)
	if err == io.EOF {
		log.Infof("Connection Closed")
	}
	info, err = cli.ContainerExecInspect(ctx, eresp.ID)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
		return
	}
	return
}
<|endoftext|>