<commit_before>package handler_test

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/deiwin/luncher-api/db"
	"github.com/deiwin/luncher-api/db/model"
	. "github.com/deiwin/luncher-api/handler"
	. "github.com/deiwin/luncher-api/router"
	"github.com/deiwin/luncher-api/session"

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

var _ = Describe("RestaurantsHandlers", func() {
	Describe("GET /restaurants", func() {
		var (
			mockRestaurantsCollection db.Restaurants
			handler                   Handler
		)

		BeforeEach(func() {
			mockRestaurantsCollection = &mockRestaurants{}
		})

		JustBeforeEach(func() {
			handler = Restaurants(mockRestaurantsCollection)
		})

		It("should succeed", func(done Done) {
			defer close(done)
			err := handler(responseRecorder, request)
			Expect(err).To(BeNil())
		})

		It("should return json", func(done Done) {
			defer close(done)
			handler(responseRecorder, request)
			contentTypes := responseRecorder.HeaderMap["Content-Type"]
			Expect(contentTypes).To(HaveLen(1))
			Expect(contentTypes[0]).To(Equal("application/json"))
		})

		Context("with simple mocked result from DB", func() {
			var (
				mockResult []*model.Restaurant
			)
			BeforeEach(func() {
				mockResult = []*model.Restaurant{&model.Restaurant{Name: "somerestaurant"}}
				mockRestaurantsCollection = &mockRestaurants{
					func() (restaurants []*model.Restaurant, err error) {
						restaurants = mockResult
						return
					},
					nil,
				}
			})

			It("should write the returned data to responsewriter", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				var result []*model.Restaurant
				json.Unmarshal(responseRecorder.Body.Bytes(), &result)
				Expect(result).To(HaveLen(1))
				Expect(result[0].Name).To(Equal(mockResult[0].Name))
			})
		})

		Context("with an error returned from the DB", func() {
			var dbErr = errors.New("DB stuff failed")

			BeforeEach(func() {
				mockRestaurantsCollection = &mockRestaurants{
					func() (restaurants []*model.Restaurant, err error) {
						err = dbErr
						return
					},
					nil,
				}
			})

			It("should return error 500", func(done Done) {
				defer close(done)
				err := handler(responseRecorder, request)
				Expect(err.Code).To(Equal(http.StatusInternalServerError))
			})
		})
	})

	Describe("GET /restaurant", func() {
		var (
			sessionManager            session.Manager
			mockRestaurantsCollection db.Restaurants
			mockUsersCollection       db.Users
			handler                   Handler
		)

		BeforeEach(func() {
			mockRestaurantsCollection = &mockRestaurants{}
		})

		JustBeforeEach(func() {
			handler = Restaurant(mockRestaurantsCollection, sessionManager, mockUsersCollection)
		})

		ExpectUserToBeLoggedIn(func() *HandlerError {
			return handler(responseRecorder, request)
		}, func(mgr session.Manager, users db.Users) {
			sessionManager = mgr
			mockUsersCollection = users
		})

		Context("with user logged in", func() {
			BeforeEach(func() {
				sessionManager = &mockSessionManager{isSet: true, id: "correctSession"}
				mockUsersCollection = mockUsers{}
			})

			It("should succeed", func(done Done) {
				defer close(done)
				err := handler(responseRecorder, request)
				Expect(err).To(BeNil())
			})

			It("should return json", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				contentTypes := responseRecorder.HeaderMap["Content-Type"]
				Expect(contentTypes).To(HaveLen(1))
				Expect(contentTypes[0]).To(Equal("application/json"))
			})

			It("should include the updated offer in the response", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				var restaurant *model.Restaurant
				json.Unmarshal(responseRecorder.Body.Bytes(), &restaurant)
				Expect(restaurant.Name).To(Equal("Asian Chef"))
			})
		})
	})

	Describe("GET /restaurant/offers", func() {
		var (
			sessionManager            session.Manager
			mockRestaurantsCollection db.Restaurants
			mockUsersCollection       db.Users
			handler                   Handler
			mockOffersCollection      db.Offers
		)

		BeforeEach(func() {
			mockRestaurantsCollection = &mockRestaurants{}
		})

		JustBeforeEach(func() {
			handler = RestaurantOffers(mockRestaurantsCollection, sessionManager, mockUsersCollection, mockOffersCollection)
		})

		ExpectUserToBeLoggedIn(func() *HandlerError {
			return handler(responseRecorder, request)
		}, func(mgr session.Manager, users db.Users) {
			sessionManager = mgr
			mockUsersCollection = users
		})

		Context("with user logged in", func() {
			BeforeEach(func() {
				sessionManager = &mockSessionManager{isSet: true, id: "correctSession"}
				mockUsersCollection = mockUsers{}
				mockOffersCollection = mockOffers{}
			})

			It("should succeed", func(done Done) {
				defer close(done)
				err := handler(responseRecorder, request)
				Expect(err).To(BeNil())
			})

			It("should return json", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				contentTypes := responseRecorder.HeaderMap["Content-Type"]
				Expect(contentTypes).To(HaveLen(1))
				Expect(contentTypes[0]).To(Equal("application/json"))
			})

			It("should include the offers in the response", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				var result []*model.Offer
				json.Unmarshal(responseRecorder.Body.Bytes(), &result)
				Expect(result).To(HaveLen(2))
				Expect(result[0].Title).To(Equal("a"))
				Expect(result[1].Title).To(Equal("b"))
			})
		})
	})
})

type mockRestaurants struct {
	getFunc func() ([]*model.Restaurant, error)
	db.Restaurants
}

func (mock mockRestaurants) Get() (restaurants []*model.Restaurant, err error) {
	if mock.getFunc != nil {
		restaurants, err = mock.getFunc()
	}
	return
}

func (c mockOffers) GetForRestaurant(restaurant string, startTime time.Time) ([]*model.Offer, error) {
	Expect(restaurant).To(Equal("Asian Chef"))
	Expect(startTime.Sub(time.Now())).To(BeNumerically("~", 0, time.Second))
	return []*model.Offer{
		&model.Offer{
			Title: "a",
		},
		&model.Offer{
			Title: "b",
		},
	}, nil
}
<commit_msg>fix /restaurant/offers tests and include tests for image paths<commit_after>package handler_test

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/deiwin/imstor"
	"github.com/deiwin/luncher-api/db"
	"github.com/deiwin/luncher-api/db/model"
	. "github.com/deiwin/luncher-api/handler"
	. "github.com/deiwin/luncher-api/router"
	"github.com/deiwin/luncher-api/session"

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

var _ = Describe("RestaurantsHandlers", func() {
	Describe("GET /restaurants", func() {
		var (
			mockRestaurantsCollection db.Restaurants
			handler                   Handler
		)

		BeforeEach(func() {
			mockRestaurantsCollection = &mockRestaurants{}
		})

		JustBeforeEach(func() {
			handler = Restaurants(mockRestaurantsCollection)
		})

		It("should succeed", func(done Done) {
			defer close(done)
			err := handler(responseRecorder, request)
			Expect(err).To(BeNil())
		})

		It("should return json", func(done Done) {
			defer close(done)
			handler(responseRecorder, request)
			contentTypes := responseRecorder.HeaderMap["Content-Type"]
			Expect(contentTypes).To(HaveLen(1))
			Expect(contentTypes[0]).To(Equal("application/json"))
		})

		Context("with simple mocked result from DB", func() {
			var (
				mockResult []*model.Restaurant
			)
			BeforeEach(func() {
				mockResult = []*model.Restaurant{&model.Restaurant{Name: "somerestaurant"}}
				mockRestaurantsCollection = &mockRestaurants{
					func() (restaurants []*model.Restaurant, err error) {
						restaurants = mockResult
						return
					},
					nil,
				}
			})

			It("should write the returned data to responsewriter", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				var result []*model.Restaurant
				json.Unmarshal(responseRecorder.Body.Bytes(), &result)
				Expect(result).To(HaveLen(1))
				Expect(result[0].Name).To(Equal(mockResult[0].Name))
			})
		})

		Context("with an error returned from the DB", func() {
			var dbErr = errors.New("DB stuff failed")

			BeforeEach(func() {
				mockRestaurantsCollection = &mockRestaurants{
					func() (restaurants []*model.Restaurant, err error) {
						err = dbErr
						return
					},
					nil,
				}
			})

			It("should return error 500", func(done Done) {
				defer close(done)
				err := handler(responseRecorder, request)
				Expect(err.Code).To(Equal(http.StatusInternalServerError))
			})
		})
	})

	Describe("GET /restaurant", func() {
		var (
			sessionManager            session.Manager
			mockRestaurantsCollection db.Restaurants
			mockUsersCollection       db.Users
			handler                   Handler
		)

		BeforeEach(func() {
			mockRestaurantsCollection = &mockRestaurants{}
		})

		JustBeforeEach(func() {
			handler = Restaurant(mockRestaurantsCollection, sessionManager, mockUsersCollection)
		})

		ExpectUserToBeLoggedIn(func() *HandlerError {
			return handler(responseRecorder, request)
		}, func(mgr session.Manager, users db.Users) {
			sessionManager = mgr
			mockUsersCollection = users
		})

		Context("with user logged in", func() {
			BeforeEach(func() {
				sessionManager = &mockSessionManager{isSet: true, id: "correctSession"}
				mockUsersCollection = mockUsers{}
			})

			It("should succeed", func(done Done) {
				defer close(done)
				err := handler(responseRecorder, request)
				Expect(err).To(BeNil())
			})

			It("should return json", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				contentTypes := responseRecorder.HeaderMap["Content-Type"]
				Expect(contentTypes).To(HaveLen(1))
				Expect(contentTypes[0]).To(Equal("application/json"))
			})

			It("should include the updated offer in the response", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				var restaurant *model.Restaurant
				json.Unmarshal(responseRecorder.Body.Bytes(), &restaurant)
				Expect(restaurant.Name).To(Equal("Asian Chef"))
			})
		})
	})

	Describe("GET /restaurant/offers", func() {
		var (
			sessionManager            session.Manager
			mockRestaurantsCollection db.Restaurants
			mockUsersCollection       db.Users
			handler                   Handler
			mockOffersCollection      db.Offers
			imageStorage              imstor.Storage
		)

		BeforeEach(func() {
			mockRestaurantsCollection = &mockRestaurants{}
			imageStorage = &mockImageStorage{}
		})

		JustBeforeEach(func() {
			handler = RestaurantOffers(mockRestaurantsCollection, sessionManager, mockUsersCollection, mockOffersCollection, imageStorage)
		})

		ExpectUserToBeLoggedIn(func() *HandlerError {
			return handler(responseRecorder, request)
		}, func(mgr session.Manager, users db.Users) {
			sessionManager = mgr
			mockUsersCollection = users
		})

		Context("with user logged in", func() {
			BeforeEach(func() {
				sessionManager = &mockSessionManager{isSet: true, id: "correctSession"}
				mockUsersCollection = mockUsers{}
				mockOffersCollection = mockOffers{}
			})

			It("should succeed", func(done Done) {
				defer close(done)
				err := handler(responseRecorder, request)
				Expect(err).To(BeNil())
			})

			It("should return json", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				contentTypes := responseRecorder.HeaderMap["Content-Type"]
				Expect(contentTypes).To(HaveLen(1))
				Expect(contentTypes[0]).To(Equal("application/json"))
			})

			It("should include the offers in the response", func(done Done) {
				defer close(done)
				handler(responseRecorder, request)
				var result []*model.Offer
				json.Unmarshal(responseRecorder.Body.Bytes(), &result)
				Expect(result).To(HaveLen(2))
				Expect(result[0].Title).To(Equal("a"))
				Expect(result[1].Title).To(Equal("b"))
				Expect(result[0].Image).To(Equal("images/a large image path"))
				Expect(result[1].Image).To(Equal(""))
			})
		})
	})
})

type mockRestaurants struct {
	getFunc func() ([]*model.Restaurant, error)
	db.Restaurants
}

func (mock mockRestaurants) Get() (restaurants []*model.Restaurant, err error) {
	if mock.getFunc != nil {
		restaurants, err = mock.getFunc()
	}
	return
}

func (c mockOffers) GetForRestaurant(restaurant string, startTime time.Time) ([]*model.Offer, error) {
	Expect(restaurant).To(Equal("Asian Chef"))
	Expect(startTime.Sub(time.Now())).To(BeNumerically("~", 0, time.Second))
	return []*model.Offer{
		&model.Offer{
			Title: "a",
			Image: "image checksum",
		},
		&model.Offer{
			Title: "b",
		},
	}, nil
}
<|endoftext|>


<commit_before>// +build windows

package testtask

import (
	"fmt"
	"os"
)

func executeProcessGroup(gid string) {
	fmt.Fprintf(os.Stderr, "process groups are not supported on windows\n")
	os.Exit(1)
}
<commit_msg>Update helper/testtask/testtask_windows.go<commit_after>// +build windows

package testtask

import (
	"fmt"
	"os"
)

func executeProcessGroup(gid string) {
	fmt.Fprintf(os.Stderr, "TODO: implement process groups are on windows\n")
	fmt.Fprintf(os.Stderr, "TODO: see https://github.com/hashicorp/nomad/blob/109c5ef650206fc62334d202002cda92ceb67399/drivers/shared/executor/executor_windows.go#L9-L17\n")
	os.Exit(1)
}
<|endoftext|>


<commit_before>package raster

import (
	"io"
	"os"
	"testing"
)

func open(s string, t *testing.T) *os.File {
	f, err := os.Open(s)
	if err != nil {
		t.Fatal(err)
	}
	return f
}

func TestDecode(t *testing.T) {
	f, err := os.Open("testdata/raster")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}
	p, err := d.NextPage()
	if err != nil {
		t.Fatal(err)
	}
	//spew.Dump(p.Header)
	b := make([]byte, p.TotalSize())
	err = p.ReadAll(b)
	if err != nil {
		t.Fatal(err)
	}
	//fmt.Printf("%s", b)
}

func TestDecodeMultiplePages(t *testing.T) {
	f, err := os.Open("testdata/two_pages")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}

	i := 0
	for {
		p, err := d.NextPage()
		if err == io.EOF {
			t.Logf("read %d pages", i)
			break
		}
		i++
		if err != nil {
			t.Fatal(err)
		}
		//spew.Dump(p.Header)
		b := make([]byte, p.TotalSize())
		err = p.ReadAll(b)
		if err != nil {
			t.Fatal(err)
		}
		//fmt.Printf("%s", b)
	}
}

func TestDecodeTruncatedLine(t *testing.T) {
	f := open("testdata/raster_truncated", t)
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}
	p, err := d.NextPage()
	if err != nil {
		t.Fatal(err)
	}
	b := make([]byte, p.LineSize())
	lines := p.TotalSize() / p.LineSize()
	var i uint32
	for i = 1; i <= lines; i++ {
		err = p.ReadLine(b)
		if err != nil {
			break
		}
	}
	const brokenLine = 236
	if err != nil && i != brokenLine {
		t.Errorf("got read error %q after %d iterations, expected %d iterations", err, i, brokenLine)
	}
	if err != io.ErrUnexpectedEOF {
		t.Errorf("got %v, want io.ErrUnexpectedEOF", err)
	}
}

func TestDecodeMissingLine(t *testing.T) {
	t.Skip("TODO(dh): provide fixture")
	f := open("testdata/raster_missing_line", t)
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}
	p, err := d.NextPage()
	if err != nil {
		t.Fatal(err)
	}
	b := make([]byte, p.TotalSize())
	err = p.ReadAll(b)
	if err != io.ErrUnexpectedEOF {
		t.Errorf("got %v, want io.ErrUnexpectedEOF", err)
	}
}

func TestDecodeTruncatedHeader(t *testing.T) {
	f := open("testdata/truncated_header", t)
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}
	_, err = d.NextPage()
	if err != io.ErrUnexpectedEOF {
		t.Errorf("got %q, want io.ErrUnexpectedEOF", err)
	}
}
<commit_msg>raster: improve existing tests<commit_after>package raster

import (
	"io"
	"os"
	"testing"
)

func open(s string, t *testing.T) *os.File {
	f, err := os.Open(s)
	if err != nil {
		t.Fatal(err)
	}
	return f
}

func TestDecodeMultiplePages(t *testing.T) {
	f := open("testdata/two_pages", t)
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}

	i := 0
	for {
		p, err := d.NextPage()
		if err == io.EOF {
			break
		}
		i++
		if err != nil {
			t.Errorf("got error %q advancing page, want nil", err)
		}
		b := make([]byte, p.TotalSize())
		err = p.ReadAll(b)
		if err != nil {
			t.Errorf("got error %q reading page, want nil", err)
		}
	}
}

func TestDecodeTruncatedLine(t *testing.T) {
	f := open("testdata/raster_truncated", t)
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}
	p, err := d.NextPage()
	if err != nil {
		t.Fatal(err)
	}
	b := make([]byte, p.LineSize())
	lines := p.UnreadLines()
	var i int
	for i = 1; i <= lines; i++ {
		err = p.ReadLine(b)
		if err != nil {
			break
		}
	}
	const brokenLine = 236
	if err != nil && i != brokenLine {
		t.Errorf("got read error %q after %d iterations, expected %d iterations", err, i, brokenLine)
	}
	if err != io.ErrUnexpectedEOF {
		t.Errorf("got %v, want io.ErrUnexpectedEOF", err)
	}
}

func TestDecodeMissingLine(t *testing.T) {
	t.Skip("TODO(dh): provide fixture")
	f := open("testdata/raster_missing_line", t)
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}
	p, err := d.NextPage()
	if err != nil {
		t.Fatal(err)
	}
	b := make([]byte, p.TotalSize())
	err = p.ReadAll(b)
	if err != io.ErrUnexpectedEOF {
		t.Errorf("got %v, want io.ErrUnexpectedEOF", err)
	}
}

func TestDecodeTruncatedHeader(t *testing.T) {
	f := open("testdata/truncated_header", t)
	defer f.Close()
	d, err := NewDecoder(f)
	if err != nil {
		t.Fatal(err)
	}
	_, err = d.NextPage()
	if err != io.ErrUnexpectedEOF {
		t.Errorf("got %q, want io.ErrUnexpectedEOF", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/nsf/termbox-go"
)

type region_indent_mode struct {
	stub_overlay_mode
	godit *godit
}

func init_region_indent_mode(godit *godit, dir int) region_indent_mode {
	v := godit.active.leaf
	r := region_indent_mode{godit: godit}
	beg := v.cursor
	end := v.cursor
	if v.buf.is_mark_set() {
		end = v.buf.mark
	}

	if beg.line_num > end.line_num {
		beg, end = end, beg
	}
	beg.boffset = 0
	end.boffset = len(end.line.data)

	v.set_tags(view_tag{
		begin_line:   beg.line_num,
		begin_offset: beg.boffset,
		end_line:     end.line_num,
		end_offset:   end.boffset,
		fg:           termbox.ColorDefault,
		bg:           termbox.ColorBlue,
	})
	if dir > 0 {
		v.on_vcommand(vcommand_indent_region, 0)
	} else if dir < 0 {
		v.on_vcommand(vcommand_deindent_region, 0)
	}
	v.dirty = dirty_everything
	godit.set_status("(Type > or < to indent/deindent respectively)")
	return r
}

func (r region_indent_mode) exit() {
	v := r.godit.active.leaf
	v.set_tags()
	v.dirty = dirty_everything
}

func (r region_indent_mode) on_key(ev *termbox.Event) {
	g := r.godit
	v := g.active.leaf
	if ev.Mod == 0 {
		switch ev.Ch {
		case '>':
			v.on_vcommand(vcommand_indent_region, 0)
			g.set_status("(Type > or < to indent/deindent respectively)")
			return
		case '<':
			v.on_vcommand(vcommand_deindent_region, 0)
			g.set_status("(Type > or < to indent/deindent respectively)")
			return
		}
	}

	g.set_overlay_mode(nil)
	g.on_key(ev)
}
<commit_msg>Fix region indent mode highlighting.<commit_after>package main

import (
	"github.com/nsf/termbox-go"
)

type region_indent_mode struct {
	stub_overlay_mode
	godit *godit
}

func init_region_indent_mode(godit *godit, dir int) region_indent_mode {
	v := godit.active.leaf
	r := region_indent_mode{godit: godit}

	beg, end := v.line_region()
	if dir > 0 {
		v.on_vcommand(vcommand_indent_region, 0)
	} else if dir < 0 {
		v.on_vcommand(vcommand_deindent_region, 0)
	}
	v.set_tags(view_tag{
		beg_line:   beg.line_num,
		beg_offset: beg.boffset,
		end_line:   end.line_num,
		end_offset: end.boffset,
		fg:         termbox.ColorDefault,
		bg:         termbox.ColorBlue,
	})
	v.dirty = dirty_everything
	godit.set_status("(Type > or < to indent/deindent respectively)")
	return r
}

func (r region_indent_mode) exit() {
	v := r.godit.active.leaf
	v.set_tags()
	v.dirty = dirty_everything
}

func (r region_indent_mode) on_key(ev *termbox.Event) {
	g := r.godit
	v := g.active.leaf
	beg, end := v.line_region()
	if ev.Mod == 0 {
		switch ev.Ch {
		case '>':
			v.on_vcommand(vcommand_indent_region, 0)
			g.set_status("(Type > or < to indent/deindent respectively)")
			goto update_tag
		case '<':
			v.on_vcommand(vcommand_deindent_region, 0)
			g.set_status("(Type > or < to indent/deindent respectively)")
			goto update_tag
		}
	}

	g.set_overlay_mode(nil)
	g.on_key(ev)
	return

update_tag:
	v.set_tags(view_tag{
		beg_line:   beg.line_num,
		beg_offset: beg.boffset,
		end_line:   end.line_num,
		end_offset: end.boffset,
		fg:         termbox.ColorDefault,
		bg:         termbox.ColorBlue,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"

	"github.com/brnstz/routine/wikimg"
)

var (
	// print a blank line with the given 256 ANSI color
	fmtSpec = "\x1b[30;48;5;%dm%-80s\x1b[0m\n"
)

func main() {
	var max, workers int
	var imgURLs chan string

	flag.IntVar(&max, "max", 10, "maximum number of images to retrieve")
	flag.IntVar(&workers, "workers", 5, "number of background workers")
	flag.Parse()

	// Create a new image puller with our max
	p := wikimg.NewPuller(max)

	// Loop to retrieve more images
	for {
		imgURL, err := p.Next()

		if err == wikimg.EndOfResults {
			// Break from loop when end of results is reached
			break

		} else if err != nil {
			// Log error and continue getting URLs
			log.Println(err)
			continue
		}

		imgURLs <- imgURL
	}

	for i := 0; i < workers; i++ {
		go func() {
			for imgURL := range imgURLs {
				// Get the top color in this image
				color, err := wikimg.OneColor(imgURL)
				if err != nil {
					log.Println(err)
					continue
				}

				// Print color to the terminal
				fmt.Printf(fmtSpec, color.XTermCode, "")
			}
		}()
	}
}
<commit_msg>moving worker above generator<commit_after>package main

import (
	"flag"
	"fmt"
	"log"

	"github.com/brnstz/routine/wikimg"
)

var (
	// print a blank line with the given 256 ANSI color
	fmtSpec = "\x1b[30;48;5;%dm%-80s\x1b[0m\n"
)

func main() {
	var max, workers, buffer int

	flag.IntVar(&max, "max", 10, "maximum number of images to retrieve")
	flag.IntVar(&workers, "workers", 5, "number of background workers")
	flag.IntVar(&buffer, "buffer", 0, "buffer size of image channel")
	flag.Parse()

	// Create a new image puller with our max
	p := wikimg.NewPuller(max)

	// Create a buffered channel for communicating between image
	// puller loop and workers
	imgURLs := make(chan string, buffer)

	for i := 0; i < workers; i++ {
		go func() {
			for imgURL := range imgURLs {
				// Get the top color in this image
				color, err := wikimg.OneColor(imgURL)
				if err != nil {
					log.Println(err)
					continue
				}

				// Print color to the terminal
				fmt.Printf(fmtSpec, color.XTermCode, "")
			}
		}()
	}

	// Loop to retrieve more images
	for {
		imgURL, err := p.Next()

		if err == wikimg.EndOfResults {
			// Break from loop when end of results is reached
			break

		} else if err != nil {
			// Log error and continue getting URLs
			log.Println(err)
			continue
		}

		imgURLs <- imgURL
	}

}
<|endoftext|>


<commit_before>package router

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

	"github.com/go-errors/errors"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/julienschmidt/httprouter"
	"github.com/octavore/naga/service"
	"github.com/octavore/nagax/logger"
	"github.com/octavore/nagax/router"

	"github.com/octavore/press/proto/press/api"
)

var ErrNotFound = fmt.Errorf("not found")

type Module struct {
	*router.Module
	Logger *logger.Module
}

func (m *Module) Init(c *service.Config) {
}

func (m *Module) Subrouter(path string) *httprouter.Router {
	r := httprouter.New()
	m.Handle(path, r)
	return r
}

func Proto(rw http.ResponseWriter, pb proto.Message) error {
	rw.Header().Set("Content-Type", "application/json")
	err := JSON.Marshal(rw, pb)
	if err != nil {
		return fmt.Errorf("router: %v", err)
	}
	return nil
}

func (m *Module) InternalError(rw http.ResponseWriter, err error) {
	if err == ErrNotFound {
		NotFound(rw)
		return
	}

	switch e := err.(type) {
	case *errors.Error:
		s := e.StackFrames()[0]
		m.Logger.Errorf("[%s:%d] %v", s.Package, s.LineNumber, e.Error())
	default:
		m.Logger.Error(e)
	}
	rw.WriteHeader(http.StatusInternalServerError)
	err = Proto(rw, &api.Error{
		Code:   api.ErrorCode_INTERNAL_SERVER_ERROR.Enum(),
		Detail: proto.String("Internal server error."),
	})
	if err != nil {
		log.Println("router: ", err)
	}
}

func NotFound(rw http.ResponseWriter) {
	rw.WriteHeader(http.StatusNotFound)
	err := Proto(rw, &api.Error{
		Code:   api.ErrorCode_NOT_FOUND.Enum(),
		Detail: proto.String("Not found."),
	})
	if err != nil {
		log.Println("router: ", err)
	}
}

var JSON = &jsonpb.Marshaler{
	EnumsAsInts: false,
	Indent:      "  ",
}
<commit_msg>router: Log file name as well.<commit_after>package router

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

	"github.com/go-errors/errors"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/julienschmidt/httprouter"
	"github.com/octavore/naga/service"
	"github.com/octavore/nagax/logger"
	"github.com/octavore/nagax/router"

	"path"

	"github.com/octavore/press/proto/press/api"
)

var ErrNotFound = fmt.Errorf("not found")

type Module struct {
	*router.Module
	Logger *logger.Module
}

func (m *Module) Init(c *service.Config) {
}

func (m *Module) Subrouter(path string) *httprouter.Router {
	r := httprouter.New()
	m.Handle(path, r)
	return r
}

func Proto(rw http.ResponseWriter, pb proto.Message) error {
	rw.Header().Set("Content-Type", "application/json")
	err := JSON.Marshal(rw, pb)
	if err != nil {
		return fmt.Errorf("router: %v", err)
	}
	return nil
}

func (m *Module) InternalError(rw http.ResponseWriter, err error) {
	if err == ErrNotFound {
		NotFound(rw)
		return
	}

	switch e := err.(type) {
	case *errors.Error:
		s := e.StackFrames()[0]
		f := path.Base(s.File)
		m.Logger.Errorf("[%s/%s:%d] %v", s.Package, f, s.LineNumber, e.Error())
	default:
		m.Logger.Error(e)
	}
	rw.WriteHeader(http.StatusInternalServerError)
	err = Proto(rw, &api.Error{
		Code:   api.ErrorCode_INTERNAL_SERVER_ERROR.Enum(),
		Detail: proto.String("Internal server error."),
	})
	if err != nil {
		log.Println("router: ", err)
	}
}

func NotFound(rw http.ResponseWriter) {
	rw.WriteHeader(http.StatusNotFound)
	err := Proto(rw, &api.Error{
		Code:   api.ErrorCode_NOT_FOUND.Enum(),
		Detail: proto.String("Not found."),
	})
	if err != nil {
		log.Println("router: ", err)
	}
}

var JSON = &jsonpb.Marshaler{
	EnumsAsInts: false,
	Indent:      "  ",
}
<|endoftext|>


<commit_before>package bosh

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"bitbucket.org/engineerbetter/concourse-up/config"
	"bitbucket.org/engineerbetter/concourse-up/terraform"
	"bitbucket.org/engineerbetter/concourse-up/util"
	boshcmd "github.com/cloudfoundry/bosh-cli/cmd"
	boshui "github.com/cloudfoundry/bosh-cli/ui"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
)

const boshInitLogLevel = boshlog.LevelWarn
const pemFilename = "director.pem"
const directorManifestFilename = "director.yml"
const concourseManifestFilename = "concourse.yml"
const cloudConfigFilename = "cloud-config.yml"
const caCertFilename = "ca-cert.pem"

var defaultBoshArgs = []string{"--non-interactive", "--tty", "--no-color"}

// StateFilename is default name for bosh-init state file
const StateFilename = "director-state.json"

// Client is a concrete implementation of the IClient interface
type Client struct {
	tempDir               string
	directorManifestPath  string
	concourseManifestPath string
	cloudConfigPath       string
	stateFilePath         string
	caCertPath            string
	config                *config.Config
	metadata              *terraform.Metadata
	stdout                io.Writer
	stderr                io.Writer
}

// IClient is a client for performing bosh-init commands
type IClient interface {
	Deploy() ([]byte, error)
	Delete() error
	Cleanup() error
}

// ClientFactory creates a new IClient
type ClientFactory func(config *config.Config, metadata *terraform.Metadata, stateFileBytes []byte, stdout, stderr io.Writer) (IClient, error)

// NewClient creates a new Client
func NewClient(config *config.Config, metadata *terraform.Metadata, stateFileBytes []byte, stdout, stderr io.Writer) (IClient, error) {
	tempDir, err := ioutil.TempDir("", "concourse-up")
	if err != nil {
		return nil, err
	}

	keyfileBytes := []byte(config.PrivateKey)
	keyFilePath := filepath.Join(tempDir, pemFilename)
	if err = ioutil.WriteFile(keyFilePath, keyfileBytes, 0700); err != nil {
		return nil, err
	}

	manifestBytes, err := generateBoshInitManifest(config, metadata)
	if err != nil {
		return nil, err
	}

	directorManifestPath := filepath.Join(tempDir, directorManifestFilename)
	if err = ioutil.WriteFile(directorManifestPath, manifestBytes, 0700); err != nil {
		return nil, err
	}

	concourseManifestBytes, err := generateConcourseManifest(config, metadata)
	if err != nil {
		return nil, err
	}

	concourseManfestPath := filepath.Join(tempDir, concourseManifestFilename)
	if err = ioutil.WriteFile(concourseManfestPath, concourseManifestBytes, 0700); err != nil {
		return nil, err
	}

	cloudConfigBytes, err := generateCloudConfig(config, metadata)
	if err != nil {
		return nil, err
	}

	cloudConfigPath := filepath.Join(tempDir, cloudConfigFilename)
	if err = ioutil.WriteFile(cloudConfigPath, cloudConfigBytes, 0700); err != nil {
		return nil, err
	}

	caCertPath := filepath.Join(tempDir, caCertFilename)
	if err = ioutil.WriteFile(caCertPath, []byte(config.DirectorCACert), 0700); err != nil {
		return nil, err
	}

	stateFilePath := filepath.Join(tempDir, StateFilename)
	if stateFileBytes != nil {
		if err = ioutil.WriteFile(stateFilePath, stateFileBytes, 0700); err != nil {
			return nil, err
		}
	}

	return &Client{
		tempDir:              tempDir,
		directorManifestPath: directorManifestPath,
		cloudConfigPath:      cloudConfigPath,
		stateFilePath:        stateFilePath,
		caCertPath:           caCertPath,
		config:               config,
		metadata:             metadata,
		stdout:               stdout,
		stderr:               stderr,
	}, nil
}

// Cleanup cleans up temporary files associated with bosh init
func (client *Client) Cleanup() error {
	return os.RemoveAll(client.tempDir)
}

// Deploy deploys a new Bosh director or converges an existing deployment
// Returns new contents of bosh state file
func (client *Client) Deploy() ([]byte, error) {
	if err := client.createEnv(); err != nil {
		return nil, err
	}

	if err := client.updateCloudConfig(); err != nil {
		return nil, err
	}

	if err := client.uploadConcourse(); err != nil {
		return nil, err
	}

	if err := client.createDefaultDatabases(); err != nil {
		return nil, err
	}

	return ioutil.ReadFile(client.stateFilePath)
}

func (client *Client) createEnv() error {
	// deploy command needs to be run from directory with bosh state file
	var combinedOutput []byte
	err := util.PushDir(client.tempDir, func() error {
		var e error
		combinedOutput, e = client.runBoshCommand(
			"create-env",
			client.directorManifestPath,
			"--state",
			client.stateFilePath,
		)
		return e
	})
	if err != nil {
		return err
	}
	if !strings.Contains(string(combinedOutput), "Finished deploying") && !strings.Contains(string(combinedOutput), "Skipping deploy") {
		return errors.New("Couldn't find string `Finished deploying` or `Skipping deploy` in bosh stdout/stderr output")
	}

	return nil
}

// Delete deletes a bosh director
func (client *Client) Delete() error {
	_, err := client.runBoshCommand(
		"delete-env",
		client.directorManifestPath,
		"--state",
		client.stateFilePath,
	)

	return err
}

func (client *Client) updateCloudConfig() error {
	_, err := client.runAuthenticatedBoshCommand(
		"update-cloud-config",
		client.cloudConfigPath,
	)

	return err
}

func (client *Client) runAuthenticatedBoshCommand(args ...string) ([]byte, error) {
	args = append([]string{
		"--environment",
		fmt.Sprintf("https://%s", client.metadata.DirectorPublicIP.Value),
		"--ca-cert",
		client.caCertPath,
		"--client",
		client.config.DirectorUsername,
		"--client-secret",
		client.config.DirectorPassword,
	}, args...)

	return client.runBoshCommand(args...)
}

// https://github.com/cloudfoundry/bosh-cli/blob/master/main.go
func (client *Client) runBoshCommand(args ...string) ([]byte, error) {
	combinedOutputBuffer := bytes.NewBuffer(nil)
	stdout := io.MultiWriter(client.stdout, combinedOutputBuffer)
	stderr := io.MultiWriter(client.stderr, combinedOutputBuffer)

	logger := boshlog.NewWriterLogger(boshInitLogLevel,
		stdout,
		stderr,
	)

	ui := boshui.NewConfUI(logger)
	defer ui.Flush()
	writerUI := boshui.NewWriterUI(stdout, stderr, logger)

	// NOTE SetParent is implemented manually on vendored version of bosh-cli
	ui.SetParent(writerUI)

	basicDeps := boshcmd.NewBasicDeps(ui, logger)
	cmdFactory := boshcmd.NewFactory(basicDeps)

	args = append(defaultBoshArgs, args...)

	cmd, err := cmdFactory.New(args)
	if err != nil {
		return nil, err
	}

	if err = cmd.Execute(); err != nil {
		return nil, err
	}

	ui.Flush()

	stdoutBytes, err := ioutil.ReadAll(combinedOutputBuffer)
	if err != nil {
		return nil, err
	}

	return stdoutBytes, nil
}
<commit_msg>minor refactor of bosh client<commit_after>package bosh

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"bitbucket.org/engineerbetter/concourse-up/config"
	"bitbucket.org/engineerbetter/concourse-up/terraform"
	"bitbucket.org/engineerbetter/concourse-up/util"
	boshcmd "github.com/cloudfoundry/bosh-cli/cmd"
	boshui "github.com/cloudfoundry/bosh-cli/ui"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
)

const boshInitLogLevel = boshlog.LevelWarn
const pemFilename = "director.pem"
const directorManifestFilename = "director.yml"
const concourseManifestFilename = "concourse.yml"
const cloudConfigFilename = "cloud-config.yml"
const caCertFilename = "ca-cert.pem"

var defaultBoshArgs = []string{"--non-interactive", "--tty", "--no-color"}

// StateFilename is default name for bosh-init state file
const StateFilename = "director-state.json"

// Client is a concrete implementation of the IClient interface
type Client struct {
	config   *config.Config
	metadata *terraform.Metadata
	stdout   io.Writer
	stderr   io.Writer
	*tempFiles
}

type tempFiles struct {
	tempDir               string
	directorManifestPath  string
	concourseManifestPath string
	cloudConfigPath       string
	stateFilePath         string
	caCertPath            string
	keyFilePath           string
}

// IClient is a client for performing bosh-init commands
type IClient interface {
	Deploy() ([]byte, error)
	Delete() error
	Cleanup() error
}

// ClientFactory creates a new IClient
type ClientFactory func(config *config.Config, metadata *terraform.Metadata, stateFileBytes []byte, stdout, stderr io.Writer) (IClient, error)

// NewClient creates a new Client
func NewClient(config *config.Config, metadata *terraform.Metadata, stateFileBytes []byte, stdout, stderr io.Writer) (IClient, error) {
	tempFiles, err := writeClientTempFiles(config, metadata, stateFileBytes)
	if err != nil {
		return nil, err
	}

	return &Client{
		config:    config,
		metadata:  metadata,
		stdout:    stdout,
		stderr:    stderr,
		tempFiles: tempFiles,
	}, nil
}

// Cleanup cleans up temporary files associated with bosh init
func (client *Client) Cleanup() error {
	return os.RemoveAll(client.tempDir)
}

// Deploy deploys a new Bosh director or converges an existing deployment
// Returns new contents of bosh state file
func (client *Client) Deploy() ([]byte, error) {
	if err := client.createEnv(); err != nil {
		return nil, err
	}

	if err := client.updateCloudConfig(); err != nil {
		return nil, err
	}

	if err := client.uploadConcourse(); err != nil {
		return nil, err
	}

	if err := client.createDefaultDatabases(); err != nil {
		return nil, err
	}

	return ioutil.ReadFile(client.stateFilePath)
}

func (client *Client) createEnv() error {
	// deploy command needs to be run from directory with bosh state file
	var combinedOutput []byte
	err := util.PushDir(client.tempDir, func() error {
		var e error
		combinedOutput, e = client.runBoshCommand(
			"create-env",
			client.directorManifestPath,
			"--state",
			client.stateFilePath,
		)
		return e
	})
	if err != nil {
		return err
	}
	if !strings.Contains(string(combinedOutput), "Finished deploying") && !strings.Contains(string(combinedOutput), "Skipping deploy") {
		return errors.New("Couldn't find string `Finished deploying` or `Skipping deploy` in bosh stdout/stderr output")
	}

	return nil
}

// Delete deletes a bosh director
func (client *Client) Delete() error {
	_, err := client.runBoshCommand(
		"delete-env",
		client.directorManifestPath,
		"--state",
		client.stateFilePath,
	)

	return err
}

func (client *Client) updateCloudConfig() error {
	_, err := client.runAuthenticatedBoshCommand(
		"update-cloud-config",
		client.cloudConfigPath,
	)

	return err
}

func (client *Client) runAuthenticatedBoshCommand(args ...string) ([]byte, error) {
	args = append([]string{
		"--environment",
		fmt.Sprintf("https://%s", client.metadata.DirectorPublicIP.Value),
		"--ca-cert",
		client.caCertPath,
		"--client",
		client.config.DirectorUsername,
		"--client-secret",
		client.config.DirectorPassword,
	}, args...)

	return client.runBoshCommand(args...)
}

// https://github.com/cloudfoundry/bosh-cli/blob/master/main.go
func (client *Client) runBoshCommand(args ...string) ([]byte, error) {
	combinedOutputBuffer := bytes.NewBuffer(nil)
	stdout := io.MultiWriter(client.stdout, combinedOutputBuffer)
	stderr := io.MultiWriter(client.stderr, combinedOutputBuffer)

	logger := boshlog.NewWriterLogger(boshInitLogLevel,
		stdout,
		stderr,
	)

	ui := boshui.NewConfUI(logger)
	defer ui.Flush()
	writerUI := boshui.NewWriterUI(stdout, stderr, logger)

	// NOTE SetParent is implemented manually on vendored version of bosh-cli
	ui.SetParent(writerUI)

	basicDeps := boshcmd.NewBasicDeps(ui, logger)
	cmdFactory := boshcmd.NewFactory(basicDeps)

	args = append(defaultBoshArgs, args...)

	cmd, err := cmdFactory.New(args)
	if err != nil {
		return nil, err
	}

	if err = cmd.Execute(); err != nil {
		return nil, err
	}

	ui.Flush()

	stdoutBytes, err := ioutil.ReadAll(combinedOutputBuffer)
	if err != nil {
		return nil, err
	}

	return stdoutBytes, nil
}

func writeClientTempFiles(config *config.Config, metadata *terraform.Metadata, stateFileBytes []byte) (*tempFiles, error) {
	tempDir, err := ioutil.TempDir("", "concourse-up")
	if err != nil {
		return nil, err
	}

	manifestBytes, err := generateBoshInitManifest(config, metadata)
	if err != nil {
		return nil, err
	}

	directorManifestPath := filepath.Join(tempDir, directorManifestFilename)
	if err = ioutil.WriteFile(directorManifestPath, manifestBytes, 0700); err != nil {
		return nil, err
	}

	concourseManifestBytes, err := generateConcourseManifest(config, metadata)
	if err != nil {
		return nil, err
	}

	concourseManifestPath := filepath.Join(tempDir, concourseManifestFilename)
	if err = ioutil.WriteFile(concourseManifestPath, concourseManifestBytes, 0700); err != nil {
		return nil, err
	}

	cloudConfigBytes, err := generateCloudConfig(config, metadata)
	if err != nil {
		return nil, err
	}

	cloudConfigPath := filepath.Join(tempDir, cloudConfigFilename)
	if err = ioutil.WriteFile(cloudConfigPath, cloudConfigBytes, 0700); err != nil {
		return nil, err
	}

	keyFileBytes := []byte(config.PrivateKey)
	keyFilePath := filepath.Join(tempDir, pemFilename)
	if err = ioutil.WriteFile(keyFilePath, keyFileBytes, 0700); err != nil {
		return nil, err
	}

	caCertPath := filepath.Join(tempDir, caCertFilename)
	if err = ioutil.WriteFile(caCertPath, []byte(config.DirectorCACert), 0700); err != nil {
		return nil, err
	}

	stateFilePath := filepath.Join(tempDir, StateFilename)
	if stateFileBytes != nil {
		if err = ioutil.WriteFile(stateFilePath, stateFileBytes, 0700); err != nil {
			return nil, err
		}
	}

	return &tempFiles{
		tempDir:               tempDir,
		stateFilePath:         stateFilePath,
		caCertPath:            caCertPath,
		directorManifestPath:  directorManifestPath,
		concourseManifestPath: concourseManifestPath,
		keyFilePath:           keyFilePath,
		cloudConfigPath:       cloudConfigPath,
	}, nil
}
<|endoftext|>


<commit_before>package bot

import (
	"github.com/bwmarrin/discordgo"
	"go.uber.org/zap"
	"fmt"
	"github.com/FederationOfFathers/dashboard/messaging"
)

type DiscordAPI struct {
	Config DiscordCfg
	discord *discordgo.Session
	assignmentMsgs map[string]map[string]string
}

type EmojiRole struct {
	EmojiId string `yaml:"emojiId"`
	RoleId string `yaml:"roleId"`
}

type DiscordEmojiRoleGroup struct {
	MessageTitle string `yaml:"messageTitle"`
	MessageBody string `yaml:"messageBody"`
	Roles []EmojiRole `yaml:"roles"`

}

type DiscordRoleCfg struct {
	ChannelId string `yaml:"channelId"`
	EmojiRoleGroups []DiscordEmojiRoleGroup `yaml:"emojiRoles"`

}

type DiscordCfg struct {
	ClientId string `yaml:"appClientId"`
	Token string `yaml:"botToken"`
	StreamChannelId string `yaml:"streamChannelId"`
	GuildId string `yaml:"guildId"`
	RoleCfg DiscordRoleCfg `yaml:"roleConfig"`
}

func NewDiscordAPI(cfg DiscordCfg) DiscordAPI {
	return DiscordAPI{
		Config: cfg,
	}
}

func (d DiscordAPI) roleAssignmentHandler(s *discordgo.Session, event *discordgo.MessageReactionAdd) {

	// skip if the event was from the bot/app
	if event.UserID == d.Config.ClientId {
		return
	}

	// only handle if the message is one we have configured
	if roles, ok := d.assignmentMsgs[event.MessageID]; ok {

		// Unicode emojis use the unicode character (name) as the id. Others use the name and integer as the id.
		emojiId := event.Emoji.Name
		if event.Emoji.ID != "" {
			emojiId = fmt.Sprintf(":%s:%s", event.Emoji.Name, event.Emoji.ID)
		}

		if roleId, ok := roles[emojiId]; ok{
			// get the user from the server/guild
			member, err := d.discord.GuildMember(d.Config.GuildId, event.UserID)
			if err != nil {
				Logger.Error("Unable to get member", zap.Error(err))
				return
			}

			userHadRole := false
			// if the member has the mapped role, remove it
			for _, id := range member.Roles {
				if id == roleId {
					d.removeRoleFromUser(event.UserID, roleId)
					userHadRole = true // true, even if an attempt was made and failed
				}
			}

			// if the member does not have the mapped role, add it
			if !userHadRole {
				d.addRoleToUser(event.UserID, roleId)
				d.discord.GuildMemberRoleAdd(d.Config.GuildId, event.UserID, roleId)
				Logger.Info("added role to user", zap.String("userId", event.UserID), zap.String("roleId", roleId))

			}
		}

		// remove the users reaction. If the add/remove failed, they can click it again to re-trigger
		d.discord.MessageReactionRemove(
			event.ChannelID,
			event.MessageID,
			emojiId,
			event.UserID,
		)
	}

}

// Needs to be called before any other API function work
func (d *DiscordAPI) Connect() {
	dg, err := discordgo.New("Bot " + d.Config.Token)
	if err != nil {
		Logger.Error("Unable to create discord connection", zap.Error(err))
		return
	}

	d.discord = dg
	dg.Open()
}

func (d *DiscordAPI) StartRoleHandlers(){
	d.listRoles()
	d.clearRoleChannel()
	d.createRoleMessages()
	d.discord.AddHandler(d.roleAssignmentHandler)
	Logger.Info("Role assignment handler started")
}

// Needs to be called to disconnect from discord
func (d *DiscordAPI) Shutdown() {
	d.discord.Close()
}

func (d DiscordAPI) PostStreamMessage(sm messaging.StreamMessage) error {
	if d.discord == nil {
		return fmt.Errorf("discord API not connected")
	}
	if d.Config.StreamChannelId == "" {
		return fmt.Errorf("stream channel id not configured")
	}
	author := discordgo.MessageEmbedAuthor{
		Name: fmt.Sprintf("%s is live!", sm.Username),
	}
	thumbnail := discordgo.MessageEmbedThumbnail{
		URL: sm.UserLogo,
	}
	footer := discordgo.MessageEmbedFooter{
		Text: fmt.Sprintf("%s | %s", sm.Platform, sm.Timestamp),
		IconURL: sm.PlatformLogo,
	}
	messageEmbed := discordgo.MessageEmbed{
		Description: sm.URL,
		Color: sm.PlatformColorInt,
		URL: sm.URL,
		Author: &author,
		Thumbnail: &thumbnail,
		Footer: &footer,
		Fields: []*discordgo.MessageEmbedField{
			{
				Name: "Game",
				Value: fmt.Sprintf("%s - %s", sm.Game, sm.Description),
				Inline: false,
			},
		},
	}
	_, err := d.discord.ChannelMessageSendEmbed(d.Config.StreamChannelId, &messageEmbed)
	return err
}

// removes all messages entered by this bot in the channel. Uses the ClientID of the bot/app
func (d *DiscordAPI) clearRoleChannel() {
	channelId := d.Config.RoleCfg.ChannelId

	messages, err := d.discord.ChannelMessages(channelId,50,"","","")
	if err != nil {
		Logger.Error("Unable to access channel messages", zap.String("channelId", channelId), zap.Error(err))
		return
	}
	for _, message := range messages {
		if message.Author.ID == d.Config.ClientId {
			err := d.discord.ChannelMessageDelete(channelId, message.ID)
			if err == nil {
				Logger.Info("Removed bot message", zap.String("messageId", message.ID))
			} else {
				Logger.Error("Unable to remove message", zap.String("messageId", message.ID), zap.Error(err))
			}

		}
	}
}

// creates messages in the channel and adds the emojis
func (d *DiscordAPI) createRoleMessages() {
	d.assignmentMsgs = make(map[string]map[string]string)
	for _, group := range d.Config.RoleCfg.EmojiRoleGroups {
		messageEmbed := discordgo.MessageEmbed{
			Title: group.MessageTitle,
			Description: group.MessageBody,
			Color: 15581239,
		}
		// create the message
		message, err := d.discord.ChannelMessageSendEmbed(d.Config.RoleCfg.ChannelId, &messageEmbed)
		if err != nil {
			Logger.Error("Unable to create message", zap.Error(err))
			continue
		}
		Logger.Info("Added role message", zap.String("message", group.MessageTitle), zap.String("messageId", message.ID))

		// add the emojis
		emojiRoles := make(map[string]string)
		for _, role := range group.Roles {
			emojiRoles[role.EmojiId] = role.RoleId
			err := d.discord.MessageReactionAdd(d.Config.RoleCfg.ChannelId, message.ID, role.EmojiId)
			if err != nil {
				Logger.Error("Unable to add emoji to message",
					zap.String("emojiId", role.EmojiId),
					zap.String("messageId", message.ID),
					zap.Error(err),
				)
			}
		}

		d.assignmentMsgs[message.ID] = emojiRoles
	}
}

func (d DiscordAPI) addRoleToUser(userId string, roleId string) {
	err := d.discord.GuildMemberRoleAdd(d.Config.GuildId, userId, roleId)
	if err != nil {
		Logger.Error("could not add role",
			zap.String("userId", userId),
			zap.String("roleId", roleId),
			zap.Error(err))
	} else {
		Logger.Info("added role to user",
			zap.String("userId", userId),
			zap.String("roleId", roleId),
		)
	}
}

func (d DiscordAPI) removeRoleFromUser(userId string, roleId string) {
	err := d.discord.GuildMemberRoleRemove(d.Config.GuildId, userId, roleId)
	if err != nil {
		Logger.Error("could not remove role", zap.Error(err))
	} else {
		Logger.Info("removed role from user",
			zap.String("userId", userId),
			zap.String("roleId", roleId),
		)
	}
}
func (d *DiscordAPI) listRoles() {
	roles, err := d.discord.GuildRoles(d.Config.GuildId)

	if err != nil {
		Logger.Error("Could not get roles", zap.Error(err))
		return
	}

	for i, role := range roles {
		Logger.Info(fmt.Sprintf("Role %d", i), zap.Any("role",role))
	}
}<commit_msg>Removing extra role addition<commit_after>package bot

import (
	"github.com/bwmarrin/discordgo"
	"go.uber.org/zap"
	"fmt"
	"github.com/FederationOfFathers/dashboard/messaging"
)

type DiscordAPI struct {
	Config DiscordCfg
	discord *discordgo.Session
	assignmentMsgs map[string]map[string]string
}

type EmojiRole struct {
	EmojiId string `yaml:"emojiId"`
	RoleId string `yaml:"roleId"`
}

type DiscordEmojiRoleGroup struct {
	MessageTitle string `yaml:"messageTitle"`
	MessageBody string `yaml:"messageBody"`
	Roles []EmojiRole `yaml:"roles"`

}

type DiscordRoleCfg struct {
	ChannelId string `yaml:"channelId"`
	EmojiRoleGroups []DiscordEmojiRoleGroup `yaml:"emojiRoles"`

}

type DiscordCfg struct {
	ClientId string `yaml:"appClientId"`
	Token string `yaml:"botToken"`
	StreamChannelId string `yaml:"streamChannelId"`
	GuildId string `yaml:"guildId"`
	RoleCfg DiscordRoleCfg `yaml:"roleConfig"`
}

func NewDiscordAPI(cfg DiscordCfg) DiscordAPI {
	return DiscordAPI{
		Config: cfg,
	}
}

func (d DiscordAPI) roleAssignmentHandler(s *discordgo.Session, event *discordgo.MessageReactionAdd) {

	// skip if the event was from the bot/app
	if event.UserID == d.Config.ClientId {
		return
	}

	// only handle if the message is one we have configured
	if roles, ok := d.assignmentMsgs[event.MessageID]; ok {

		// Unicode emojis use the unicode character (name) as the id. Others use the name and integer as the id.
		emojiId := event.Emoji.Name
		if event.Emoji.ID != "" {
			emojiId = fmt.Sprintf(":%s:%s", event.Emoji.Name, event.Emoji.ID)
		}

		if roleId, ok := roles[emojiId]; ok{
			// get the user from the server/guild
			member, err := d.discord.GuildMember(d.Config.GuildId, event.UserID)
			if err != nil {
				Logger.Error("Unable to get member", zap.Error(err))
				return
			}

			userHadRole := false
			// if the member has the mapped role, remove it
			for _, id := range member.Roles {
				if id == roleId {
					d.removeRoleFromUser(event.UserID, roleId)
					userHadRole = true // true, even if an attempt was made and failed
				}
			}

			// if the member does not have the mapped role, add it
			if !userHadRole {
				d.addRoleToUser(event.UserID, roleId)
			}
		}

		// remove the users reaction. If the add/remove failed, they can click it again to re-trigger
		d.discord.MessageReactionRemove(
			event.ChannelID,
			event.MessageID,
			emojiId,
			event.UserID,
		)
	}

}

// Needs to be called before any other API function work
func (d *DiscordAPI) Connect() {
	dg, err := discordgo.New("Bot " + d.Config.Token)
	if err != nil {
		Logger.Error("Unable to create discord connection", zap.Error(err))
		return
	}

	d.discord = dg
	dg.Open()
}

func (d *DiscordAPI) StartRoleHandlers(){
	d.listRoles()
	d.clearRoleChannel()
	d.createRoleMessages()
	d.discord.AddHandler(d.roleAssignmentHandler)
	Logger.Info("Role assignment handler started")
}

// Needs to be called to disconnect from discord
func (d *DiscordAPI) Shutdown() {
	d.discord.Close()
}

func (d DiscordAPI) PostStreamMessage(sm messaging.StreamMessage) error {
	if d.discord == nil {
		return fmt.Errorf("discord API not connected")
	}
	if d.Config.StreamChannelId == "" {
		return fmt.Errorf("stream channel id not configured")
	}
	author := discordgo.MessageEmbedAuthor{
		Name: fmt.Sprintf("%s is live!", sm.Username),
	}
	thumbnail := discordgo.MessageEmbedThumbnail{
		URL: sm.UserLogo,
	}
	footer := discordgo.MessageEmbedFooter{
		Text: fmt.Sprintf("%s | %s", sm.Platform, sm.Timestamp),
		IconURL: sm.PlatformLogo,
	}
	messageEmbed := discordgo.MessageEmbed{
		Description: sm.URL,
		Color: sm.PlatformColorInt,
		URL: sm.URL,
		Author: &author,
		Thumbnail: &thumbnail,
		Footer: &footer,
		Fields: []*discordgo.MessageEmbedField{
			{
				Name: "Game",
				Value: fmt.Sprintf("%s - %s", sm.Game, sm.Description),
				Inline: false,
			},
		},
	}
	_, err := d.discord.ChannelMessageSendEmbed(d.Config.StreamChannelId, &messageEmbed)
	return err
}

// removes all messages entered by this bot in the channel. Uses the ClientID of the bot/app
func (d *DiscordAPI) clearRoleChannel() {
	channelId := d.Config.RoleCfg.ChannelId

	messages, err := d.discord.ChannelMessages(channelId,50,"","","")
	if err != nil {
		Logger.Error("Unable to access channel messages", zap.String("channelId", channelId), zap.Error(err))
		return
	}
	for _, message := range messages {
		if message.Author.ID == d.Config.ClientId {
			err := d.discord.ChannelMessageDelete(channelId, message.ID)
			if err == nil {
				Logger.Info("Removed bot message", zap.String("messageId", message.ID))
			} else {
				Logger.Error("Unable to remove message", zap.String("messageId", message.ID), zap.Error(err))
			}

		}
	}
}

// creates messages in the channel and adds the emojis
func (d *DiscordAPI) createRoleMessages() {
	d.assignmentMsgs = make(map[string]map[string]string)
	for _, group := range d.Config.RoleCfg.EmojiRoleGroups {
		messageEmbed := discordgo.MessageEmbed{
			Title: group.MessageTitle,
			Description: group.MessageBody,
			Color: 15581239,
		}
		// create the message
		message, err := d.discord.ChannelMessageSendEmbed(d.Config.RoleCfg.ChannelId, &messageEmbed)
		if err != nil {
			Logger.Error("Unable to create message", zap.Error(err))
			continue
		}
		Logger.Info("Added role message", zap.String("message", group.MessageTitle), zap.String("messageId", message.ID))

		// add the emojis
		emojiRoles := make(map[string]string)
		for _, role := range group.Roles {
			emojiRoles[role.EmojiId] = role.RoleId
			err := d.discord.MessageReactionAdd(d.Config.RoleCfg.ChannelId, message.ID, role.EmojiId)
			if err != nil {
				Logger.Error("Unable to add emoji to message",
					zap.String("emojiId", role.EmojiId),
					zap.String("messageId", message.ID),
					zap.Error(err),
				)
			}
		}

		d.assignmentMsgs[message.ID] = emojiRoles
	}
}

func (d DiscordAPI) addRoleToUser(userId string, roleId string) {
	err := d.discord.GuildMemberRoleAdd(d.Config.GuildId, userId, roleId)
	if err != nil {
		Logger.Error("could not add role",
			zap.String("userId", userId),
			zap.String("roleId", roleId),
			zap.Error(err))
	} else {
		Logger.Info("added role to user",
			zap.String("userId", userId),
			zap.String("roleId", roleId),
		)
	}
}

func (d DiscordAPI) removeRoleFromUser(userId string, roleId string) {
	err := d.discord.GuildMemberRoleRemove(d.Config.GuildId, userId, roleId)
	if err != nil {
		Logger.Error("could not remove role", zap.Error(err))
	} else {
		Logger.Info("removed role from user",
			zap.String("userId", userId),
			zap.String("roleId", roleId),
		)
	}
}
func (d *DiscordAPI) listRoles() {
	roles, err := d.discord.GuildRoles(d.Config.GuildId)

	if err != nil {
		Logger.Error("Could not get roles", zap.Error(err))
		return
	}

	for i, role := range roles {
		Logger.Info(fmt.Sprintf("Role %d", i), zap.Any("role",role))
	}
}<|endoftext|>


<commit_before>package dialects

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"math/rand"
	"path"
	"strconv"
	"time"
)

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

// Generates an `n` length random string.
func RandStringBytes(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

// Get a random name for the blob
func GetRandomPath(basePath string, extension string) string {
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	fileName := fmt.Sprintf("%s-%s.%s.gz", timestamp, RandStringBytes(20), extension)
	return path.Join(basePath, fileName)
}

// Compress the given string
func Compress(msg *bytes.Buffer) (*bytes.Buffer, error) {
	b := new(bytes.Buffer)
	gz := gzip.NewWriter(b)
	if _, err := gz.Write(msg.Bytes()); err != nil {
		return b, err
	}
	if err := gz.Flush(); err != nil {
		return b, err
	}
	if err := gz.Close(); err != nil {
		return b, err
	}
	return b, nil
}
<commit_msg>Use flags in the blob path, like date<commit_after>package dialects

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"math/rand"
	"path"
	"strconv"
	"strings"
	"time"
)

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

// Generates an `n` length random string.
func RandStringBytes(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

// Resolves custom names in the path
func ResolvePath(basePath string) string {
	newPath := basePath
	if strings.Contains(basePath, "{date}") {
		newPath = strings.Replace(basePath, "{date}", time.Now().UTC().Format("2006-01-02"), -1)
	}
	return newPath
}

// Get a random name for the blob
func GetRandomPath(basePath string, extension string) string {
	timestamp := strconv.Itoa(int(time.Now().Unix()))
	fileName := fmt.Sprintf("%s-%s.%s.gz", timestamp, RandStringBytes(20), extension)
	return path.Join(ResolvePath(basePath), fileName)
}

// Compress the given string
func Compress(msg *bytes.Buffer) (*bytes.Buffer, error) {
	b := new(bytes.Buffer)
	gz := gzip.NewWriter(b)
	if _, err := gz.Write(msg.Bytes()); err != nil {
		return b, err
	}
	if err := gz.Flush(); err != nil {
		return b, err
	}
	if err := gz.Close(); err != nil {
		return b, err
	}
	return b, nil
}
<|endoftext|>


<commit_before>package roll

import (
	"math/rand"
	"regexp"
	"sort"
	"strconv"
)

var regex = regexp.MustCompile(`(?i)(?P<op>[×*/^v+-])?\s*((?P<num>\d{0,3})d(?P<sides>[f%]|\d{1,4})(?P<explode>!)?(?P<max>[<>]\d{1,4})?(?P<keep>k-?\d{1,3})?|(?P<alt>\d{1,5})(?P<fudge>f)?)( for (?P<for>[^,;]+))?`)

const (
	Add      = "+"
	Subtract = "-"
	Multiply = "*"
	Divide   = "/"
	Max      = "^"
	Min      = "v"
)

type Dice struct {
	Operator string
	Number   int
	Sides    int
	Minimum  int
	Maximum  int
	Keep     int
	Rolls    []int
	Removed  []int
	Explode  bool
	Fudge    bool
	Total    int
	For      string
}

func Parse(text string) []*Dice {
	var rolls []*Dice
	for _, m := range regex.FindAllStringSubmatch(text, 5) {
		dice := &Dice{
			Operator: Add,
			Number:   2,
			Sides:    6,
		}
		for i, name := range regex.SubexpNames() {
			switch name {
			case "op":
				if m[i] == "×" {
					dice.Operator = "*"
				} else if m[i] != "" {
					dice.Operator = m[i]
				}
			case "explode":
				dice.Explode = m[i] != ""
			case "fudge":
				if m[i] != "" {
					dice.Fudge = true
					dice.Sides = 3
				}
			case "alt":
				if m[i] != "" {
					num, _ := strconv.Atoi(m[i])
					dice.Number = num
					dice.Sides = 1
				}
			case "num":
				num, _ := strconv.Atoi(m[i])
				if num < 1 {
					num = 1
				}
				if num > 100 {
					num = 100
				}
				dice.Number = num
			case "sides":
				if m[i] == "" {
					dice.Sides = 6
				} else if m[i] == "f" {
					dice.Fudge = true
					dice.Sides = 3
				} else if m[i] == "%" {
					dice.Sides = 100
				} else {
					dice.Sides, _ = strconv.Atoi(m[i])
					if dice.Sides < 1 {
						dice.Sides = 1
					}
					if dice.Sides > 1000 {
						dice.Sides = 1000
					}
				}
			case "keep":
				if m[i] == "" {
					break
				}
				dice.Keep, _ = strconv.Atoi(m[i][1:])
				if dice.Keep > dice.Number {
					dice.Keep = dice.Number
				} else if dice.Keep < -dice.Number {
					dice.Keep = -dice.Number
				}
			case "max":
				if m[i] == "" {
					break
				}
				if m[i][0] == '>' {
					dice.Minimum, _ = strconv.Atoi(m[i][1:])
					if dice.Minimum >= dice.Sides {
						dice.Minimum = dice.Sides - 1
					}
				} else {
					dice.Maximum, _ = strconv.Atoi(m[i][1:])
					if dice.Maximum < 2 {
						dice.Maximum = 2
					}
				}
			case "for":
				dice.For = m[i]
			}
		}
		rolls = append(rolls, dice)
	}
	if len(rolls) == 0 {
		rolls = append(rolls, &Dice{
			Operator: Add,
			Number:   2,
			Sides:    6,
		})
	}
	return rolls
}

func (r *Dice) Roll() {
	r.Total = 0
	r.Rolls = []int{}
	if r.Sides == 1 {
		r.Total = r.Number
		return
	}
	num := r.Number
	for i := 0; i < num; i++ {
		n := rand.Intn(r.Sides) + 1
		if r.Fudge {
			n -= 2
		}
		r.Total += n
		r.Rolls = append(r.Rolls, n)
		if r.Explode && n == r.Sides {
			num++
		}
	}
	if r.Keep != 0 {
		sort.Ints(r.Rolls)
		if r.Keep > 0 {
			split := len(r.Rolls) - r.Keep
			r.Removed = r.Rolls[:split]
			r.Rolls = r.Rolls[split:]
		} else {
			split := -r.Keep
			r.Removed = r.Rolls[split:]
			r.Rolls = r.Rolls[:split]
		}
		r.Total = 0
		for _, n := range r.Rolls {
			r.Total += n
		}
	}
}
<commit_msg>Allow [Ff]ate<commit_after>package roll

import (
	"math/rand"
	"regexp"
	"sort"
	"strconv"
)

var regex = regexp.MustCompile(`(?i)(?P<op>[×*/^v+-])?\s*((?P<num>\d{0,3})d(?P<sides>[f%]|\d{1,4})(?P<explode>!)?(?P<max>[<>]\d{1,4})?(?P<keep>k-?\d{1,3})?|(?P<alt>\d{1,5})(?P<fudge>f)?)( for (?P<for>[^,;]+))?`)

const (
	Add      = "+"
	Subtract = "-"
	Multiply = "*"
	Divide   = "/"
	Max      = "^"
	Min      = "v"
)

type Dice struct {
	Operator string
	Number   int
	Sides    int
	Minimum  int
	Maximum  int
	Keep     int
	Rolls    []int
	Removed  []int
	Explode  bool
	Fudge    bool
	Total    int
	For      string
}

func Parse(text string) []*Dice {
	var rolls []*Dice
	for _, m := range regex.FindAllStringSubmatch(text, 5) {
		dice := &Dice{
			Operator: Add,
			Number:   2,
			Sides:    6,
		}
		for i, name := range regex.SubexpNames() {
			switch name {
			case "op":
				if m[i] == "×" {
					dice.Operator = "*"
				} else if m[i] != "" {
					dice.Operator = m[i]
				}
			case "explode":
				dice.Explode = m[i] != ""
			case "fudge":
				if m[i] != "" {
					dice.Fudge = true
					dice.Sides = 3
				}
			case "alt":
				if m[i] != "" {
					num, _ := strconv.Atoi(m[i])
					dice.Number = num
					dice.Sides = 1
				}
			case "num":
				num, _ := strconv.Atoi(m[i])
				if num < 1 {
					num = 1
				}
				if num > 100 {
					num = 100
				}
				dice.Number = num
			case "sides":
				if m[i] == "" {
					dice.Sides = 6
				} else if m[i] == "f" || m[i] == "F" {
					dice.Fudge = true
					dice.Sides = 3
				} else if m[i] == "%" {
					dice.Sides = 100
				} else {
					dice.Sides, _ = strconv.Atoi(m[i])
					if dice.Sides < 1 {
						dice.Sides = 1
					}
					if dice.Sides > 1000 {
						dice.Sides = 1000
					}
				}
			case "keep":
				if m[i] == "" {
					break
				}
				dice.Keep, _ = strconv.Atoi(m[i][1:])
				if dice.Keep > dice.Number {
					dice.Keep = dice.Number
				} else if dice.Keep < -dice.Number {
					dice.Keep = -dice.Number
				}
			case "max":
				if m[i] == "" {
					break
				}
				if m[i][0] == '>' {
					dice.Minimum, _ = strconv.Atoi(m[i][1:])
					if dice.Minimum >= dice.Sides {
						dice.Minimum = dice.Sides - 1
					}
				} else {
					dice.Maximum, _ = strconv.Atoi(m[i][1:])
					if dice.Maximum < 2 {
						dice.Maximum = 2
					}
				}
			case "for":
				dice.For = m[i]
			}
		}
		rolls = append(rolls, dice)
	}
	if len(rolls) == 0 {
		rolls = append(rolls, &Dice{
			Operator: Add,
			Number:   2,
			Sides:    6,
		})
	}
	return rolls
}

func (r *Dice) Roll() {
	r.Total = 0
	r.Rolls = []int{}
	if r.Sides == 1 {
		r.Total = r.Number
		return
	}
	num := r.Number
	for i := 0; i < num; i++ {
		n := rand.Intn(r.Sides) + 1
		if r.Fudge {
			n -= 2
		}
		r.Total += n
		r.Rolls = append(r.Rolls, n)
		if r.Explode && n == r.Sides {
			num++
		}
	}
	if r.Keep != 0 {
		sort.Ints(r.Rolls)
		if r.Keep > 0 {
			split := len(r.Rolls) - r.Keep
			r.Removed = r.Rolls[:split]
			r.Rolls = r.Rolls[split:]
		} else {
			split := -r.Keep
			r.Removed = r.Rolls[split:]
			r.Rolls = r.Rolls[:split]
		}
		r.Total = 0
		for _, n := range r.Rolls {
			r.Total += n
		}
	}
}
<|endoftext|>


<commit_before>package create

import (
	"github.com/juju/errgo"
)

var (
	invalidConfigError           = errgo.New("invalid config")
	secretsRetrievalFailedError  = errgo.New("could not retrieve secrets")
	missingCloudConfigKeyError   = errgo.New("missing required key in the cloudconfig")
	malformedCloudConfigKeyError = errgo.New("malformed key in the cloudconfig")
)

// IsInvalidConfig asserts invalidConfigError.
func IsInvalidConfig(err error) bool {
	return errgo.Cause(err) == invalidConfigError
}

func IsMissingCloudConfigKey(err error) bool {
	return errgo.Cause(err) == missingCloudConfigKeyError
}

func IsMalformedCloudConfigKey(err error) bool {
	return errgo.Cause(err) == malformedCloudConfigKeyError
}
<commit_msg>aligned service errors with other microservices<commit_after>package create

import (
	"github.com/juju/errgo"
)

var invalidConfigError = errgo.New("invalid config")

// IsInvalidConfig asserts invalidConfigError.
func IsInvalidConfig(err error) bool {
	return errgo.Cause(err) == invalidConfigError
}

var malformedCloudConfigKeyError = errgo.New("malformed key in the cloudconfig")

// IsMalformedCloudConfigKey asserts malformedCloudConfigKeyError.
func IsMalformedCloudConfigKey(err error) bool {
	return errgo.Cause(err) == malformedCloudConfigKeyError
}

var missingCloudConfigKeyError = errgo.New("missing cloud config key")

// IsMissingCloudConfigKey asserts missingCloudConfigKeyError.
func IsMissingCloudConfigKey(err error) bool {
	return errgo.Cause(err) == missingCloudConfigKeyError
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"code.google.com/p/go-uuid/uuid"
	"github.com/pachyderm/pfs/lib/btrfs"
	"github.com/pachyderm/pfs/lib/mapreduce"
)

var jobDir string = "job"

func commitParam(r *http.Request) string {
	if p := r.URL.Query().Get("commit"); p != "" {
		return p
	}
	return "master"
}

func branchParam(r *http.Request) string {
	if p := r.URL.Query().Get("branch"); p != "" {
		return p
	}
	return "master"
}

func hasBranch(r *http.Request) bool {
	return (r.URL.Query().Get("branch") == "")
}

func materializeParam(r *http.Request) string {
	if _, ok := r.URL.Query()["run"]; ok {
		return "true"
	}
	return "false"
}

func indexOf(haystack []string, needle string) int {
	for i, s := range haystack {
		if s == needle {
			return i
		}
	}
	return -1
}

func cat(w http.ResponseWriter, name string) {
	f, err := btrfs.Open(name)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
	}
	defer f.Close()

	if _, err := io.Copy(w, f); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
	}
}

type Shard struct {
	dataRepo, compRepo string
	shard, modulos     uint64
}

func ShardFromArgs() (Shard, error) {
	s_m := strings.Split(os.Args[1], "-")
	shard, err := strconv.ParseUint(s_m[0], 10, 64)
	if err != nil {
		return Shard{}, err
	}
	modulos, err := strconv.ParseUint(s_m[1], 10, 64)
	if err != nil {
		return Shard{}, err
	}
	return Shard{
		dataRepo: "data-" + os.Args[1],
		compRepo: "comp-" + os.Args[1],
		shard:    shard,
		modulos:  modulos,
	}, nil
}

func (s Shard) EnsureRepos() error {
	if err := btrfs.Ensure(s.dataRepo); err != nil {
		return err
	}
	if err := btrfs.Ensure(s.compRepo); err != nil {
		return err
	}
	return nil
}

//func parseArgs() {
//	// os.Args[1] looks like 2-16
//	dataRepo = "data-" + os.Args[1]
//	compRepo = "comp-" + os.Args[1]
//	logFile = "log-" + os.Args[1]
//	s_m := strings.Split(os.Args[1], "-")
//	var err error
//	shard, err = strconv.ParseUint(s_m[0], 10, 64)
//	if err != nil {
//		log.Fatal(err)
//	}
//	modulos, err = strconv.ParseUint(s_m[1], 10, 64)
//	if err != nil {
//		log.Fatal(err)
//	}
//}

// genericFileHandler serves files from fs. It's used after branch and commit
// info have already been extracted and ignores those aspects of the URL.
func genericFileHandler(fs string, w http.ResponseWriter, r *http.Request) {
	url := strings.Split(r.URL.Path, "/")
	// url looks like: /foo/bar/.../file/<file>
	fileStart := indexOf(url, "file") + 1
	// file is the path in the filesystem we're getting
	file := path.Join(append([]string{fs}, url[fileStart:]...)...)

	if r.Method == "GET" {
		if strings.Contains(file, "*") {
			if !strings.HasSuffix(file, "*") {
				http.Error(w, "Illegal path containing internal `*`. `*` is currently only allowed as the last character of a path.", 400)
			} else {
				dir := path.Dir(file)
				files, err := btrfs.ReadDir(dir)
				if err != nil {
					http.Error(w, err.Error(), 500)
					return
				}
				for _, fi := range files {
					if fi.IsDir() {
						continue
					} else {
						cat(w, path.Join(dir, fi.Name()))
					}
				}
			}
		} else {
			cat(w, file)
		}
	} else if r.Method == "POST" {
		btrfs.MkdirAll(path.Dir(file))
		size, err := btrfs.CreateFromReader(file, r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created %s, size: %d.\n", file, size)
	} else if r.Method == "PUT" {
		btrfs.MkdirAll(path.Dir(file))
		size, err := btrfs.WriteFile(file, r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created %s, size: %d.\n", file, size)
	} else if r.Method == "DELETE" {
		if err := btrfs.Remove(file); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Deleted %s.\n", file)
	}
}

// FileHandler is the core route for modifying the contents of the fileystem.
func (s Shard) FileHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" {
		genericFileHandler(path.Join(s.dataRepo, branchParam(r)), w, r)
	} else if r.Method == "GET" {
		genericFileHandler(path.Join(s.dataRepo, commitParam(r)), w, r)
	} else {
		http.Error(w, "Invalid method.", 405)
	}
}

// CommitHandler creates a snapshot of outstanding changes.
func (s Shard) CommitHandler(w http.ResponseWriter, r *http.Request) {
	url := strings.Split(r.URL.Path, "/")
	// url looks like [, commit, <commit>, file, <file>]
	if len(url) > 3 && url[3] == "file" {
		genericFileHandler(path.Join(s.dataRepo, url[2]), w, r)
		return
	}
	if r.Method == "GET" {
		btrfs.Commits(s.dataRepo, "t0", func(c btrfs.CommitInfo) error {
			isReadOnly, err := btrfs.IsReadOnly(c.Path)
			if err != nil {
				return err
			}
			if isReadOnly {
				fi, err := btrfs.Stat(c.Path)
				if err != nil {
					return err
				}
				fmt.Fprintf(w, "%s    %s\n", fi.Name(), fi.ModTime().Format("2006-01-02T15:04:05.999999-07:00"))
			}
			return nil
		})
	} else if r.Method == "POST" && r.ContentLength == 0 {
		// Create a commit from local data
		var commit string
		if commit = r.URL.Query().Get("commit"); commit == "" {
			commit = uuid.New()
		}
		err := btrfs.Commit(s.dataRepo, commit, branchParam(r))
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}

		if materializeParam(r) == "true" {
			go func() {
				err := mapreduce.Materialize(s.dataRepo, branchParam(r), commit,
					s.compRepo, jobDir, s.shard, s.modulos)
				if err != nil {
					log.Print(err)
				}
			}()
		}

		fmt.Fprint(w, commit)
	} else if r.Method == "POST" {
		// Commit being pushed via a diff
		replica := btrfs.NewLocalReplica(s.dataRepo)
		if err := replica.Commit(r.Body); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
	} else {
		http.Error(w, "Unsupported method.", http.StatusMethodNotAllowed)
		log.Printf("Unsupported method %s in request to %s.", r.Method, r.URL.String())
		return
	}
}

// BranchHandler creates a new branch from commit.
func (s Shard) BranchHandler(w http.ResponseWriter, r *http.Request) {
	url := strings.Split(r.URL.Path, "/")
	// url looks like [, commit, <commit>, file, <file>]
	if len(url) > 3 && url[3] == "file" {
		genericFileHandler(path.Join(s.dataRepo, url[2]), w, r)
		return
	}
	if r.Method == "GET" {
		btrfs.Commits(s.dataRepo, "t0", func(c btrfs.CommitInfo) error {
			isReadOnly, err := btrfs.IsReadOnly(c.Path)
			if err != nil {
				return err
			}
			if !isReadOnly {
				fi, err := btrfs.Stat(c.Path)
				if err != nil {
					return err
				}
				fmt.Fprintf(w, "%s    %s\n", fi.Name(), fi.ModTime().Format("2006-01-02T15:04:05.999999-07:00"))
			}
			return nil
		})
	} else if r.Method == "POST" {
		if err := btrfs.Branch(s.dataRepo, commitParam(r), branchParam(r)); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created branch. (%s) -> %s.\n", commitParam(r), branchParam(r))
	} else {
		http.Error(w, "Invalid method.", 405)
		log.Print("Invalid method %s.", r.Method)
		return
	}
}

func (s Shard) JobHandler(w http.ResponseWriter, r *http.Request) {
	url := strings.Split(r.URL.Path, "/")
	if r.Method == "GET" && len(url) > 3 && url[3] == "file" {
		// url looks like [, job, <job>, file, <file>]
		if hasBranch(r) {
			err := mapreduce.WaitJob(s.compRepo, branchParam(r), commitParam(r), url[2])
			if err != nil {
				http.Error(w, err.Error(), 500)
				log.Print(err)
				return
			}
			genericFileHandler(path.Join(s.compRepo, branchParam(r), url[2]), w, r)
		} else {
			genericFileHandler(path.Join(s.compRepo, commitParam(r), url[2]), w, r)
		}
		return
	} else if r.Method == "POST" {
		r.URL.Path = path.Join("/file", jobDir, url[2])
		log.Print("URL with reset path:\n", r.URL)
		genericFileHandler(path.Join(s.dataRepo, branchParam(r)), w, r)
	} else {
		http.Error(w, "Invalid method.", 405)
		log.Print("Invalid method %s.", r.Method)
		return
	}
}

func (s Shard) PullHandler(w http.ResponseWriter, r *http.Request) {
	from := r.URL.Query().Get("from")
	cb := NewMultiPartCommitBrancher(multipart.NewWriter(w))
	localReplica := btrfs.NewLocalReplica(s.dataRepo)
	_, err := localReplica.Pull(from, cb)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}

}

// ShardMux creates a multiplexer for a Shard writing to the passed in FS.
func (s Shard) ShardMux() *http.ServeMux {
	mux := http.NewServeMux()

	mux.HandleFunc("/branch", s.BranchHandler)
	mux.HandleFunc("/commit", s.CommitHandler)
	mux.HandleFunc("/file/", s.FileHandler)
	mux.HandleFunc("/job/", s.JobHandler)
	mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "pong\n") })
	mux.HandleFunc("/pull", s.PullHandler)

	return mux
}

// RunServer runs a shard server listening on port 80
func (s Shard) RunServer() {
	http.ListenAndServe(":80", s.ShardMux())
}

func main() {
	log.SetFlags(log.Lshortfile)
	if err := os.MkdirAll("/var/lib/pfs/log", 0777); err != nil {
		log.Fatal(err)
	}
	logF, err := os.Create(path.Join("/var/lib/pfs/log", "log-"+os.Args[1]))
	if err != nil {
		log.Fatal(err)
	}
	defer logF.Close()
	log.SetOutput(logF)

	s, err := ShardFromArgs()
	if err != nil {
		log.Fatal(err)
	}
	err = s.EnsureRepos()
	if err != nil {
		log.Fatal(err)
	}
	log.Print("Listening on port 80...")
	log.Printf("dataRepo: %s, compRepo: %s.", s.dataRepo, s.compRepo)
	s.RunServer()
}
<commit_msg>Adds a NewShard method.<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"code.google.com/p/go-uuid/uuid"
	"github.com/pachyderm/pfs/lib/btrfs"
	"github.com/pachyderm/pfs/lib/mapreduce"
)

var jobDir string = "job"

func commitParam(r *http.Request) string {
	if p := r.URL.Query().Get("commit"); p != "" {
		return p
	}
	return "master"
}

func branchParam(r *http.Request) string {
	if p := r.URL.Query().Get("branch"); p != "" {
		return p
	}
	return "master"
}

func hasBranch(r *http.Request) bool {
	return (r.URL.Query().Get("branch") == "")
}

func materializeParam(r *http.Request) string {
	if _, ok := r.URL.Query()["run"]; ok {
		return "true"
	}
	return "false"
}

func indexOf(haystack []string, needle string) int {
	for i, s := range haystack {
		if s == needle {
			return i
		}
	}
	return -1
}

func cat(w http.ResponseWriter, name string) {
	f, err := btrfs.Open(name)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
	}
	defer f.Close()

	if _, err := io.Copy(w, f); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
	}
}

type Shard struct {
	dataRepo, compRepo string
	shard, modulos     uint64
}

func ShardFromArgs() (Shard, error) {
	s_m := strings.Split(os.Args[1], "-")
	shard, err := strconv.ParseUint(s_m[0], 10, 64)
	if err != nil {
		return Shard{}, err
	}
	modulos, err := strconv.ParseUint(s_m[1], 10, 64)
	if err != nil {
		return Shard{}, err
	}
	return Shard{
		dataRepo: "data-" + os.Args[1],
		compRepo: "comp-" + os.Args[1],
		shard:    shard,
		modulos:  modulos,
	}, nil
}

func NewShard(dataRepo, compRepo string, shard, modulos uint64) Shard {
	return Shard{
		dataRepo: dataRepo,
		compRepo: compRepo,
		shard:    shard,
		modulos:  modulos,
	}
}

func (s Shard) EnsureRepos() error {
	if err := btrfs.Ensure(s.dataRepo); err != nil {
		return err
	}
	if err := btrfs.Ensure(s.compRepo); err != nil {
		return err
	}
	return nil
}

//func parseArgs() {
//	// os.Args[1] looks like 2-16
//	dataRepo = "data-" + os.Args[1]
//	compRepo = "comp-" + os.Args[1]
//	logFile = "log-" + os.Args[1]
//	s_m := strings.Split(os.Args[1], "-")
//	var err error
//	shard, err = strconv.ParseUint(s_m[0], 10, 64)
//	if err != nil {
//		log.Fatal(err)
//	}
//	modulos, err = strconv.ParseUint(s_m[1], 10, 64)
//	if err != nil {
//		log.Fatal(err)
//	}
//}

// genericFileHandler serves files from fs. It's used after branch and commit
// info have already been extracted and ignores those aspects of the URL.
func genericFileHandler(fs string, w http.ResponseWriter, r *http.Request) {
	url := strings.Split(r.URL.Path, "/")
	// url looks like: /foo/bar/.../file/<file>
	fileStart := indexOf(url, "file") + 1
	// file is the path in the filesystem we're getting
	file := path.Join(append([]string{fs}, url[fileStart:]...)...)

	if r.Method == "GET" {
		if strings.Contains(file, "*") {
			if !strings.HasSuffix(file, "*") {
				http.Error(w, "Illegal path containing internal `*`. `*` is currently only allowed as the last character of a path.", 400)
			} else {
				dir := path.Dir(file)
				files, err := btrfs.ReadDir(dir)
				if err != nil {
					http.Error(w, err.Error(), 500)
					return
				}
				for _, fi := range files {
					if fi.IsDir() {
						continue
					} else {
						cat(w, path.Join(dir, fi.Name()))
					}
				}
			}
		} else {
			cat(w, file)
		}
	} else if r.Method == "POST" {
		btrfs.MkdirAll(path.Dir(file))
		size, err := btrfs.CreateFromReader(file, r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created %s, size: %d.\n", file, size)
	} else if r.Method == "PUT" {
		btrfs.MkdirAll(path.Dir(file))
		size, err := btrfs.WriteFile(file, r.Body)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created %s, size: %d.\n", file, size)
	} else if r.Method == "DELETE" {
		if err := btrfs.Remove(file); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Deleted %s.\n", file)
	}
}

// FileHandler is the core route for modifying the contents of the fileystem.
func (s Shard) FileHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" {
		genericFileHandler(path.Join(s.dataRepo, branchParam(r)), w, r)
	} else if r.Method == "GET" {
		genericFileHandler(path.Join(s.dataRepo, commitParam(r)), w, r)
	} else {
		http.Error(w, "Invalid method.", 405)
	}
}

// CommitHandler creates a snapshot of outstanding changes.
func (s Shard) CommitHandler(w http.ResponseWriter, r *http.Request) {
	url := strings.Split(r.URL.Path, "/")
	// url looks like [, commit, <commit>, file, <file>]
	if len(url) > 3 && url[3] == "file" {
		genericFileHandler(path.Join(s.dataRepo, url[2]), w, r)
		return
	}
	if r.Method == "GET" {
		btrfs.Commits(s.dataRepo, "t0", func(c btrfs.CommitInfo) error {
			isReadOnly, err := btrfs.IsReadOnly(c.Path)
			if err != nil {
				return err
			}
			if isReadOnly {
				fi, err := btrfs.Stat(c.Path)
				if err != nil {
					return err
				}
				fmt.Fprintf(w, "%s    %s\n", fi.Name(), fi.ModTime().Format("2006-01-02T15:04:05.999999-07:00"))
			}
			return nil
		})
	} else if r.Method == "POST" && r.ContentLength == 0 {
		// Create a commit from local data
		var commit string
		if commit = r.URL.Query().Get("commit"); commit == "" {
			commit = uuid.New()
		}
		err := btrfs.Commit(s.dataRepo, commit, branchParam(r))
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}

		if materializeParam(r) == "true" {
			go func() {
				err := mapreduce.Materialize(s.dataRepo, branchParam(r), commit,
					s.compRepo, jobDir, s.shard, s.modulos)
				if err != nil {
					log.Print(err)
				}
			}()
		}

		fmt.Fprint(w, commit)
	} else if r.Method == "POST" {
		// Commit being pushed via a diff
		replica := btrfs.NewLocalReplica(s.dataRepo)
		if err := replica.Commit(r.Body); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
	} else {
		http.Error(w, "Unsupported method.", http.StatusMethodNotAllowed)
		log.Printf("Unsupported method %s in request to %s.", r.Method, r.URL.String())
		return
	}
}

// BranchHandler creates a new branch from commit.
func (s Shard) BranchHandler(w http.ResponseWriter, r *http.Request) {
	url := strings.Split(r.URL.Path, "/")
	// url looks like [, commit, <commit>, file, <file>]
	if len(url) > 3 && url[3] == "file" {
		genericFileHandler(path.Join(s.dataRepo, url[2]), w, r)
		return
	}
	if r.Method == "GET" {
		btrfs.Commits(s.dataRepo, "t0", func(c btrfs.CommitInfo) error {
			isReadOnly, err := btrfs.IsReadOnly(c.Path)
			if err != nil {
				return err
			}
			if !isReadOnly {
				fi, err := btrfs.Stat(c.Path)
				if err != nil {
					return err
				}
				fmt.Fprintf(w, "%s    %s\n", fi.Name(), fi.ModTime().Format("2006-01-02T15:04:05.999999-07:00"))
			}
			return nil
		})
	} else if r.Method == "POST" {
		if err := btrfs.Branch(s.dataRepo, commitParam(r), branchParam(r)); err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		fmt.Fprintf(w, "Created branch. (%s) -> %s.\n", commitParam(r), branchParam(r))
	} else {
		http.Error(w, "Invalid method.", 405)
		log.Print("Invalid method %s.", r.Method)
		return
	}
}

func (s Shard) JobHandler(w http.ResponseWriter, r *http.Request) {
	url := strings.Split(r.URL.Path, "/")
	if r.Method == "GET" && len(url) > 3 && url[3] == "file" {
		// url looks like [, job, <job>, file, <file>]
		if hasBranch(r) {
			err := mapreduce.WaitJob(s.compRepo, branchParam(r), commitParam(r), url[2])
			if err != nil {
				http.Error(w, err.Error(), 500)
				log.Print(err)
				return
			}
			genericFileHandler(path.Join(s.compRepo, branchParam(r), url[2]), w, r)
		} else {
			genericFileHandler(path.Join(s.compRepo, commitParam(r), url[2]), w, r)
		}
		return
	} else if r.Method == "POST" {
		r.URL.Path = path.Join("/file", jobDir, url[2])
		log.Print("URL with reset path:\n", r.URL)
		genericFileHandler(path.Join(s.dataRepo, branchParam(r)), w, r)
	} else {
		http.Error(w, "Invalid method.", 405)
		log.Print("Invalid method %s.", r.Method)
		return
	}
}

func (s Shard) PullHandler(w http.ResponseWriter, r *http.Request) {
	from := r.URL.Query().Get("from")
	cb := NewMultiPartCommitBrancher(multipart.NewWriter(w))
	localReplica := btrfs.NewLocalReplica(s.dataRepo)
	_, err := localReplica.Pull(from, cb)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}

}

// ShardMux creates a multiplexer for a Shard writing to the passed in FS.
func (s Shard) ShardMux() *http.ServeMux {
	mux := http.NewServeMux()

	mux.HandleFunc("/branch", s.BranchHandler)
	mux.HandleFunc("/commit", s.CommitHandler)
	mux.HandleFunc("/file/", s.FileHandler)
	mux.HandleFunc("/job/", s.JobHandler)
	mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "pong\n") })
	mux.HandleFunc("/pull", s.PullHandler)

	return mux
}

// RunServer runs a shard server listening on port 80
func (s Shard) RunServer() {
	http.ListenAndServe(":80", s.ShardMux())
}

func main() {
	log.SetFlags(log.Lshortfile)
	if err := os.MkdirAll("/var/lib/pfs/log", 0777); err != nil {
		log.Fatal(err)
	}
	logF, err := os.Create(path.Join("/var/lib/pfs/log", "log-"+os.Args[1]))
	if err != nil {
		log.Fatal(err)
	}
	defer logF.Close()
	log.SetOutput(logF)

	s, err := ShardFromArgs()
	if err != nil {
		log.Fatal(err)
	}
	err = s.EnsureRepos()
	if err != nil {
		log.Fatal(err)
	}
	log.Print("Listening on port 80...")
	log.Printf("dataRepo: %s, compRepo: %s.", s.dataRepo, s.compRepo)
	s.RunServer()
}
<|endoftext|>