<commit_before>package gui

/*
#cgo pkg-config: gdk-3.0
#include <gdk/gdk.h>
*/
import "C"

import (
	"container/list"
	"fmt"
	"github.com/amrhassan/psmpc/mpdinfo"
	"github.com/amrhassan/psmpc/resources"
	"github.com/conformal/gotk3/gdk"
	"github.com/conformal/gotk3/glib"
	"github.com/conformal/gotk3/gtk"
	"log"
	"os"
	"unsafe"
)

/*
 * The paths where the static resources are looked up from. The paths are tried in the order
 * they are listed in, and the first one that exists is used.
 */
var static_resource_file_paths = []string{
	".",
	"~/.local/share/psmpc/",
	"/usr/local/share/psmpc/",
	"/usr/share/psmpc/",
}

// An action type label
type Action int

// Handlers for fired actions from the GUI
type ActionHandler func([]interface{})

// Player actions that can be performed from the GUI
const (
	ACTION_PLAY      = iota
	ACTION_PAUSE     = iota
	ACTION_PLAYPAUSE = iota
	ACTION_NEXT      = iota
	ACTION_PREVIOUS  = iota
	ACTION_QUIT      = iota
)

type GUI struct {
	builder                    *gtk.Builder
	main_window                *gtk.Window
	title_label                *gtk.Label
	artist_label               *gtk.Label
	stopped_header             *gtk.Box
	playback_header            *gtk.Box
	controls_box               *gtk.Box
	artist_box                 *gtk.Box
	previous_button            *gtk.Button
	playpause_button           *gtk.Button
	next_button                *gtk.Button
	play_image                 *gtk.Image
	pause_image                *gtk.Image
	status_icon                *gtk.StatusIcon
	album_box                  *gtk.Box
	album_label                *gtk.Label
	album_art_image            *gtk.Image
	registered_action_handlers map[Action]*list.List
	buttonKeyMap               map[int]Action
	resourceManager            *resources.ResourceManager
}

func error_panic(message string, err error) {
	panic(message + ": " + err.Error())
}

func path_exists(path string) bool {
	_, path_error := os.Stat(path)
	return path_error == nil || os.IsExist(path_error)
}

func get_static_resource_path(resourceName string) string {
	for _, path := range static_resource_file_paths {
		full_path := path + "/gui/" + resourceName
		if path_exists(full_path) {
			log.Printf("Using %s file from: %s", resourceName, full_path)
			return full_path
		}
	}

	log.Panicf("Can't find %s file", resourceName)
	return ""
}

// Constructs and initializes a new GUI instance
// Args:
// 	buttonKeyMap: a mapping between button key values and GUI actions
func NewGUI(buttonKeyMap map[int]Action) *GUI {
	gtk.Init(nil)

	builder, err := gtk.BuilderNew()
	if err != nil {
		error_panic("Failed to create gtk.Builder", err)
	}

	err = builder.AddFromFile(get_static_resource_path("ui.glade"))
	if err != nil {
		error_panic("Failed to load the Glade UI file", err)
	}

	getGtkObject := func(name string) glib.IObject {
		object, err := builder.GetObject(name)
		if err != nil {
			panic("Failed to retrieve GTK object " + name)
		}
		return object
	}

	main_window := getGtkObject("main_window").(*gtk.Window)
	main_window.SetIconFromFile(get_static_resource_path("icon.png"))

	artist_label := getGtkObject("artist_label").(*gtk.Label)
	title_label := getGtkObject("title_label").(*gtk.Label)
	playback_header := getGtkObject("playback_header_box").(*gtk.Box)
	controls_box := getGtkObject("controls_box").(*gtk.Box)
	artist_box := getGtkObject("artist_box").(*gtk.Box)
	playpause_button := getGtkObject("play-pause_button").(*gtk.Button)
	previous_button := getGtkObject("previous_button").(*gtk.Button)
	next_button := getGtkObject("next_button").(*gtk.Button)
	pause_image, _ := gtk.ImageNewFromIconName("gtk-media-pause", gtk.ICON_SIZE_BUTTON)
	play_image := getGtkObject("play_image").(*gtk.Image)
	status_icon, _ := gtk.StatusIconNewFromFile(get_static_resource_path("icon.png"))
	album_box := getGtkObject("album_box").(*gtk.Box)
	album_label := getGtkObject("album_label").(*gtk.Label)
	album_art_image := getGtkObject("album_art").(*gtk.Image)

	return &GUI{
		builder:                    builder,
		main_window:                main_window,
		title_label:                title_label,
		artist_label:               artist_label,
		controls_box:               controls_box,
		artist_box:                 artist_box,
		playpause_button:           playpause_button,
		previous_button:            previous_button,
		next_button:                next_button,
		playback_header:            playback_header,
		play_image:                 play_image,
		pause_image:                pause_image,
		status_icon:                status_icon,
		album_box:                  album_box,
		album_label:                album_label,
		album_art_image:            album_art_image,
		registered_action_handlers: make(map[Action]*list.List),
		buttonKeyMap:               buttonKeyMap,
		resourceManager:            resources.NewResourceManager(),
	}
}

// Initiates the GUI
func (this *GUI) Run() {

	this.main_window.Connect("destroy", func() {
		this.fireAction(ACTION_QUIT)
	})

	this.status_icon.Connect("activate", func() {
		log.Println("Status Icon clicked!")
		this.main_window.SetVisible(!this.main_window.GetVisible())
	})

	this.playpause_button.Connect("clicked", func() {
		this.fireAction(ACTION_PLAYPAUSE)
	})

	this.previous_button.Connect("clicked", func() {
		this.fireAction(ACTION_PREVIOUS)
	})

	this.next_button.Connect("clicked", func() {
		this.fireAction(ACTION_NEXT)
	})

	this.main_window.Connect("key-release-event", func(window *gtk.Window, event *gdk.Event) {
		key := extract_key_from_gdk_event(event)
		action, mapped := this.buttonKeyMap[key.value]

		if mapped {
			this.fireAction(action)
		}
	})

	this.main_window.ShowAll()
	gtk.Main()
}

// A keyboard key
type key struct {
	value          int
	representation string
}

// Extracts a key instance from the GdkEventKey wrapped in the given gdk.Event
func extract_key_from_gdk_event(gdk_key_event *gdk.Event) key {
	log.Printf("Extracting pressed key")
	value := (*C.GdkEventKey)(unsafe.Pointer(gdk_key_event.Native())).keyval
	repr := (*C.char)(C.gdk_keyval_name(value))
	return key{
		value:          int(value),
		representation: C.GoString(repr),
	}
}

// Shuts down the GUI
func (this *GUI) Quit() {
	glib.IdleAdd(func() {
		gtk.MainQuit()
	})
}

// Updates the GUI with the currently-playing song information
func (this *GUI) UpdateCurrentSong(current_song *mpdinfo.CurrentSong) {
	log.Printf("Updating current song: %v", current_song)

	executeInGlibLoop(func() {
		this.title_label.SetText(current_song.Title)
		this.artist_label.SetText(current_song.Artist)

		if current_song.Album != "" {
			this.album_label.SetText(current_song.Album)
		} else {
			this.album_box.Hide()
		}

		this.main_window.SetTitle(fmt.Sprintf("psmpc: %s - %s", current_song.Artist, current_song.Title))
		this.status_icon.SetTooltipText(fmt.Sprintf("psmpc: %s - %s", current_song.Artist, current_song.Title))
		this.album_art_image.SetFromFile(get_static_resource_path("album.png"))
	})

	go func() {
		album_art_fp, err :=
			this.resourceManager.GetResourceAsFilePath(&resources.Track{current_song}, resources.ALBUM_ART)
		if err != nil {
			log.Println("Failed to get album art for %s", current_song)
		} else {
			executeInGlibLoop(func() {
				this.album_art_image.SetFromFile(album_art_fp)
			})
		}
	}()
}

// The only thread-safe way to execute GTK-manipulating code.
func executeInGlibLoop(code func()) {
	_, err := glib.IdleAdd(code)
	if err != nil {
		log.Fatal("Failed to do glib.IdleAdd()")
	}
}

// Updates the GUI with the current MPD status
func (this *GUI) UpdateCurrentStatus(current_status *mpdinfo.Status) {
	log.Printf("Updating current status: %v", current_status)
	_, err := glib.IdleAdd(func() {
		switch current_status.State {

		case mpdinfo.STATE_STOPPED:
			this.controls_box.Hide()
			this.artist_box.Hide()
			this.album_box.Hide()
			this.title_label.SetText("Stopped")
			this.main_window.SetTitle("psmpc")
			this.status_icon.SetTooltipText("psmpc")
			this.album_art_image.SetFromFile(get_static_resource_path("album.png"))

		case mpdinfo.STATE_PLAYING:
			this.controls_box.Show()
			this.artist_box.Show()
			this.album_box.Show()
			this.playpause_button.SetImage(this.pause_image)

		case mpdinfo.STATE_PAUSED:
			this.controls_box.Show()
			this.artist_box.Show()
			this.album_box.Show()
			this.playpause_button.SetImage(this.play_image)
		}
	})

	if err != nil {
		log.Fatal("Failed to do glib.IdleAdd()")
	}
}

// Fires the action specified by the given Action, passing the given arguments to all the
// subscribed handlers
func (this *GUI) fireAction(action_type Action, args ...interface{}) {
	log.Printf("Firing action %v", action_type)

	handlers, any := this.registered_action_handlers[action_type]

	if any == false {
		// None are registered
		log.Println("No action handlers found")
		return
	}

	log.Println("Handlers found ", handlers)

	for e := handlers.Front(); e != nil; e = e.Next() {
		log.Println("Executing handler", e.Value)
		handler := e.Value.(ActionHandler)
		go handler(args)
	}
}

// Registers an action handler to be executed when a specific action is fired
func (this *GUI) RegisterActionHandler(action_type Action, handler ActionHandler) {
	_, handlers_exist := this.registered_action_handlers[action_type]
	if !handlers_exist {
		this.registered_action_handlers[action_type] = list.New()
	}

	this.registered_action_handlers[action_type].PushFront(handler)
}
<commit_msg>Static resources are cached in memory and are not looked up in the filesystem everytime they are used.<commit_after>package gui

/*
#cgo pkg-config: gdk-3.0
#include <gdk/gdk.h>
*/
import "C"

import (
	"container/list"
	"fmt"
	"github.com/amrhassan/psmpc/mpdinfo"
	"github.com/amrhassan/psmpc/resources"
	"github.com/conformal/gotk3/gdk"
	"github.com/conformal/gotk3/glib"
	"github.com/conformal/gotk3/gtk"
	"log"
	"os"
	"unsafe"
)

/*
 * The paths where the static resources are looked up from. The paths are tried in the order
 * they are listed in, and the first one that exists is used.
 */
var static_resource_file_paths = []string{
	".",
	"~/.local/share/psmpc/",
	"/usr/local/share/psmpc/",
	"/usr/share/psmpc/",
}

// An action type label
type Action int

// Handlers for fired actions from the GUI
type ActionHandler func([]interface{})

// Player actions that can be performed from the GUI
const (
	ACTION_PLAY      = iota
	ACTION_PAUSE     = iota
	ACTION_PLAYPAUSE = iota
	ACTION_NEXT      = iota
	ACTION_PREVIOUS  = iota
	ACTION_QUIT      = iota
)

type GUI struct {
	builder                    *gtk.Builder
	main_window                *gtk.Window
	title_label                *gtk.Label
	artist_label               *gtk.Label
	stopped_header             *gtk.Box
	playback_header            *gtk.Box
	controls_box               *gtk.Box
	artist_box                 *gtk.Box
	previous_button            *gtk.Button
	playpause_button           *gtk.Button
	next_button                *gtk.Button
	play_image                 *gtk.Image
	pause_image                *gtk.Image
	status_icon                *gtk.StatusIcon
	album_box                  *gtk.Box
	album_label                *gtk.Label
	album_art_image            *gtk.Image
	registered_action_handlers map[Action]*list.List
	buttonKeyMap               map[int]Action
	resourceManager            *resources.ResourceManager
}

func error_panic(message string, err error) {
	panic(message + ": " + err.Error())
}

func path_exists(path string) bool {
	_, path_error := os.Stat(path)
	return path_error == nil || os.IsExist(path_error)
}

// A global cahe of static resources. Used by get_static_resource_path()
var staticResources = make(map[string]string)

func get_static_resource_path(resourceName string) string {

	entry, exists := staticResources[resourceName]
	if exists {
		return entry
	}

	for _, path := range static_resource_file_paths {
		full_path := path + "/gui/" + resourceName
		if path_exists(full_path) {
			log.Printf("Using %s file from: %s", resourceName, full_path)
			staticResources[resourceName] = full_path
			return full_path
		}
	}

	log.Panicf("Can't find %s file", resourceName)
	return ""
}

// Constructs and initializes a new GUI instance
// Args:
// 	buttonKeyMap: a mapping between button key values and GUI actions
func NewGUI(buttonKeyMap map[int]Action) *GUI {
	gtk.Init(nil)

	builder, err := gtk.BuilderNew()
	if err != nil {
		error_panic("Failed to create gtk.Builder", err)
	}

	err = builder.AddFromFile(get_static_resource_path("ui.glade"))
	if err != nil {
		error_panic("Failed to load the Glade UI file", err)
	}

	getGtkObject := func(name string) glib.IObject {
		object, err := builder.GetObject(name)
		if err != nil {
			panic("Failed to retrieve GTK object " + name)
		}
		return object
	}

	main_window := getGtkObject("main_window").(*gtk.Window)
	main_window.SetIconFromFile(get_static_resource_path("icon.png"))

	artist_label := getGtkObject("artist_label").(*gtk.Label)
	title_label := getGtkObject("title_label").(*gtk.Label)
	playback_header := getGtkObject("playback_header_box").(*gtk.Box)
	controls_box := getGtkObject("controls_box").(*gtk.Box)
	artist_box := getGtkObject("artist_box").(*gtk.Box)
	playpause_button := getGtkObject("play-pause_button").(*gtk.Button)
	previous_button := getGtkObject("previous_button").(*gtk.Button)
	next_button := getGtkObject("next_button").(*gtk.Button)
	pause_image, _ := gtk.ImageNewFromIconName("gtk-media-pause", gtk.ICON_SIZE_BUTTON)
	play_image := getGtkObject("play_image").(*gtk.Image)
	status_icon, _ := gtk.StatusIconNewFromFile(get_static_resource_path("icon.png"))
	album_box := getGtkObject("album_box").(*gtk.Box)
	album_label := getGtkObject("album_label").(*gtk.Label)
	album_art_image := getGtkObject("album_art").(*gtk.Image)

	return &GUI{
		builder:                    builder,
		main_window:                main_window,
		title_label:                title_label,
		artist_label:               artist_label,
		controls_box:               controls_box,
		artist_box:                 artist_box,
		playpause_button:           playpause_button,
		previous_button:            previous_button,
		next_button:                next_button,
		playback_header:            playback_header,
		play_image:                 play_image,
		pause_image:                pause_image,
		status_icon:                status_icon,
		album_box:                  album_box,
		album_label:                album_label,
		album_art_image:            album_art_image,
		registered_action_handlers: make(map[Action]*list.List),
		buttonKeyMap:               buttonKeyMap,
		resourceManager:            resources.NewResourceManager(),
	}
}

// Initiates the GUI
func (this *GUI) Run() {

	this.main_window.Connect("destroy", func() {
		this.fireAction(ACTION_QUIT)
	})

	this.status_icon.Connect("activate", func() {
		log.Println("Status Icon clicked!")
		this.main_window.SetVisible(!this.main_window.GetVisible())
	})

	this.playpause_button.Connect("clicked", func() {
		this.fireAction(ACTION_PLAYPAUSE)
	})

	this.previous_button.Connect("clicked", func() {
		this.fireAction(ACTION_PREVIOUS)
	})

	this.next_button.Connect("clicked", func() {
		this.fireAction(ACTION_NEXT)
	})

	this.main_window.Connect("key-release-event", func(window *gtk.Window, event *gdk.Event) {
		key := extract_key_from_gdk_event(event)
		action, mapped := this.buttonKeyMap[key.value]

		if mapped {
			this.fireAction(action)
		}
	})

	this.main_window.ShowAll()
	gtk.Main()
}

// A keyboard key
type key struct {
	value          int
	representation string
}

// Extracts a key instance from the GdkEventKey wrapped in the given gdk.Event
func extract_key_from_gdk_event(gdk_key_event *gdk.Event) key {
	log.Printf("Extracting pressed key")
	value := (*C.GdkEventKey)(unsafe.Pointer(gdk_key_event.Native())).keyval
	repr := (*C.char)(C.gdk_keyval_name(value))
	return key{
		value:          int(value),
		representation: C.GoString(repr),
	}
}

// Shuts down the GUI
func (this *GUI) Quit() {
	glib.IdleAdd(func() {
		gtk.MainQuit()
	})
}

// Updates the GUI with the currently-playing song information
func (this *GUI) UpdateCurrentSong(current_song *mpdinfo.CurrentSong) {
	log.Printf("Updating current song: %v", current_song)

	executeInGlibLoop(func() {
		this.title_label.SetText(current_song.Title)
		this.artist_label.SetText(current_song.Artist)

		if current_song.Album != "" {
			this.album_label.SetText(current_song.Album)
		} else {
			this.album_box.Hide()
		}

		this.main_window.SetTitle(fmt.Sprintf("psmpc: %s - %s", current_song.Artist, current_song.Title))
		this.status_icon.SetTooltipText(fmt.Sprintf("psmpc: %s - %s", current_song.Artist, current_song.Title))
		this.album_art_image.SetFromFile(get_static_resource_path("album.png"))
	})

	go func() {
		album_art_fp, err :=
			this.resourceManager.GetResourceAsFilePath(&resources.Track{current_song}, resources.ALBUM_ART)
		if err != nil {
			log.Println("Failed to get album art for %s", current_song)
		} else {
			executeInGlibLoop(func() {
				this.album_art_image.SetFromFile(album_art_fp)
			})
		}
	}()
}

// The only thread-safe way to execute GTK-manipulating code.
func executeInGlibLoop(code func()) {
	_, err := glib.IdleAdd(code)
	if err != nil {
		log.Fatal("Failed to do glib.IdleAdd()")
	}
}

// Updates the GUI with the current MPD status
func (this *GUI) UpdateCurrentStatus(current_status *mpdinfo.Status) {
	log.Printf("Updating current status: %v", current_status)
	_, err := glib.IdleAdd(func() {
		switch current_status.State {

		case mpdinfo.STATE_STOPPED:
			this.controls_box.Hide()
			this.artist_box.Hide()
			this.album_box.Hide()
			this.title_label.SetText("Stopped")
			this.main_window.SetTitle("psmpc")
			this.status_icon.SetTooltipText("psmpc")
			this.album_art_image.SetFromFile(get_static_resource_path("album.png"))

		case mpdinfo.STATE_PLAYING:
			this.controls_box.Show()
			this.artist_box.Show()
			this.album_box.Show()
			this.playpause_button.SetImage(this.pause_image)

		case mpdinfo.STATE_PAUSED:
			this.controls_box.Show()
			this.artist_box.Show()
			this.album_box.Show()
			this.playpause_button.SetImage(this.play_image)
		}
	})

	if err != nil {
		log.Fatal("Failed to do glib.IdleAdd()")
	}
}

// Fires the action specified by the given Action, passing the given arguments to all the
// subscribed handlers
func (this *GUI) fireAction(action_type Action, args ...interface{}) {
	log.Printf("Firing action %v", action_type)

	handlers, any := this.registered_action_handlers[action_type]

	if any == false {
		// None are registered
		log.Println("No action handlers found")
		return
	}

	log.Println("Handlers found ", handlers)

	for e := handlers.Front(); e != nil; e = e.Next() {
		log.Println("Executing handler", e.Value)
		handler := e.Value.(ActionHandler)
		go handler(args)
	}
}

// Registers an action handler to be executed when a specific action is fired
func (this *GUI) RegisterActionHandler(action_type Action, handler ActionHandler) {
	_, handlers_exist := this.registered_action_handlers[action_type]
	if !handlers_exist {
		this.registered_action_handlers[action_type] = list.New()
	}

	this.registered_action_handlers[action_type].PushFront(handler)
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

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

	"github.com/codegangsta/negroni"
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/app"
	"github.com/tsuru/tsuru/auth"
	_ "github.com/tsuru/tsuru/auth/native"
	_ "github.com/tsuru/tsuru/auth/oauth"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/log"
	"github.com/tsuru/tsuru/provision"
	"github.com/tsuru/tsuru/router"
)

const Version = "0.9.1"

func getProvisioner() (string, error) {
	provisioner, err := config.GetString("provisioner")
	if provisioner == "" {
		provisioner = "docker"
	}
	return provisioner, err
}

type TsuruHandler struct {
	method string
	path   string
	h      http.Handler
}

func fatal(err error) {
	fmt.Println(err.Error())
	log.Fatal(err.Error())
}

var tsuruHandlerList []TsuruHandler

//RegisterHandler inserts a handler on a list of handlers
func RegisterHandler(path string, method string, h http.Handler) {
	var th TsuruHandler
	th.path = path
	th.method = method
	th.h = h
	tsuruHandlerList = append(tsuruHandlerList, th)
}

func getAuthScheme() (string, error) {
	name, err := config.GetString("auth:scheme")
	if name == "" {
		name = "native"
	}
	return name, err
}

// RunServer starts tsuru API server. The dry parameter indicates whether the
// server should run in dry mode, not starting the HTTP listener (for testing
// purposes).
func RunServer(dry bool) http.Handler {
	log.Init()
	connString, err := config.GetString("database:url")
	if err != nil {
		connString = db.DefaultDatabaseURL
	}
	dbName, err := config.GetString("database:name")
	if err != nil {
		dbName = db.DefaultDatabaseName
	}
	fmt.Printf("Using mongodb database %q from the server %q.\n", dbName, connString)

	m := &delayedRouter{}

	for _, handler := range tsuruHandlerList {
		m.Add(handler.method, handler.path, handler.h)
	}

	m.Add("Get", "/info", Handler(info))

	m.Add("Get", "/services/instances", authorizationRequiredHandler(serviceInstances))
	m.Add("Get", "/services/instances/{name}", authorizationRequiredHandler(serviceInstance))
	m.Add("Delete", "/services/instances/{name}", authorizationRequiredHandler(removeServiceInstance))
	m.Add("Post", "/services/instances", authorizationRequiredHandler(createServiceInstance))
	m.Add("Put", "/services/instances/{instance}/{app}", authorizationRequiredHandler(bindServiceInstance))
	m.Add("Delete", "/services/instances/{instance}/{app}", authorizationRequiredHandler(unbindServiceInstance))
	m.Add("Get", "/services/instances/{instance}/status", authorizationRequiredHandler(serviceInstanceStatus))

	m.AddAll("/services/proxy/{instance}", authorizationRequiredHandler(serviceProxy))

	m.Add("Get", "/services", authorizationRequiredHandler(serviceList))
	m.Add("Post", "/services", authorizationRequiredHandler(serviceCreate))
	m.Add("Put", "/services", authorizationRequiredHandler(serviceUpdate))
	m.Add("Delete", "/services/{name}", authorizationRequiredHandler(serviceDelete))
	m.Add("Get", "/services/{name}", authorizationRequiredHandler(serviceInfo))
	m.Add("Get", "/services/{name}/plans", authorizationRequiredHandler(servicePlans))
	m.Add("Get", "/services/{name}/doc", authorizationRequiredHandler(serviceDoc))
	m.Add("Put", "/services/{name}/doc", authorizationRequiredHandler(serviceAddDoc))
	m.Add("Put", "/services/{service}/{team}", authorizationRequiredHandler(grantServiceAccess))
	m.Add("Delete", "/services/{service}/{team}", authorizationRequiredHandler(revokeServiceAccess))

	m.Add("Delete", "/apps/{app}", authorizationRequiredHandler(appDelete))
	m.Add("Get", "/apps/{app}", authorizationRequiredHandler(appInfo))
	m.Add("Post", "/apps/{app}/cname", authorizationRequiredHandler(setCName))
	m.Add("Delete", "/apps/{app}/cname", authorizationRequiredHandler(unsetCName))
	runHandler := authorizationRequiredHandler(runCommand)
	m.Add("Post", "/apps/{app}/run", runHandler)
	m.Add("Post", "/apps/{app}/restart", authorizationRequiredHandler(restart))
	m.Add("Post", "/apps/{app}/start", authorizationRequiredHandler(start))
	m.Add("Post", "/apps/{app}/stop", authorizationRequiredHandler(stop))
	m.Add("Get", "/apps/{appname}/quota", AdminRequiredHandler(getAppQuota))
	m.Add("Post", "/apps/{appname}/quota", AdminRequiredHandler(changeAppQuota))
	m.Add("Get", "/apps/{app}/env", authorizationRequiredHandler(getEnv))
	m.Add("Post", "/apps/{app}/env", authorizationRequiredHandler(setEnv))
	m.Add("Delete", "/apps/{app}/env", authorizationRequiredHandler(unsetEnv))
	m.Add("Get", "/apps", authorizationRequiredHandler(appList))
	m.Add("Post", "/apps", authorizationRequiredHandler(createApp))
	m.Add("Post", "/apps/{app}/team-owner", authorizationRequiredHandler(setTeamOwner))
	forceDeleteLockHandler := AdminRequiredHandler(forceDeleteLock)
	m.Add("Delete", "/apps/{app}/lock", forceDeleteLockHandler)
	m.Add("Put", "/apps/{app}/units", authorizationRequiredHandler(addUnits))
	m.Add("Delete", "/apps/{app}/units", authorizationRequiredHandler(removeUnits))
	registerUnitHandler := authorizationRequiredHandler(registerUnit)
	m.Add("Post", "/apps/{app}/units/register", registerUnitHandler)
	setUnitStatusHandler := authorizationRequiredHandler(setUnitStatus)
	m.Add("Post", "/apps/{app}/units/{unit}", setUnitStatusHandler)
	m.Add("Put", "/apps/{app}/teams/{team}", authorizationRequiredHandler(grantAppAccess))
	m.Add("Delete", "/apps/{app}/teams/{team}", authorizationRequiredHandler(revokeAppAccess))
	m.Add("Get", "/apps/{app}/log", authorizationRequiredHandler(appLog))
	logPostHandler := authorizationRequiredHandler(addLog)
	m.Add("Post", "/apps/{app}/log", logPostHandler)
	saveCustomDataHandler := authorizationRequiredHandler(saveAppCustomData)
	m.Add("Post", "/apps/{app}/customdata", saveCustomDataHandler)
	m.Add("Post", "/apps/{appname}/deploy/rollback", authorizationRequiredHandler(deployRollback))

	m.Add("Get", "/autoscale", authorizationRequiredHandler(autoScaleHistoryHandler))
	m.Add("Put", "/autoscale/{app}", authorizationRequiredHandler(autoScaleConfig))
	m.Add("Put", "/autoscale/{app}/enable", authorizationRequiredHandler(autoScaleEnable))
	m.Add("Put", "/autoscale/{app}/disable", authorizationRequiredHandler(autoScaleDisable))

	m.Add("Get", "/deploys", authorizationRequiredHandler(deploysList))
	m.Add("Get", "/deploys/{deploy}", authorizationRequiredHandler(deployInfo))

	m.Add("Get", "/platforms", authorizationRequiredHandler(platformList))
	m.Add("Post", "/platforms", AdminRequiredHandler(platformAdd))
	m.Add("Put", "/platforms/{name}", AdminRequiredHandler(platformUpdate))
	m.Add("Delete", "/platforms/{name}", AdminRequiredHandler(platformRemove))

	// These handlers don't use :app on purpose. Using :app means that only
	// the token generate for the given app is valid, but these handlers
	// use a token generated for Gandalf.
	m.Add("Get", "/apps/{appname}/available", authorizationRequiredHandler(appIsAvailable))
	m.Add("Post", "/apps/{appname}/repository/clone", authorizationRequiredHandler(deploy))
	m.Add("Post", "/apps/{appname}/deploy", authorizationRequiredHandler(deploy))

	m.Add("Get", "/users", AdminRequiredHandler(listUsers))
	m.Add("Post", "/users", Handler(createUser))
	m.Add("Get", "/auth/scheme", Handler(authScheme))
	m.Add("Post", "/auth/login", Handler(login))
	m.Add("Post", "/users/{email}/password", Handler(resetPassword))
	m.Add("Post", "/users/{email}/tokens", Handler(login))
	m.Add("Get", "/users/{email}/quota", AdminRequiredHandler(getUserQuota))
	m.Add("Post", "/users/{email}/quota", AdminRequiredHandler(changeUserQuota))
	m.Add("Delete", "/users/tokens", authorizationRequiredHandler(logout))
	m.Add("Put", "/users/password", authorizationRequiredHandler(changePassword))
	m.Add("Delete", "/users", authorizationRequiredHandler(removeUser))
	m.Add("Get", "/users/keys", authorizationRequiredHandler(listKeys))
	m.Add("Post", "/users/keys", authorizationRequiredHandler(addKeyToUser))
	m.Add("Delete", "/users/keys", authorizationRequiredHandler(removeKeyFromUser))
	m.Add("Get", "/users/api-key", authorizationRequiredHandler(showAPIToken))
	m.Add("Post", "/users/api-key", authorizationRequiredHandler(regenerateAPIToken))

	m.Add("Delete", "/logs", AdminRequiredHandler(logRemove))

	m.Add("Get", "/teams", authorizationRequiredHandler(teamList))
	m.Add("Post", "/teams", authorizationRequiredHandler(createTeam))
	m.Add("Get", "/teams/{name}", authorizationRequiredHandler(getTeam))
	m.Add("Delete", "/teams/{name}", authorizationRequiredHandler(removeTeam))
	m.Add("Put", "/teams/{team}/{user}", authorizationRequiredHandler(addUserToTeam))
	m.Add("Delete", "/teams/{team}/{user}", authorizationRequiredHandler(removeUserFromTeam))

	m.Add("Put", "/swap", authorizationRequiredHandler(swap))

	m.Add("Get", "/healthcheck/", http.HandlerFunc(healthcheck))

	m.Add("Get", "/iaas/machines", AdminRequiredHandler(machinesList))
	m.Add("Delete", "/iaas/machines/{machine_id}", AdminRequiredHandler(machineDestroy))
	m.Add("Get", "/iaas/templates", AdminRequiredHandler(templatesList))
	m.Add("Post", "/iaas/templates", AdminRequiredHandler(templateCreate))
	m.Add("Delete", "/iaas/templates/{template_name}", AdminRequiredHandler(templateDestroy))

	m.Add("Get", "/plans", authorizationRequiredHandler(listPlans))
	m.Add("Post", "/plans", AdminRequiredHandler(addPlan))
	m.Add("Delete", "/plans/{planname}", AdminRequiredHandler(removePlan))

	m.Add("Get", "/debug/goroutines", AdminRequiredHandler(dumpGoroutines))

	n := negroni.New()
	n.Use(negroni.NewRecovery())
	n.Use(newLoggerMiddleware())
	n.UseHandler(m)
	n.Use(negroni.HandlerFunc(contextClearerMiddleware))
	n.Use(negroni.HandlerFunc(flushingWriterMiddleware))
	n.Use(negroni.HandlerFunc(errorHandlingMiddleware))
	n.Use(negroni.HandlerFunc(setVersionHeadersMiddleware))
	n.Use(negroni.HandlerFunc(authTokenMiddleware))
	n.Use(&appLockMiddleware{excludedHandlers: []http.Handler{
		logPostHandler,
		runHandler,
		forceDeleteLockHandler,
		registerUnitHandler,
		saveCustomDataHandler,
		setUnitStatusHandler,
	}})
	n.UseHandler(http.HandlerFunc(runDelayedHandler))

	if !dry {
		routerType, err := config.GetString("docker:router")
		if err != nil {
			fatal(err)
		}
		routerObj, err := router.Get(routerType)
		if err != nil {
			fatal(err)
		}
		if messageRouter, ok := routerObj.(router.MessageRouter); ok {
			startupMessage, err := messageRouter.StartupMessage()
			if err == nil && startupMessage != "" {
				fmt.Print(startupMessage)
			}
		}
		provisioner, err := getProvisioner()
		if err != nil {
			fmt.Printf("Warning: configuration didn't declare a provisioner, using default provisioner.\n")
		}
		app.Provisioner, err = provision.Get(provisioner)
		if err != nil {
			fatal(err)
		}
		fmt.Printf("Using %q provisioner.\n", provisioner)
		if initializableProvisioner, ok := app.Provisioner.(provision.InitializableProvisioner); ok {
			err = initializableProvisioner.Initialize()
			if err != nil {
				fatal(err)
			}
		}
		if messageProvisioner, ok := app.Provisioner.(provision.MessageProvisioner); ok {
			startupMessage, err := messageProvisioner.StartupMessage()
			if err == nil && startupMessage != "" {
				fmt.Print(startupMessage)
			}
		}
		scheme, err := getAuthScheme()
		if err != nil {
			fmt.Printf("Warning: configuration didn't declare a auth:scheme, using default scheme.\n")
		}
		app.AuthScheme, err = auth.GetScheme(scheme)
		if err != nil {
			fatal(err)
		}
		fmt.Printf("Using %q auth scheme.\n", scheme)
		listen, err := config.GetString("listen")
		if err != nil {
			fatal(err)
		}
		app.StartAutoScale()
		tls, _ := config.GetBool("use-tls")
		if tls {
			certFile, err := config.GetString("tls:cert-file")
			if err != nil {
				fatal(err)
			}
			keyFile, err := config.GetString("tls:key-file")
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP/TLS server listening at %s...\n", listen)
			fatal(http.ListenAndServeTLS(listen, certFile, keyFile, n))
		} else {
			listener, err := net.Listen("tcp", listen)
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP server listening at %s...\n", listen)
			http.Handle("/", n)
			fatal(http.Serve(listener, nil))
		}
	}
	return n
}
<commit_msg>api/server: create route to app ssh<commit_after>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

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

	"github.com/codegangsta/negroni"
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/app"
	"github.com/tsuru/tsuru/auth"
	_ "github.com/tsuru/tsuru/auth/native"
	_ "github.com/tsuru/tsuru/auth/oauth"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/log"
	"github.com/tsuru/tsuru/provision"
	"github.com/tsuru/tsuru/router"
)

const Version = "0.9.1"

func getProvisioner() (string, error) {
	provisioner, err := config.GetString("provisioner")
	if provisioner == "" {
		provisioner = "docker"
	}
	return provisioner, err
}

type TsuruHandler struct {
	method string
	path   string
	h      http.Handler
}

func fatal(err error) {
	fmt.Println(err.Error())
	log.Fatal(err.Error())
}

var tsuruHandlerList []TsuruHandler

//RegisterHandler inserts a handler on a list of handlers
func RegisterHandler(path string, method string, h http.Handler) {
	var th TsuruHandler
	th.path = path
	th.method = method
	th.h = h
	tsuruHandlerList = append(tsuruHandlerList, th)
}

func getAuthScheme() (string, error) {
	name, err := config.GetString("auth:scheme")
	if name == "" {
		name = "native"
	}
	return name, err
}

// RunServer starts tsuru API server. The dry parameter indicates whether the
// server should run in dry mode, not starting the HTTP listener (for testing
// purposes).
func RunServer(dry bool) http.Handler {
	log.Init()
	connString, err := config.GetString("database:url")
	if err != nil {
		connString = db.DefaultDatabaseURL
	}
	dbName, err := config.GetString("database:name")
	if err != nil {
		dbName = db.DefaultDatabaseName
	}
	fmt.Printf("Using mongodb database %q from the server %q.\n", dbName, connString)

	m := &delayedRouter{}

	for _, handler := range tsuruHandlerList {
		m.Add(handler.method, handler.path, handler.h)
	}

	m.Add("Get", "/info", Handler(info))

	m.Add("Get", "/services/instances", authorizationRequiredHandler(serviceInstances))
	m.Add("Get", "/services/instances/{name}", authorizationRequiredHandler(serviceInstance))
	m.Add("Delete", "/services/instances/{name}", authorizationRequiredHandler(removeServiceInstance))
	m.Add("Post", "/services/instances", authorizationRequiredHandler(createServiceInstance))
	m.Add("Put", "/services/instances/{instance}/{app}", authorizationRequiredHandler(bindServiceInstance))
	m.Add("Delete", "/services/instances/{instance}/{app}", authorizationRequiredHandler(unbindServiceInstance))
	m.Add("Get", "/services/instances/{instance}/status", authorizationRequiredHandler(serviceInstanceStatus))

	m.AddAll("/services/proxy/{instance}", authorizationRequiredHandler(serviceProxy))

	m.Add("Get", "/services", authorizationRequiredHandler(serviceList))
	m.Add("Post", "/services", authorizationRequiredHandler(serviceCreate))
	m.Add("Put", "/services", authorizationRequiredHandler(serviceUpdate))
	m.Add("Delete", "/services/{name}", authorizationRequiredHandler(serviceDelete))
	m.Add("Get", "/services/{name}", authorizationRequiredHandler(serviceInfo))
	m.Add("Get", "/services/{name}/plans", authorizationRequiredHandler(servicePlans))
	m.Add("Get", "/services/{name}/doc", authorizationRequiredHandler(serviceDoc))
	m.Add("Put", "/services/{name}/doc", authorizationRequiredHandler(serviceAddDoc))
	m.Add("Put", "/services/{service}/{team}", authorizationRequiredHandler(grantServiceAccess))
	m.Add("Delete", "/services/{service}/{team}", authorizationRequiredHandler(revokeServiceAccess))

	m.Add("Delete", "/apps/{app}", authorizationRequiredHandler(appDelete))
	m.Add("Get", "/apps/{app}", authorizationRequiredHandler(appInfo))
	m.Add("Post", "/apps/{app}/cname", authorizationRequiredHandler(setCName))
	m.Add("Delete", "/apps/{app}/cname", authorizationRequiredHandler(unsetCName))
	runHandler := authorizationRequiredHandler(runCommand)
	m.Add("Post", "/apps/{app}/run", runHandler)
	m.Add("Post", "/apps/{app}/restart", authorizationRequiredHandler(restart))
	m.Add("Post", "/apps/{app}/start", authorizationRequiredHandler(start))
	m.Add("Post", "/apps/{app}/stop", authorizationRequiredHandler(stop))
	m.Add("Get", "/apps/{appname}/quota", AdminRequiredHandler(getAppQuota))
	m.Add("Post", "/apps/{appname}/quota", AdminRequiredHandler(changeAppQuota))
	m.Add("Get", "/apps/{app}/env", authorizationRequiredHandler(getEnv))
	m.Add("Post", "/apps/{app}/env", authorizationRequiredHandler(setEnv))
	m.Add("Delete", "/apps/{app}/env", authorizationRequiredHandler(unsetEnv))
	m.Add("Get", "/apps", authorizationRequiredHandler(appList))
	m.Add("Post", "/apps", authorizationRequiredHandler(createApp))
	m.Add("Post", "/apps/{app}/team-owner", authorizationRequiredHandler(setTeamOwner))
	forceDeleteLockHandler := AdminRequiredHandler(forceDeleteLock)
	m.Add("Delete", "/apps/{app}/lock", forceDeleteLockHandler)
	m.Add("Put", "/apps/{app}/units", authorizationRequiredHandler(addUnits))
	m.Add("Delete", "/apps/{app}/units", authorizationRequiredHandler(removeUnits))
	registerUnitHandler := authorizationRequiredHandler(registerUnit)
	m.Add("Post", "/apps/{app}/units/register", registerUnitHandler)
	setUnitStatusHandler := authorizationRequiredHandler(setUnitStatus)
	m.Add("Post", "/apps/{app}/units/{unit}", setUnitStatusHandler)
	m.Add("Put", "/apps/{app}/teams/{team}", authorizationRequiredHandler(grantAppAccess))
	m.Add("Delete", "/apps/{app}/teams/{team}", authorizationRequiredHandler(revokeAppAccess))
	m.Add("Get", "/apps/{app}/log", authorizationRequiredHandler(appLog))
	logPostHandler := authorizationRequiredHandler(addLog)
	m.Add("Post", "/apps/{app}/log", logPostHandler)
	saveCustomDataHandler := authorizationRequiredHandler(saveAppCustomData)
	m.Add("Post", "/apps/{app}/customdata", saveCustomDataHandler)
	m.Add("Post", "/apps/{appname}/deploy/rollback", authorizationRequiredHandler(deployRollback))
	m.Add("Get", "/apps/{app}/ssh", authorizationRequiredHandler(sshHandler))

	m.Add("Get", "/autoscale", authorizationRequiredHandler(autoScaleHistoryHandler))
	m.Add("Put", "/autoscale/{app}", authorizationRequiredHandler(autoScaleConfig))
	m.Add("Put", "/autoscale/{app}/enable", authorizationRequiredHandler(autoScaleEnable))
	m.Add("Put", "/autoscale/{app}/disable", authorizationRequiredHandler(autoScaleDisable))

	m.Add("Get", "/deploys", authorizationRequiredHandler(deploysList))
	m.Add("Get", "/deploys/{deploy}", authorizationRequiredHandler(deployInfo))

	m.Add("Get", "/platforms", authorizationRequiredHandler(platformList))
	m.Add("Post", "/platforms", AdminRequiredHandler(platformAdd))
	m.Add("Put", "/platforms/{name}", AdminRequiredHandler(platformUpdate))
	m.Add("Delete", "/platforms/{name}", AdminRequiredHandler(platformRemove))

	// These handlers don't use :app on purpose. Using :app means that only
	// the token generate for the given app is valid, but these handlers
	// use a token generated for Gandalf.
	m.Add("Get", "/apps/{appname}/available", authorizationRequiredHandler(appIsAvailable))
	m.Add("Post", "/apps/{appname}/repository/clone", authorizationRequiredHandler(deploy))
	m.Add("Post", "/apps/{appname}/deploy", authorizationRequiredHandler(deploy))

	m.Add("Get", "/users", AdminRequiredHandler(listUsers))
	m.Add("Post", "/users", Handler(createUser))
	m.Add("Get", "/auth/scheme", Handler(authScheme))
	m.Add("Post", "/auth/login", Handler(login))
	m.Add("Post", "/users/{email}/password", Handler(resetPassword))
	m.Add("Post", "/users/{email}/tokens", Handler(login))
	m.Add("Get", "/users/{email}/quota", AdminRequiredHandler(getUserQuota))
	m.Add("Post", "/users/{email}/quota", AdminRequiredHandler(changeUserQuota))
	m.Add("Delete", "/users/tokens", authorizationRequiredHandler(logout))
	m.Add("Put", "/users/password", authorizationRequiredHandler(changePassword))
	m.Add("Delete", "/users", authorizationRequiredHandler(removeUser))
	m.Add("Get", "/users/keys", authorizationRequiredHandler(listKeys))
	m.Add("Post", "/users/keys", authorizationRequiredHandler(addKeyToUser))
	m.Add("Delete", "/users/keys", authorizationRequiredHandler(removeKeyFromUser))
	m.Add("Get", "/users/api-key", authorizationRequiredHandler(showAPIToken))
	m.Add("Post", "/users/api-key", authorizationRequiredHandler(regenerateAPIToken))

	m.Add("Delete", "/logs", AdminRequiredHandler(logRemove))

	m.Add("Get", "/teams", authorizationRequiredHandler(teamList))
	m.Add("Post", "/teams", authorizationRequiredHandler(createTeam))
	m.Add("Get", "/teams/{name}", authorizationRequiredHandler(getTeam))
	m.Add("Delete", "/teams/{name}", authorizationRequiredHandler(removeTeam))
	m.Add("Put", "/teams/{team}/{user}", authorizationRequiredHandler(addUserToTeam))
	m.Add("Delete", "/teams/{team}/{user}", authorizationRequiredHandler(removeUserFromTeam))

	m.Add("Put", "/swap", authorizationRequiredHandler(swap))

	m.Add("Get", "/healthcheck/", http.HandlerFunc(healthcheck))

	m.Add("Get", "/iaas/machines", AdminRequiredHandler(machinesList))
	m.Add("Delete", "/iaas/machines/{machine_id}", AdminRequiredHandler(machineDestroy))
	m.Add("Get", "/iaas/templates", AdminRequiredHandler(templatesList))
	m.Add("Post", "/iaas/templates", AdminRequiredHandler(templateCreate))
	m.Add("Delete", "/iaas/templates/{template_name}", AdminRequiredHandler(templateDestroy))

	m.Add("Get", "/plans", authorizationRequiredHandler(listPlans))
	m.Add("Post", "/plans", AdminRequiredHandler(addPlan))
	m.Add("Delete", "/plans/{planname}", AdminRequiredHandler(removePlan))

	m.Add("Get", "/debug/goroutines", AdminRequiredHandler(dumpGoroutines))

	n := negroni.New()
	n.Use(negroni.NewRecovery())
	n.Use(newLoggerMiddleware())
	n.UseHandler(m)
	n.Use(negroni.HandlerFunc(contextClearerMiddleware))
	n.Use(negroni.HandlerFunc(flushingWriterMiddleware))
	n.Use(negroni.HandlerFunc(errorHandlingMiddleware))
	n.Use(negroni.HandlerFunc(setVersionHeadersMiddleware))
	n.Use(negroni.HandlerFunc(authTokenMiddleware))
	n.Use(&appLockMiddleware{excludedHandlers: []http.Handler{
		logPostHandler,
		runHandler,
		forceDeleteLockHandler,
		registerUnitHandler,
		saveCustomDataHandler,
		setUnitStatusHandler,
	}})
	n.UseHandler(http.HandlerFunc(runDelayedHandler))

	if !dry {
		routerType, err := config.GetString("docker:router")
		if err != nil {
			fatal(err)
		}
		routerObj, err := router.Get(routerType)
		if err != nil {
			fatal(err)
		}
		if messageRouter, ok := routerObj.(router.MessageRouter); ok {
			startupMessage, err := messageRouter.StartupMessage()
			if err == nil && startupMessage != "" {
				fmt.Print(startupMessage)
			}
		}
		provisioner, err := getProvisioner()
		if err != nil {
			fmt.Printf("Warning: configuration didn't declare a provisioner, using default provisioner.\n")
		}
		app.Provisioner, err = provision.Get(provisioner)
		if err != nil {
			fatal(err)
		}
		fmt.Printf("Using %q provisioner.\n", provisioner)
		if initializableProvisioner, ok := app.Provisioner.(provision.InitializableProvisioner); ok {
			err = initializableProvisioner.Initialize()
			if err != nil {
				fatal(err)
			}
		}
		if messageProvisioner, ok := app.Provisioner.(provision.MessageProvisioner); ok {
			startupMessage, err := messageProvisioner.StartupMessage()
			if err == nil && startupMessage != "" {
				fmt.Print(startupMessage)
			}
		}
		scheme, err := getAuthScheme()
		if err != nil {
			fmt.Printf("Warning: configuration didn't declare a auth:scheme, using default scheme.\n")
		}
		app.AuthScheme, err = auth.GetScheme(scheme)
		if err != nil {
			fatal(err)
		}
		fmt.Printf("Using %q auth scheme.\n", scheme)
		listen, err := config.GetString("listen")
		if err != nil {
			fatal(err)
		}
		app.StartAutoScale()
		tls, _ := config.GetBool("use-tls")
		if tls {
			certFile, err := config.GetString("tls:cert-file")
			if err != nil {
				fatal(err)
			}
			keyFile, err := config.GetString("tls:key-file")
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP/TLS server listening at %s...\n", listen)
			fatal(http.ListenAndServeTLS(listen, certFile, keyFile, n))
		} else {
			listener, err := net.Listen("tcp", listen)
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP server listening at %s...\n", listen)
			http.Handle("/", n)
			fatal(http.Serve(listener, nil))
		}
	}
	return n
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "image"
    "image/color"
    "image/png"
    "os"
    "sort"

    "github.com/zagrodzki/goscope/dummy"
    "github.com/zagrodzki/goscope/scope"
)

type Plot struct {
    *image.RGBA
}

func (plot Plot) Fill(col color.RGBA) {
    bounds := plot.Bounds()
    for i := bounds.Min.X; i < bounds.Max.X; i++ {
        for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
            plot.Set(i, j, col)
        }
    }
}

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

func Max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func Abs(a int) int {
    if a < 0 {
        return -a
    }
    return a
}

type AggrPoint struct {
    x int
    sumY int
    sizeY int
}

func (p AggrPoint) add(x, y int) AggrPoint {
    p.x = x
    p.sumY += y
    p.sizeY++
    return p
}

func (p AggrPoint) toPoint() image.Point {
    return image.Point{p.x, p.sumY / p.sizeY}
}

func SamplesToPoints(s []scope.Sample, start, end image.Point) []image.Point {
    if len(s) == 0 {
        return nil
    }
    minY := s[0]
    maxY := s[0]
    for _, y := range s {
        if minY > y {
            minY = y
        }
        if maxY < y {
            maxY = y
        }
    }

    rangeX := float64(len(s)-1)
    rangeY := float64(maxY - minY)
    aggrPoints := make(map[int]AggrPoint)
    for i, y := range s {
        mapX := int(float64(start.X) + float64(i) / rangeX * float64(end.X - start.X))
        mapY := int(float64(end.Y) - float64(y - minY) / rangeY * float64(end.Y - start.Y))
        aggrPoints[mapX] = aggrPoints[mapX].add(mapX, mapY)
    }
    fmt.Println(aggrPoints)
    var points []image.Point
    for _, p := range aggrPoints {
        points = append(points, p.toPoint())
    }

    return points
}

func (plot Plot) DrawLine(p1, p2 image.Point, col color.RGBA) {
    if p1.X == p2.X {
        for i := Min(p1.Y, p2.Y); i <= Max(p1.Y, p2.Y); i++ {
            plot.Set(p1.X, i, col)
        }
        return
    }
    a := float64(p1.Y - p2.Y) / float64(p1.X - p2.X)
    b := float64(p1.Y) - float64(p1.X) * a

    if Abs(p1.X - p2.X) >= Abs(p1.Y - p2.Y) {
        for i := Min(p1.X, p2.X); i <= Max(p1.X, p2.X); i++ {
            plot.Set(i, int(a * float64(i) + b), col)
        }
    } else {
        for i := Min(p1.Y, p2.Y); i <= Max(p1.Y, p2.Y); i++ {
            plot.Set(int((float64(i) - b) / a), i, col)
        }
    }
}

type XSorter []image.Point

func (a XSorter) Len() int           { return len(a) }
func (a XSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a XSorter) Less(i, j int) bool { return a[i].X < a[j].X }

func (plot Plot) DrawSamples(start, end image.Point, s []scope.Sample, col color.RGBA) {
    points := SamplesToPoints(s, start, end)
    sort.Sort(XSorter(points))
    for i := 1; i < len(points); i++ {
        plot.DrawLine(points[i-1], points[i], col)
    }
}

func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, col color.RGBA) {
    b := plot.Bounds()
    x1 := b.Min.X + 10
    x2 := b.Max.X - 10
    y1 := b.Min.Y + 10
    y2 := b.Min.Y + 10 + int((b.Max.Y - b.Min.Y - 10 * (len(samples) + 1)) / len(samples))
    step := y2 - b.Min.Y
    for _, v := range samples {
        plot.DrawSamples(image.Point{x1, y1}, image.Point{x2, y2}, v, col)
        y1 = y1 + step
        y2 = y2 + step
    }
}

func main() {
    dum, _ := dummy.Open("")
    data, stop, _ := dum.StartSampling()

    samples := (<-data).Samples
    fmt.Println(samples)
    stop()

    plot := Plot{image.NewRGBA(image.Rect(0, 0, 800, 600))}
    colWhite := color.RGBA{255, 255, 255, 255}
    colRed := color.RGBA{255, 0, 0, 255}
    plot.Fill(colWhite)
    plot.DrawAll(samples, colRed)

    f, err := os.Create("draw.png")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    png.Encode(f, plot)
}
<commit_msg>separate color for each plot<commit_after>package main

import (
    "fmt"
    "image"
    "image/color"
    "image/png"
    "os"
    "sort"

    "github.com/zagrodzki/goscope/dummy"
    "github.com/zagrodzki/goscope/scope"
)

type Plot struct {
    *image.RGBA
}

func (plot Plot) Fill(col color.RGBA) {
    bounds := plot.Bounds()
    for i := bounds.Min.X; i < bounds.Max.X; i++ {
        for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
            plot.Set(i, j, col)
        }
    }
}

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

func Max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func Abs(a int) int {
    if a < 0 {
        return -a
    }
    return a
}

type AggrPoint struct {
    x int
    sumY int
    sizeY int
}

func (p AggrPoint) add(x, y int) AggrPoint {
    p.x = x
    p.sumY += y
    p.sizeY++
    return p
}

func (p AggrPoint) toPoint() image.Point {
    return image.Point{p.x, p.sumY / p.sizeY}
}

func SamplesToPoints(s []scope.Sample, start, end image.Point) []image.Point {
    if len(s) == 0 {
        return nil
    }
    minY := s[0]
    maxY := s[0]
    for _, y := range s {
        if minY > y {
            minY = y
        }
        if maxY < y {
            maxY = y
        }
    }

    rangeX := float64(len(s)-1)
    rangeY := float64(maxY - minY)
    aggrPoints := make(map[int]AggrPoint)
    for i, y := range s {
        mapX := int(float64(start.X) + float64(i) / rangeX * float64(end.X - start.X))
        mapY := int(float64(end.Y) - float64(y - minY) / rangeY * float64(end.Y - start.Y))
        aggrPoints[mapX] = aggrPoints[mapX].add(mapX, mapY)
    }
    fmt.Println(aggrPoints)
    var points []image.Point
    for _, p := range aggrPoints {
        points = append(points, p.toPoint())
    }

    return points
}

func (plot Plot) DrawLine(p1, p2 image.Point, col color.RGBA) {
    if p1.X == p2.X {
        for i := Min(p1.Y, p2.Y); i <= Max(p1.Y, p2.Y); i++ {
            plot.Set(p1.X, i, col)
        }
        return
    }
    a := float64(p1.Y - p2.Y) / float64(p1.X - p2.X)
    b := float64(p1.Y) - float64(p1.X) * a

    if Abs(p1.X - p2.X) >= Abs(p1.Y - p2.Y) {
        for i := Min(p1.X, p2.X); i <= Max(p1.X, p2.X); i++ {
            plot.Set(i, int(a * float64(i) + b), col)
        }
    } else {
        for i := Min(p1.Y, p2.Y); i <= Max(p1.Y, p2.Y); i++ {
            plot.Set(int((float64(i) - b) / a), i, col)
        }
    }
}

type XSorter []image.Point

func (a XSorter) Len() int           { return len(a) }
func (a XSorter) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a XSorter) Less(i, j int) bool { return a[i].X < a[j].X }

func (plot Plot) DrawSamples(start, end image.Point, s []scope.Sample, col color.RGBA) {
    points := SamplesToPoints(s, start, end)
    sort.Sort(XSorter(points))
    for i := 1; i < len(points); i++ {
        plot.DrawLine(points[i-1], points[i], col)
    }
}

func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, cols map[scope.ChanID]color.RGBA) {
    b := plot.Bounds()
    x1 := b.Min.X + 10
    x2 := b.Max.X - 10
    y1 := b.Min.Y + 10
    y2 := b.Min.Y + 10 + int((b.Max.Y - b.Min.Y - 10 * (len(samples) + 1)) / len(samples))
    step := y2 - b.Min.Y
    for id, v := range samples {
        plot.DrawSamples(image.Point{x1, y1}, image.Point{x2, y2}, v, cols[id])
        y1 = y1 + step
        y2 = y2 + step
    }
}

func main() {
    dum, _ := dummy.Open("")
    data, stop, _ := dum.StartSampling()

    samples := (<-data).Samples
    fmt.Println(samples)
    stop()

    plot := Plot{image.NewRGBA(image.Rect(0, 0, 800, 600))}
    colWhite := color.RGBA{255, 255, 255, 255}

    colRed := color.RGBA{255, 0, 0, 255}
    colGreen := color.RGBA{0, 255, 0, 255}
    colBlue := color.RGBA{0, 0, 255, 255}
    colBlack := color.RGBA{0, 0, 0, 255}
    chanCols := [4]color.RGBA{colRed, colGreen, colBlue, colBlack}

    plot.Fill(colWhite)
    cols := make(map[scope.ChanID]color.RGBA)
    next := 0
    for _, id := range dum.Channels() {
        cols[id] = chanCols[next]
        next = (next + 1) % 4
    }
    plot.DrawAll(samples, cols)

    f, err := os.Create("draw.png")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    png.Encode(f, plot)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
// Licensed under the same terms as Go itself:
// https://code.google.com/p/go/source/browse/LICENSE

/*
The h2i command is an interactive HTTP/2 console.

Usage:
  $ h2i [flags] <hostname>

Interactive commands in the console:

  ping [data]
  settings ack
*/
package main

import (
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"

	"github.com/bradfitz/http2"
	"golang.org/x/crypto/ssh/terminal"
)

// Flags
var (
	flagNextProto = flag.String("nextproto", "h2,h2-14", "Comma-separated list of NPN/ALPN protocol names to negotiate.")
	flagInsecure  = flag.Bool("insecure", false, "Whether to skip TLS cert validation")
)

type command func(*h2i, []string) error

var commands = map[string]command{
	"ping":     (*h2i).sendPing,
	"settings": (*h2i).sendSettings,
	"quit":     (*h2i).cmdQuit,
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: h2i <hostname>\n\n")
	flag.PrintDefaults()
	os.Exit(1)
}

// withPort adds ":443" if another port isn't already present.
func withPort(host string) string {
	if _, _, err := net.SplitHostPort(host); err != nil {
		return net.JoinHostPort(host, "443")
	}
	return host
}

// h2i is the app's state.
type h2i struct {
	host   string
	tc     *tls.Conn
	framer *http2.Framer
	term   *terminal.Terminal
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 {
		usage()
	}
	log.SetFlags(0)

	host := flag.Arg(0)
	app := &h2i{
		host: host,
	}
	if err := app.Main(); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}

func (a *h2i) Main() error {
	cfg := &tls.Config{
		ServerName:         a.host,
		NextProtos:         strings.Split(*flagNextProto, ","),
		InsecureSkipVerify: *flagInsecure,
	}

	hostAndPort := withPort(a.host)
	log.Printf("Connecting to %s ...", hostAndPort)
	tc, err := tls.Dial("tcp", hostAndPort, cfg)
	if err != nil {
		return fmt.Errorf("Error dialing %s: %v", withPort(a.host), err)
	}
	log.Printf("Connected to %v", tc.RemoteAddr())
	defer tc.Close()

	if err := tc.Handshake(); err != nil {
		return fmt.Errorf("TLS handshake: %v", err)
	}
	if !*flagInsecure {
		if err := tc.VerifyHostname(a.host); err != nil {
			return fmt.Errorf("VerifyHostname: %v", err)
		}
	}
	state := tc.ConnectionState()
	log.Printf("Negotiated protocol %q", state.NegotiatedProtocol)
	if !state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol == "" {
		return fmt.Errorf("Could not negotiate protocol mutually")
	}

	if _, err := io.WriteString(tc, http2.ClientPreface); err != nil {
		return err
	}

	a.framer = http2.NewFramer(tc, tc)

	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		return err
	}
	defer terminal.Restore(0, oldState)

	var screen = struct {
		io.Reader
		io.Writer
	}{os.Stdin, os.Stdout}

	a.term = terminal.NewTerminal(screen, "> ")
	a.term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
		if key != '\t' {
			return
		}
		name, _, ok := lookupCommand(line)
		if !ok {
			return
		}
		return name, len(name), true
	}

	errc := make(chan error, 2)
	go func() { errc <- a.readFrames() }()
	go func() { errc <- a.readConsole() }()
	return <-errc
}

func (a *h2i) logf(format string, args ...interface{}) {
	fmt.Fprintf(a.term, format+"\n", args...)
}

func (a *h2i) readConsole() error {
	for {
		line, err := a.term.ReadLine()
		if err != nil {
			return fmt.Errorf("terminal.ReadLine: %v", err)
		}
		f := strings.Fields(line)
		if len(f) == 0 {
			continue
		}
		cmd, args := f[0], f[1:]
		if _, fn, ok := lookupCommand(cmd); ok {
			err = fn(a, args)
		} else {
			a.logf("Unknown command %q", line)
		}
		if err == errExitApp {
			return nil
		}
		if err != nil {
			return err
		}
	}
}

func lookupCommand(prefix string) (name string, c command, ok bool) {
	prefix = strings.ToLower(prefix)
	if c, ok = commands[prefix]; ok {
		return prefix, c, ok
	}

	for full, candidate := range commands {
		if strings.HasPrefix(full, prefix) {
			if c != nil {
				return "", nil, false // ambiguous
			}
			c = candidate
			name = full
		}
	}
	return name, c, c != nil
}

var errExitApp = errors.New("internal sentinel error value to quit the console reading loop")

func (a *h2i) cmdQuit(args []string) error {
	if len(args) > 0 {
		a.logf("the QUIT command takes no argument")
		return nil
	}
	return errExitApp
}

func (a *h2i) sendSettings(args []string) error {
	if len(args) == 1 && args[0] == "ack" {
		return a.framer.WriteSettingsAck()
	}
	a.logf("TODO: unhandled SETTINGS")
	return nil
}

func (a *h2i) sendPing(args []string) error {
	if len(args) > 1 {
		a.logf("invalid PING usage: only accepts 0 or 1 args")
		return nil // nil means don't end the program
	}
	var data [8]byte
	if len(args) == 1 {
		copy(data[:], args[0])
	} else {
		copy(data[:], "h2i_ping")
	}
	return a.framer.WritePing(false, data)
}

func (a *h2i) readFrames() error {
	for {
		f, err := a.framer.ReadFrame()
		if err != nil {
			return fmt.Errorf("ReadFrame: %v", err)
		}
		switch f := f.(type) {
		case *http2.PingFrame:
			fmt.Fprintf(a.term, "%v %q\n", f, f.Data)
		default:
			fmt.Fprintf(a.term, "%v\n", f)
		}
	}
}
<commit_msg>h2i: show Settings, WindowUpdate, GoAway frame details.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
// Licensed under the same terms as Go itself:
// https://code.google.com/p/go/source/browse/LICENSE

/*
The h2i command is an interactive HTTP/2 console.

Usage:
  $ h2i [flags] <hostname>

Interactive commands in the console:

  ping [data]
  settings ack
*/
package main

import (
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"

	"github.com/bradfitz/http2"
	"golang.org/x/crypto/ssh/terminal"
)

// Flags
var (
	flagNextProto = flag.String("nextproto", "h2,h2-14", "Comma-separated list of NPN/ALPN protocol names to negotiate.")
	flagInsecure  = flag.Bool("insecure", false, "Whether to skip TLS cert validation")
)

type command func(*h2i, []string) error

var commands = map[string]command{
	"ping":     (*h2i).sendPing,
	"settings": (*h2i).sendSettings,
	"quit":     (*h2i).cmdQuit,
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: h2i <hostname>\n\n")
	flag.PrintDefaults()
	os.Exit(1)
}

// withPort adds ":443" if another port isn't already present.
func withPort(host string) string {
	if _, _, err := net.SplitHostPort(host); err != nil {
		return net.JoinHostPort(host, "443")
	}
	return host
}

// h2i is the app's state.
type h2i struct {
	host   string
	tc     *tls.Conn
	framer *http2.Framer
	term   *terminal.Terminal
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 {
		usage()
	}
	log.SetFlags(0)

	host := flag.Arg(0)
	app := &h2i{
		host: host,
	}
	if err := app.Main(); err != nil {
		if app.term != nil {
			app.logf("%v\n", err)
		} else {
			fmt.Fprintf(os.Stderr, "%v\n", err)
		}
		os.Exit(1)
	}
}

func (a *h2i) Main() error {
	cfg := &tls.Config{
		ServerName:         a.host,
		NextProtos:         strings.Split(*flagNextProto, ","),
		InsecureSkipVerify: *flagInsecure,
	}

	hostAndPort := withPort(a.host)
	log.Printf("Connecting to %s ...", hostAndPort)
	tc, err := tls.Dial("tcp", hostAndPort, cfg)
	if err != nil {
		return fmt.Errorf("Error dialing %s: %v", withPort(a.host), err)
	}
	log.Printf("Connected to %v", tc.RemoteAddr())
	defer tc.Close()

	if err := tc.Handshake(); err != nil {
		return fmt.Errorf("TLS handshake: %v", err)
	}
	if !*flagInsecure {
		if err := tc.VerifyHostname(a.host); err != nil {
			return fmt.Errorf("VerifyHostname: %v", err)
		}
	}
	state := tc.ConnectionState()
	log.Printf("Negotiated protocol %q", state.NegotiatedProtocol)
	if !state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol == "" {
		return fmt.Errorf("Could not negotiate protocol mutually")
	}

	if _, err := io.WriteString(tc, http2.ClientPreface); err != nil {
		return err
	}

	a.framer = http2.NewFramer(tc, tc)

	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		return err
	}
	defer terminal.Restore(0, oldState)

	var screen = struct {
		io.Reader
		io.Writer
	}{os.Stdin, os.Stdout}

	a.term = terminal.NewTerminal(screen, "> ")
	a.term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
		if key != '\t' {
			return
		}
		name, _, ok := lookupCommand(line)
		if !ok {
			return
		}
		return name, len(name), true
	}

	errc := make(chan error, 2)
	go func() { errc <- a.readFrames() }()
	go func() { errc <- a.readConsole() }()
	return <-errc
}

func (a *h2i) logf(format string, args ...interface{}) {
	fmt.Fprintf(a.term, format+"\n", args...)
}

func (a *h2i) readConsole() error {
	for {
		line, err := a.term.ReadLine()
		if err != nil {
			return fmt.Errorf("terminal.ReadLine: %v", err)
		}
		f := strings.Fields(line)
		if len(f) == 0 {
			continue
		}
		cmd, args := f[0], f[1:]
		if _, fn, ok := lookupCommand(cmd); ok {
			err = fn(a, args)
		} else {
			a.logf("Unknown command %q", line)
		}
		if err == errExitApp {
			return nil
		}
		if err != nil {
			return err
		}
	}
}

func lookupCommand(prefix string) (name string, c command, ok bool) {
	prefix = strings.ToLower(prefix)
	if c, ok = commands[prefix]; ok {
		return prefix, c, ok
	}

	for full, candidate := range commands {
		if strings.HasPrefix(full, prefix) {
			if c != nil {
				return "", nil, false // ambiguous
			}
			c = candidate
			name = full
		}
	}
	return name, c, c != nil
}

var errExitApp = errors.New("internal sentinel error value to quit the console reading loop")

func (a *h2i) cmdQuit(args []string) error {
	if len(args) > 0 {
		a.logf("the QUIT command takes no argument")
		return nil
	}
	return errExitApp
}

func (a *h2i) sendSettings(args []string) error {
	if len(args) == 1 && args[0] == "ack" {
		return a.framer.WriteSettingsAck()
	}
	a.logf("TODO: unhandled SETTINGS")
	return nil
}

func (a *h2i) sendPing(args []string) error {
	if len(args) > 1 {
		a.logf("invalid PING usage: only accepts 0 or 1 args")
		return nil // nil means don't end the program
	}
	var data [8]byte
	if len(args) == 1 {
		copy(data[:], args[0])
	} else {
		copy(data[:], "h2i_ping")
	}
	return a.framer.WritePing(false, data)
}

func (a *h2i) readFrames() error {
	for {
		f, err := a.framer.ReadFrame()
		if err != nil {
			return fmt.Errorf("ReadFrame: %v", err)
		}
		a.logf("%v", f)
		switch f := f.(type) {
		case *http2.PingFrame:
			a.logf("  Data = %q", f.Data)
		case *http2.SettingsFrame:
			f.ForeachSetting(func(s http2.Setting) error {
				a.logf("  %v", s)
				return nil
			})
		case *http2.WindowUpdateFrame:
			a.logf("  Window-Increment = %v\n", f.Increment)
		case *http2.GoAwayFrame:
			a.logf("  Last-Stream-ID = %d; Error-Code = %v (%d)\n", f.LastStreamID, f.ErrCode, f.ErrCode)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net"

	"github.com/miekg/dns"
)

type Question struct {
	qname  string
	qtype  string
	qclass string
}

func (q *Question) String() string {
	return q.qname + " " + q.qclass + " " + q.qtype
}

type GnoccoHandler struct {
	Cache    *Cache
	Resolver *Resolver
	MaxJobs  int
	Jobs     int
}

func NewHandler(m int) *GnoccoHandler {
	c := NewCache(int64(Config.Cache.MaxCount), int32(Config.Cache.Expire))
	r := initResolver()
	return &GnoccoHandler{c, r, m, 0}
}

func (h *GnoccoHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
	if h.Jobs < h.MaxJobs {
		q := req.Question[0]
		Q := Question{q.Name, dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}

		var remote net.IP
		if Net == "tcp" {
			remote = w.RemoteAddr().(*net.TCPAddr).IP
		} else {
			remote = w.RemoteAddr().(*net.UDPAddr).IP
		}

		logger.Info("%s lookup　%s", remote, Q.String())
		h.Jobs++
		switch {
		case Q.qclass == "IN":
			if recs, err := h.Cache.Get(h.Cache.MakeKey(Q.qname, Q.qtype)); err == nil {
				//we have an answer now construct a dns.Msg
				result := new(dns.Msg)
				result.SetReply(req)
				for _, z := range recs.Value {
					rec, _ := dns.NewRR(dns.Fqdn(Q.qname) + " " + Q.qtype + " " + z)
					result.Answer = append(result.Answer, rec)
				}
				w.WriteMsg(result)
			} else {
				if rcs, err := h.Cache.Get(h.Cache.MakeKey(Q.qname, "CNAME")); err == nil {
					logger.Info("Found CNAME %s", rcs.String())
					result := new(dns.Msg)
					result.SetReply(req)
					for _, z := range rcs.Value {
						rc, _ := dns.NewRR(dns.Fqdn(Q.qname) + " " + "CNAME" + " " + z)
						result.Answer = append(result.Answer, rc)
						if rt, err := h.Cache.Get(h.Cache.MakeKey(z, Q.qtype)); err == nil {
							for _, ey := range rt.Value {
								gg, _ := dns.NewRR(z + " " + Q.qtype + " " + ey)
								result.Answer = append(result.Answer, gg)
							}
						}
					}
					w.WriteMsg(result)
				} else {
					h.Resolver.Lookup(h.Cache, w, req)
				}
			}
		case Q.qclass == "CH", Q.qtype == "TXT":
			m := new(dns.Msg)
			m.SetReply(req)
			hdr := dns.RR_Header{Name: Q.qname, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
			switch Q.qname {
			default:
				m.SetRcode(req, 4)
				w.WriteMsg(m)
			case "authors.bind.":
				m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{"Nagy Karoly Gabriel <k@jpi.io>"}})
			case "version.bind.", "version.server.":
				m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"Version" + Version + "built on" + BuildTime}}}
			case "hostname.bind.", "id.server.":
				m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"localhost"}}}
			}
			w.WriteMsg(m)

		default:
		}
		h.Jobs--
	} else {
		m := new(dns.Msg)
		m.SetRcode(req, 2)
		w.WriteMsg(m)
	}
}

func (h *GnoccoHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
	h.do("tcp", w, req)
}

func (h *GnoccoHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
	h.do("udp", w, req)
}
<commit_msg>gnocco: beautiful means readable too.<commit_after>package main

import (
	"net"

	"github.com/miekg/dns"
)

type Question struct {
	qname  string
	qtype  string
	qclass string
}

func (q *Question) String() string {
	return q.qname + " " + q.qclass + " " + q.qtype
}

type GnoccoHandler struct {
	Cache    *Cache
	Resolver *Resolver
	MaxJobs  int
	Jobs     int
}

func NewHandler(m int) *GnoccoHandler {
	c := NewCache(int64(Config.Cache.MaxCount), int32(Config.Cache.Expire))
	r := initResolver()
	return &GnoccoHandler{c, r, m, 0}
}

func (h *GnoccoHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
	if h.Jobs < h.MaxJobs {
		q := req.Question[0]
		Q := Question{q.Name, dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}

		var remote net.IP
		if Net == "tcp" {
			remote = w.RemoteAddr().(*net.TCPAddr).IP
		} else {
			remote = w.RemoteAddr().(*net.UDPAddr).IP
		}

		logger.Info("%s lookup　%s", remote, Q.String())
		h.Jobs++
		switch {
		case Q.qclass == "IN":
			if recs, err := h.Cache.Get(h.Cache.MakeKey(Q.qname, Q.qtype)); err == nil {
				//we have an answer now construct a dns.Msg
				result := new(dns.Msg)
				result.SetReply(req)
				for _, z := range recs.Value {
					rec, _ := dns.NewRR(dns.Fqdn(Q.qname) + " " + Q.qtype + " " + z)
					result.Answer = append(result.Answer, rec)
				}
				w.WriteMsg(result)
			} else {
				if rcs, err := h.Cache.Get(h.Cache.MakeKey(Q.qname, "CNAME")); err == nil {
					logger.Info("Found CNAME %s", rcs.String())
					result := new(dns.Msg)
					result.SetReply(req)
					for _, z := range rcs.Value {
						rc, _ := dns.NewRR(dns.Fqdn(Q.qname) + " " + "CNAME" + " " + z)
						result.Answer = append(result.Answer, rc)
						if rt, err := h.Cache.Get(h.Cache.MakeKey(z, Q.qtype)); err == nil {
							for _, ey := range rt.Value {
								gg, _ := dns.NewRR(z + " " + Q.qtype + " " + ey)
								result.Answer = append(result.Answer, gg)
							}
						}
					}
					w.WriteMsg(result)
				} else {
					h.Resolver.Lookup(h.Cache, w, req)
				}
			}
		case Q.qclass == "CH", Q.qtype == "TXT":
			m := new(dns.Msg)
			m.SetReply(req)
			hdr := dns.RR_Header{Name: Q.qname, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
			switch Q.qname {
			default:
				m.SetRcode(req, 4)
				w.WriteMsg(m)
			case "authors.bind.":
				m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{"Nagy Karoly Gabriel <k@jpi.io>"}})
			case "version.bind.", "version.server.":
				m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"Version " + Version + " built on " + BuildTime}}}
			case "hostname.bind.", "id.server.":
				m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"localhost"}}}
			}
			w.WriteMsg(m)

		default:
		}
		h.Jobs--
	} else {
		m := new(dns.Msg)
		m.SetRcode(req, 2)
		w.WriteMsg(m)
	}
}

func (h *GnoccoHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
	h.do("tcp", w, req)
}

func (h *GnoccoHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
	h.do("udp", w, req)
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"strings"
)

const (
	origin  = "https://www.pinterest.com/"
	repoURL = "https://github.com/attilaolah/pinfeed"
)

var (
	thumb       = regexp.MustCompile("\\b(https?://[0-9a-z-]+.pinimg.com/)(\\d+x)(/[/0-9a-f]+.jpg)\\b")
	replacement = []byte("${1}1200x${3}")
	headers     = []string{
		// Cache control headers
		"Age",
		"Cache-Control",
		"Content-Type",
		"Date",
		"Etag",
		"Last-Modified",
		"Vary",
		// Pinterest-specific stuff
		"Pinterest-Breed",
		"Pinterest-Generated-By",
		"Pinterest-Version",
	}
)

func pinFeed(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		http.Redirect(w, r, repoURL, http.StatusMovedPermanently)
		return
	}
	res, err := http.Get(feedURL(r.URL.Path))
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer res.Body.Close()
	for _, key := range headers {
		if val := res.Header.Get(key); val != "" {
			w.Header().Set(key, val)
		}
	}
	w.WriteHeader(res.StatusCode)
	buf, err := replaceThumbs(res.Body)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Write(buf)
}

func feedURL(path string) string {
	username, feed := userAndFeed(path)
	if feed == "" {
		feed = "feed"
	}
	return origin + "/" + username + "/" + feed + ".rss"
}

func userAndFeed(path string) (username, feed string) {
	parts := strings.SplitN(path, "/", 4)
	if len(parts) > 1 {
		username = parts[1]
	}
	if len(parts) > 2 {
		feed = parts[2]
	}
	return
}

func replaceThumbs(r io.Reader) (buf []byte, err error) {
	if buf, err = ioutil.ReadAll(r); err == nil {
		buf = thumb.ReplaceAll(buf, replacement)
	}
	return
}
<commit_msg>ignore .rss suffix<commit_after>package main

import (
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"strings"
)

const (
	origin  = "https://www.pinterest.com/"
	repoURL = "https://github.com/attilaolah/pinfeed"
)

var (
	thumb       = regexp.MustCompile("\\b(https?://[0-9a-z-]+.pinimg.com/)(\\d+x)(/[/0-9a-f]+.jpg)\\b")
	replacement = []byte("${1}1200x${3}")
	headers     = []string{
		// Cache control headers
		"Age",
		"Cache-Control",
		"Content-Type",
		"Date",
		"Etag",
		"Last-Modified",
		"Vary",
		// Pinterest-specific stuff
		"Pinterest-Breed",
		"Pinterest-Generated-By",
		"Pinterest-Version",
	}
)

func pinFeed(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		http.Redirect(w, r, repoURL, http.StatusMovedPermanently)
		return
	}
	res, err := http.Get(feedURL(r.URL.Path))
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer res.Body.Close()
	for _, key := range headers {
		if val := res.Header.Get(key); val != "" {
			w.Header().Set(key, val)
		}
	}
	w.WriteHeader(res.StatusCode)
	buf, err := replaceThumbs(res.Body)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.Write(buf)
}

func feedURL(path string) string {
	username, feed := userAndFeed(path)
	if feed == "" {
		feed = "feed"
	}
	return origin + "/" + username + "/" + feed + ".rss"
}

func userAndFeed(path string) (username, feed string) {
	path = strings.TrimSuffix(path, ".rss")
	parts := strings.SplitN(path, "/", 4)
	if len(parts) > 1 {
		username = parts[1]
	}
	if len(parts) > 2 {
		feed = parts[2]
	}
	return
}

func replaceThumbs(r io.Reader) (buf []byte, err error) {
	if buf, err = ioutil.ReadAll(r); err == nil {
		buf = thumb.ReplaceAll(buf, replacement)
	}
	return
}
<|endoftext|>


<commit_before>// Package infuse provides an immutable, concurrency-safe middleware handler
// that conforms to http.Handler. An infuse.Handler is fully compatible with
// the standard library, supports flexible chaining, and provides a shared
// context between middleware handlers within a single request without
// relying on global state, locks, or shared closures.
package infuse

import "net/http"

// A Handler is a middleware handler that conforms to http.Handler.
//
// An http.Handler passed to the Handle method can call the http.Handler
// provided immediately after it using infuse.Next. All handlers within a
// single request-response cycle can share data of any type using infuse.Get
// and infuse.Set.
type Handler interface {
	// Handle returns a new infuse.Handler that serves the provided
	// http.Handler after all existing http.Handlers are served. The
	// newly-provided http.Handler will be called when the previously-provided
	// http.Handler calls infuse.Next. The provided http.Handler may be
	// an infuse.Handler itself.
	Handle(handler http.Handler) Handler

	// HandleFunc is the same as Handle, but it takes a handler function
	// instead of an http.Handler.
	HandleFunc(handler func(http.ResponseWriter, *http.Request)) Handler

	// ServeHTTP serves the first http.Handler provided to the infuse.Handler.
	ServeHTTP(response http.ResponseWriter, request *http.Request)
}

// New returns a new infuse.Handler.
func New() Handler {
	return (*layer)(nil)
}

type layer struct {
	handler http.Handler
	prev    *layer
}

func (l *layer) Handle(handler http.Handler) Handler {
	return &layer{handler, l}
}

func (l *layer) HandleFunc(handler func(http.ResponseWriter, *http.Request)) Handler {
	return l.Handle(http.HandlerFunc(handler))
}

func (l *layer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	if l == nil {
		return
	}

	sharedResponse := convertResponse(response)

	current := l
	for ; current.prev != nil; current = current.prev {
		sharedResponse.layers = append(sharedResponse.layers, current)
	}
	current.handler.ServeHTTP(sharedResponse, request)
}

func convertResponse(response http.ResponseWriter) *contextualResponse {
	converted, ok := response.(*contextualResponse)
	if !ok {
		return &contextualResponse{ResponseWriter: response}
	}
	return converted
}

type contextualResponse struct {
	http.ResponseWriter
	context interface{}
	layers  []*layer
}

// Next should only be called from within an http.Handler that is served by an
// infuse.Handler. Next serves the next http.Handler in the middleware stack.
//
// Next returns false if no subsequent http.Handler is available, or if
// the provided response object was not served by an infuse.Handler. Calling
// Next multiple times in the same handler will run the rest of the middleware
// stack for each call.
//
// Next must be called with the same response object provided to the
// caller. Like http.ResponseWriter, Next is not safe for concurrent usage
// with other parts of the same request-response cycle.
func Next(response http.ResponseWriter, request *http.Request) bool {
	sharedResponse := convertResponse(response)
	layers := sharedResponse.layers
	if len(layers) == 0 {
		return false
	}

	next := layers[len(layers)-1]
	sharedResponse.layers = layers[:len(layers)-1]
	defer func() { sharedResponse.layers = layers }()

	next.handler.ServeHTTP(response, request)
	return true
}

// Get will retrieve a value that is shared by the every infuse-served
// http.Handler in the same request-response cycle. Any changes to data
// pointed to by the returned value will be seen by other http.Handlers
// that call infuse.Get in the same request-response cycle.
//
// To retrieve a value of a particular type, wrap infuse.Get as such:
//   func GetMyContext(response http.ResponseWriter) *MyContext {
//      return infuse.Get(response).(*MyContext)
//   }
//
// Example using a map:
//   func GetMyMap(response http.ResponseWriter) map[string]string {
//      return infuse.Get(response).(map[string]string)
//   }
//
//   func CreateMyMap(response http.ResponseWriter) {
//      infuse.Set(response, make(make[string]string))
//   }
func Get(response http.ResponseWriter) interface{} {
	return response.(*contextualResponse).context
}

// Set will store a value that will be shared by the every infuse-served
// http.Handler in the same request-response cycle.
func Set(response http.ResponseWriter, value interface{}) {
	response.(*contextualResponse).context = value
}
<commit_msg>Implement infuse.Next on contextualResponse<commit_after>// Package infuse provides an immutable, concurrency-safe middleware handler
// that conforms to http.Handler. An infuse.Handler is fully compatible with
// the standard library, supports flexible chaining, and provides a shared
// context between middleware handlers within a single request without
// relying on global state, locks, or shared closures.
package infuse

import "net/http"

// A Handler is a middleware handler that conforms to http.Handler.
//
// An http.Handler passed to the Handle method can call the http.Handler
// provided immediately after it using infuse.Next. All handlers within a
// single request-response cycle can share data of any type using infuse.Get
// and infuse.Set.
type Handler interface {
	// Handle returns a new infuse.Handler that serves the provided
	// http.Handler after all existing http.Handlers are served. The
	// newly-provided http.Handler will be called when the previously-provided
	// http.Handler calls infuse.Next. The provided http.Handler may be
	// an infuse.Handler itself.
	Handle(handler http.Handler) Handler

	// HandleFunc is the same as Handle, but it takes a handler function
	// instead of an http.Handler.
	HandleFunc(handler func(http.ResponseWriter, *http.Request)) Handler

	// ServeHTTP serves the first http.Handler provided to the infuse.Handler.
	ServeHTTP(response http.ResponseWriter, request *http.Request)
}

// New returns a new infuse.Handler.
func New() Handler {
	return (*layer)(nil)
}

type layer struct {
	handler http.Handler
	prev    *layer
}

func (l *layer) Handle(handler http.Handler) Handler {
	return &layer{handler, l}
}

func (l *layer) HandleFunc(handler func(http.ResponseWriter, *http.Request)) Handler {
	return l.Handle(http.HandlerFunc(handler))
}

func (l *layer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	if l == nil {
		return
	}

	sharedResponse := convertResponse(response)

	current := l
	for ; current.prev != nil; current = current.prev {
		sharedResponse.layers = append(sharedResponse.layers, current)
	}
	current.handler.ServeHTTP(sharedResponse, request)
}

func convertResponse(response http.ResponseWriter) *contextualResponse {
	converted, ok := response.(*contextualResponse)
	if !ok {
		return &contextualResponse{ResponseWriter: response}
	}
	return converted
}

type contextualResponse struct {
	http.ResponseWriter
	context interface{}
	layers  []*layer
}

func (c *contextualResponse) next(request *http.Request) bool {
	if len(c.layers) == 0 {
		return false
	}

	originalLayers := c.layers
	nextLayer := c.layers[len(c.layers)-1]
	c.layers = c.layers[:len(c.layers)-1]
	defer func() { c.layers = originalLayers }()

	nextLayer.handler.ServeHTTP(c, request)
	return true
}

// Next should only be called from within an http.Handler that is served by an
// infuse.Handler. Next serves the next http.Handler in the middleware stack.
//
// Next returns false if no subsequent http.Handler is available, or if
// the provided response object was not served by an infuse.Handler. Calling
// Next multiple times in the same handler will run the rest of the middleware
// stack for each call.
//
// Next must be called with the same response object provided to the
// caller. Like http.ResponseWriter, Next is not safe for concurrent usage
// with other parts of the same request-response cycle.
func Next(response http.ResponseWriter, request *http.Request) bool {
	return convertResponse(response).next(request)
}

// Get will retrieve a value that is shared by the every infuse-served
// http.Handler in the same request-response cycle. Any changes to data
// pointed to by the returned value will be seen by other http.Handlers
// that call infuse.Get in the same request-response cycle.
//
// To retrieve a value of a particular type, wrap infuse.Get as such:
//   func GetMyContext(response http.ResponseWriter) *MyContext {
//      return infuse.Get(response).(*MyContext)
//   }
//
// Example using a map:
//   func GetMyMap(response http.ResponseWriter) map[string]string {
//      return infuse.Get(response).(map[string]string)
//   }
//
//   func CreateMyMap(response http.ResponseWriter) {
//      infuse.Set(response, make(make[string]string))
//   }
func Get(response http.ResponseWriter) interface{} {
	return response.(*contextualResponse).context
}

// Set will store a value that will be shared by the every infuse-served
// http.Handler in the same request-response cycle.
func Set(response http.ResponseWriter, value interface{}) {
	response.(*contextualResponse).context = value
}
<|endoftext|>


<commit_before>package main

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

	"github.com/chadweimer/gomp/modules/conf"
	"github.com/chadweimer/gomp/modules/upload"
	"github.com/julienschmidt/httprouter"
	"github.com/unrolled/render"
)

type uiHandler struct {
	cfg   *conf.Config
	uiMux *httprouter.Router
	*render.Render
}

func newUIHandler(cfg *conf.Config, renderer *render.Render) http.Handler {
	h := uiHandler{
		cfg:    cfg,
		Render: renderer,
	}

	h.uiMux = httprouter.New()
	if cfg.IsDevelopment {
		h.uiMux.ServeFiles("/static/*filepath", justFilesFileSystem{http.Dir("static")})
	} else {
		h.uiMux.ServeFiles("/static/*filepath",
justFilesFileSystem{http.Dir("static/build/default")})
	}
	if h.cfg.UploadDriver == "fs" {
		h.uiMux.ServeFiles("/uploads/*filepath", justFilesFileSystem{http.Dir(h.cfg.UploadPath)})
	} else if h.cfg.UploadDriver == "s3" {
		h.uiMux.GET("/uploads/*filepath", upload.HandleS3Uploads(h.cfg.UploadPath))
	}
	h.uiMux.NotFound = http.HandlerFunc(h.notFound)
	h.uiMux.PanicHandler = h.handlePanic

	return h.uiMux
}

func (h uiHandler) servePage(templateName string) httprouter.Handle {
	return func(resp http.ResponseWriter, req *http.Request, p httprouter.Params) {
		h.HTML(resp, http.StatusOK, templateName, nil)
	}
}

func (h uiHandler) notFound(resp http.ResponseWriter, req *http.Request) {
	h.showError(resp, http.StatusNotFound, nil)
}

func (h uiHandler) handlePanic(resp http.ResponseWriter, req *http.Request, data interface{}) {
	h.showError(resp, http.StatusInternalServerError, data)
}

func (h uiHandler) showError(resp http.ResponseWriter, status int, data interface{}) {
	h.HTML(resp, status, fmt.Sprintf("status/%d", status), data)
}

type justFilesFileSystem struct {
	fs http.FileSystem
}

func (fs justFilesFileSystem) Open(name string) (http.File, error) {
	name = strings.TrimPrefix(name, "/")

	f, err := fs.fs.Open(name)
	if err != nil {
		return nil, err
	}

	stat, err := f.Stat()
	if err != nil {
		return nil, err
	}

	if stat.IsDir() {
		return nil, os.ErrPermission
	}

	return f, nil
}
<commit_msg>Removed an extraneous newline<commit_after>package main

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

	"github.com/chadweimer/gomp/modules/conf"
	"github.com/chadweimer/gomp/modules/upload"
	"github.com/julienschmidt/httprouter"
	"github.com/unrolled/render"
)

type uiHandler struct {
	cfg   *conf.Config
	uiMux *httprouter.Router
	*render.Render
}

func newUIHandler(cfg *conf.Config, renderer *render.Render) http.Handler {
	h := uiHandler{
		cfg:    cfg,
		Render: renderer,
	}

	h.uiMux = httprouter.New()
	if cfg.IsDevelopment {
		h.uiMux.ServeFiles("/static/*filepath", justFilesFileSystem{http.Dir("static")})
	} else {
		h.uiMux.ServeFiles("/static/*filepath", justFilesFileSystem{http.Dir("static/build/default")})
	}
	if h.cfg.UploadDriver == "fs" {
		h.uiMux.ServeFiles("/uploads/*filepath", justFilesFileSystem{http.Dir(h.cfg.UploadPath)})
	} else if h.cfg.UploadDriver == "s3" {
		h.uiMux.GET("/uploads/*filepath", upload.HandleS3Uploads(h.cfg.UploadPath))
	}
	h.uiMux.NotFound = http.HandlerFunc(h.notFound)
	h.uiMux.PanicHandler = h.handlePanic

	return h.uiMux
}

func (h uiHandler) servePage(templateName string) httprouter.Handle {
	return func(resp http.ResponseWriter, req *http.Request, p httprouter.Params) {
		h.HTML(resp, http.StatusOK, templateName, nil)
	}
}

func (h uiHandler) notFound(resp http.ResponseWriter, req *http.Request) {
	h.showError(resp, http.StatusNotFound, nil)
}

func (h uiHandler) handlePanic(resp http.ResponseWriter, req *http.Request, data interface{}) {
	h.showError(resp, http.StatusInternalServerError, data)
}

func (h uiHandler) showError(resp http.ResponseWriter, status int, data interface{}) {
	h.HTML(resp, status, fmt.Sprintf("status/%d", status), data)
}

type justFilesFileSystem struct {
	fs http.FileSystem
}

func (fs justFilesFileSystem) Open(name string) (http.File, error) {
	name = strings.TrimPrefix(name, "/")

	f, err := fs.fs.Open(name)
	if err != nil {
		return nil, err
	}

	stat, err := f.Stat()
	if err != nil {
		return nil, err
	}

	if stat.IsDir() {
		return nil, os.ErrPermission
	}

	return f, nil
}
<|endoftext|>


<commit_before>package test_helpers

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"strings"

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

func CompareExpectedToGeneratedPo(expectedFilePath string, generatedFilePath string) {
	expectedTranslation := ReadPo(expectedFilePath)
	generatedTranslation := ReadPo(generatedFilePath)

	Ω(reflect.DeepEqual(expectedTranslation, generatedTranslation)).Should(BeTrue())
}

func CompareExpectedToGeneratedTraslationJson(expectedFilePath string, generatedFilePath string) {
	expectedTranslation := ReadJson(expectedFilePath)
	generatedTranslation := ReadJson(generatedFilePath)

	Ω(reflect.DeepEqual(expectedTranslation, generatedTranslation)).Should(BeTrue())
}

func CompareExpectedToGeneratedExtendedJson(expectedFilePath string, generatedFilePath string) {
	expectedTranslation := ReadJsonExtended(expectedFilePath)
	generatedTranslation := ReadJsonExtended(generatedFilePath)

	Ω(reflect.DeepEqual(expectedTranslation, generatedTranslation)).Should(BeTrue(), fmt.Sprintf("expected extracted json %s to exactly match %s", expectedFilePath, generatedFilePath))
}

func GetFilePath(input_dir string, fileName string) string {
	return filepath.Join(os.Getenv("PWD"), input_dir, fileName)
}

func RemoveAllFiles(args ...string) {
	for _, arg := range args {
		os.Remove(arg)
	}
}

func Runi18n(args ...string) *Session {
	session := RunCommand(gi18nExec, args...)
	return session
}

func RunCommand(cmd string, args ...string) *Session {
	command := exec.Command(cmd, args...)
	session, err := Start(command, GinkgoWriter, GinkgoWriter)
	Ω(err).ShouldNot(HaveOccurred())
	session.Wait()
	return session
}

func ReadPo(fileName string) map[string]string {
	file, _ := os.Open(fileName)
	r := bufio.NewReader(file)

	myMap := make(map[string]string)
	for rawLine, _, err := r.ReadLine(); err != io.EOF; rawLine, _, err = r.ReadLine() {
		if err != nil {
			Fail(fmt.Sprintf("Error: %v", err))
		}

		line := string(rawLine)
		if strings.HasPrefix(line, "msgid") {
			rawLine, _, err = r.ReadLine()
			if err != nil {
				Fail(fmt.Sprintf("Error: %v", err))
			}

			myMap[line] = string(rawLine)
		}
	}

	return myMap
}

func ReadJson(fileName string) map[string]string {
	fileByte, err := ioutil.ReadFile(fileName)
	if err != nil {
		Fail("Cannot open json file:" + fileName)
	}

	var b interface{}

	if err := json.Unmarshal(fileByte, &b); err != nil {
		Fail(fmt.Sprintf("Cannot unmarshal: %v", err))
	}

	myMap := make(map[string]string)

	for _, value := range b.([]interface{}) {
		valueMap := value.(map[string]interface{})
		myMap[valueMap["id"].(string)] = valueMap["translation"].(string)
	}

	return myMap
}

func ReadJsonExtended(fileName string) map[string]map[string]string {
	fileByte, err := ioutil.ReadFile(fileName)
	if err != nil {
		Fail("Cannot open json file:" + fileName)
	}

	var b interface{}

	if err := json.Unmarshal(fileByte, &b); err != nil {
		Fail(fmt.Sprintf("Cannot unmarshal: %v", err))
	}

	myMap := make(map[string]map[string]string)

	for _, value := range b.([]interface{}) {
		valueMap := value.(map[string]interface{})

		dataMap := make(map[string]string)

		for key, val := range valueMap {
			switch val.(type) {
			case string:
				dataMap[key] = val.(string)
			case float64:
				dataMap[key] = fmt.Sprintf("%v", int(val.(float64)))
			default:
				fmt.Println("We did something wrong", key)
			}
		}

		myMap[valueMap["value"].(string)] = dataMap
	}

	return myMap
}

func CopyFile(srcFile, destFile string) {
	content, err := ioutil.ReadFile(srcFile)
	Ω(err).ShouldNot(HaveOccurred())
	err = ioutil.WriteFile(destFile, content, 0644)
	Ω(err).ShouldNot(HaveOccurred())
}

func CompareExpectedOutputToGeneratedOutput(expectedOutputFile, generatedOutputFile string) {
	bytes, err := ioutil.ReadFile(expectedOutputFile)
	Ω(err).ShouldNot(HaveOccurred())

	expectedOutput := string(bytes)

	bytes, err = ioutil.ReadFile(generatedOutputFile)
	Ω(err).ShouldNot(HaveOccurred())

	actualOutput := string(bytes)
	Ω(actualOutput).Should(Equal(expectedOutput))
}
<commit_msg>Test helper will output diff if two json files are different.<commit_after>package test_helpers

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"strings"

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

func CompareExpectedToGeneratedPo(expectedFilePath string, generatedFilePath string) {
	expectedTranslation := ReadPo(expectedFilePath)
	generatedTranslation := ReadPo(generatedFilePath)

	Ω(reflect.DeepEqual(expectedTranslation, generatedTranslation)).Should(BeTrue())
}

func CompareExpectedToGeneratedTraslationJson(expectedFilePath string, generatedFilePath string) {
	expectedTranslation := ReadJson(expectedFilePath)
	generatedTranslation := ReadJson(generatedFilePath)

	jsonExpected, _ := json.Marshal(expectedTranslation)
	jsonGenerated, _ := json.Marshal(generatedTranslation)

	Ω(reflect.DeepEqual(expectedTranslation, generatedTranslation)).Should(BeTrue(),
		fmt.Sprintf("Expected\n%v\nto equal\n%v\n", string(jsonGenerated), string(jsonExpected)))
}

func CompareExpectedToGeneratedExtendedJson(expectedFilePath string, generatedFilePath string) {
	expectedTranslation := ReadJsonExtended(expectedFilePath)
	generatedTranslation := ReadJsonExtended(generatedFilePath)

	Ω(reflect.DeepEqual(expectedTranslation, generatedTranslation)).Should(BeTrue(), fmt.Sprintf("expected extracted json %s to exactly match %s", expectedFilePath, generatedFilePath))
}

func GetFilePath(input_dir string, fileName string) string {
	return filepath.Join(os.Getenv("PWD"), input_dir, fileName)
}

func RemoveAllFiles(args ...string) {
	for _, arg := range args {
		os.Remove(arg)
	}
}

func Runi18n(args ...string) *Session {
	session := RunCommand(gi18nExec, args...)
	return session
}

func RunCommand(cmd string, args ...string) *Session {
	command := exec.Command(cmd, args...)
	session, err := Start(command, GinkgoWriter, GinkgoWriter)
	Ω(err).ShouldNot(HaveOccurred())
	session.Wait()
	return session
}

func ReadPo(fileName string) map[string]string {
	file, _ := os.Open(fileName)
	r := bufio.NewReader(file)

	myMap := make(map[string]string)
	for rawLine, _, err := r.ReadLine(); err != io.EOF; rawLine, _, err = r.ReadLine() {
		if err != nil {
			Fail(fmt.Sprintf("Error: %v", err))
		}

		line := string(rawLine)
		if strings.HasPrefix(line, "msgid") {
			rawLine, _, err = r.ReadLine()
			if err != nil {
				Fail(fmt.Sprintf("Error: %v", err))
			}

			myMap[line] = string(rawLine)
		}
	}

	return myMap
}

func ReadJson(fileName string) map[string]string {
	fileByte, err := ioutil.ReadFile(fileName)
	if err != nil {
		Fail("Cannot open json file:" + fileName)
	}

	var b interface{}

	if err := json.Unmarshal(fileByte, &b); err != nil {
		Fail(fmt.Sprintf("Cannot unmarshal: %v", err))
	}

	myMap := make(map[string]string)

	for _, value := range b.([]interface{}) {
		valueMap := value.(map[string]interface{})
		myMap[valueMap["id"].(string)] = valueMap["translation"].(string)
	}

	return myMap
}

func ReadJsonExtended(fileName string) map[string]map[string]string {
	fileByte, err := ioutil.ReadFile(fileName)
	if err != nil {
		Fail("Cannot open json file:" + fileName)
	}

	var b interface{}

	if err := json.Unmarshal(fileByte, &b); err != nil {
		Fail(fmt.Sprintf("Cannot unmarshal: %v", err))
	}

	myMap := make(map[string]map[string]string)

	for _, value := range b.([]interface{}) {
		valueMap := value.(map[string]interface{})

		dataMap := make(map[string]string)

		for key, val := range valueMap {
			switch val.(type) {
			case string:
				dataMap[key] = val.(string)
			case float64:
				dataMap[key] = fmt.Sprintf("%v", int(val.(float64)))
			default:
				fmt.Println("We did something wrong", key)
			}
		}

		myMap[valueMap["value"].(string)] = dataMap
	}

	return myMap
}

func CopyFile(srcFile, destFile string) {
	content, err := ioutil.ReadFile(srcFile)
	Ω(err).ShouldNot(HaveOccurred())
	err = ioutil.WriteFile(destFile, content, 0644)
	Ω(err).ShouldNot(HaveOccurred())
}

func CompareExpectedOutputToGeneratedOutput(expectedOutputFile, generatedOutputFile string) {
	bytes, err := ioutil.ReadFile(expectedOutputFile)
	Ω(err).ShouldNot(HaveOccurred())

	expectedOutput := string(bytes)

	bytes, err = ioutil.ReadFile(generatedOutputFile)
	Ω(err).ShouldNot(HaveOccurred())

	actualOutput := string(bytes)
	Ω(actualOutput).Should(Equal(expectedOutput))
}
<|endoftext|>