<commit_before>package db_test

import (
	"errors"

	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/db/dbfakes"
	"github.com/lib/pq"

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

var _ = Describe("NotificationBus", func() {

	var (
		c            chan *pq.Notification
		fakeExecutor *dbfakes.FakeExecutor
		fakeListener *dbfakes.FakeListener

		bus db.NotificationsBus
	)

	BeforeEach(func() {
		c = make(chan *pq.Notification, 1)

		fakeExecutor = new(dbfakes.FakeExecutor)
		fakeListener = new(dbfakes.FakeListener)
		fakeListener.NotificationChannelReturns(c)

		bus = db.NewNotificationsBus(fakeListener, fakeExecutor)
	})

	Context("Notify", func() {
		var (
			err error
		)

		JustBeforeEach(func() {
			err = bus.Notify("some-channel")
		})

		It("notifies the channel", func() {
			Expect(fakeExecutor.ExecCallCount()).To(Equal(1))
			msg, _ := fakeExecutor.ExecArgsForCall(0)
			Expect(msg).To(Equal("NOTIFY some-channel"))
		})

		Context("when the executor errors", func() {
			BeforeEach(func() {
				fakeExecutor.ExecReturns(nil, errors.New("nope"))
			})

			It("errors", func() {
				Expect(err).To(HaveOccurred())
			})
		})

		Context("when the executor succeeds", func() {
			BeforeEach(func() {
				fakeExecutor.ExecReturns(nil, nil)
			})

			It("succeeds", func() {
				Expect(err).NotTo(HaveOccurred())
			})
		})
	})

	Context("Listen", func() {
		var (
			err error
		)

		JustBeforeEach(func() {
			_, err = bus.Listen("some-channel")
		})

		Context("when not already listening on channel", func() {
			It("listens on the given channel", func() {
				Expect(fakeListener.ListenCallCount()).To(Equal(1))
				channel := fakeListener.ListenArgsForCall(0)
				Expect(channel).To(Equal("some-channel"))
			})

			Context("when listening errors", func() {
				BeforeEach(func() {
					fakeListener.ListenReturns(errors.New("nope"))
				})

				It("errors", func() {
					Expect(err).To(HaveOccurred())
				})
			})

			Context("when listening succeeds", func() {
				BeforeEach(func() {
					fakeListener.ListenReturns(nil)
				})

				It("succeeds", func() {
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		Context("when already listening on the channel", func() {
			BeforeEach(func() {
				_, err := bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			It("only listens once", func() {
				Expect(fakeListener.ListenCallCount()).To(Equal(1))
			})
		})
	})

	Context("Unlisten", func() {
		var (
			err error
			c   chan bool
		)

		JustBeforeEach(func() {
			err = bus.Unlisten("some-channel", c)
		})

		Context("when there's only one listener", func() {
			BeforeEach(func() {
				c, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			It("unlistens on the given channel", func() {
				Expect(fakeListener.UnlistenCallCount()).To(Equal(1))
				channel := fakeListener.UnlistenArgsForCall(0)
				Expect(channel).To(Equal("some-channel"))
			})

			Context("when unlistening errors", func() {
				BeforeEach(func() {
					fakeListener.UnlistenReturns(errors.New("nope"))
				})

				It("errors", func() {
					Expect(err).To(HaveOccurred())
				})
			})

			Context("when unlistening succeeds", func() {
				BeforeEach(func() {
					fakeListener.UnlistenReturns(nil)
				})

				It("succeeds", func() {
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		Context("when there's multiple listeners", func() {
			BeforeEach(func() {
				c, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())

				_, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			It("succeeds", func() {
				Expect(err).NotTo(HaveOccurred())
			})

			It("does not unlisten on the given channel", func() {
				Expect(fakeListener.UnlistenCallCount()).To(Equal(0))
			})
		})
	})

	Describe("Receiving Notifications", func() {
		var (
			err error
			a   chan bool
			b   chan bool
		)

		Context("when there are multiple listeners for the same channel", func() {
			BeforeEach(func() {
				a, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())

				b, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			Context("when it receives an upstream notification", func() {

				BeforeEach(func() {
					c <- &pq.Notification{Channel: "some-channel"}
				})

				It("delivers the notification to all listeners", func() {
					Eventually(a).Should(Receive(Equal(true)))
					Eventually(b).Should(Receive(Equal(true)))
				})
			})

			Context("when it receives an upstream disconnect notice", func() {

				BeforeEach(func() {
					c <- nil
				})

				It("delivers the notification to all listeners", func() {
					Eventually(a).Should(Receive(Equal(false)))
					Eventually(b).Should(Receive(Equal(false)))
				})
			})
		})

		Context("when there are multiple listeners on different channels", func() {
			BeforeEach(func() {
				a, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())

				b, err = bus.Listen("some-other-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			Context("when it receives an upstream notification", func() {

				BeforeEach(func() {
					c <- &pq.Notification{Channel: "some-channel"}
				})

				It("delivers the notification to only specific listeners", func() {
					Eventually(a).Should(Receive(Equal(true)))
					Consistently(b).ShouldNot(Receive())
				})
			})

			Context("when it receives an upstream disconnect notice", func() {

				BeforeEach(func() {
					c <- nil
				})

				It("delivers the notification to all listeners", func() {
					Eventually(a).Should(Receive(Equal(false)))
					Eventually(b).Should(Receive(Equal(false)))
				})
			})
		})
	})
})
<commit_msg>atc: add regression test for deadlcok<commit_after>package db_test

import (
	"errors"

	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/db/dbfakes"
	"github.com/lib/pq"

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

var _ = Describe("NotificationBus", func() {

	var (
		c            chan *pq.Notification
		fakeExecutor *dbfakes.FakeExecutor
		fakeListener *dbfakes.FakeListener

		bus db.NotificationsBus
	)

	BeforeEach(func() {
		c = make(chan *pq.Notification, 1)

		fakeExecutor = new(dbfakes.FakeExecutor)
		fakeListener = new(dbfakes.FakeListener)
		fakeListener.NotificationChannelReturns(c)

		bus = db.NewNotificationsBus(fakeListener, fakeExecutor)
	})

	Context("Notify", func() {
		var (
			err error
		)

		JustBeforeEach(func() {
			err = bus.Notify("some-channel")
		})

		It("notifies the channel", func() {
			Expect(fakeExecutor.ExecCallCount()).To(Equal(1))
			msg, _ := fakeExecutor.ExecArgsForCall(0)
			Expect(msg).To(Equal("NOTIFY some-channel"))
		})

		Context("when the executor errors", func() {
			BeforeEach(func() {
				fakeExecutor.ExecReturns(nil, errors.New("nope"))
			})

			It("errors", func() {
				Expect(err).To(HaveOccurred())
			})
		})

		Context("when the executor succeeds", func() {
			BeforeEach(func() {
				fakeExecutor.ExecReturns(nil, nil)
			})

			It("succeeds", func() {
				Expect(err).NotTo(HaveOccurred())
			})
		})
	})

	Context("Listen", func() {
		var (
			err error
		)

		JustBeforeEach(func() {
			_, err = bus.Listen("some-channel")
		})

		Context("when not already listening on channel", func() {
			It("listens on the given channel", func() {
				Expect(fakeListener.ListenCallCount()).To(Equal(1))
				channel := fakeListener.ListenArgsForCall(0)
				Expect(channel).To(Equal("some-channel"))
			})

			Context("when listening errors", func() {
				BeforeEach(func() {
					fakeListener.ListenReturns(errors.New("nope"))
				})

				It("errors", func() {
					Expect(err).To(HaveOccurred())
				})
			})

			Context("when listening succeeds", func() {
				BeforeEach(func() {
					fakeListener.ListenReturns(nil)
				})

				It("succeeds", func() {
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		Context("when already listening on the channel", func() {
			BeforeEach(func() {
				_, err := bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			It("only listens once", func() {
				Expect(fakeListener.ListenCallCount()).To(Equal(1))
			})
		})
	})

	Context("Unlisten", func() {
		var (
			err error
			c   chan bool
		)

		JustBeforeEach(func() {
			err = bus.Unlisten("some-channel", c)
		})

		Context("when there's only one listener", func() {
			BeforeEach(func() {
				c, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			It("unlistens on the given channel", func() {
				Expect(fakeListener.UnlistenCallCount()).To(Equal(1))
				channel := fakeListener.UnlistenArgsForCall(0)
				Expect(channel).To(Equal("some-channel"))
			})

			Context("when unlistening errors", func() {
				BeforeEach(func() {
					fakeListener.UnlistenReturns(errors.New("nope"))
				})

				It("errors", func() {
					Expect(err).To(HaveOccurred())
				})
			})

			Context("when unlistening succeeds", func() {
				BeforeEach(func() {
					fakeListener.UnlistenReturns(nil)
				})

				It("succeeds", func() {
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		Context("when there's multiple listeners", func() {
			BeforeEach(func() {
				c, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())

				_, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			It("succeeds", func() {
				Expect(err).NotTo(HaveOccurred())
			})

			It("does not unlisten on the given channel", func() {
				Expect(fakeListener.UnlistenCallCount()).To(Equal(0))
			})
		})
	})

	Describe("Receiving Notifications", func() {
		var (
			err error
			a   chan bool
			b   chan bool
		)

		Context("when there are multiple listeners for the same channel", func() {
			BeforeEach(func() {
				a, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())

				b, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			Context("when it receives an upstream notification", func() {

				BeforeEach(func() {
					c <- &pq.Notification{Channel: "some-channel"}
				})

				It("delivers the notification to all listeners", func() {
					Eventually(a).Should(Receive(Equal(true)))
					Eventually(b).Should(Receive(Equal(true)))
				})
			})

			Context("when it receives an upstream disconnect notice", func() {

				BeforeEach(func() {
					c <- nil
				})

				It("delivers the notification to all listeners", func() {
					Eventually(a).Should(Receive(Equal(false)))
					Eventually(b).Should(Receive(Equal(false)))
				})
			})
		})

		Context("when there are multiple listeners on different channels", func() {
			BeforeEach(func() {
				a, err = bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())

				b, err = bus.Listen("some-other-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			Context("when it receives an upstream notification", func() {

				BeforeEach(func() {
					c <- &pq.Notification{Channel: "some-channel"}
				})

				It("delivers the notification to only specific listeners", func() {
					Eventually(a).Should(Receive(Equal(true)))
					Consistently(b).ShouldNot(Receive())
				})
			})

			Context("when it receives an upstream disconnect notice", func() {

				BeforeEach(func() {
					c <- nil
				})

				It("delivers the notification to all listeners", func() {
					Eventually(a).Should(Receive(Equal(false)))
					Eventually(b).Should(Receive(Equal(false)))
				})
			})
		})

		Context("when the notification channel is full", func() {

			var ready chan bool

			BeforeEach(func() {
				ready = make(chan bool)

				go func() {
					close(ready)
					c <- &pq.Notification{Channel: "some-channel"}
					c <- &pq.Notification{Channel: "some-channel"}
					c <- &pq.Notification{Channel: "some-channel"}
					c <- &pq.Notification{Channel: "some-channel"}
					c <- &pq.Notification{Channel: "some-channel"}
					c <- &pq.Notification{Channel: "some-channel"}
					c <- &pq.Notification{Channel: "some-channel"}
				}()
			})

			It("should still be able to listen for notifications", func() {
				<-ready

				_, err := bus.Listen("some-channel")
				Expect(err).NotTo(HaveOccurred())

				_, err = bus.Listen("some-other-channel")
				Expect(err).NotTo(HaveOccurred())

				_, err = bus.Listen("some-new-channel")
				Expect(err).NotTo(HaveOccurred())
			})

			It("should still be able to unlisten for notifications", func() {
				<-ready

				err := bus.Unlisten("some-channel", a)
				Expect(err).NotTo(HaveOccurred())

				err = bus.Unlisten("some-other-channel", b)
				Expect(err).NotTo(HaveOccurred())
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Conformal Systems <info@conformal.com>
//
// This file originated from: http://opensource.conformal.com/
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

// This file includes wrapers for symbols included since GTK 3.10, and
// and should not be included in a build intended to target any older GTK
// versions.  To target an older build, such as 3.8, use
// 'go build -tags gtk_3_8'.  Otherwise, if no build tags are used, GTK 3.10
// is assumed and this file is built.
// +build !gtk_3_6,!gtk_3_8

package gtk

// #cgo pkg-config: gtk+-3.0
// #include <stdlib.h>
// #include <gtk/gtk.h>
// #include "gtk_3_10.go.h"
import "C"
import (
	"github.com/conformal/gotk3/glib"
	"runtime"
	"unsafe"
)

/*
 * Constants
 */

const (
	ALIGN_BASELINE Align = C.GTK_ALIGN_BASELINE
)

/*
 * GtkHeaderBar
 */

type HeaderBar struct {
	Container
}

// Native returns a pointer to the underlying GtkHeaderBar.
func (v *HeaderBar) Native() *C.GtkHeaderBar {
	if v == nil || v.GObject == nil {
		return nil
	}
	p := unsafe.Pointer(v.GObject)
	return C.toGtkHeaderBar(p)
}

func wrapHeaderBar(obj *glib.Object) *HeaderBar {
	return &HeaderBar{Container{Widget{glib.InitiallyUnowned{obj}}}}
}

// HeaderBarNew is a wrapper around gtk_header_bar_new().
func HeaderBarNew() (*HeaderBar, error) {
	c := C.gtk_header_bar_new()
	if c == nil {
		return nil, nilPtrErr
	}
	obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
	h := wrapHeaderBar(obj)
	obj.RefSink()
	runtime.SetFinalizer(obj, (*glib.Object).Unref)
	return h, nil
}

// SetTitle is a wrapper around gtk_header_bar_set_title().
func (v *HeaderBar) SetTitle(title string) {
	cstr := C.CString(title)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_header_bar_set_title(v.Native(), (*C.gchar)(cstr))
}

// GetTitle is a wrapper around gtk_header_bar_get_title().
func (v *HeaderBar) GetTitle() string {
	cstr := C.gtk_header_bar_get_title(v.Native())
	return C.GoString((*C.char)(cstr))
}

// SetSubtitle is a wrapper around gtk_header_bar_set_subtitle().
func (v *HeaderBar) SetSubtitle(subtitle string) {
	cstr := C.CString(subtitle)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_header_bar_set_subtitle(v.Native(), (*C.gchar)(cstr))
}

// GetSubtitle is a wrapper around gtk_header_bar_get_subtitle().
func (v *HeaderBar) GetSubtitle() string {
	cstr := C.gtk_header_bar_get_subtitle(v.Native())
	return C.GoString((*C.char)(cstr))
}

// SetCustomTitle is a wrapper around gtk_header_bar_set_custom_title().
func (v *HeaderBar) SetCustomTitle(titleWidget IWidget) {
	C.gtk_header_bar_set_custom_title(v.Native(), titleWidget.toWidget())
}

// GetCustomTitle is a wrapper around gtk_header_bar_get_custom_title().
func (v *HeaderBar) GetCustomTitle() (*Widget, error) {
	c := C.gtk_header_bar_get_custom_title(v.Native())
	if c == nil {
		return nil, nilPtrErr
	}
	obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
	w := wrapWidget(obj)
	obj.RefSink()
	runtime.SetFinalizer(obj, (*glib.Object).Unref)
	return w, nil
}

// PackStart is a wrapper around gtk_header_bar_pack_start().
func (v *HeaderBar) PackStart(child IWidget) {
	C.gtk_header_bar_pack_start(v.Native(), child.toWidget())
}

// PackEnd is a wrapper around gtk_header_bar_pack_end().
func (v *HeaderBar) PackEnd(child IWidget) {
	C.gtk_header_bar_pack_end(v.Native(), child.toWidget())
}

// SetShowCloseButton is a wrapper around gtk_header_bar_set_show_close_button().
func (v *HeaderBar) SetShowCloseButton(setting bool) {
	C.gtk_header_bar_set_show_close_button(v.Native(), gbool(setting))
}

// GetShowCloseButton is a wrapper around gtk_header_bar_get_show_close_button().
func (v *HeaderBar) GetShowCloseButton() bool {
	c := C.gtk_header_bar_get_show_close_button(v.Native())
	return gobool(c)
}

/*
 * GtkWindow
 */

// Close is a wrapper around gtk_window_close().
func (v *Window) Close() {
	C.gtk_window_close(v.Native())
}
<commit_msg>Add SetTitlebar support<commit_after>// Copyright (c) 2013-2014 Conformal Systems <info@conformal.com>
//
// This file originated from: http://opensource.conformal.com/
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

// This file includes wrapers for symbols included since GTK 3.10, and
// and should not be included in a build intended to target any older GTK
// versions.  To target an older build, such as 3.8, use
// 'go build -tags gtk_3_8'.  Otherwise, if no build tags are used, GTK 3.10
// is assumed and this file is built.
// +build !gtk_3_6,!gtk_3_8

package gtk

// #cgo pkg-config: gtk+-3.0
// #include <stdlib.h>
// #include <gtk/gtk.h>
// #include "gtk_3_10.go.h"
import "C"
import (
	"github.com/conformal/gotk3/glib"
	"runtime"
	"unsafe"
)

/*
 * Constants
 */

const (
	ALIGN_BASELINE Align = C.GTK_ALIGN_BASELINE
)

/*
 * GtkHeaderBar
 */

type HeaderBar struct {
	Container
}

// Native returns a pointer to the underlying GtkHeaderBar.
func (v *HeaderBar) Native() *C.GtkHeaderBar {
	if v == nil || v.GObject == nil {
		return nil
	}
	p := unsafe.Pointer(v.GObject)
	return C.toGtkHeaderBar(p)
}

func wrapHeaderBar(obj *glib.Object) *HeaderBar {
	return &HeaderBar{Container{Widget{glib.InitiallyUnowned{obj}}}}
}

// HeaderBarNew is a wrapper around gtk_header_bar_new().
func HeaderBarNew() (*HeaderBar, error) {
	c := C.gtk_header_bar_new()
	if c == nil {
		return nil, nilPtrErr
	}
	obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
	h := wrapHeaderBar(obj)
	obj.RefSink()
	runtime.SetFinalizer(obj, (*glib.Object).Unref)
	return h, nil
}

// SetTitle is a wrapper around gtk_header_bar_set_title().
func (v *HeaderBar) SetTitle(title string) {
	cstr := C.CString(title)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_header_bar_set_title(v.Native(), (*C.gchar)(cstr))
}

// GetTitle is a wrapper around gtk_header_bar_get_title().
func (v *HeaderBar) GetTitle() string {
	cstr := C.gtk_header_bar_get_title(v.Native())
	return C.GoString((*C.char)(cstr))
}

// SetSubtitle is a wrapper around gtk_header_bar_set_subtitle().
func (v *HeaderBar) SetSubtitle(subtitle string) {
	cstr := C.CString(subtitle)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_header_bar_set_subtitle(v.Native(), (*C.gchar)(cstr))
}

// GetSubtitle is a wrapper around gtk_header_bar_get_subtitle().
func (v *HeaderBar) GetSubtitle() string {
	cstr := C.gtk_header_bar_get_subtitle(v.Native())
	return C.GoString((*C.char)(cstr))
}

// SetCustomTitle is a wrapper around gtk_header_bar_set_custom_title().
func (v *HeaderBar) SetCustomTitle(titleWidget IWidget) {
	C.gtk_header_bar_set_custom_title(v.Native(), titleWidget.toWidget())
}

// GetCustomTitle is a wrapper around gtk_header_bar_get_custom_title().
func (v *HeaderBar) GetCustomTitle() (*Widget, error) {
	c := C.gtk_header_bar_get_custom_title(v.Native())
	if c == nil {
		return nil, nilPtrErr
	}
	obj := &glib.Object{glib.ToGObject(unsafe.Pointer(c))}
	w := wrapWidget(obj)
	obj.RefSink()
	runtime.SetFinalizer(obj, (*glib.Object).Unref)
	return w, nil
}

// PackStart is a wrapper around gtk_header_bar_pack_start().
func (v *HeaderBar) PackStart(child IWidget) {
	C.gtk_header_bar_pack_start(v.Native(), child.toWidget())
}

// PackEnd is a wrapper around gtk_header_bar_pack_end().
func (v *HeaderBar) PackEnd(child IWidget) {
	C.gtk_header_bar_pack_end(v.Native(), child.toWidget())
}

// SetShowCloseButton is a wrapper around gtk_header_bar_set_show_close_button().
func (v *HeaderBar) SetShowCloseButton(setting bool) {
	C.gtk_header_bar_set_show_close_button(v.Native(), gbool(setting))
}

// GetShowCloseButton is a wrapper around gtk_header_bar_get_show_close_button().
func (v *HeaderBar) GetShowCloseButton() bool {
	c := C.gtk_header_bar_get_show_close_button(v.Native())
	return gobool(c)
}

/*
 * GtkWindow
 */

// SetTitlebar is a wrapper around gtk_window_set_titlebar().
func (v *Window) SetTitlebar(titlebar IWidget) {
	C.gtk_window_set_titlebar(v.Native(), titlebar.toWidget())
}

// Close is a wrapper around gtk_window_close().
func (v *Window) Close() {
	C.gtk_window_close(v.Native())
}
<|endoftext|>


<commit_before>package handlers

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

	"github.com/byuoitav/sony-control-microservice/helpers"
	"github.com/labstack/echo"
)

func PowerOn(context echo.Context) error {
	log.Printf("Powering on %s...", context.Param("address"))

	err := helpers.SetPower(context.Param("address"), true)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func Standby(context echo.Context) error {
	log.Printf("Powering off %s...", context.Param("address"))

	err := helpers.SetPower(context.Param("address"), false)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func GetPower(context echo.Context) error {
	log.Printf("Getting power status of %s...", context.Param("address"))

	response, err := helpers.GetPower(context.Param("address"))
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	return context.JSON(http.StatusOK, response)
}

func SwitchInput(context echo.Context) error {
	log.Printf("Switching input for %s to %s ...", context.Param("address"), context.Param("port"))
	address := context.Param("address")
	port := context.Param("port")

	splitPort := strings.Split(port, "!")

	params := make(map[string]interface{})
	params["uri"] = fmt.Sprintf("extInput:%s?port=%s", splitPort[0], splitPort[1])

	err := helpers.BuildAndSendPayload(address, "avContent", "setPlayContent", params)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func SetVolume(context echo.Context) error {
	address := context.Param("address")
	value := context.Param("value")

	log.Printf("Setting volume for %s to %v...", address, value)

	params := make(map[string]interface{})
	params["target"] = "speaker"
	params["volume"] = value

	err := helpers.BuildAndSendPayload(address, "audio", "setAudioVolume", params)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func VolumeUnmute(context echo.Context) error {
	address := context.Param("address")
	log.Printf("Unmuting %s...", address)

	err := setMute(address, false)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func setMute(address string, status bool) error {
	params := make(map[string]interface{})
	params["status"] = status

	return helpers.BuildAndSendPayload(address, "audio", "setAudioMute", params)
}

func VolumeMute(context echo.Context) error {
	log.Printf("Muting %s...", context.Param("address"))

	err := setMute(context.Param("address"), true)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func BlankDisplay(context echo.Context) error {
	return Standby(context)
}

func UnblankDisplay(context echo.Context) error {
	return PowerOn(context)
}

func GetVolume(context echo.Context) error {
	log.Printf("Getting volume for %s...", context.Param("address"))

	return nil
}

func GetInput(context echo.Context) error {
	return nil
}

func GetInputList(context echo.Context) error {
	return nil
}

func GetMute(context echo.Context) error {
	return nil
}

func GetBlank(context echo.Context) error {
	return nil
}
<commit_msg>gets volume and returns int<commit_after>package handlers

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

	"github.com/byuoitav/sony-control-microservice/helpers"
	"github.com/labstack/echo"
)

func PowerOn(context echo.Context) error {
	log.Printf("Powering on %s...", context.Param("address"))

	err := helpers.SetPower(context.Param("address"), true)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func Standby(context echo.Context) error {
	log.Printf("Powering off %s...", context.Param("address"))

	err := helpers.SetPower(context.Param("address"), false)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func GetPower(context echo.Context) error {
	log.Printf("Getting power status of %s...", context.Param("address"))

	response, err := helpers.GetPower(context.Param("address"))
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	return context.JSON(http.StatusOK, response)
}

func SwitchInput(context echo.Context) error {
	log.Printf("Switching input for %s to %s ...", context.Param("address"), context.Param("port"))
	address := context.Param("address")
	port := context.Param("port")

	splitPort := strings.Split(port, "!")

	params := make(map[string]interface{})
	params["uri"] = fmt.Sprintf("extInput:%s?port=%s", splitPort[0], splitPort[1])

	err := helpers.BuildAndSendPayload(address, "avContent", "setPlayContent", params)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func SetVolume(context echo.Context) error {
	address := context.Param("address")
	value := context.Param("value")

	log.Printf("Setting volume for %s to %v...", address, value)

	params := make(map[string]interface{})
	params["target"] = "speaker"
	params["volume"] = value

	err := helpers.BuildAndSendPayload(address, "audio", "setAudioVolume", params)
	if err != nil {
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func VolumeUnmute(context echo.Context) error {
	address := context.Param("address")
	log.Printf("Unmuting %s...", address)

	err := setMute(address, false)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func setMute(address string, status bool) error {
	params := make(map[string]interface{})
	params["status"] = status

	return helpers.BuildAndSendPayload(address, "audio", "setAudioMute", params)
}

func VolumeMute(context echo.Context) error {
	log.Printf("Muting %s...", context.Param("address"))

	err := setMute(context.Param("address"), true)
	if err != nil {
		log.Printf("Error: %v", err.Error())
		return context.JSONBlob(http.StatusInternalServerError, []byte(err.Error()))
	}

	log.Printf("Done.")
	return nil
}

func BlankDisplay(context echo.Context) error {
	return Standby(context)
}

func UnblankDisplay(context echo.Context) error {
	return PowerOn(context)
}

func GetVolume(context echo.Context) error {
	response, err := helpers.GetVolume(context.Param("address"))
	if err != nil {
		return context.JSON(http.StatusInternalServerError, err.Error())
	}

	return context.JSON(http.StatusOK, response)
}

func GetInput(context echo.Context) error {
	return nil
}

func GetInputList(context echo.Context) error {
	return nil
}

func GetMute(context echo.Context) error {
	return nil
}

func GetBlank(context echo.Context) error {
	return nil
}
<|endoftext|>


<commit_before>package bolster_test

import (
	"flag"
	"testing"

	"github.com/nochso/bolster"
	"github.com/nochso/bolster/internal"
)

var updateGold = flag.Bool("update", false, "update golden test files")

func TestTx_Insert_withoutAutoincrement(t *testing.T) {
	st, closer := internal.OpenTestStore(t)
	defer closer()

	err := st.Register(structWithID{})
	if err != nil {
		t.Error(err)
	}
	t.Run("first", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err != nil {
			t.Error(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("duplicate", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("duplicateLazy", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			tx.Insert(&structWithID{})
			tx.Insert(&structWithID{})
			tx.Insert(&structWithID{})
			return nil
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("surrounding", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			for i := -5; i < 6; i++ {
				if i == 0 {
					continue
				}
				err := tx.Insert(&structWithID{ID: i})
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			t.Error(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
}
<commit_msg>Test validation of struct when inserting<commit_after>package bolster_test

import (
	"flag"
	"testing"

	"github.com/nochso/bolster"
	"github.com/nochso/bolster/internal"
)

var updateGold = flag.Bool("update", false, "update golden test files")

func TestTx_Insert_withoutAutoincrement(t *testing.T) {
	st, closer := internal.OpenTestStore(t)
	defer closer()

	err := st.Register(structWithID{})
	if err != nil {
		t.Error(err)
	}
	t.Run("first", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err != nil {
			t.Error(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("duplicate", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("duplicateLazy", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			tx.Insert(&structWithID{})
			tx.Insert(&structWithID{})
			tx.Insert(&structWithID{})
			return nil
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("withoutPointer", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	t.Run("pointerToNonStruct", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(new(int))
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	t.Run("surrounding", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			for i := -5; i < 6; i++ {
				if i == 0 {
					continue
				}
				err := tx.Insert(&structWithID{ID: i})
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			t.Error(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
}
<|endoftext|>


<commit_before>package jws

import "errors"

func (c Compact) Verify(v Verifier) error {
	h, err := c.Header.Base64Encode()
	if err != nil {
		return err
	}

	p, err := c.Payload.Base64Encode()
	if err != nil {
		return err
	}

	buf := append(append(h, '.'), p...)
	if err := v.Verify(buf, c.Signature); err != nil {
		return err
	}
	return nil
}

func (m Message) Verify(v Verifier) error {
	p, err := m.Payload.Base64Encode()
	if err != nil {
		return err
	}

	for _, sig := range m.Signatures {
		h, err := sig.Header.Base64Encode()
		if err != nil {
			return err
		}

		buf := append(append(h, '.'), p...)
		if err := v.Verify(buf, sig.Signature); err == nil {
			return nil
		}
	}

	return errors.New("none of the signatures could be verified")
}
<commit_msg>Remove unused file<commit_after><|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	mailgun "github.com/mailgun/mailgun-go"
)

const (
	defaultCIDR = "0.0.0.0/0"

	arinAPIEndpoint = "http://whois.arin.net/rest/ip/%s"

	emailSender = "k8scan@jessfraz.com"
)

var (
	timeoutPing time.Duration
	timeoutGet  time.Duration

	cidr string

	defaultPorts = intSlice{80, 443, 8001, 9001}
	ports        intSlice

	mailgunDomain  string
	mailgunAPIKey  string
	emailRecipient string

	debug bool
)

// intSlice is a slice of ints
type intSlice []int

// implement the flag interface for intSlice
func (i *intSlice) String() (out string) {
	for k, v := range *i {
		if k < len(*i)-1 {
			out += fmt.Sprintf("%d,", v)
		} else {
			out += fmt.Sprintf("%d", v)
		}
	}
	return out
}

func (i *intSlice) Set(value string) error {
	// Set the default if nothing was given.
	if len(value) <= 0 {
		*i = defaultPorts
		return nil
	}

	// Split on "," for individual ports and ranges.
	r := strings.Split(value, ",")
	for _, pr := range r {
		// Split on "-" to denote a range.
		if strings.Contains(pr, "-") {
			p := strings.SplitN(pr, "-", 2)
			begin, err := strconv.Atoi(p[0])
			if err != nil {
				return err
			}
			end, err := strconv.Atoi(p[1])
			if err != nil {
				return err
			}
			if begin > end {
				return fmt.Errorf("end port can not be greater than the beginning port: %d > %d", end, begin)
			}
			for port := begin; port <= end; port++ {
				*i = append(*i, port)
			}

			continue
		}

		// It is not a range just parse the port
		port, err := strconv.Atoi(pr)
		if err != nil {
			return err
		}
		*i = append(*i, port)
	}

	return nil
}

func init() {
	flag.DurationVar(&timeoutPing, "timeout-ping", 2*time.Second, "Timeout for checking that the port is open")
	flag.DurationVar(&timeoutGet, "timeout-get", 10*time.Second, "Timeout for getting the contents of the URL")

	flag.StringVar(&cidr, "cidr", defaultCIDR, "IP CIDR to scan")
	flag.Var(&ports, "ports", fmt.Sprintf("Ports to scan (ex. 80-443 or 80,443,8080 or 1-20,22,80-443) (default %q)", defaultPorts.String()))

	flag.StringVar(&mailgunAPIKey, "mailgun-api-key", "", "Mailgun API Key to use for sending email (optional)")
	flag.StringVar(&mailgunDomain, "mailgun-domain", "", "Mailgun Domain to use for sending email (optional)")
	flag.StringVar(&emailRecipient, "email-recipient", "", "Recipient for email notifications (optional)")

	flag.BoolVar(&debug, "d", false, "Run in debug mode")

	flag.Usage = func() {
		flag.PrintDefaults()
	}

	flag.Parse()

	// set log level
	if debug {
		logrus.SetLevel(logrus.DebugLevel)
	}
}

func main() {
	// On ^C, or SIGTERM handle exit.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	go func() {
		for sig := range c {
			logrus.Infof("Received %s, exiting.", sig.String())
			os.Exit(0)
		}
	}()

	// Set the logger to nil so we ignore messages from the Dial that don't matter.
	// See: https://github.com/golang/go/issues/19895#issuecomment-292793756
	log.SetFlags(0)
	log.SetOutput(ioutil.Discard)

	logrus.Infof("Scanning for Kubernetes Dashboards and API Servers on %s over port range %#v", cidr, ports)
	if len(mailgunDomain) > 0 && len(mailgunAPIKey) > 0 && len(emailRecipient) > 0 {
		logrus.Infof("Using Mailgun Domain %s, API Key %s to send emails to %s", mailgunDomain, mailgunAPIKey, emailRecipient)
	}
	logrus.Infof("This may take a bit...")

	startTime := time.Now()

	ip, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		logrus.Fatal(err)
	}

	var wg sync.WaitGroup
	for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
		for _, port := range ports {
			wg.Add(1)
			go func(ip string, port int) {
				defer wg.Done()

				scanIP(ip, port)

			}(ip.String(), port)
		}
	}

	wg.Wait()

	since := time.Since(startTime)
	logrus.Infof("Scan took: %s", since.String())
}

func scanIP(ip string, port int) {
	// Check if the port is open.
	ok := portOpen(ip, port)
	if !ok {
		return
	}

	// Check if it's a kubernetes dashboard.
	ok, uri := isKubernetesDashboard(ip, port)
	if !ok {
		return
	}

	// Get the info for the ip address.
	info, err := getIPInfo(ip)
	if err != nil {
		logrus.Warnf("ip info err: %v", err)
	}

	fmt.Printf("%s:%d\t%s\t%s\t%s\n",
		ip, port,
		info.Net.Organization.Handle, info.Net.Organization.Name, info.Net.Organization.Reference)

	// send an email
	if len(mailgunDomain) > 0 && len(mailgunAPIKey) > 0 && len(emailRecipient) > 0 {
		if err := sendEmail(uri, ip, port, info); err != nil {
			logrus.Warn(err)
		}
	}
}

func portOpen(ip string, port int) bool {
	c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeoutPing)
	if err != nil {
		logrus.Debugf("listen at %s:%s failed: %v", ip, port, err)
		return false
	}
	if c != nil {
		c.Close()
	}

	return true
}

func isKubernetesDashboard(ip string, port int) (bool, string) {
	client := &http.Client{
		Timeout: timeoutGet,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}

	tryAddrs := []string{
		fmt.Sprintf("http://%s:%d", ip, port),
		fmt.Sprintf("https://%s:%d", ip, port),
		fmt.Sprintf("http://%s:%d/api/", ip, port),
		fmt.Sprintf("https://%s:%d/api/", ip, port),
	}

	var (
		resp *http.Response
		err  = errors.New("not yet run")
		uri  string
	)

	for i := 0; i < len(tryAddrs) && err != nil; i++ {
		uri = tryAddrs[i]
		resp, err = client.Get(uri)
	}
	if err != nil {
		logrus.Debugf("getting %s:%s failed: %v", ip, port, err)
		return false, ""
	}
	defer resp.Body.Close()

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return false, ""
	}

	body := strings.ToLower(string(b))
	if (strings.Contains(body, "kubernetes") && strings.Contains(body, "dashboard")) ||
		(strings.Contains(body, `"versions"`) && strings.Contains(body, `"serverAddress`)) ||
		(strings.Contains(body, `"paths"`) && strings.Contains(body, `"/api"`)) {
		return true, uri
	}

	return false, ""
}

// ARINResponse describes the data struct that holds the response from ARIN.
type ARINResponse struct {
	Net NetJSON `json:"net,omitempty"`
}

// NetJSON holds the net data from the ARIN response.
type NetJSON struct {
	Organization OrganizationJSON `json:"orgRef,omitempty"`
}

// OrganizationJSON holds the organization data from the ARIN response.
type OrganizationJSON struct {
	Handle    string `json:"@handle,omitempty"`
	Name      string `json:"@name,omitempty"`
	Reference string `json:"$,omitempty"`
}

func getIPInfo(ip string) (b ARINResponse, err error) {
	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(arinAPIEndpoint, ip), nil)
	if err != nil {
		return b, err
	}
	req.Header.Set("Accept", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return b, err
	}
	defer resp.Body.Close()

	if err := json.NewDecoder(resp.Body).Decode(&b); err != nil {
		return b, err
	}

	return b, nil
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

func sendEmail(uri, ip string, port int, arinInfo ARINResponse) error {
	mailgunClient := mailgun.NewMailgun(mailgunDomain, mailgunAPIKey, "")

	msg, _, err := mailgunClient.Send(mailgunClient.NewMessage(
		/* From */ fmt.Sprintf("%s <%s>", emailSender, emailSender),
		/* Subject */ fmt.Sprintf("[k8scan]: found dashboard %s", uri),
		/* Body */ fmt.Sprintf(`Time: %s

IP: %s:%d
URL: %s

ARIN: %s
	  %s
	  %s
`,
			time.Now().Format(time.UnixDate),
			ip,
			port,
			uri,
			arinInfo.Net.Organization.Handle,
			arinInfo.Net.Organization.Name,
			arinInfo.Net.Organization.Reference,
		),
		/* To */ emailRecipient,
	))
	if err != nil {
		return fmt.Errorf("sending Mailgun message failed: response: %#v error: %v", msg, err)
	}

	return nil
}
<commit_msg>use masscan for k8scan<commit_after>package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	mailgun "github.com/mailgun/mailgun-go"
)

const (
	defaultCIDR = "0.0.0.0/0"

	arinAPIEndpoint = "http://whois.arin.net/rest/ip/%s"

	emailSender = "k8scan@jessfraz.com"
)

var (
	timeoutPing time.Duration
	timeoutGet  time.Duration

	cidr string

	defaultPorts  = intSlice{80, 443, 8001, 9001}
	originalPorts string
	ports         intSlice

	useMasscan bool

	mailgunDomain  string
	mailgunAPIKey  string
	emailRecipient string

	debug bool
)

// intSlice is a slice of ints
type intSlice []int

// implement the flag interface for intSlice
func (i *intSlice) String() (out string) {
	for k, v := range *i {
		if k < len(*i)-1 {
			out += fmt.Sprintf("%d,", v)
		} else {
			out += fmt.Sprintf("%d", v)
		}
	}
	return out
}

func (i *intSlice) Set(value string) error {
	originalPorts = value

	// Set the default if nothing was given.
	if len(value) <= 0 {
		*i = defaultPorts
		return nil
	}

	// Split on "," for individual ports and ranges.
	r := strings.Split(value, ",")
	for _, pr := range r {
		// Split on "-" to denote a range.
		if strings.Contains(pr, "-") {
			p := strings.SplitN(pr, "-", 2)
			begin, err := strconv.Atoi(p[0])
			if err != nil {
				return err
			}
			end, err := strconv.Atoi(p[1])
			if err != nil {
				return err
			}
			if begin > end {
				return fmt.Errorf("end port can not be greater than the beginning port: %d > %d", end, begin)
			}
			for port := begin; port <= end; port++ {
				*i = append(*i, port)
			}

			continue
		}

		// It is not a range just parse the port
		port, err := strconv.Atoi(pr)
		if err != nil {
			return err
		}
		*i = append(*i, port)
	}

	return nil
}

func init() {
	flag.DurationVar(&timeoutPing, "timeout-ping", 2*time.Second, "Timeout for checking that the port is open")
	flag.DurationVar(&timeoutGet, "timeout-get", 10*time.Second, "Timeout for getting the contents of the URL")

	flag.StringVar(&cidr, "cidr", defaultCIDR, "IP CIDR to scan")
	flag.Var(&ports, "ports", fmt.Sprintf("Ports to scan (ex. 80-443 or 80,443,8080 or 1-20,22,80-443) (default %q)", defaultPorts.String()))

	flag.BoolVar(&useMasscan, "masscan", true, "Use masscan binary for scanning (this is faster than using pure golang)")

	flag.StringVar(&mailgunAPIKey, "mailgun-api-key", "", "Mailgun API Key to use for sending email (optional)")
	flag.StringVar(&mailgunDomain, "mailgun-domain", "", "Mailgun Domain to use for sending email (optional)")
	flag.StringVar(&emailRecipient, "email-recipient", "", "Recipient for email notifications (optional)")

	flag.BoolVar(&debug, "d", false, "Run in debug mode")

	flag.Usage = func() {
		flag.PrintDefaults()
	}

	flag.Parse()

	// Set the log level.
	if debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	// Set the default ports.
	if len(ports) <= 0 {
		ports = defaultPorts
	}
}

func main() {
	// On ^C, or SIGTERM handle exit.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	go func() {
		for sig := range c {
			logrus.Infof("Received %s, exiting.", sig.String())
			os.Exit(0)
		}
	}()

	// Set the logger to nil so we ignore messages from the Dial that don't matter.
	// See: https://github.com/golang/go/issues/19895#issuecomment-292793756
	log.SetFlags(0)
	log.SetOutput(ioutil.Discard)

	logrus.Infof("Scanning for Kubernetes Dashboards and API Servers on %s over port range %s", cidr, originalPorts)
	if len(mailgunDomain) > 0 && len(mailgunAPIKey) > 0 && len(emailRecipient) > 0 {
		logrus.Infof("Using Mailgun Domain %s, API Key %s to send emails to %s", mailgunDomain, mailgunAPIKey, emailRecipient)
	}
	logrus.Infof("This may take a bit...")

	var (
		startTime = time.Now()
		wg        sync.WaitGroup
	)

	if useMasscan {
		m, err := doMasscan()
		if err != nil {
			logrus.Fatal(err)
		}

		for _, result := range m {
			for _, port := range result.Ports {
				wg.Add(1)
				go func(ip string, port int) {
					defer wg.Done()

					scanIP(ip, port)

				}(result.IP, port.Port)
			}
		}
	} else {
		ip, ipnet, err := net.ParseCIDR(cidr)
		if err != nil {
			logrus.Fatal(err)
		}

		for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
			for _, port := range ports {
				wg.Add(1)
				go func(ip string, port int) {
					defer wg.Done()

					// Check if the port is open.
					ok := portOpen(ip, port)
					if !ok {
						return
					}

					scanIP(ip, port)

				}(ip.String(), port)
			}
		}
	}

	wg.Wait()

	since := time.Since(startTime)
	logrus.Infof("Scan took: %s", since.String())
}

func scanIP(ip string, port int) {
	// Check if it's a kubernetes dashboard.
	ok, uri := isKubernetesDashboard(ip, port)
	if !ok {
		return
	}

	// Get the info for the ip address.
	info, err := getIPInfo(ip)
	if err != nil {
		logrus.Warnf("ip info err: %v", err)
	}

	fmt.Printf("%s:%d\t%s\t%s\t%s\n",
		ip, port,
		info.Net.Organization.Handle, info.Net.Organization.Name, info.Net.Organization.Reference)

	// send an email
	if len(mailgunDomain) > 0 && len(mailgunAPIKey) > 0 && len(emailRecipient) > 0 {
		if err := sendEmail(uri, ip, port, info); err != nil {
			logrus.Warn(err)
		}
	}
}

func portOpen(ip string, port int) bool {
	c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeoutPing)
	if err != nil {
		logrus.Debugf("listen at %s:%s failed: %v", ip, port, err)
		return false
	}
	if c != nil {
		c.Close()
	}

	return true
}

func isKubernetesDashboard(ip string, port int) (bool, string) {
	client := &http.Client{
		Timeout: timeoutGet,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}

	tryAddrs := []string{
		fmt.Sprintf("http://%s:%d", ip, port),
		fmt.Sprintf("https://%s:%d", ip, port),
		fmt.Sprintf("http://%s:%d/api/", ip, port),
		fmt.Sprintf("https://%s:%d/api/", ip, port),
	}

	var (
		resp *http.Response
		err  = errors.New("not yet run")
		uri  string
	)

	for i := 0; i < len(tryAddrs) && err != nil; i++ {
		uri = tryAddrs[i]
		resp, err = client.Get(uri)
	}
	if err != nil {
		logrus.Debugf("getting %s:%s failed: %v", ip, port, err)
		return false, ""
	}
	defer resp.Body.Close()

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return false, ""
	}

	body := strings.ToLower(string(b))
	if (strings.Contains(body, "kubernetes") && strings.Contains(body, "dashboard")) ||
		(strings.Contains(body, `"versions"`) && strings.Contains(body, `"serverAddress`)) ||
		(strings.Contains(body, `"paths"`) && strings.Contains(body, `"/api"`)) {
		return true, uri
	}

	return false, ""
}

// ARINResponse describes the data struct that holds the response from ARIN.
type ARINResponse struct {
	Net NetJSON `json:"net,omitempty"`
}

// NetJSON holds the net data from the ARIN response.
type NetJSON struct {
	Organization OrganizationJSON `json:"orgRef,omitempty"`
}

// OrganizationJSON holds the organization data from the ARIN response.
type OrganizationJSON struct {
	Handle    string `json:"@handle,omitempty"`
	Name      string `json:"@name,omitempty"`
	Reference string `json:"$,omitempty"`
}

// MasscanResult holds the masscan results data struct.
// Looks like:
// [
//   {
//     "ip": "104.198.238.41",
//     "timestamp": "1531524211",
//     "ports": [
//       {
//         "port": 22,
//         "proto": "tcp",
//         "status": "open",
//         "reason": "syn-ack",
//         "ttl": 56
//       }
//     ]
//   },
//   ...
// ]
type MasscanResult struct {
	IP        string        `json:"ip,omitempty"`
	Timestamp MasscanTime   `json:"timestamp,omitempty"`
	Ports     []MasscanPort `json:"ports,omitempty"`
}

// MasscanPort defines the data struct for a masscan port.
type MasscanPort struct {
	Port     int    `json:"port,omitempty"`
	Protocol string `json:"proto,omitempty"`
	Status   string `json:"status,omitempty"`
	Reason   string `json:"reason,omitempty"`
	TTL      int    `json:"ttl,omitempty"`
}

// MasscanTime is the time format returned by masscan.
type MasscanTime struct {
	time.Time
}

// UnmarshalJSON sets MasscanTime correctly from a string.
func (t *MasscanTime) UnmarshalJSON(b []byte) error {
	s := strings.Trim(strings.TrimSpace(string(b)), `"`)

	i, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return err
	}

	*t = MasscanTime{time.Unix(i, 0)}

	return nil
}

func getIPInfo(ip string) (b ARINResponse, err error) {
	req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(arinAPIEndpoint, ip), nil)
	if err != nil {
		return b, err
	}
	req.Header.Set("Accept", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return b, err
	}
	defer resp.Body.Close()

	if err := json.NewDecoder(resp.Body).Decode(&b); err != nil {
		return b, err
	}

	return b, nil
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

func sendEmail(uri, ip string, port int, arinInfo ARINResponse) error {
	mailgunClient := mailgun.NewMailgun(mailgunDomain, mailgunAPIKey, "")

	msg, _, err := mailgunClient.Send(mailgunClient.NewMessage(
		/* From */ fmt.Sprintf("%s <%s>", emailSender, emailSender),
		/* Subject */ fmt.Sprintf("[k8scan]: found dashboard %s", uri),
		/* Body */ fmt.Sprintf(`Time: %s

IP: %s:%d
URL: %s

ARIN: %s
	  %s
	  %s
`,
			time.Now().Format(time.UnixDate),
			ip,
			port,
			uri,
			arinInfo.Net.Organization.Handle,
			arinInfo.Net.Organization.Name,
			arinInfo.Net.Organization.Reference,
		),
		/* To */ emailRecipient,
	))
	if err != nil {
		return fmt.Errorf("sending Mailgun message failed: response: %#v error: %v", msg, err)
	}

	return nil
}

func doMasscan() ([]MasscanResult, error) {
	// Create a temporary directory for the output.
	dir, err := ioutil.TempDir(os.TempDir(), "masscan")
	if err != nil {
		return nil, fmt.Errorf("creating temporary directory failed: %v", err)
	}
	defer os.RemoveAll(dir)

	file := filepath.Join(dir, "scan.json")

	cmd := exec.Command("masscan",
		fmt.Sprintf("-p%s", ports.String()),
		cidr,
		"--output-format", "json",
		"--output-file", file,
		"--rate", "1000000",
		"--exclude", "255.255.255.255",
	)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	logrus.Infof("Running masscan command: `%s`", strings.Join(cmd.Args, " "))
	if err := cmd.Run(); err != nil {
		return nil, fmt.Errorf("running masscan failed: %v", err)
	}

	b, err := cleanMasscanOutputFile(file)
	if err != nil {
		return nil, fmt.Errorf("cleaning up masscan file failed: %v", err)
	}

	m := []MasscanResult{}
	if err := json.Unmarshal(b, &m); err != nil {
		return nil, fmt.Errorf("unmarshal json failed: %v", err)
	}

	logrus.Debugf("masscan result: %#v", m)

	return m, nil
}

func cleanMasscanOutputFile(file string) ([]byte, error) {
	b, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	return []byte(strings.TrimSuffix(strings.TrimSpace(string(b)), ",\n]") + "]"), nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"time"
	"log"

	"gopkg.in/toast.v1"
	"gopkg.in/urfave/cli.v1"
)

func main() {
	app := cli.NewApp()

	app.Name = "toast"
	app.Usage = "Windows 10 toasts"
	app.Version = "v1"
	app.Compiled = time.Now()
	app.Authors = []cli.Author{
		cli.Author{
			Name:  "Jacob Marshall",
			Email: "go-toast@jacobmarshall.co",
		},
	}

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name: "app-id, id",
			Usage: "the app identifier (used for grouping multiple toasts)",
		},
		cli.StringFlag{
			Name: "title, t",
			Usage: "the main toast title/heading",
		},
		cli.StringFlag{
			Name: "message, m",
			Usage: "the toast's main message (new lines as separator)",
		},
		cli.StringFlag{
			Name: "icon, i",
			Usage: "the app icon path (displays to the left of the toast)",
		},
		cli.StringFlag{
			Name: "activation-type",
			Value: "protocol",
			Usage: "the type of action to invoke when the user clicks the toast",
		},
		cli.StringFlag{
			Name: "activation-arg",
			Usage: "the activation argument",
		},
		cli.StringSliceFlag{
			Name: "action",
			Usage: "optional action button",
		},
		cli.StringSliceFlag{
			Name: "action-type",
			Usage: "the type of action button",
		},
		cli.StringSliceFlag{
			Name: "action-arg",
			Usage: "the action button argument",
		},
		cli.StringFlag{
			Name: "audio",
			Value: "silent",
			Usage: "which kind of audio should be played",
		},
		cli.BoolFlag{
			Name: "loop",
			Usage: "whether to loop the audio",
		},
		cli.StringFlag{
			Name: "duration",
			Value: "short",
			Usage: "how long the toast should display for",
		},
	}

	app.Action = func(c *cli.Context) error {
		appID := c.String("app-id")
		title := c.String("title")
		message := c.String("message")
		icon := c.String("icon")
		activationType := c.String("activation-type")
		activationArg := c.String("activation-arg")
		audio, _ := toast.Audio(c.String("audio"))
		duration, _ := toast.Duration(c.String("duration"))
		loop := c.Bool("loop")

		var actions []toast.Action
		actionTexts := c.StringSlice("action")
		actionTypes := c.StringSlice("action-type")
		actionArgs := c.StringSlice("action-arg")

		for index, actionLabel := range actionTexts {
			var actionType string = "protocol"
			var actionArg string
			if len(actionTypes) > index {
				actionType = actionTypes[index]
			}
			if len(actionArgs) > index {
				actionArg = actionArgs[index]
			}
			actions = append(actions, toast.Action{
				Type: actionType,
				Label: actionLabel,
				Arguments: actionArg,
			})
		}

		notification := &toast.Notification{
			AppID:               appID,
			Title:               title,
			Message:             message,
			Icon:                icon,
			Actions:             actions,
			ActivationType:      activationType,
			ActivationArguments: activationArg,
			Audio:               audio,
			Loop:                loop,
			Duration:            duration,
		}

		if err := notification.Push(); err != nil {
			log.Fatalln(err)
		}

		return nil
	}

	app.Run(os.Args)
}
<commit_msg>gofmt (cli/main.go)<commit_after>package main

import (
	"log"
	"os"
	"time"

	"gopkg.in/toast.v1"
	"gopkg.in/urfave/cli.v1"
)

func main() {
	app := cli.NewApp()

	app.Name = "toast"
	app.Usage = "Windows 10 toasts"
	app.Version = "v1"
	app.Compiled = time.Now()
	app.Authors = []cli.Author{
		cli.Author{
			Name:  "Jacob Marshall",
			Email: "go-toast@jacobmarshall.co",
		},
	}

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "app-id, id",
			Usage: "the app identifier (used for grouping multiple toasts)",
		},
		cli.StringFlag{
			Name:  "title, t",
			Usage: "the main toast title/heading",
		},
		cli.StringFlag{
			Name:  "message, m",
			Usage: "the toast's main message (new lines as separator)",
		},
		cli.StringFlag{
			Name:  "icon, i",
			Usage: "the app icon path (displays to the left of the toast)",
		},
		cli.StringFlag{
			Name:  "activation-type",
			Value: "protocol",
			Usage: "the type of action to invoke when the user clicks the toast",
		},
		cli.StringFlag{
			Name:  "activation-arg",
			Usage: "the activation argument",
		},
		cli.StringSliceFlag{
			Name:  "action",
			Usage: "optional action button",
		},
		cli.StringSliceFlag{
			Name:  "action-type",
			Usage: "the type of action button",
		},
		cli.StringSliceFlag{
			Name:  "action-arg",
			Usage: "the action button argument",
		},
		cli.StringFlag{
			Name:  "audio",
			Value: "silent",
			Usage: "which kind of audio should be played",
		},
		cli.BoolFlag{
			Name:  "loop",
			Usage: "whether to loop the audio",
		},
		cli.StringFlag{
			Name:  "duration",
			Value: "short",
			Usage: "how long the toast should display for",
		},
	}

	app.Action = func(c *cli.Context) error {
		appID := c.String("app-id")
		title := c.String("title")
		message := c.String("message")
		icon := c.String("icon")
		activationType := c.String("activation-type")
		activationArg := c.String("activation-arg")
		audio, _ := toast.Audio(c.String("audio"))
		duration, _ := toast.Duration(c.String("duration"))
		loop := c.Bool("loop")

		var actions []toast.Action
		actionTexts := c.StringSlice("action")
		actionTypes := c.StringSlice("action-type")
		actionArgs := c.StringSlice("action-arg")

		for index, actionLabel := range actionTexts {
			var actionType string = "protocol"
			var actionArg string
			if len(actionTypes) > index {
				actionType = actionTypes[index]
			}
			if len(actionArgs) > index {
				actionArg = actionArgs[index]
			}
			actions = append(actions, toast.Action{
				Type:      actionType,
				Label:     actionLabel,
				Arguments: actionArg,
			})
		}

		notification := &toast.Notification{
			AppID:               appID,
			Title:               title,
			Message:             message,
			Icon:                icon,
			Actions:             actions,
			ActivationType:      activationType,
			ActivationArguments: activationArg,
			Audio:               audio,
			Loop:                loop,
			Duration:            duration,
		}

		if err := notification.Push(); err != nil {
			log.Fatalln(err)
		}

		return nil
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>/*

cli is a simple package to allow a game to render to the screen and be
interacted with. It's intended primarily as a tool to diagnose and play around
with a game while its moves and logic are being defined.

*/
package cli

import (
	"encoding/json"
	"fmt"
	"github.com/jkomoros/boardgame"
	"github.com/jroimartin/gocui"
)

type renderType int

const (
	renderJSON renderType = iota
	renderRender
	renderChest
)

//Controller is the primary type of the package.
type Controller struct {
	game        *boardgame.Game
	gui         *gocui.Gui
	render      renderType
	mode        inputMode
	mainView    *gocui.View
	overlayView *gocui.View
}

func NewController(game *boardgame.Game) *Controller {
	return &Controller{
		game: game,
	}
}

//keyPressed is the func we use to "edit" the main view. gocui's keybinding is
//a bit verbose for us (especially to register handlers for many keys), so
//instead we have the main view ALWAYS be current, set it to have this as an
//editor, and then route all input through it. This is a perversion of gocui's
//model, and basically circumvents half the value of the package. Oh well, the
//abstractions around windows are still useful for rendering. :-/
func (c *Controller) keyPressed(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
	c.mode.handleInput(key, ch, mod)
}

//Implement the gocui.Manager interface
func (c *Controller) Layout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView("main", 0, 0, maxX-1, maxY-2); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = "JSON"
		v.Frame = true
		v.Editable = true
		v.Editor = gocui.EditorFunc(c.keyPressed)
		c.gui.SetCurrentView("main")
		c.mainView = v
	}

	if v, err := g.SetView("status", 0, maxY-2, maxX-1, maxY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.FgColor = gocui.ColorBlack
		v.BgColor = gocui.ColorWhite
		v.Frame = false
	}

	if c.mode.showOverlay() {

		overlayX := 0
		overlayY := maxY - 30
		overlayX1 := maxX - 1
		overlayY1 := maxY - 2

		if v, err := g.SetView("overlay", overlayX, overlayY, overlayX1, overlayY1); err != nil {
			if err != gocui.ErrUnknownView {
				return err
			}
			v.Frame = true
			v.Title = c.mode.overlayTitle()

			g.SetViewOnTop("overlay")

			c.overlayView = v

			//We'll render the content below
		}

		row, col := c.mode.overlaySelectedCell()

		if row != -1 && col != -1 {

			//We're going to show the overlay selectd content. Where does it go?

			cols := c.mode.overlayContent(maxX - 2).ColumnWidths()

			x := 0

			for i := 0; i < col; i++ {
				x += cols[i]
			}

			if v, err := g.SetView("overlay-selected", x, overlayY+row, x+cols[col]+1, overlayY+row+2); err != nil {
				if err != gocui.ErrUnknownView {
					return err
				}
				v.FgColor = gocui.ColorBlack
				v.BgColor = gocui.ColorWhite
				v.Frame = false

				g.SetViewOnTop("overlay-selected")

			}
		} else {
			//TODO: it's a smell that we have the code to get rid of overlay-selected twice.
			if err := g.DeleteView("overlay-selected"); err != nil {
				//It's OK if it's ErrUnknownView because that just means it wasn't
				//in there and was a no op.
				if err != gocui.ErrUnknownView {
					return err
				}
			}
		}
	} else {
		//Delete the view, if it exists
		if err := g.DeleteView("overlay"); err != nil {
			//It's OK if it's ErrUnknownView because that just means it wasn't
			//in there and was a no op.
			if err != gocui.ErrUnknownView {
				return err
			}
		}
		if err := g.DeleteView("overlay-selected"); err != nil {
			//It's OK if it's ErrUnknownView because that just means it wasn't
			//in there and was a no op.
			if err != gocui.ErrUnknownView {
				return err
			}
		}
	}

	//Update the json field of view

	if view, err := g.View("main"); err == nil {
		view.Clear()
		switch c.render {
		case renderRender:
			//Print renderered view
			fmt.Fprint(view, c.renderRendered())
			view.Title = "Rendered"
		case renderJSON:
			//Print JSON view
			fmt.Fprint(view, c.renderJSON())
			view.Title = "JSON"
		case renderChest:
			fmt.Fprint(view, c.renderChest())
			view.Title = "Chest"
		}

	}

	if view, err := g.View("overlay"); err == nil {

		view.Clear()

		width, _ := view.Size()

		fmt.Fprint(view, c.mode.overlayContent(width))
	}

	if view, err := g.View("overlay-selected"); err == nil {

		view.Clear()

		row, col := c.mode.overlaySelectedCell()

		//x0, y0, x1, y1, _ := g.ViewPosition("overlay-selected")

		//panic(fmt.Sprint(x0, y0, x1, y1))

		width, _ := g.Size()

		fmt.Fprint(view, (*c.mode.overlayContent(width))[row][col])

	}

	if view, err := g.View("status"); err == nil {
		view.Clear()
		fmt.Fprint(view, c.mode.statusLine())
	}

	return nil
}

func (c *Controller) renderMoves() []string {

	var result []string

	for _, move := range c.game.Moves() {
		result = append(result, move.Name()+" : "+move.Description())
	}

	if len(result) == 0 {
		result = []string{"No moves configured for this game."}
	}

	return result

}

func (c *Controller) renderRendered() string {
	return c.game.State.Payload.Diagram()
}

func (c *Controller) renderJSON() string {
	return string(boardgame.Serialize(c.game.State.JSON()))
}

func (c *Controller) renderChest() string {

	deck := make(map[string][]interface{})

	for _, name := range c.game.Chest().DeckNames() {

		components := c.game.Chest().Deck(name).Components()

		values := make([]interface{}, len(components))

		for i, component := range components {
			values[i] = struct {
				Index  int
				Values interface{}
			}{
				i,
				component.Values,
			}
		}

		deck[name] = values
	}

	json, err := json.MarshalIndent(deck, "", "  ")

	if err != nil {
		panic(err)
	}

	return string(json)

}

func (c *Controller) ScrollUp() {
	var view *gocui.View
	var err error
	if view, err = c.gui.View("main"); err != nil {
		return
	}
	x, y := view.Origin()
	view.SetOrigin(x, y-1)
}

func (c *Controller) ScrollDown() {
	var view *gocui.View
	var err error
	if view, err = c.gui.View("main"); err != nil {
		return
	}
	x, y := view.Origin()
	view.SetOrigin(x, y+1)
}

func (c *Controller) PickMoveToEdit(move boardgame.Move) {
	c.EnterMode(newModeEditMove(c, move))
}

func (c *Controller) ToggleRender() {
	c.render++
	if c.render > renderChest {
		c.render = 0
	}
}

func (c *Controller) StartProposingMove() {
	c.EnterMode(newModePickMove(c))
}

//Cancels any mode that we're in by going back to normal mode.
func (c *Controller) CancelMode() {
	c.EnterMode(newModeNormal(c))
}

func (c *Controller) EnterMode(m inputMode) {
	g := c.gui

	//Clear out all keybindings; mode. enterMode will reestablish them.
	g.DeleteKeybindings("")
	for _, view := range g.Views() {
		g.DeleteKeybindings(view.Name())
	}

	m.enterMode()
	c.mode = m
}

//Once the controller is set up, call Start. It will block until it is time
//to exit.
func (c *Controller) Start() {

	g, err := gocui.NewGui(gocui.OutputNormal)

	if err != nil {
		panic("Couldn't create gui:" + err.Error())
	}

	defer g.Close()

	c.gui = g

	g.InputEsc = true

	//manager has to be set before setting keybindings, because it clears all
	//keybindings when set.
	g.SetManager(c)

	c.EnterMode(newModeNormal(c))

	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
		panic(err)
	}

}
<commit_msg>Added a comment about a hack in the last commit to fix later. Part of #26.<commit_after>/*

cli is a simple package to allow a game to render to the screen and be
interacted with. It's intended primarily as a tool to diagnose and play around
with a game while its moves and logic are being defined.

*/
package cli

import (
	"encoding/json"
	"fmt"
	"github.com/jkomoros/boardgame"
	"github.com/jroimartin/gocui"
)

type renderType int

const (
	renderJSON renderType = iota
	renderRender
	renderChest
)

//Controller is the primary type of the package.
type Controller struct {
	game        *boardgame.Game
	gui         *gocui.Gui
	render      renderType
	mode        inputMode
	mainView    *gocui.View
	overlayView *gocui.View
}

func NewController(game *boardgame.Game) *Controller {
	return &Controller{
		game: game,
	}
}

//keyPressed is the func we use to "edit" the main view. gocui's keybinding is
//a bit verbose for us (especially to register handlers for many keys), so
//instead we have the main view ALWAYS be current, set it to have this as an
//editor, and then route all input through it. This is a perversion of gocui's
//model, and basically circumvents half the value of the package. Oh well, the
//abstractions around windows are still useful for rendering. :-/
func (c *Controller) keyPressed(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
	c.mode.handleInput(key, ch, mod)
}

//Implement the gocui.Manager interface
func (c *Controller) Layout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView("main", 0, 0, maxX-1, maxY-2); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = "JSON"
		v.Frame = true
		v.Editable = true
		v.Editor = gocui.EditorFunc(c.keyPressed)
		c.gui.SetCurrentView("main")
		c.mainView = v
	}

	if v, err := g.SetView("status", 0, maxY-2, maxX-1, maxY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.FgColor = gocui.ColorBlack
		v.BgColor = gocui.ColorWhite
		v.Frame = false
	}

	if c.mode.showOverlay() {

		overlayX := 0
		overlayY := maxY - 30
		overlayX1 := maxX - 1
		overlayY1 := maxY - 2

		if v, err := g.SetView("overlay", overlayX, overlayY, overlayX1, overlayY1); err != nil {
			if err != gocui.ErrUnknownView {
				return err
			}
			v.Frame = true
			v.Title = c.mode.overlayTitle()

			g.SetViewOnTop("overlay")

			c.overlayView = v

			//We'll render the content below
		}

		row, col := c.mode.overlaySelectedCell()

		if row != -1 && col != -1 {

			//We're going to show the overlay selectd content. Where does it go?

			cols := c.mode.overlayContent(maxX - 2).ColumnWidths()

			x := 0

			for i := 0; i < col; i++ {
				x += cols[i]
			}

			if v, err := g.SetView("overlay-selected", x, overlayY+row, x+cols[col]+1, overlayY+row+2); err != nil {
				if err != gocui.ErrUnknownView {
					return err
				}
				v.FgColor = gocui.ColorBlack
				v.BgColor = gocui.ColorWhite
				v.Frame = false

				g.SetViewOnTop("overlay-selected")

			}
		} else {
			//TODO: it's a smell that we have the code to get rid of overlay-selected twice.
			if err := g.DeleteView("overlay-selected"); err != nil {
				//It's OK if it's ErrUnknownView because that just means it wasn't
				//in there and was a no op.
				if err != gocui.ErrUnknownView {
					return err
				}
			}
		}
	} else {
		//Delete the view, if it exists
		if err := g.DeleteView("overlay"); err != nil {
			//It's OK if it's ErrUnknownView because that just means it wasn't
			//in there and was a no op.
			if err != gocui.ErrUnknownView {
				return err
			}
		}
		if err := g.DeleteView("overlay-selected"); err != nil {
			//It's OK if it's ErrUnknownView because that just means it wasn't
			//in there and was a no op.
			if err != gocui.ErrUnknownView {
				return err
			}
		}
	}

	//Update the json field of view

	if view, err := g.View("main"); err == nil {
		view.Clear()
		switch c.render {
		case renderRender:
			//Print renderered view
			fmt.Fprint(view, c.renderRendered())
			view.Title = "Rendered"
		case renderJSON:
			//Print JSON view
			fmt.Fprint(view, c.renderJSON())
			view.Title = "JSON"
		case renderChest:
			fmt.Fprint(view, c.renderChest())
			view.Title = "Chest"
		}

	}

	if view, err := g.View("overlay"); err == nil {

		view.Clear()

		width, _ := view.Size()

		fmt.Fprint(view, c.mode.overlayContent(width))
	}

	if view, err := g.View("overlay-selected"); err == nil {

		view.Clear()

		row, col := c.mode.overlaySelectedCell()

		width, _ := g.Size()

		//TODO: we use varying numbers for width in overlayContent at various
		//times... We should audit that.

		fmt.Fprint(view, (*c.mode.overlayContent(width))[row][col])

	}

	if view, err := g.View("status"); err == nil {
		view.Clear()
		fmt.Fprint(view, c.mode.statusLine())
	}

	return nil
}

func (c *Controller) renderMoves() []string {

	var result []string

	for _, move := range c.game.Moves() {
		result = append(result, move.Name()+" : "+move.Description())
	}

	if len(result) == 0 {
		result = []string{"No moves configured for this game."}
	}

	return result

}

func (c *Controller) renderRendered() string {
	return c.game.State.Payload.Diagram()
}

func (c *Controller) renderJSON() string {
	return string(boardgame.Serialize(c.game.State.JSON()))
}

func (c *Controller) renderChest() string {

	deck := make(map[string][]interface{})

	for _, name := range c.game.Chest().DeckNames() {

		components := c.game.Chest().Deck(name).Components()

		values := make([]interface{}, len(components))

		for i, component := range components {
			values[i] = struct {
				Index  int
				Values interface{}
			}{
				i,
				component.Values,
			}
		}

		deck[name] = values
	}

	json, err := json.MarshalIndent(deck, "", "  ")

	if err != nil {
		panic(err)
	}

	return string(json)

}

func (c *Controller) ScrollUp() {
	var view *gocui.View
	var err error
	if view, err = c.gui.View("main"); err != nil {
		return
	}
	x, y := view.Origin()
	view.SetOrigin(x, y-1)
}

func (c *Controller) ScrollDown() {
	var view *gocui.View
	var err error
	if view, err = c.gui.View("main"); err != nil {
		return
	}
	x, y := view.Origin()
	view.SetOrigin(x, y+1)
}

func (c *Controller) PickMoveToEdit(move boardgame.Move) {
	c.EnterMode(newModeEditMove(c, move))
}

func (c *Controller) ToggleRender() {
	c.render++
	if c.render > renderChest {
		c.render = 0
	}
}

func (c *Controller) StartProposingMove() {
	c.EnterMode(newModePickMove(c))
}

//Cancels any mode that we're in by going back to normal mode.
func (c *Controller) CancelMode() {
	c.EnterMode(newModeNormal(c))
}

func (c *Controller) EnterMode(m inputMode) {
	g := c.gui

	//Clear out all keybindings; mode. enterMode will reestablish them.
	g.DeleteKeybindings("")
	for _, view := range g.Views() {
		g.DeleteKeybindings(view.Name())
	}

	m.enterMode()
	c.mode = m
}

//Once the controller is set up, call Start. It will block until it is time
//to exit.
func (c *Controller) Start() {

	g, err := gocui.NewGui(gocui.OutputNormal)

	if err != nil {
		panic("Couldn't create gui:" + err.Error())
	}

	defer g.Close()

	c.gui = g

	g.InputEsc = true

	//manager has to be set before setting keybindings, because it clears all
	//keybindings when set.
	g.SetManager(c)

	c.EnterMode(newModeNormal(c))

	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
		panic(err)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path"
)

const RcmHome string = ".rcm"
const ClusterConfFileName string = "cluster.yml"

type Clusters struct {
	baseDir string
}

func NewClusters(baseDir string) (result Clusters, err error) {

	err = os.MkdirAll(baseDir, 750)

	if err != nil {
		return
	}

	result = Clusters{
		baseDir: baseDir,
	}

	return
}

func NewClustersInHomeDir() (_ Clusters, err error) {
	usr, err := user.Current()
	if err != nil {
		return
	}
	return NewClusters(path.Join(usr.HomeDir, RcmHome))
}

func (self Clusters) New(name string, conf ClusterConf) (result Cluster, err error) {

	if self.Exists(name) {
		err = fmt.Errorf("Cluster %s already exists", name)
		return
	}

	err = os.MkdirAll(self.clusterBaseDir(name), 750)

	if err != nil {
		return
	}

	err = SaveClusterConf(self.clusterConfFile(name), &conf)

	if err != nil {
		return
	}

	result = Cluster{
		baseDir: self.clusterBaseDir(name),
		conf:    conf,
	}

	return
}

func (self Clusters) Exists(name string) bool {
	_, err := os.Stat(self.clusterBaseDir(name))
	return !os.IsNotExist(err)
}

func (self Clusters) Open(name string) (result Cluster, err error) {

	if !self.Exists(name) {
		err = fmt.Errorf("Cluster %s not exists", name)
		return
	}

	conf, err := LoadClusterConf(self.clusterConfFile(name))

	if err != nil {
		return
	}

	result = Cluster{
		baseDir: self.clusterBaseDir(name),
		conf:    *conf,
	}

	return
}

func (self Clusters) Remove(name string) error {
	return nil
}

func (self Clusters) ListNames() (result []string) {
	files, _ := ioutil.ReadDir(self.baseDir)

	result = make([]string, len(files))

	for i, f := range files {
		result[i] = f.Name()
	}
	return
}

func (self Clusters) clusterBaseDir(name string) string {
	return path.Join(self.baseDir, name)
}

func (self Clusters) clusterConfFile(name string) string {
	return path.Join(self.clusterBaseDir(name), ClusterConfFileName)
}

type Cluster struct {
	baseDir string
	conf    ClusterConf
}
<commit_msg>File mode change for newly created directories<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path"
)

const RcmHome string = ".rcm"
const ClusterConfFileName string = "cluster.yml"

type Clusters struct {
	baseDir string
}

func NewClusters(baseDir string) (result Clusters, err error) {

	err = os.MkdirAll(baseDir, 0750)

	if err != nil {
		return
	}

	result = Clusters{
		baseDir: baseDir,
	}

	return
}

func NewClustersInHomeDir() (_ Clusters, err error) {
	usr, err := user.Current()
	if err != nil {
		return
	}
	return NewClusters(path.Join(usr.HomeDir, RcmHome))
}

func (self Clusters) New(name string, conf ClusterConf) (result Cluster, err error) {

	if self.Exists(name) {
		err = fmt.Errorf("Cluster %s already exists", name)
		return
	}

	err = os.MkdirAll(self.clusterBaseDir(name), 0750)

	if err != nil {
		return
	}

	err = SaveClusterConf(self.clusterConfFile(name), &conf)

	if err != nil {
		return
	}

	result = Cluster{
		baseDir: self.clusterBaseDir(name),
		conf:    conf,
	}

	return
}

func (self Clusters) Exists(name string) bool {
	_, err := os.Stat(self.clusterBaseDir(name))
	return !os.IsNotExist(err)
}

func (self Clusters) Open(name string) (result Cluster, err error) {

	if !self.Exists(name) {
		err = fmt.Errorf("Cluster %s not exists", name)
		return
	}

	conf, err := LoadClusterConf(self.clusterConfFile(name))

	if err != nil {
		return
	}

	result = Cluster{
		baseDir: self.clusterBaseDir(name),
		conf:    *conf,
	}

	return
}

func (self Clusters) Remove(name string) error {
	return nil
}

func (self Clusters) ListNames() (result []string) {
	files, _ := ioutil.ReadDir(self.baseDir)

	result = make([]string, len(files))

	for i, f := range files {
		result[i] = f.Name()
	}
	return
}

func (self Clusters) clusterBaseDir(name string) string {
	return path.Join(self.baseDir, name)
}

func (self Clusters) clusterConfFile(name string) string {
	return path.Join(self.clusterBaseDir(name), ClusterConfFileName)
}

type Cluster struct {
	baseDir string
	conf    ClusterConf
}
<|endoftext|>


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

package cmd

import (
	"fmt"
	"strings"

	"launchpad.net/gnuflag"
)

// StringsValue implements gnuflag.Value for a comma separated list of
// strings.  This allows flags to be created where the target is []string, and
// the caller is after comma separated values.
type StringsValue []string

var _ gnuflag.Value = (*StringsValue)(nil)

// NewStringsValue is used to create the type passed into the gnuflag.FlagSet Var function.
// f.Var(cmd.NewStringsValue(defaultValue, &someMember), "name", "help")
func NewStringsValue(defaultValue []string, target *[]string) *StringsValue {
	value := (*StringsValue)(target)
	*value = defaultValue
	return value
}

// Implements gnuflag.Value Set.
func (v *StringsValue) Set(s string) error {
	*v = strings.Split(s, ",")
	return nil
}

// Implements gnuflag.Value String.
func (v *StringsValue) String() string {
	return strings.Join(*v, ",")
}

// AppendStringsValue implements gnuflag.Value for a value that can be set
// multiple times, and it appends each value to the slice.
type AppendStringsValue []string

var _ gnuflag.Value = (*AppendStringsValue)(nil)

// NewAppendStringsValue is used to create the type passed into the gnuflag.FlagSet Var function.
// f.Var(cmd.NewAppendStringsValue(defaultValue, &someMember), "name", "help")
func NewAppendStringsValue(target *[]string) *AppendStringsValue {
	return (*AppendStringsValue)(target)
}

// Implements gnuflag.Value Set.
func (v *AppendStringsValue) Set(s string) error {
	*v = append(*v, s)
	return nil
}

// Implements gnuflag.Value String.
func (v *AppendStringsValue) String() string {
	return fmt.Sprint(*v)
}
<commit_msg>Fix comment and string repr.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package cmd

import (
	"strings"

	"launchpad.net/gnuflag"
)

// StringsValue implements gnuflag.Value for a comma separated list of
// strings.  This allows flags to be created where the target is []string, and
// the caller is after comma separated values.
type StringsValue []string

var _ gnuflag.Value = (*StringsValue)(nil)

// NewStringsValue is used to create the type passed into the gnuflag.FlagSet Var function.
// f.Var(cmd.NewStringsValue(defaultValue, &someMember), "name", "help")
func NewStringsValue(defaultValue []string, target *[]string) *StringsValue {
	value := (*StringsValue)(target)
	*value = defaultValue
	return value
}

// Implements gnuflag.Value Set.
func (v *StringsValue) Set(s string) error {
	*v = strings.Split(s, ",")
	return nil
}

// Implements gnuflag.Value String.
func (v *StringsValue) String() string {
	return strings.Join(*v, ",")
}

// AppendStringsValue implements gnuflag.Value for a value that can be set
// multiple times, and it appends each value to the slice.
type AppendStringsValue []string

var _ gnuflag.Value = (*AppendStringsValue)(nil)

// NewAppendStringsValue is used to create the type passed into the gnuflag.FlagSet Var function.
// f.Var(cmd.NewAppendStringsValue(&someMember), "name", "help")
func NewAppendStringsValue(target *[]string) *AppendStringsValue {
	return (*AppendStringsValue)(target)
}

// Implements gnuflag.Value Set.
func (v *AppendStringsValue) Set(s string) error {
	*v = append(*v, s)
	return nil
}

// Implements gnuflag.Value String.
func (v *AppendStringsValue) String() string {
	return strings.Join(*v, ",")
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"math"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/codegangsta/cli"
	"github.com/geckoboard/prism/profiler"
	"github.com/geckoboard/prism/util"
)

const (
	diffEpsilon = 0.01
)

type idToEntryMap map[int]*profiler.Entry
type correlatedEntriesMap map[string]idToEntryMap

// Pretty print a n-way diff between two or more profiles.
func DiffProfiles(ctx *cli.Context) {
	var err error

	args := ctx.Args()
	if len(args) < 2 {
		util.ExitWithError("error: diff requires at least 2 profiles")
	}

	diffCols := util.ParseTableColumList(ctx.String("columns"))
	if len(diffCols) == 0 {
		util.ExitWithError("error: no diff table columns specified")
	}

	threshold := ctx.Float64("threshold")

	profiles := make([]*profiler.Entry, len(args))
	for index, arg := range args {
		profiles[index], err = util.LoadJsonProfile(arg)
		if err != nil {
			util.ExitWithError(err.Error())
		}
	}

	// Correlate profile nodes, build diff payload and tabularize it
	correlMap := correlateEntries(profiles)
	diffTable := tabularizeDiff(profiles, correlMap, diffCols, threshold)

	// If stdout is not a terminal we need to strip ANSI characters
	stripAnsiChars := !terminal.IsTerminal(int(os.Stdout.Fd()))
	diffTable.Write(os.Stdout, stripAnsiChars)
}

// Process each profile and return back a map which groups by function name
// profile entries between all profiles.
func correlateEntries(profiles []*profiler.Entry) correlatedEntriesMap {
	// Traverse profile trees and group all entries by name
	entryGroupsByName := make(correlatedEntriesMap, 0)
	for profileId, profile := range profiles {
		populateEntryGroups(profileId, profile, entryGroupsByName)
	}

	return entryGroupsByName
}

// Traverse profile entries and group them together with other profile entries
// that share the same entry name.
func populateEntryGroups(profileId int, pe *profiler.Entry, entryGroupsByName correlatedEntriesMap) {
	if list, exists := entryGroupsByName[pe.Name]; exists {
		// We are working on a copy of the map struct so we need to
		// update its parent with the updated list contents
		list[profileId] = pe
		entryGroupsByName[pe.Name] = list
	} else {
		entryGroupsByName[pe.Name] = idToEntryMap{
			profileId: pe,
		}
	}

	for _, child := range pe.Children {
		populateEntryGroups(profileId, child, entryGroupsByName)
	}
}

// Generate a table with that summarizes all profiles and includes a speedup
// factor for each profile compared to the first (baseline) profile.
func tabularizeDiff(profiles []*profiler.Entry, entryGroupsByName correlatedEntriesMap, diffCols []util.TableColumnType, threshold float64) *util.Table {
	t := &util.Table{
		Headers:      make([]string, len(profiles)*len(diffCols)+1),
		HeaderGroups: make([]util.TableHeaderGroup, len(profiles)+1),
		Rows:         make([][]string, 0),
		Padding:      1,
	}

	// Populate headers
	t.Alignment = make([]util.Alignment, len(t.Headers))

	t.Alignment[0] = util.AlignLeft
	t.Headers[0] = "call stack"
	t.HeaderGroups[0].ColSpan = 1

	startOffset := 1
	for index, _ := range profiles {
		baseIndex := startOffset + index*len(diffCols)

		if index == 0 {
			t.HeaderGroups[startOffset+index].Header = "baseline"
		} else {
			t.HeaderGroups[startOffset+index].Header = fmt.Sprintf("profile %d", index)
		}
		t.HeaderGroups[startOffset+index].ColSpan = len(diffCols)

		for dIndex, dType := range diffCols {
			t.Alignment[baseIndex+dIndex] = util.AlignRight
			t.Headers[baseIndex+dIndex] = dType.Header()
		}
	}

	// Populate rows using first profile
	populateDiffRows(profiles[0], len(profiles), entryGroupsByName, t, diffCols, threshold)

	return t
}

// Populate table rows with profile entry metrics and comparison data.
func populateDiffRows(pe *profiler.Entry, numProfiles int, entryGroupsByName correlatedEntriesMap, t *util.Table, diffCols []util.TableColumnType, threshold float64) {
	row := make([]string, numProfiles*len(diffCols)+1)

	// Fill in call
	call := strings.Repeat("| ", pe.Depth)
	if len(pe.Children) == 0 {
		call += "- "
	} else {
		call += "+ "
	}
	row[0] = call + pe.Name

	baseLine := entryGroupsByName[pe.Name][0]

	// Populate measurement columns
	for profileId, entry := range entryGroupsByName[pe.Name] {
		totalTime := float64(entry.TotalTime.Nanoseconds()) / 1.0e6
		avgTime := float64(entry.TotalTime.Nanoseconds()) / float64(entry.Invocations*1e6)
		minTime := float64(entry.MinTime.Nanoseconds()) / 1.0e6
		maxTime := float64(entry.MaxTime.Nanoseconds()) / 1.0e6

		baseIndex := profileId*len(diffCols) + 1
		if baseLine != nil && profileId != 0 {

			baseTotalTime := float64(baseLine.TotalTime.Nanoseconds()) / 1.0e6
			baseAvgTime := float64(baseLine.TotalTime.Nanoseconds()) / float64(baseLine.Invocations*1e6)
			baseMinTime := float64(baseLine.MinTime.Nanoseconds()) / 1.0e6
			baseMaxTime := float64(baseLine.MaxTime.Nanoseconds()) / 1.0e6

			for dIndex, dType := range diffCols {
				switch dType {
				case util.TableColTotal:
					row[baseIndex+dIndex] = fmtDiff(baseTotalTime, totalTime, threshold)
				case util.TableColAvg:
					row[baseIndex+dIndex] = fmtDiff(baseAvgTime, avgTime, threshold)
				case util.TableColMin:
					row[baseIndex+dIndex] = fmtDiff(baseMinTime, minTime, threshold)
				case util.TableColMax:
					row[baseIndex+dIndex] = fmtDiff(baseMaxTime, maxTime, threshold)
				case util.TableColInvocations:
					row[baseIndex+dIndex] = fmt.Sprintf("%d", entry.Invocations, threshold)
				}
			}
		} else {
			for dIndex, dType := range diffCols {
				switch dType {
				case util.TableColTotal:
					row[baseIndex+dIndex] = fmt.Sprintf("%1.2f (---)", totalTime)
				case util.TableColAvg:
					row[baseIndex+dIndex] = fmt.Sprintf("%1.2f (---)", avgTime)
				case util.TableColMin:
					row[baseIndex+dIndex] = fmt.Sprintf("%1.2f (---)", minTime)
				case util.TableColMax:
					row[baseIndex+dIndex] = fmt.Sprintf("%1.2f (---)", maxTime)
				case util.TableColInvocations:
					row[baseIndex+dIndex] = fmt.Sprintf("%d", entry.Invocations)
				}
			}
		}
	}

	// Append row to table
	t.Rows = append(t.Rows, row)

	//  Process children
	for _, child := range pe.Children {
		populateDiffRows(child, numProfiles, entryGroupsByName, t, diffCols, threshold)
	}
}

// Colorize and format candidate including a comparison to the baseline value.
// This method treats lower values as better. If the abs delta difference
// of the two values is less than the threshold then no comparison will be performed.
func fmtDiff(baseLine, candidate float64, threshold float64) string {
	absDelta := math.Abs(baseLine - candidate)
	if absDelta < threshold {
		return fmt.Sprintf("%1.2f (--)", candidate)
	}

	var speedup float64 = 0.0
	if candidate != 0 {
		speedup = baseLine / candidate
	}

	if absDelta < diffEpsilon {
		speedup = 1.0
	}

	var color string
	if speedup == 0.0 || speedup == 1.0 {
		color = "\033[33m" // yellow
	} else if speedup >= 1.0 {
		color = "\033[32m" // green
	} else {
		color = "\033[31m" // red
	}

	return fmt.Sprintf("%1.2f (%s%2.1fx\033[0m)", candidate, color, speedup)
}
<commit_msg>Fix formatting bug for invocation columns<commit_after>package cmd

import (
	"fmt"
	"math"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/codegangsta/cli"
	"github.com/geckoboard/prism/profiler"
	"github.com/geckoboard/prism/util"
)

const (
	diffEpsilon = 0.01
)

type idToEntryMap map[int]*profiler.Entry
type correlatedEntriesMap map[string]idToEntryMap

// Pretty print a n-way diff between two or more profiles.
func DiffProfiles(ctx *cli.Context) {
	var err error

	args := ctx.Args()
	if len(args) < 2 {
		util.ExitWithError("error: diff requires at least 2 profiles")
	}

	diffCols := util.ParseTableColumList(ctx.String("columns"))
	if len(diffCols) == 0 {
		util.ExitWithError("error: no diff table columns specified")
	}

	threshold := ctx.Float64("threshold")

	profiles := make([]*profiler.Entry, len(args))
	for index, arg := range args {
		profiles[index], err = util.LoadJsonProfile(arg)
		if err != nil {
			util.ExitWithError(err.Error())
		}
	}

	// Correlate profile nodes, build diff payload and tabularize it
	correlMap := correlateEntries(profiles)
	diffTable := tabularizeDiff(profiles, correlMap, diffCols, threshold)

	// If stdout is not a terminal we need to strip ANSI characters
	stripAnsiChars := !terminal.IsTerminal(int(os.Stdout.Fd()))
	diffTable.Write(os.Stdout, stripAnsiChars)
}

// Process each profile and return back a map which groups by function name
// profile entries between all profiles.
func correlateEntries(profiles []*profiler.Entry) correlatedEntriesMap {
	// Traverse profile trees and group all entries by name
	entryGroupsByName := make(correlatedEntriesMap, 0)
	for profileId, profile := range profiles {
		populateEntryGroups(profileId, profile, entryGroupsByName)
	}

	return entryGroupsByName
}

// Traverse profile entries and group them together with other profile entries
// that share the same entry name.
func populateEntryGroups(profileId int, pe *profiler.Entry, entryGroupsByName correlatedEntriesMap) {
	if list, exists := entryGroupsByName[pe.Name]; exists {
		// We are working on a copy of the map struct so we need to
		// update its parent with the updated list contents
		list[profileId] = pe
		entryGroupsByName[pe.Name] = list
	} else {
		entryGroupsByName[pe.Name] = idToEntryMap{
			profileId: pe,
		}
	}

	for _, child := range pe.Children {
		populateEntryGroups(profileId, child, entryGroupsByName)
	}
}

// Generate a table with that summarizes all profiles and includes a speedup
// factor for each profile compared to the first (baseline) profile.
func tabularizeDiff(profiles []*profiler.Entry, entryGroupsByName correlatedEntriesMap, diffCols []util.TableColumnType, threshold float64) *util.Table {
	t := &util.Table{
		Headers:      make([]string, len(profiles)*len(diffCols)+1),
		HeaderGroups: make([]util.TableHeaderGroup, len(profiles)+1),
		Rows:         make([][]string, 0),
		Padding:      1,
	}

	// Populate headers
	t.Alignment = make([]util.Alignment, len(t.Headers))

	t.Alignment[0] = util.AlignLeft
	t.Headers[0] = "call stack"
	t.HeaderGroups[0].ColSpan = 1

	startOffset := 1
	for index, _ := range profiles {
		baseIndex := startOffset + index*len(diffCols)

		if index == 0 {
			t.HeaderGroups[startOffset+index].Header = "baseline"
		} else {
			t.HeaderGroups[startOffset+index].Header = fmt.Sprintf("profile %d", index)
		}
		t.HeaderGroups[startOffset+index].ColSpan = len(diffCols)

		for dIndex, dType := range diffCols {
			t.Alignment[baseIndex+dIndex] = util.AlignRight
			t.Headers[baseIndex+dIndex] = dType.Header()
		}
	}

	// Populate rows using first profile
	populateDiffRows(profiles[0], len(profiles), entryGroupsByName, t, diffCols, threshold)

	return t
}

// Populate table rows with profile entry metrics and comparison data.
func populateDiffRows(pe *profiler.Entry, numProfiles int, entryGroupsByName correlatedEntriesMap, t *util.Table, diffCols []util.TableColumnType, threshold float64) {
	row := make([]string, numProfiles*len(diffCols)+1)

	// Fill in call
	call := strings.Repeat("| ", pe.Depth)
	if len(pe.Children) == 0 {
		call += "- "
	} else {
		call += "+ "
	}
	row[0] = call + pe.Name

	baseLine := entryGroupsByName[pe.Name][0]

	// Populate measurement columns
	for profileId, entry := range entryGroupsByName[pe.Name] {
		totalTime := float64(entry.TotalTime.Nanoseconds()) / 1.0e6
		avgTime := float64(entry.TotalTime.Nanoseconds()) / float64(entry.Invocations*1e6)
		minTime := float64(entry.MinTime.Nanoseconds()) / 1.0e6
		maxTime := float64(entry.MaxTime.Nanoseconds()) / 1.0e6

		baseIndex := profileId*len(diffCols) + 1
		if baseLine != nil && profileId != 0 {

			baseTotalTime := float64(baseLine.TotalTime.Nanoseconds()) / 1.0e6
			baseAvgTime := float64(baseLine.TotalTime.Nanoseconds()) / float64(baseLine.Invocations*1e6)
			baseMinTime := float64(baseLine.MinTime.Nanoseconds()) / 1.0e6
			baseMaxTime := float64(baseLine.MaxTime.Nanoseconds()) / 1.0e6

			for dIndex, dType := range diffCols {
				switch dType {
				case util.TableColTotal:
					row[baseIndex+dIndex] = fmtDiff(baseTotalTime, totalTime, threshold)
				case util.TableColAvg:
					row[baseIndex+dIndex] = fmtDiff(baseAvgTime, avgTime, threshold)
				case util.TableColMin:
					row[baseIndex+dIndex] = fmtDiff(baseMinTime, minTime, threshold)
				case util.TableColMax:
					row[baseIndex+dIndex] = fmtDiff(baseMaxTime, maxTime, threshold)
				case util.TableColInvocations:
					row[baseIndex+dIndex] = fmt.Sprintf("%d", entry.Invocations)
				}
			}
		} else {
			for dIndex, dType := range diffCols {
				switch dType {
				case util.TableColTotal:
					row[baseIndex+dIndex] = fmt.Sprintf("%1.2f (---)", totalTime)
				case util.TableColAvg:
					row[baseIndex+dIndex] = fmt.Sprintf("%1.2f (---)", avgTime)
				case util.TableColMin:
					row[baseIndex+dIndex] = fmt.Sprintf("%1.2f (---)", minTime)
				case util.TableColMax:
					row[baseIndex+dIndex] = fmt.Sprintf("%1.2f (---)", maxTime)
				case util.TableColInvocations:
					row[baseIndex+dIndex] = fmt.Sprintf("%d", entry.Invocations)
				}
			}
		}
	}

	// Append row to table
	t.Rows = append(t.Rows, row)

	//  Process children
	for _, child := range pe.Children {
		populateDiffRows(child, numProfiles, entryGroupsByName, t, diffCols, threshold)
	}
}

// Colorize and format candidate including a comparison to the baseline value.
// This method treats lower values as better. If the abs delta difference
// of the two values is less than the threshold then no comparison will be performed.
func fmtDiff(baseLine, candidate float64, threshold float64) string {
	absDelta := math.Abs(baseLine - candidate)
	if absDelta < threshold {
		return fmt.Sprintf("%1.2f (--)", candidate)
	}

	var speedup float64 = 0.0
	if candidate != 0 {
		speedup = baseLine / candidate
	}

	if absDelta < diffEpsilon {
		speedup = 1.0
	}

	var color string
	if speedup == 0.0 || speedup == 1.0 {
		color = "\033[33m" // yellow
	} else if speedup >= 1.0 {
		color = "\033[32m" // green
	} else {
		color = "\033[31m" // red
	}

	return fmt.Sprintf("%1.2f (%s%2.1fx\033[0m)", candidate, color, speedup)
}
<|endoftext|>