<commit_before>package firmata

import "net"

// TCPAdaptor represents a TCP based connection to a microcontroller running
// WiFiFirmata
type TCPAdaptor struct {
	*Adaptor
}

// NewTCPAdaptor opens and uses a TCP connection to a microcontroller running
// WiFiFirmata
func NewTCPAdaptor(args ...interface{}) *TCPAdaptor {
	conn, err := net.Dial("tcp", args[0].(string))
	if err != nil {
		// TODO: handle error
	}

	a := NewAdaptor(conn)
	a.SetName("TCPFirmata")

	return &TCPAdaptor{
		Adaptor: a,
	}
}
<commit_msg>firmata: display address in log for TCPFirmata connections<commit_after>package firmata

import "net"

// TCPAdaptor represents a TCP based connection to a microcontroller running
// WiFiFirmata
type TCPAdaptor struct {
	*Adaptor
}

// NewTCPAdaptor opens and uses a TCP connection to a microcontroller running
// WiFiFirmata
func NewTCPAdaptor(args ...interface{}) *TCPAdaptor {
	address := args[0].(string)
	conn, err := net.Dial("tcp", address)
	if err != nil {
		// TODO: handle error
	}

	a := NewAdaptor(conn, address)
	a.SetName("TCPFirmata")

	return &TCPAdaptor{
		Adaptor: a,
	}
}
<|endoftext|>


<commit_before>package plugin

import (
	"zvr/server"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"zvr/utils"
	"strings"
)

const (
	CREATE_PORT_FORWARDING_PATH = "/createportforwarding"
	REVOKE_PORT_FORWARDING_PATH = "/revokeportforwarding"
	SYNC_PORT_FORWARDING_PATH = "/syncportforwarding"
)

type dnatInfo struct {
	VipPortStart int `json:"vipPortStart"`
	VipPortEnd int `json:"vipPortEnd"`
	PrivatePortStart int `json:"privatePortStart"`
	PrivatePortEnd int `json:"privatePortEnd"`
	ProtocolType string `json:"protocolType"`
	VipIp string `json:"vipIp"`
	PrivateIp string `json:"privateIp"`
	PrivateMac string `json:"privateMac"`
	AllowedCidr string `json:"allowedCidr"`
	SnatInboundTraffic bool `json:"snatInboundTraffic"`
}

type setDnatCmd struct {
	Rules []dnatInfo `json:"rules"`
}

type removeDnatCmd struct {
	Rules []dnatInfo `json:"rules"`
}

type syncDnatCmd struct {
	Rules []dnatInfo `json:"rules"`
}

func syncDnatHandler(ctx *server.CommandContext) interface{} {
	cmd := &syncDnatCmd{}
	ctx.GetCommand(cmd)

	tree := server.NewParserFromShowConfiguration().Tree
	tree.Delete("nat destination")
	setRuleInTree(tree, cmd.Rules)
	tree.Apply(false)
	return nil
}

func getRule(tree *server.VyosConfigTree, description string) *server.VyosConfigNode {
	rs := tree.Get("nat destination rule")
	if rs == nil {
		return nil
	}

	for _, r := range rs.Children() {
		if des := r.Get("description"); des != nil && des.Value() == description {
			return r
		}
	}

	return nil
}

func makeDnatDescription(r dnatInfo) string {
	return fmt.Sprintf("%v-%v-%v-%v-%v-%v-%v", r.VipIp, r.VipPortStart, r.VipPortEnd, r.PrivateMac, r.PrivatePortStart, r.PrivatePortEnd, r.ProtocolType)
}

func setRuleInTree(tree *server.VyosConfigTree, rules []dnatInfo) {
	for _, r := range rules {
		des := makeDnatDescription(r)
		if currentRule := getRule(tree, des); currentRule != nil {
			log.Debugf("dnat rule %s exists, skip it", des)
			continue
		}

		var sport string
		if r.VipPortStart == r.VipPortEnd {
			sport = fmt.Sprintf("%v", r.VipPortStart)
		} else {
			sport = fmt.Sprintf("%v-%v", r.VipPortStart, r.VipPortEnd)
		}
		var dport string
		if r.PrivatePortStart == r.PrivatePortEnd {
			dport = fmt.Sprintf("%v", r.PrivatePortStart)
		} else {
			dport = fmt.Sprintf("%v-%v", r.PrivatePortStart, r.PrivatePortEnd)
		}

		pubNicName, err := utils.GetNicNameByIp(r.VipIp); utils.PanicOnError(err)

		tree.SetDnat(
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("destination address %v", r.VipIp),
			fmt.Sprintf("destination port %v", sport),
			fmt.Sprintf("inbound-interface %v", pubNicName),
			fmt.Sprintf("protocol %v", strings.ToLower(r.ProtocolType)),
			fmt.Sprintf("translation address %v", r.PrivateIp),
			fmt.Sprintf("translation port %v", dport),
		)

		if fr := tree.FindFirewallRuleByDescription(pubNicName, "in", des); fr == nil {
			tree.SetFirewallOnInterface(pubNicName, "in",
				"action accept",
				fmt.Sprintf("description %v", des),
				fmt.Sprintf("destination address %v", r.PrivateIp),
				fmt.Sprintf("destination port %v", dport),
				fmt.Sprintf("protocol %s", strings.ToLower(r.ProtocolType)),
				"state new enable",
			)
		}

		tree.AttachFirewallToInterface(pubNicName, "in")
	}
}

func setDnatHandler(ctx *server.CommandContext) interface{} {
	cmd := &setDnatCmd{}
	ctx.GetCommand(cmd)

	tree := server.NewParserFromShowConfiguration().Tree
	setRuleInTree(tree, cmd.Rules)
	tree.Apply(false)

	return nil
}

func removeDnatHandler(ctx *server.CommandContext) interface{} {
	cmd := &removeDnatCmd{}
	ctx.GetCommand(cmd)

	tree := server.NewParserFromShowConfiguration().Tree
	for _, r := range cmd.Rules {
		des := makeDnatDescription(r)
		if c := getRule(tree, des); c != nil {
			c.Delete()
		}

		pubNicName, err := utils.GetNicNameByIp(r.VipIp); utils.PanicOnError(err)
		if fr := tree.FindFirewallRuleByDescription(pubNicName, "in", des); fr != nil {
			fr.Delete()
		}
	}
	tree.Apply(false)

	return nil
}

func DnatEntryPoint() {
	server.RegisterAsyncCommandHandler(CREATE_PORT_FORWARDING_PATH, server.VyosLock(setDnatHandler))
	server.RegisterAsyncCommandHandler(REVOKE_PORT_FORWARDING_PATH, server.VyosLock(removeDnatHandler))
	server.RegisterAsyncCommandHandler(SYNC_PORT_FORWARDING_PATH, server.VyosLock(syncDnatHandler))
}
<commit_msg>add allowCidr for dnat<commit_after>package plugin

import (
	"zvr/server"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"zvr/utils"
	"strings"
)

const (
	CREATE_PORT_FORWARDING_PATH = "/createportforwarding"
	REVOKE_PORT_FORWARDING_PATH = "/revokeportforwarding"
	SYNC_PORT_FORWARDING_PATH = "/syncportforwarding"
)

type dnatInfo struct {
	VipPortStart int `json:"vipPortStart"`
	VipPortEnd int `json:"vipPortEnd"`
	PrivatePortStart int `json:"privatePortStart"`
	PrivatePortEnd int `json:"privatePortEnd"`
	ProtocolType string `json:"protocolType"`
	VipIp string `json:"vipIp"`
	PrivateIp string `json:"privateIp"`
	PrivateMac string `json:"privateMac"`
	AllowedCidr string `json:"allowedCidr"`
	SnatInboundTraffic bool `json:"snatInboundTraffic"`
}

type setDnatCmd struct {
	Rules []dnatInfo `json:"rules"`
}

type removeDnatCmd struct {
	Rules []dnatInfo `json:"rules"`
}

type syncDnatCmd struct {
	Rules []dnatInfo `json:"rules"`
}

func syncDnatHandler(ctx *server.CommandContext) interface{} {
	cmd := &syncDnatCmd{}
	ctx.GetCommand(cmd)

	tree := server.NewParserFromShowConfiguration().Tree
	tree.Delete("nat destination")
	setRuleInTree(tree, cmd.Rules)
	tree.Apply(false)
	return nil
}

func getRule(tree *server.VyosConfigTree, description string) *server.VyosConfigNode {
	rs := tree.Get("nat destination rule")
	if rs == nil {
		return nil
	}

	for _, r := range rs.Children() {
		if des := r.Get("description"); des != nil && des.Value() == description {
			return r
		}
	}

	return nil
}

func makeDnatDescription(r dnatInfo) string {
	return fmt.Sprintf("%v-%v-%v-%v-%v-%v-%v", r.VipIp, r.VipPortStart, r.VipPortEnd, r.PrivateMac, r.PrivatePortStart, r.PrivatePortEnd, r.ProtocolType)
}

func setRuleInTree(tree *server.VyosConfigTree, rules []dnatInfo) {
	for _, r := range rules {
		des := makeDnatDescription(r)
		if currentRule := getRule(tree, des); currentRule != nil {
			log.Debugf("dnat rule %s exists, skip it", des)
			continue
		}

		var sport string
		if r.VipPortStart == r.VipPortEnd {
			sport = fmt.Sprintf("%v", r.VipPortStart)
		} else {
			sport = fmt.Sprintf("%v-%v", r.VipPortStart, r.VipPortEnd)
		}
		var dport string
		if r.PrivatePortStart == r.PrivatePortEnd {
			dport = fmt.Sprintf("%v", r.PrivatePortStart)
		} else {
			dport = fmt.Sprintf("%v-%v", r.PrivatePortStart, r.PrivatePortEnd)
		}

		pubNicName, err := utils.GetNicNameByIp(r.VipIp); utils.PanicOnError(err)

		tree.SetDnat(
			fmt.Sprintf("description %v", des),
			fmt.Sprintf("destination address %v", r.VipIp),
			fmt.Sprintf("destination port %v", sport),
			fmt.Sprintf("inbound-interface %v", pubNicName),
			fmt.Sprintf("protocol %v", strings.ToLower(r.ProtocolType)),
			fmt.Sprintf("translation address %v", r.PrivateIp),
			fmt.Sprintf("translation port %v", dport),
		)

		if fr := tree.FindFirewallRuleByDescription(pubNicName, "in", des); fr == nil {
			if r.AllowedCidr != "" && r.AllowedCidr != "0.0.0.0/0" {
				tree.SetFirewallOnInterface(pubNicName, "in",
					"action accept",
					fmt.Sprintf("source address %v", r.AllowedCidr),
					fmt.Sprintf("description %v", des),
					fmt.Sprintf("destination address %v", r.PrivateIp),
					fmt.Sprintf("destination port %v", dport),
					fmt.Sprintf("protocol %s", strings.ToLower(r.ProtocolType)),
					"state new enable",
				)
			} else {
				tree.SetFirewallOnInterface(pubNicName, "in",
					"action accept",
					fmt.Sprintf("description %v", des),
					fmt.Sprintf("destination address %v", r.PrivateIp),
					fmt.Sprintf("destination port %v", dport),
					fmt.Sprintf("protocol %s", strings.ToLower(r.ProtocolType)),
					"state new enable",
				)
			}
		}

		tree.AttachFirewallToInterface(pubNicName, "in")
	}
}

func setDnatHandler(ctx *server.CommandContext) interface{} {
	cmd := &setDnatCmd{}
	ctx.GetCommand(cmd)

	tree := server.NewParserFromShowConfiguration().Tree
	setRuleInTree(tree, cmd.Rules)
	tree.Apply(false)

	return nil
}

func removeDnatHandler(ctx *server.CommandContext) interface{} {
	cmd := &removeDnatCmd{}
	ctx.GetCommand(cmd)

	tree := server.NewParserFromShowConfiguration().Tree
	for _, r := range cmd.Rules {
		des := makeDnatDescription(r)
		if c := getRule(tree, des); c != nil {
			c.Delete()
		}

		pubNicName, err := utils.GetNicNameByIp(r.VipIp); utils.PanicOnError(err)
		if fr := tree.FindFirewallRuleByDescription(pubNicName, "in", des); fr != nil {
			fr.Delete()
		}
	}
	tree.Apply(false)

	return nil
}

func DnatEntryPoint() {
	server.RegisterAsyncCommandHandler(CREATE_PORT_FORWARDING_PATH, server.VyosLock(setDnatHandler))
	server.RegisterAsyncCommandHandler(REVOKE_PORT_FORWARDING_PATH, server.VyosLock(removeDnatHandler))
	server.RegisterAsyncCommandHandler(SYNC_PORT_FORWARDING_PATH, server.VyosLock(syncDnatHandler))
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

// Package fake provides a fake client interface to arbitrary Kubernetes
// APIs that exposes common high level operations and exposes common
// metadata.
package fake

import (
	"context"

	autoscalingapi "k8s.io/api/autoscaling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/scale"
	"k8s.io/client-go/testing"
)

// FakeScaleClient provides a fake implementation of scale.ScalesGetter.
type FakeScaleClient struct {
	testing.Fake
}

func (f *FakeScaleClient) Scales(namespace string) scale.ScaleInterface {
	return &fakeNamespacedScaleClient{
		namespace: namespace,
		fake:      &f.Fake,
	}
}

type fakeNamespacedScaleClient struct {
	namespace string
	fake      *testing.Fake
}

func (f *fakeNamespacedScaleClient) Get(ctx context.Context, resource schema.GroupResource, name string, opts metav1.GetOptions) (*autoscalingapi.Scale, error) {
	obj, err := f.fake.
		Invokes(testing.NewGetSubresourceAction(resource.WithVersion(""), f.namespace, "scale", name), &autoscalingapi.Scale{})

	if err != nil {
		return nil, err
	}

	return obj.(*autoscalingapi.Scale), err
}

func (f *fakeNamespacedScaleClient) Update(ctx context.Context, resource schema.GroupResource, scale *autoscalingapi.Scale, opts metav1.UpdateOptions) (*autoscalingapi.Scale, error) {
	obj, err := f.fake.
		Invokes(testing.NewUpdateSubresourceAction(resource.WithVersion(""), f.namespace, "scale", scale), &autoscalingapi.Scale{})

	if err != nil {
		return nil, err
	}

	return obj.(*autoscalingapi.Scale), err
}

func (f *fakeNamespacedScaleClient) Patch(ctx context.Context, gvr schema.GroupVersionResource, name string, pt types.PatchType, patch []byte, opts metav1.PatchOptions) (*autoscalingapi.Scale, error) {
	obj, err := f.fake.
		Invokes(testing.NewPatchSubresourceAction(gvr, f.namespace, name, pt, patch, "scale"), &autoscalingapi.Scale{})

	if err != nil {
		return nil, err
	}

	return obj.(*autoscalingapi.Scale), err
}
<commit_msg>[client-go] Fix argument ordering for fake scale client update<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

// Package fake provides a fake client interface to arbitrary Kubernetes
// APIs that exposes common high level operations and exposes common
// metadata.
package fake

import (
	"context"

	autoscalingapi "k8s.io/api/autoscaling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/scale"
	"k8s.io/client-go/testing"
)

// FakeScaleClient provides a fake implementation of scale.ScalesGetter.
type FakeScaleClient struct {
	testing.Fake
}

func (f *FakeScaleClient) Scales(namespace string) scale.ScaleInterface {
	return &fakeNamespacedScaleClient{
		namespace: namespace,
		fake:      &f.Fake,
	}
}

type fakeNamespacedScaleClient struct {
	namespace string
	fake      *testing.Fake
}

func (f *fakeNamespacedScaleClient) Get(ctx context.Context, resource schema.GroupResource, name string, opts metav1.GetOptions) (*autoscalingapi.Scale, error) {
	obj, err := f.fake.
		Invokes(testing.NewGetSubresourceAction(resource.WithVersion(""), f.namespace, "scale", name), &autoscalingapi.Scale{})

	if err != nil {
		return nil, err
	}

	return obj.(*autoscalingapi.Scale), err
}

func (f *fakeNamespacedScaleClient) Update(ctx context.Context, resource schema.GroupResource, scale *autoscalingapi.Scale, opts metav1.UpdateOptions) (*autoscalingapi.Scale, error) {
	obj, err := f.fake.
		Invokes(testing.NewUpdateSubresourceAction(resource.WithVersion(""), "scale", f.namespace, scale), &autoscalingapi.Scale{})

	if err != nil {
		return nil, err
	}

	return obj.(*autoscalingapi.Scale), err
}

func (f *fakeNamespacedScaleClient) Patch(ctx context.Context, gvr schema.GroupVersionResource, name string, pt types.PatchType, patch []byte, opts metav1.PatchOptions) (*autoscalingapi.Scale, error) {
	obj, err := f.fake.
		Invokes(testing.NewPatchSubresourceAction(gvr, f.namespace, name, pt, patch, "scale"), &autoscalingapi.Scale{})

	if err != nil {
		return nil, err
	}

	return obj.(*autoscalingapi.Scale), err
}
<|endoftext|>


<commit_before>package schedule

import (
	"github.com/PuerkitoBio/goquery"
	"fmt"
	"strings"
	"strconv"
)

const scheduleURL = "http://web.mh-xr.jp/schedule/index"
const eventScheduleBlockClassName = "label-main"
const dateBlockClassName = "label-wood"
const eventBlockClassName = "bg-paper text-center"
const eventNowBlockClassName = "bg-paper text-center now"

type EventSchedule struct {
	// 日付文字列
	// ex)  "2016/10/13 (木)"
	dateString string

	// イベントリスト
	events []Event
}

type Event struct {
	// イベントバナーURL
	// ex)  "//dl.mh-xr.jp/web/image/banner/asev/32ed71d517ff03f067cd2c8300ae7324632b948bc2b8c801dc3882917de8e5f3.png?1475041224"
	imageURL string

	// 開催時間リスト
	// 各時間は「00:00 〜 24:00」の形式
	openTimeRanges []string

	// 24時間タイムテーブル
	// 開催時間ならtrue
	timeTable [24]bool
}

func GetEventSchedule() (eventSchedules []EventSchedule, e error) {
	// 戻り値の初期化
	e = nil

	// ページの取得
	doc, err := goquery.NewDocument(scheduleURL)
	if err != nil {
		e = fmt.Errorf("%s is not found.", scheduleURL)
	}

	// #schedule要素の取得。この下に目的のデータがある。
	scheduleBlock := doc.Find("#schedule")
	if scheduleBlock.Length() == 0 {
		// #schedule ブロックが見つからない
		e = fmt.Errorf("#schedule not found.")
	}

	// 各日付とイベント群のスクレイピング
	// 日付のdivは class="label-wood"、イベントのdivは class="bg-paper text-center" が設定されているので、これを利用。
	// ※ class="bg-paper text-center now" は、当日のイベント（開催時間以外のイベントも含む）。
	// 日付と各イベントは同じ階層のブロック。
	// 日付ブロックの後ろにイベントブロックがn個続く形。新たに日付ブロックが出現したら、その下は次の日のイベントとなる。
	eventSchedule := EventSchedule{}
	scheduleBlock.Children().Each(func(_ int, s *goquery.Selection) {
		class, _ := s.Attr("class")

		// 日付取得
		switch class {
			case eventScheduleBlockClassName: // 「イベントスケジュール」バナー
				// 何もしない
			case dateBlockClassName: // 日付ブロック
				// 新しい日付が出現したらEventScheduleを新しく作成
				if (eventSchedule.dateString != "") {
					eventSchedules = append(eventSchedules, eventSchedule)
				}
				eventSchedule = EventSchedule{}

				// 日付のセット
				eventSchedule.dateString = s.Text()
			case eventBlockClassName: // イベントブロック
				fallthrough
			case eventNowBlockClassName: // 当日のイベントブロック
				event := Event{}

				// 開催時間の各ブロックは
の中にある
				innerBlock := s.Children()
				if !innerBlock.HasClass("inner") {
					//
が無い
					e = fmt.Errorf("No event block inner.")
				}

				// イベントバナーの取得
				img := innerBlock.ChildrenFiltered("img")
				if img.Length() == 0 {
					// イベントバナーが無い
					e = fmt.Errorf("No event image.")
				}
				event.imageURL, _ = img.Attr("src")

				// イベント開催時間の取得
				// 各開催時間ブロックは、class="bg-text-time margin-m font-red relative"が設定されているので、これを利用。
				timeBlocks := innerBlock.Find("div[class='bg-text-time margin-m font-red relative']")
				if timeBlocks.Length() == 0 {
					// 開催時間が１つもない
					e = fmt.Errorf("holding hours dose not found.")
				}
				openTimeRanges := []string{}
				timeBlocks.Each(func(_ int, s *goquery.Selection) {
					openTimeRange := s.Text()
					openTimeRanges = append(openTimeRanges, openTimeRange)
				})
				event.openTimeRanges = openTimeRanges
				// 開催時間から24時間タイムテーブルを作成
				event.timeTable = setTimeTable(event.openTimeRanges)

				eventSchedule.events = append(eventSchedule.events, event)
			default:
				// 想定外のclassが出現した
				e = fmt.Errorf("appeared class unexpected : %s", class)
		}
	})

	return
}

func setTimeTable(openTimeRanges []string) (timeTable [24]bool) {
	for _, openTimeRange := range openTimeRanges {
		times := strings.Split(openTimeRange, " 〜 ")
		startTime := times[0] // ex) "00:00"
		endTime := times[1] // ex) "24:00"
		startHours, _ := strconv.Atoi(strings.Split(startTime, ":")[0])
		endHours, _ := strconv.Atoi(strings.Split(endTime, ":")[0])
		for i := startHours; i < endHours; i++ {
			timeTable[i] = true
		}
	}
	return
}

func CreateHTML() (string, error) {
	eventSchedules, err := GetEventSchedule()
	if err != nil {
		return "", err
	}

	timeTables := [][24][]string{}
	for _, eventSchedule := range eventSchedules {
		timeTable := [24][]string{}
		//for i := 0; i < 24; i++ {
			for _, event := range eventSchedule.events {
				for i, flag := range event.timeTable {
					if (flag) {
						timeTable[i] = append(timeTable[i], event.imageURL)
					}
				}
			}
		//}
		timeTables = append(timeTables, timeTable)
	}

	html := ""
	for j, timeTable := range timeTables {
		html += "
" + eventSchedules[j].dateString + "
"
		html += "
"
		for i, times := range timeTable {
			html += "
"
			html += "" + strconv.Itoa(i) + "	"
			for _, imgOrEmpty := range times {
				html += ""
				if (imgOrEmpty != "") {
					html += ""
				}
				html += "	"
			}
			html += "
"
		}
		html += "
"
	}

	return html, nil
}

func CreateHorizonHTML() (string, error) {
	eventSchedules, err := GetEventSchedule()
	if err != nil {
		return "", err
	}

	html := ""
	indent := "    "
	for _, eventSchedule := range eventSchedules {
		html += "
\n"
		html += indent + "
\n"
		for i := 0; i < 24; i++ {
			html += indent + indent + "" + strconv.Itoa(i) + "	\n"
		}
		html += indent + "
\n"
		for _, event := range eventSchedule.events {
			html += indent + "
\n"
			for _, flag := range event.timeTable {
				html += indent + indent + "\n"
				if (flag) {
					html += indent + indent + indent +"\n"
				}
				html += indent + indent + "	\n"
			}
			html += indent + "
\n"
		}
		html += "
\n"
	}

	return html, nil
}
<commit_msg>横表示機能は削除。<commit_after>package schedule

import (
	"github.com/PuerkitoBio/goquery"
	"fmt"
	"strings"
	"strconv"
)

const scheduleURL = "http://web.mh-xr.jp/schedule/index"
const eventScheduleBlockClassName = "label-main"
const dateBlockClassName = "label-wood"
const eventBlockClassName = "bg-paper text-center"
const eventNowBlockClassName = "bg-paper text-center now"

type EventSchedule struct {
	// 日付文字列
	// ex)  "2016/10/13 (木)"
	dateString string

	// イベントリスト
	events []Event
}

type Event struct {
	// イベントバナーURL
	// ex)  "//dl.mh-xr.jp/web/image/banner/asev/32ed71d517ff03f067cd2c8300ae7324632b948bc2b8c801dc3882917de8e5f3.png?1475041224"
	imageURL string

	// 開催時間リスト
	// 各時間は「00:00 〜 24:00」の形式
	openTimeRanges []string

	// 24時間タイムテーブル
	// 開催時間ならtrue
	timeTable [24]bool
}

func GetEventSchedule() (eventSchedules []EventSchedule, e error) {
	// 戻り値の初期化
	e = nil

	// ページの取得
	doc, err := goquery.NewDocument(scheduleURL)
	if err != nil {
		e = fmt.Errorf("%s is not found.", scheduleURL)
	}

	// #schedule要素の取得。この下に目的のデータがある。
	scheduleBlock := doc.Find("#schedule")
	if scheduleBlock.Length() == 0 {
		// #schedule ブロックが見つからない
		e = fmt.Errorf("#schedule not found.")
	}

	// 各日付とイベント群のスクレイピング
	// 日付のdivは class="label-wood"、イベントのdivは class="bg-paper text-center" が設定されているので、これを利用。
	// ※ class="bg-paper text-center now" は、当日のイベント（開催時間以外のイベントも含む）。
	// 日付と各イベントは同じ階層のブロック。
	// 日付ブロックの後ろにイベントブロックがn個続く形。新たに日付ブロックが出現したら、その下は次の日のイベントとなる。
	eventSchedule := EventSchedule{}
	scheduleBlock.Children().Each(func(_ int, s *goquery.Selection) {
		class, _ := s.Attr("class")

		// 日付取得
		switch class {
			case eventScheduleBlockClassName: // 「イベントスケジュール」バナー
				// 何もしない
			case dateBlockClassName: // 日付ブロック
				// 新しい日付が出現したらEventScheduleを新しく作成
				if (eventSchedule.dateString != "") {
					eventSchedules = append(eventSchedules, eventSchedule)
				}
				eventSchedule = EventSchedule{}

				// 日付のセット
				eventSchedule.dateString = s.Text()
			case eventBlockClassName: // イベントブロック
				fallthrough
			case eventNowBlockClassName: // 当日のイベントブロック
				event := Event{}

				// 開催時間の各ブロックは
の中にある
				innerBlock := s.Children()
				if !innerBlock.HasClass("inner") {
					//
が無い
					e = fmt.Errorf("No event block inner.")
				}

				// イベントバナーの取得
				img := innerBlock.ChildrenFiltered("img")
				if img.Length() == 0 {
					// イベントバナーが無い
					e = fmt.Errorf("No event image.")
				}
				event.imageURL, _ = img.Attr("src")

				// イベント開催時間の取得
				// 各開催時間ブロックは、class="bg-text-time margin-m font-red relative"が設定されているので、これを利用。
				timeBlocks := innerBlock.Find("div[class='bg-text-time margin-m font-red relative']")
				if timeBlocks.Length() == 0 {
					// 開催時間が１つもない
					e = fmt.Errorf("holding hours dose not found.")
				}
				openTimeRanges := []string{}
				timeBlocks.Each(func(_ int, s *goquery.Selection) {
					openTimeRange := s.Text()
					openTimeRanges = append(openTimeRanges, openTimeRange)
				})
				event.openTimeRanges = openTimeRanges
				// 開催時間から24時間タイムテーブルを作成
				event.timeTable = setTimeTable(event.openTimeRanges)

				eventSchedule.events = append(eventSchedule.events, event)
			default:
				// 想定外のclassが出現した
				e = fmt.Errorf("appeared class unexpected : %s", class)
		}
	})

	return
}

func setTimeTable(openTimeRanges []string) (timeTable [24]bool) {
	for _, openTimeRange := range openTimeRanges {
		times := strings.Split(openTimeRange, " 〜 ")
		startTime := times[0] // ex) "00:00"
		endTime := times[1] // ex) "24:00"
		startHours, _ := strconv.Atoi(strings.Split(startTime, ":")[0])
		endHours, _ := strconv.Atoi(strings.Split(endTime, ":")[0])
		for i := startHours; i < endHours; i++ {
			timeTable[i] = true
		}
	}
	return
}

func CreateHTML() (string, error) {
	eventSchedules, err := GetEventSchedule()
	if err != nil {
		return "", err
	}

	timeTables := [][24][]string{}
	for _, eventSchedule := range eventSchedules {
		timeTable := [24][]string{}
		//for i := 0; i < 24; i++ {
			for _, event := range eventSchedule.events {
				for i, flag := range event.timeTable {
					if (flag) {
						timeTable[i] = append(timeTable[i], event.imageURL)
					}
				}
			}
		//}
		timeTables = append(timeTables, timeTable)
	}

	html := ""
	for j, timeTable := range timeTables {
		html += "
" + eventSchedules[j].dateString + "
"
		html += "
"
		for i, times := range timeTable {
			html += "
"
			html += "" + strconv.Itoa(i) + "	"
			for _, imgOrEmpty := range times {
				html += ""
				if (imgOrEmpty != "") {
					html += ""
				}
				html += "	"
			}
			html += "
"
		}
		html += "
"
	}

	return html, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016. All rights reserved.
Use of this source code is governed by a Apache Software
license that can be found in the LICENSE file.
*/

//Package schedule provides requests and response structures to achieve Schedule API actions.
package schedule

// Restrictions defines the structure for each rotation restrictions
type Restriction struct {
	StartDay string `json:"startDay,omitempty"`
	StartTime string `json:"startTime,omitempty"`
	EndDay string `json:"endDay,omitempty"`
	EndTime string `json:"endTime,omitempty"`
}

// Rotation defines the structure for each rotation definition
type Rotation struct {
	StartDate string `json:"startDate,omitempty"`
	EndDate string `json:"endDate,omitempty"`
	RotationType string `json:"rotationType,omitempty"`
	Participants []string `json:"participants,omitempty"`
	Name string `json:"name,omitempty"`
	RotationLength int `json:"rotationLength,omitempty"`
	Restrictions []Restriction `json:"restrictions,omitempty"`
}

// CreateScheduleRequest provides necessary parameter structure for creating Schedule
type CreateScheduleRequest struct {
	APIKey string `json:"apiKey,omitempty"`
	Name   string `json:"name,omitempty"`
	Timezone string `json:"timezone,omitempty"`
	Enabled *bool `json:"enabled,omitempty"`
        Rotations []Rotation `json:"rotations,omitempty"`
}

// UpdateScheduleRequest provides necessary parameter structure for updating an Schedule
type UpdateScheduleRequest struct {
	Id     string `json:"id,omitempty"`
	APIKey string `json:"apiKey,omitempty"`
	Name   string `json:"name,omitempty"`
	Timezone string `json:"timezone,omitempty"`
	Enabled *bool  `json:"enabled,omitempty"`
        Rotations []Rotation `json:"rotations,omitempty"`
}

// DeleteScheduleRequest provides necessary parameter structure for deleting an Schedule
type DeleteScheduleRequest struct {
	APIKey string `url:"apiKey,omitempty"`
	Id     string `url:"id,omitempty"`
        Name   string `url:"name,omitempty"`
}

// GetScheduleRequest provides necessary parameter structure for requesting Schedule information
type GetScheduleRequest struct {
	APIKey string `url:"apiKey,omitempty"`
	Id     string `url:"id,omitempty"`
        Name   string `url:"name,omitempty"`
}

// ListScheduleRequest provides necessary parameter structure for listing Schedules
type ListSchedulesRequest struct {
	APIKey string `url:"apiKey,omitempty"`

// WhoIsOnCallRequest provides necessary parameter structure for requesting who is on call for a specific schedule
type WhoIsOnCallRequest struct {
	APIKey   string `url:"apiKey,omitempty"`
	Id       string `url:"id,omitempty"`
        Name     string `url:"name,omitempty"`
        Timezone string `url:"timezone,omitempty"`
        Time     string `url:"time,omitempty"`
        Flat     string `url:"flat,omitempty"`
}
<commit_msg>fix formatting<commit_after>/*
Copyright 2016. All rights reserved.
Use of this source code is governed by a Apache Software
license that can be found in the LICENSE file.
*/

//Package schedule provides requests and response structures to achieve Schedule API actions.
package schedule

// Restrictions defines the structure for each rotation restrictions
type Restriction struct {
	StartDay string `json:"startDay,omitempty"`
	StartTime string `json:"startTime,omitempty"`
	EndDay string `json:"endDay,omitempty"`
	EndTime string `json:"endTime,omitempty"`
}

// Rotation defines the structure for each rotation definition
type Rotation struct {
	StartDate string `json:"startDate,omitempty"`
	EndDate string `json:"endDate,omitempty"`
	RotationType string `json:"rotationType,omitempty"`
	Participants []string `json:"participants,omitempty"`
	Name string `json:"name,omitempty"`
	RotationLength int `json:"rotationLength,omitempty"`
	Restrictions []Restriction `json:"restrictions,omitempty"`
}

// CreateScheduleRequest provides necessary parameter structure for creating Schedule
type CreateScheduleRequest struct {
	APIKey string `json:"apiKey,omitempty"`
	Name   string `json:"name,omitempty"`
	Timezone string `json:"timezone,omitempty"`
	Enabled *bool `json:"enabled,omitempty"`
        Rotations []Rotation `json:"rotations,omitempty"`
}

// UpdateScheduleRequest provides necessary parameter structure for updating an Schedule
type UpdateScheduleRequest struct {
	Id     string `json:"id,omitempty"`
	APIKey string `json:"apiKey,omitempty"`
	Name   string `json:"name,omitempty"`
	Timezone string `json:"timezone,omitempty"`
	Enabled *bool  `json:"enabled,omitempty"`
        Rotations []Rotation `json:"rotations,omitempty"`
}

// DeleteScheduleRequest provides necessary parameter structure for deleting an Schedule
type DeleteScheduleRequest struct {
	APIKey string `url:"apiKey,omitempty"`
	Id     string `url:"id,omitempty"`
        Name   string `url:"name,omitempty"`
}

// GetScheduleRequest provides necessary parameter structure for requesting Schedule information
type GetScheduleRequest struct {
	APIKey string `url:"apiKey,omitempty"`
	Id     string `url:"id,omitempty"`
        Name   string `url:"name,omitempty"`
}

// ListScheduleRequest provides necessary parameter structure for listing Schedules
type ListSchedulesRequest struct {
	APIKey string `url:"apiKey,omitempty"`

// WhoIsOnCallRequest provides necessary parameter structure for requesting who is on call for a specific schedule
type WhoIsOnCallRequest struct {
	APIKey   string `url:"apiKey,omitempty"`
	Id       string `url:"id,omitempty"`
	Name     string `url:"name,omitempty"`
	Timezone string `url:"timezone,omitempty"`
	Time     string `url:"time,omitempty"`
	Flat     string `url:"flat,omitempty"`
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Mirantis

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

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

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

package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	kubeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

var (
	imageUrl = flag.String("image-url",
		"http://ftp.ps.pl/pub/Linux/fedora-linux/releases/24/CloudImages/x86_64/images/Fedora-Cloud-Base-24-1.2.x86_64.qcow2",
		"Image URL to pull")
	virtletSocket = flag.String("virtlet-socket",
		"/run/virtlet.sock",
		"The unix socket to connect, e.g. /run/virtlet.sock")
)

func dial(socket string, timeout time.Duration) (net.Conn, error) {
	return net.DialTimeout("unix", socket, timeout)
}

func main() {
	flag.Parse()

	conn, err := grpc.Dial(*virtletSocket, grpc.WithInsecure(), grpc.WithDialer(dial))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot connect: %#v", err)
		os.Exit(1)
	}
	defer conn.Close()
	c := kubeapi.NewImageServiceClient(conn)

	imageSpec := &kubeapi.ImageSpec{Image: imageUrl}
	in := &kubeapi.PullImageRequest{
		Image: imageSpec,
		Auth: &kubeapi.AuthConfig{},
		SandboxConfig: &kubeapi.PodSandboxConfig{},
	}

	out, err := c.PullImage(context.Background(), in)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot pull image: %#v", err)
		os.Exit(1)
	}

	fmt.Printf("Got response: %#v\n", out)
}
<commit_msg>test: Fix gofmt issue<commit_after>/*
Copyright 2016 Mirantis

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

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

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

package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	kubeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
)

var (
	imageUrl = flag.String("image-url",
		"http://ftp.ps.pl/pub/Linux/fedora-linux/releases/24/CloudImages/x86_64/images/Fedora-Cloud-Base-24-1.2.x86_64.qcow2",
		"Image URL to pull")
	virtletSocket = flag.String("virtlet-socket",
		"/run/virtlet.sock",
		"The unix socket to connect, e.g. /run/virtlet.sock")
)

func dial(socket string, timeout time.Duration) (net.Conn, error) {
	return net.DialTimeout("unix", socket, timeout)
}

func main() {
	flag.Parse()

	conn, err := grpc.Dial(*virtletSocket, grpc.WithInsecure(), grpc.WithDialer(dial))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot connect: %#v", err)
		os.Exit(1)
	}
	defer conn.Close()
	c := kubeapi.NewImageServiceClient(conn)

	imageSpec := &kubeapi.ImageSpec{Image: imageUrl}
	in := &kubeapi.PullImageRequest{
		Image:         imageSpec,
		Auth:          &kubeapi.AuthConfig{},
		SandboxConfig: &kubeapi.PodSandboxConfig{},
	}

	out, err := c.PullImage(context.Background(), in)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot pull image: %#v", err)
		os.Exit(1)
	}

	fmt.Printf("Got response: %#v\n", out)
}
<|endoftext|>


<commit_before>package lbrelease

import (
	"fmt"

	"github.com/lhcb-org/lbx/lbctx"
)

type GetPack struct {
	ReqPkg     string // requested package
	ReqPkgVers string

	pkgs  map[string][]lbctx.RepoInfo
	projs []string
	repos lbctx.RepoInfos

	sel_repo string // selected repository
	sel_hat  string // selected repository hat

	proj_name string
	proj_vers string

	init bool
}

func (gp *GetPack) setup() error {
	var err error
	if gp.init {
		return err
	}

	err = gp.initRepos(nil, "", "")
	if err != nil {
		return err
	}

	err = gp.initPkgs()
	if err != nil {
		return err
	}

	gp.init = true
	return err
}

func (gp *GetPack) initRepos(excludes []string, user, protocol string) error {
	var err error
	if gp.repos != nil {
		return err
	}

	excl := map[string]struct{}{}
	for _, v := range excludes {
		excl[v] = struct{}{}
	}

	gp.repos = make(lbctx.RepoInfos, 3)

	// prepare repositories urls
	// filter the requested protocols for the known repositories
	for k, v := range lbctx.Repositories(user, protocol) {
		if _, dup := excl[k]; dup {
			continue
		}
		gp.repos[k] = v
	}

	if len(gp.repos) <= 0 {
		return fmt.Errorf("getpack: unable to find a repository for the specified protocol")
	}

	return err
}

func (gp *GetPack) initPkgs() error {
	var err error
	if gp.pkgs != nil {
		return err
	}

	gp.pkgs = make(map[string][]lbctx.RepoInfo)

	for _, repo := range gp.repos {
		for _, p := range repo[0].ListPackages(gp.sel_hat) {
			if _, ok := gp.pkgs[p]; !ok {
				gp.pkgs[p] = make([]lbctx.RepoInfo, 0, 1)
			}
			gp.pkgs[p] = append(gp.pkgs[p], repo[0])
		}
	}
	return err
}

func (gp *GetPack) Run() error {
	var err error
	err = gp.setup()
	if err != nil {
		return err
	}

	return err
}
<commit_msg>lbrelease: implement bare-bone GetPack-checkout<commit_after>package lbrelease

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"strings"

	"github.com/gonuts/toml"
	"github.com/lhcb-org/lbx/lbctx"
	"github.com/lhcb-org/lbx/lbctx/vcs"
)

type GetPack struct {
	ReqPkg     string // requested package
	ReqPkgVers string

	pkgs  lbctx.Packages
	projs []string
	repos lbctx.RepoDb

	sel_repo string // selected repository
	sel_hat  string // selected repository hat

	proj_name string
	proj_vers string

	init bool
}

func (gp *GetPack) setup() error {
	var err error
	if gp.init {
		return err
	}

	err = gp.initRepos(nil, "", "")
	if err != nil {
		return err
	}

	const fname = ".lbx/packages-db.toml"
	if _, err := os.Stat(fname); err == nil {
		return gp.loadPkgs(fname)
	}

	err = gp.initPkgs()
	if err != nil {
		return err
	}

	gp.init = true

	return gp.savePkgs(fname)
}

func (gp *GetPack) initRepos(excludes []string, user, protocol string) error {
	var err error
	if gp.repos != nil {
		return err
	}

	excl := map[string]struct{}{}
	for _, v := range excludes {
		excl[v] = struct{}{}
	}

	gp.repos = make(lbctx.RepoDb, 3)

	// prepare repositories urls
	// filter the requested protocols for the known repositories
	for k, v := range lbctx.Repositories(user, protocol) {
		if _, dup := excl[k]; dup {
			continue
		}
		gp.repos[k] = v
	}

	if len(gp.repos) <= 0 {
		return fmt.Errorf("getpack: unable to find a repository for the specified protocol")
	}

	return err
}

func (gp *GetPack) initPkgs() error {
	var err error
	if gp.pkgs != nil {
		return err
	}

	gp.pkgs = make(lbctx.Packages)

	pkgs := make(chan []lbctx.Package, len(gp.repos))
	for repo := range gp.repos {
		go func(n string) {
			repo := gp.repos[n]
			pkgs <- repo.ListPackages(gp.sel_hat)
		}(repo)
	}

	for _ = range gp.repos {
		ps := <-pkgs
		for _, pkg := range ps {
			gp.pkgs[pkg.Name] = pkg
		}
	}
	return err
}

func (gp *GetPack) Run() error {
	var err error
	err = gp.setup()
	if err != nil {
		return err
	}

	pkg, ok := gp.pkgs[gp.ReqPkg]
	if !ok {
		return fmt.Errorf("lbrelease: no such package [%s]", gp.ReqPkg)
	}

	var url []string
	switch gp.ReqPkgVers {
	case "", "head", "trunk":
		url = []string{pkg.Repo, pkg.Project, "trunk", pkg.Name}
	default:
		url = []string{pkg.Repo, pkg.Project, "tags", pkg.Name, gp.ReqPkgVers}
	}

	var repo *lbctx.RepoInfo
	for _, r := range gp.repos {
		if r[0].Repo == pkg.Repo {
			repo = &r[0]
			break
		}
	}

	bout, err := vcs.Run(repo.Cmd, "checkout {url} ./{dir}", "url", strings.Join(url, "/"), "dir", pkg.Name)
	if err != nil {
		scan := bufio.NewScanner(bytes.NewReader(bout))
		for scan.Scan() {
			fmt.Fprintf(os.Stderr, "%s\n", scan.Text())
		}
		return err
	}
	return err
}

func (gp *GetPack) loadPkgs(fname string) error {
	ctx := struct {
		Packages lbctx.Packages
	}{}
	_, err := toml.DecodeFile(fname, &ctx)
	if err != nil {
		return err
	}
	gp.pkgs = ctx.Packages
	gp.init = true
	return err
}

func (gp *GetPack) savePkgs(fname string) error {
	ctx := struct {
		Packages lbctx.Packages
	}{
		Packages: gp.pkgs,
	}
	f, err := os.Create(fname)
	if err != nil {
		return err
	}
	defer f.Close()

	return toml.NewEncoder(f).Encode(&ctx)
}
<|endoftext|>


<commit_before>package sdk

// Those are icon for hooks
const (
	GitlabIcon    = "Gitlab"
	GitHubIcon    = "Github"
	BitbucketIcon = "Bitbucket"
)

// FilterHooksConfig filter all hooks configuration and remove some configuration key
func (w *Workflow) FilterHooksConfig(s ...string) {
	if w.Root == nil {
		return
	}

	w.Root.FilterHooksConfig(s...)
	for i := range w.Joins {
		for j := range w.Joins[i].Triggers {
			w.Joins[i].Triggers[j].WorkflowDestNode.FilterHooksConfig(s...)
		}
	}
}

// GetHooks returns the list of all hooks in the workflow tree
func (w *Workflow) GetHooks() map[string]WorkflowNodeHook {
	if w.Root == nil {
		return nil
	}

	res := map[string]WorkflowNodeHook{}

	a := w.Root.GetHooks()
	for k, v := range a {
		res[k] = v
	}

	for _, j := range w.Joins {
		for _, t := range j.Triggers {
			b := t.WorkflowDestNode.GetHooks()
			for k, v := range b {
				res[k] = v
			}
		}
	}

	return res
}

//WorkflowNodeHook represents a hook which cann trigger the workflow from a given node
type WorkflowNodeHook struct {
	ID                  int64                  `json:"id" db:"id"`
	UUID                string                 `json:"uuid" db:"uuid"`
	WorkflowNodeID      int64                  `json:"workflow_node_id" db:"workflow_node_id"`
	WorkflowHookModelID int64                  `json:"workflow_hook_model_id" db:"workflow_hook_model_id"`
	WorkflowHookModel   WorkflowHookModel      `json:"model" db:"-"`
	Config              WorkflowNodeHookConfig `json:"config" db:"-"`
}

// WorkflowHookModelBuiltin is a constant for the builtin hook models
const WorkflowHookModelBuiltin = "builtin"

//WorkflowNodeHookConfig represents the configguration for a WorkflowNodeHook
type WorkflowNodeHookConfig map[string]WorkflowNodeHookConfigValue

//Values return values of the WorkflowNodeHookConfig
func (cfg WorkflowNodeHookConfig) Values() map[string]string {
	r := make(map[string]string)
	for k, v := range cfg {
		r[k] = v.Value
	}
	return r
}

// WorkflowNodeHookConfigValue represents the value of a node hook config
type WorkflowNodeHookConfigValue struct {
	Value        string `json:"value"`
	Configurable bool   `json:"configurable"`
}

//WorkflowHookModel represents a hook which can be used in workflows.
type WorkflowHookModel struct {
	ID            int64                  `json:"id" db:"id" cli:"-"`
	Name          string                 `json:"name" db:"name" cli:"name"`
	Type          string                 `json:"type"  db:"type"`
	Author        string                 `json:"author" db:"author"`
	Description   string                 `json:"description" db:"description"`
	Identifier    string                 `json:"identifier" db:"identifier"`
	Icon          string                 `json:"icon" db:"icon"`
	Command       string                 `json:"command" db:"command"`
	DefaultConfig WorkflowNodeHookConfig `json:"default_config" db:"-"`
	Disabled      bool                   `json:"disabled" db:"disabled"`
}

// FilterHooksConfig filter all hooks configuration and remove somme configuration key
func (n *WorkflowNode) FilterHooksConfig(s ...string) {
	if n == nil {
		return
	}

	for _, h := range n.Hooks {
		for i := range s {
			for k := range h.Config {
				if k == s[i] {
					delete(h.Config, k)
					break
				}
			}
		}
	}
}

//GetHooks returns all hooks for the node and its children
func (n *WorkflowNode) GetHooks() map[string]WorkflowNodeHook {
	res := map[string]WorkflowNodeHook{}

	for _, h := range n.Hooks {
		res[h.UUID] = h
	}

	for _, t := range n.Triggers {
		b := t.WorkflowDestNode.GetHooks()
		for k, v := range b {
			res[k] = v
		}
	}

	return res
}
<commit_msg>fix (sdk): don't export workflow hook unconfigurable values (#1942)<commit_after>package sdk

// Those are icon for hooks
const (
	GitlabIcon    = "Gitlab"
	GitHubIcon    = "Github"
	BitbucketIcon = "Bitbucket"
)

// FilterHooksConfig filter all hooks configuration and remove some configuration key
func (w *Workflow) FilterHooksConfig(s ...string) {
	if w.Root == nil {
		return
	}

	w.Root.FilterHooksConfig(s...)
	for i := range w.Joins {
		for j := range w.Joins[i].Triggers {
			w.Joins[i].Triggers[j].WorkflowDestNode.FilterHooksConfig(s...)
		}
	}
}

// GetHooks returns the list of all hooks in the workflow tree
func (w *Workflow) GetHooks() map[string]WorkflowNodeHook {
	if w.Root == nil {
		return nil
	}

	res := map[string]WorkflowNodeHook{}

	a := w.Root.GetHooks()
	for k, v := range a {
		res[k] = v
	}

	for _, j := range w.Joins {
		for _, t := range j.Triggers {
			b := t.WorkflowDestNode.GetHooks()
			for k, v := range b {
				res[k] = v
			}
		}
	}

	return res
}

//WorkflowNodeHook represents a hook which cann trigger the workflow from a given node
type WorkflowNodeHook struct {
	ID                  int64                  `json:"id" db:"id"`
	UUID                string                 `json:"uuid" db:"uuid"`
	WorkflowNodeID      int64                  `json:"workflow_node_id" db:"workflow_node_id"`
	WorkflowHookModelID int64                  `json:"workflow_hook_model_id" db:"workflow_hook_model_id"`
	WorkflowHookModel   WorkflowHookModel      `json:"model" db:"-"`
	Config              WorkflowNodeHookConfig `json:"config" db:"-"`
}

// WorkflowHookModelBuiltin is a constant for the builtin hook models
const WorkflowHookModelBuiltin = "builtin"

//WorkflowNodeHookConfig represents the configguration for a WorkflowNodeHook
type WorkflowNodeHookConfig map[string]WorkflowNodeHookConfigValue

//Values return values of the WorkflowNodeHookConfig
func (cfg WorkflowNodeHookConfig) Values() map[string]string {
	r := make(map[string]string)
	for k, v := range cfg {
		if v.Configurable {
			r[k] = v.Value
		}
	}
	return r
}

// WorkflowNodeHookConfigValue represents the value of a node hook config
type WorkflowNodeHookConfigValue struct {
	Value        string `json:"value"`
	Configurable bool   `json:"configurable"`
}

//WorkflowHookModel represents a hook which can be used in workflows.
type WorkflowHookModel struct {
	ID            int64                  `json:"id" db:"id" cli:"-"`
	Name          string                 `json:"name" db:"name" cli:"name"`
	Type          string                 `json:"type"  db:"type"`
	Author        string                 `json:"author" db:"author"`
	Description   string                 `json:"description" db:"description"`
	Identifier    string                 `json:"identifier" db:"identifier"`
	Icon          string                 `json:"icon" db:"icon"`
	Command       string                 `json:"command" db:"command"`
	DefaultConfig WorkflowNodeHookConfig `json:"default_config" db:"-"`
	Disabled      bool                   `json:"disabled" db:"disabled"`
}

// FilterHooksConfig filter all hooks configuration and remove somme configuration key
func (n *WorkflowNode) FilterHooksConfig(s ...string) {
	if n == nil {
		return
	}

	for _, h := range n.Hooks {
		for i := range s {
			for k := range h.Config {
				if k == s[i] {
					delete(h.Config, k)
					break
				}
			}
		}
	}
}

//GetHooks returns all hooks for the node and its children
func (n *WorkflowNode) GetHooks() map[string]WorkflowNodeHook {
	res := map[string]WorkflowNodeHook{}

	for _, h := range n.Hooks {
		res[h.UUID] = h
	}

	for _, t := range n.Triggers {
		b := t.WorkflowDestNode.GetHooks()
		for k, v := range b {
			res[k] = v
		}
	}

	return res
}
<|endoftext|>


<commit_before>package server

import (
	"math"
	"sync"
)

// RegionArea is the area (in coordinate degrees)
const RegionArea = 1.0

// Client is the representation of chat clients
type Client struct {
	ID string
	Lat,
	Long float64
}

//ClientNetwork is the representation of the Network of all client regions
type ClientNetwork struct {
	root            *clientRegion
	allRegions      []*clientRegion
	latRange        [2]float64
	longRange       [2]float64
	modificationMux *sync.Mutex
}

// AddClient adds a client to the network in the appropriate region
func (c *ClientNetwork) AddClient(client *Client) (connected bool) {
	lat := math.Floor(client.Lat)
	long := math.Floor(client.Long)
	// we want to also track possible connecting regions in the case that
	// we need to add a region. Key as follows:
	// 0: Up
	// 1: Left
	// 2: Down
	// 3: Right
	var possibleRegionConnects = [4]*clientRegion{nil, nil, nil, nil}
	for _, region := range c.allRegions {
		if region.lat == lat && region.long == long {
			region.AddClient(client)
			break
		}
		if (region.lat-lat) == RegionArea && (region.long-long) == 0 {
			possibleRegionConnects[0] = region
			connected = true
		}
		if (region.lat-lat) == -RegionArea && (region.long-long) == 0 {
			possibleRegionConnects[3] = region
			connected = true
		}
		if (region.lat-lat) == 0 && (region.long-long) == -RegionArea {
			possibleRegionConnects[1] = region
			connected = true
		}
		if (region.lat-lat) == 0 && (region.long-long) == RegionArea {
			possibleRegionConnects[3] = region
			connected = true
		}
	}

	if !connected {
		return
	}

	newRegion := newClientRegion()
	newRegion.AddClient(client)

	for i, r := range possibleRegionConnects {
		if r == nil {
			continue
		}
		switch i {
		case 0:
			newRegion.Up = r
			r.Down = newRegion
		case 1:
			newRegion.Left = r
			r.Right = newRegion
		case 2:
			newRegion.Down = r
			r.Up = newRegion
		case 3:
			newRegion.Right = r
			r.Left = newRegion
		}
	}
	return
}

// NewClientNetwork creates a new network of client regions
func NewClientNetwork(root *clientRegion) (network *ClientNetwork) {
	network.root = root
	network.allRegions = []*clientRegion{root}

	network.latRange = [2]float64{root.lat, root.lat + 1}
	network.longRange = [2]float64{root.long, root.long + 1}

	network.modificationMux = new(sync.Mutex)
	return
}

type clientRegion struct {
	Up,
	Left,
	Down,
	Right *clientRegion
	clients map[string]*Client
	isRoot,
	visited bool
	lat,
	long float64
}

func (c *clientRegion) isConnectedToRoot(previousConnection bool) bool {
	if c.visited {
		return previousConnection
	}
	c.visited = true
	if c.isRoot {
		return true
	}
	// Graph search order is Up Left Down Right
	if c.Up != nil && c.Up.isConnectedToRoot(previousConnection) {
		return true
	}
	if c.Left != nil && c.Left.isConnectedToRoot(previousConnection) {
		return true
	}
	if c.Down != nil && c.Down.isConnectedToRoot(previousConnection) {
		return true
	}
	if c.Right != nil && c.Right.isConnectedToRoot(previousConnection) {
		return true
	}
	return false
}

func (c *clientRegion) findClientRegion(lat, long float64) *clientRegion {
	if c.visited {
		return nil
	}
	c.visited = true
	if c.lat == lat && c.long == long {
		return c
	}
	// Graph search order is Up Left Down Right
	if c.Up != nil {
		if n := c.Up.findClientRegion(lat, long); n != nil {
			return n
		}
	}
	if c.Left != nil {
		if n := c.Left.findClientRegion(lat, long); n != nil {
			return n
		}
	}
	if c.Down != nil {
		if n := c.Down.findClientRegion(lat, long); n != nil {
			return n
		}
	}
	if c.Right != nil {
		if n := c.Right.findClientRegion(lat, long); n != nil {
			return n
		}
	}
	return nil
}

//AddClient adds a client to a region. In the case that we already have the client in the region we ignore.
func (c *clientRegion) AddClient(client *Client) {
	if _, ok := c.clients[client.ID]; ok {
		return
	}
	c.clients[client.ID] = client
}

func newClientRegion() (region *clientRegion) {
	region.clients = make(map[string]*Client)
	return
}
<commit_msg>Switched boundary calculation to use the RegionArea constant<commit_after>package server

import (
	"math"
	"sync"
)

// RegionArea is the area (in coordinate degrees)
const RegionArea = 1.0

// Client is the representation of chat clients
type Client struct {
	ID string
	Lat,
	Long float64
}

//ClientNetwork is the representation of the Network of all client regions
type ClientNetwork struct {
	root            *clientRegion
	allRegions      []*clientRegion
	latRange        [2]float64
	longRange       [2]float64
	modificationMux *sync.Mutex
}

// AddClient adds a client to the network in the appropriate region
func (c *ClientNetwork) AddClient(client *Client) (connected bool) {
	lat := math.Floor(client.Lat)
	long := math.Floor(client.Long)
	// we want to also track possible connecting regions in the case that
	// we need to add a region. Key as follows:
	// 0: Up
	// 1: Left
	// 2: Down
	// 3: Right
	var possibleRegionConnects = [4]*clientRegion{nil, nil, nil, nil}
	for _, region := range c.allRegions {
		if region.lat == lat && region.long == long {
			region.AddClient(client)
			break
		}
		if (region.lat-lat) == RegionArea && (region.long-long) == 0 {
			possibleRegionConnects[0] = region
			connected = true
		}
		if (region.lat-lat) == -RegionArea && (region.long-long) == 0 {
			possibleRegionConnects[3] = region
			connected = true
		}
		if (region.lat-lat) == 0 && (region.long-long) == -RegionArea {
			possibleRegionConnects[1] = region
			connected = true
		}
		if (region.lat-lat) == 0 && (region.long-long) == RegionArea {
			possibleRegionConnects[3] = region
			connected = true
		}
	}

	if !connected {
		return
	}

	newRegion := newClientRegion()
	newRegion.AddClient(client)

	for i, r := range possibleRegionConnects {
		if r == nil {
			continue
		}
		switch i {
		case 0:
			newRegion.Up = r
			r.Down = newRegion
		case 1:
			newRegion.Left = r
			r.Right = newRegion
		case 2:
			newRegion.Down = r
			r.Up = newRegion
		case 3:
			newRegion.Right = r
			r.Left = newRegion
		}
	}
	return
}

// NewClientNetwork creates a new network of client regions
func NewClientNetwork(root *clientRegion) (network *ClientNetwork) {
	network.root = root
	network.allRegions = []*clientRegion{root}

	network.latRange = [2]float64{root.lat, root.lat + RegionArea}
	network.longRange = [2]float64{root.long, root.long + RegionArea}

	network.modificationMux = new(sync.Mutex)
	return
}

type clientRegion struct {
	Up,
	Left,
	Down,
	Right *clientRegion
	clients map[string]*Client
	isRoot,
	visited bool
	lat,
	long float64
}

func (c *clientRegion) isConnectedToRoot(previousConnection bool) bool {
	if c.visited {
		return previousConnection
	}
	c.visited = true
	if c.isRoot {
		return true
	}
	// Graph search order is Up Left Down Right
	if c.Up != nil && c.Up.isConnectedToRoot(previousConnection) {
		return true
	}
	if c.Left != nil && c.Left.isConnectedToRoot(previousConnection) {
		return true
	}
	if c.Down != nil && c.Down.isConnectedToRoot(previousConnection) {
		return true
	}
	if c.Right != nil && c.Right.isConnectedToRoot(previousConnection) {
		return true
	}
	return false
}

func (c *clientRegion) findClientRegion(lat, long float64) *clientRegion {
	if c.visited {
		return nil
	}
	c.visited = true
	if c.lat == lat && c.long == long {
		return c
	}
	// Graph search order is Up Left Down Right
	if c.Up != nil {
		if n := c.Up.findClientRegion(lat, long); n != nil {
			return n
		}
	}
	if c.Left != nil {
		if n := c.Left.findClientRegion(lat, long); n != nil {
			return n
		}
	}
	if c.Down != nil {
		if n := c.Down.findClientRegion(lat, long); n != nil {
			return n
		}
	}
	if c.Right != nil {
		if n := c.Right.findClientRegion(lat, long); n != nil {
			return n
		}
	}
	return nil
}

//AddClient adds a client to a region. In the case that we already have the client in the region we ignore.
func (c *clientRegion) AddClient(client *Client) {
	if _, ok := c.clients[client.ID]; ok {
		return
	}
	c.clients[client.ID] = client
}

func newClientRegion() (region *clientRegion) {
	region.clients = make(map[string]*Client)
	return
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Robin Engel
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package core

import "net/url"

type Node struct {
	Name string
	URL  url.URL
}

type nodeCache struct {
	nodes map[string]Node
}

func NewNodeCache() *nodeCache {
	c := &nodeCache{}
	c.nodes = make(map[string]Node)
	return c
}

func (cache *nodeCache) Add(node Node) {
	cache.nodes[node.Name] = node
}

func (cache * nodeCache) Get(name string) Node {
	return cache.nodes[name]
}<commit_msg>implement prototyped connection to node<commit_after>/*
 * Copyright 2016 Robin Engel
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package core

import (
	"net/url"
	"net/http"
	"log"
	"bufio"
)

type Node struct {
	Name string
	URL  url.URL
}

type nodeCache struct {
	nodes map[string]*Node
}

func NewNodeCache() *nodeCache {
	c := &nodeCache{}
	c.nodes = make(map[string]*Node)
	return c
}

func (cache *nodeCache) Add(node *Node) {
	cache.nodes[node.Name] = node

	go func() {
		Connection: for {
			resp, err := http.Get(node.URL.String())

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

			reader := bufio.NewReader(resp.Body)

			for {
				line, err := reader.ReadBytes('\n')

				if err != nil {
					log.Fatal(resp.Body.Close())
					continue Connection
				}

				log.Print(string(line))
			}

		}
	}()
}

func (cache *nodeCache) Get(name string) *Node {
	return cache.nodes[name]
}<|endoftext|>


<commit_before>// gorewind is an event store server written in Python that talks ZeroMQ.
// Copyright (C) 2013  Jens Rantil
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Deals with persisting events to disk and querying them. No network is
// involved in any of the code in this package.
package server

import (
	"bytes"
	"sync"
	"math/big"
	"strconv"
	//"code.google.com/p/leveldb-go/leveldb"
	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/descriptor"
	"github.com/syndtr/goleveldb/leveldb/comparer"
	"github.com/syndtr/goleveldb/leveldb/opt"
)

const (
	EVENT_ID_CHAN_SIZE = 100
)

// Instance of an event store. All of its functions are threadsafe.
type EventStore struct {
	eventPublishersLock sync.RWMutex
	// Using a map to avoid registering a channel multiple times
	eventPublishers map[chan StoredEvent]chan StoredEvent

	// A channel where we can make read event ID:s in a lock-free
	// way.
	eventIdChan chan string

	// Write something to this channel to quit the generator
	eventIdChanGeneratorShutdown chan bool

	db *leveldb.DB
}

// An event that has not yet been persisted to disk.
type UnstoredEvent struct {
	Stream []byte
	Data []byte
}

// An event that has previously been persisted to disk.
type StoredEvent struct {
	Stream []byte
	Id []byte
	Data []byte
}

// Register a channel where are published events will be pushed to.
// Multiple channels can be registered.
func (v *EventStore) RegisterPublishedEventsChannel(publisher chan StoredEvent) {
	// TODO: Implement an UnregisterPublishedEventsChannel.
	v.eventPublishersLock.Lock()
	defer v.eventPublishersLock.Unlock()
	v.eventPublishers[publisher] = publisher
}

var streamPrefix []byte = []byte("stream")
var eventPrefix []byte = []byte("event")

// Store an event to the event store. Returns the unique event id that
// the event was stored under. As long as no error occurred, of course.
func (v *EventStore) Add(event UnstoredEvent) (string, error) {
	newId := <-v.eventIdChan

	batch := new(leveldb.Batch)

	// TODO: Benchmark how much impact this write has. We could also
	// check if it exists and not write it in that case, which is
	// probably faster. Especially if we are using bloom filter.
	// TODO: Rewrite to use eventStoreKey
	streamKeyParts := [][]byte{streamPrefix, event.Stream}
	streamKey := bytes.Join(streamKeyParts, []byte(""))
	batch.Put(streamKey, []byte(""))

	evKeyParts := [][]byte{
		eventPrefix,
		event.Stream,
		[]byte(":"),
		[]byte(newId),
	}
	evKey := bytes.Join(evKeyParts, []byte(""))
	batch.Put(evKey, event.Data)

	wo := &opt.WriteOptions{}
	err := v.db.Write(batch, wo)
	if err != nil {
		return "", err
	}

	storedEvent := StoredEvent{
		Stream: event.Stream,
		Id: []byte(newId),
		Data: event.Data,
	}
	for pubchan := range v.eventPublishers {
		pubchan <- storedEvent
	}
	return newId, nil
}

// Close an open event store. A previously closed event store must never
// be used further.
func (v* EventStore) Close() error {
	v.eventIdChanGeneratorShutdown <- true
	return nil
}

// A query request.
type QueryRequest struct {
	Stream []byte
	FromId []byte
	ToId []byte
}

// Query events from an event store. If the request is malformed in any
// way, an error is returned. Otherwise, the query result is streamed
// through the res channel in chronological order.
func (v* EventStore) Query(req QueryRequest, res chan StoredEvent) error {
	// TODO: Implement
	close(res)
	return nil
}

func startEventIdGenerator(initId *string) (chan string, chan bool) {
	// TODO: Allow nextId to be set explicitly based on what's
	// previously been stored in the event store.
	nextId := big.NewInt(0)
	if initId != nil {
		nextId.SetString(*initId, 10)
		// We do not care if this succeeded. Instead, we simply
		// initialize with zero (0).
	}
	stopChan := make(chan bool)
	idChan := make(chan string, EVENT_ID_CHAN_SIZE)
	go func() {
		for {
			select {
			case idChan <- nextId.String():
				nextId.Add(nextId, big.NewInt(1))
			case <-stopChan:
				return
			}
		}
	}()
	return idChan, stopChan
}

// Create a new event store instance.
func NewEventStore(desc descriptor.Desc) (*EventStore, error) {
	estore := new(EventStore)

	// TODO: Initialize the eventid generator with maxId+1
	initId := "0"
	idChan, idChanShutdown := startEventIdGenerator(&initId)
	estore.eventIdChan = idChan
	estore.eventIdChanGeneratorShutdown = idChanShutdown

	options := &opt.Options{
		Flag: opt.OFCreateIfMissing,
		Comparer: &EventStreamComparer{},
	}
	db, err := leveldb.Open(desc, options)
	if err != nil {
		return nil, err
	}
	estore.db = db

	return estore, nil
}

// The separator used for separating into the different eventStoreKey
// fields.
var groupSep []byte = []byte(":")

// Represents a leveldb key.
type eventStoreKey struct {
	groupKey []byte
	key []byte
	keyId *big.Int
}

// Convert a eventStoreKey to bytes. The returned byte slice is either
// "groupKey:key:keyId" if keyId is non-nil, or "groupKey:key"
// otherwise.
func (v *eventStoreKey) toBytes() []byte {
	var pieces [][]byte
	if v.keyId != nil {
		pieces = make([][]byte, 3)
		pieces[0] = v.groupKey
		pieces[1] = v.key
		pieces[2] = []byte(v.keyId.String())
	} else {
		pieces = make([][]byte, 2)
		pieces[0] = v.groupKey
		pieces[1] = v.key
	}
	return bytes.Join(pieces, groupSep)

}

// Convert a byte slice to a parsed eventStoreKey.
func neweventStoreKey(data []byte) (*eventStoreKey) {
	res := new(eventStoreKey)
	pieces := bytes.Split(data, groupSep)
	if len(pieces) > 2 {
		possibleId := big.NewInt(0)
		_, success := possibleId.SetString(string(pieces[len(pieces)-1]), 10)
		if success {
			res.keyId = possibleId
		}
	}
	if len(pieces) > 0 {
		res.groupKey = pieces[0]
	}
	if len(pieces) > 1 {
		var upperIndex int
		if res.keyId != nil {
			upperIndex = len(pieces) - 1
		} else {
			upperIndex = len(pieces)
		}
		keyPieces := pieces[1:upperIndex]
		res.key = bytes.Join(pieces, groupSep)
	}
	return res
}

// Compare to another eventStoreKey. Returns -1 if this one is smaller
// than o2, 0 same, or 1 is this one is bigger than the previous one.
func (o1 *eventStoreKey) compare(o2 *eventStoreKey) int {
	if diff := bytes.Compare(o1.groupKey, o2.groupKey); diff != 0 {
		return diff
	}
	if diff := bytes.Compare(o1.key, o2.key); diff != 0 {
		return diff
	}
	switch {
	case o1.keyId != nil && o2.keyId != nil:
		return o1.keyId.Cmp(o2.keyId)
	case o1.keyId != nil:
		return 1
	case o2.keyId != nil:
		return -1
	default:
		return 0
	}
}

// Helper functions for comparer

func getGroup(key []byte) []byte {
	return bytes.SplitN(key, groupSep, 1)[0]
}

func getRealKey(key []byte) []byte {
	pieces := bytes.Split(key, groupSep)
	if _, err := getIntegerPart(key); err != nil {
		return bytes.Join(pieces[1:len(pieces)], groupSep)
	}
	return bytes.Join(pieces[1:len(pieces) - 1], groupSep)
}

func getIntegerPart(key []byte) (int, error) {
	pieces := bytes.Split(key, groupSep)
	lastpiece := pieces[len(pieces) - 1]
	i, err := strconv.Atoi(string(lastpiece))
	if err != nil {
		return 0, err
	}
	return i, nil
}

// Comparer

type EventStreamComparer struct {
}

func (v* EventStreamComparer) Name() string {
	return "rewindd.eventStreamComparer"
}

// If 'a' < 'b', changes 'a' to a short string in [a,b).
//
// Used to minimize the size of index blocks and other data structures.
func (v* EventStreamComparer) Separator(a, b []byte) []byte {
	groupA := getGroup(a)
	groupB := getGroup(b)
	if c := bytes.Compare(groupA, groupB); c != 0 {
		bcomp := comparer.BytesComparer{}
		return bytes.Join([][]byte{
			bcomp.Separator(groupA, groupB),
			[]byte{},
		}, groupSep)
	}
	// Here we know that groupA==groupB

	realKeyA := getRealKey(a)
	realKeyB := getRealKey(b)
	if c := bytes.Compare(realKeyA, realKeyB); c != 0 {
		bcomp := comparer.BytesComparer{}
		return bytes.Join([][]byte{
			groupA,
			bcomp.Separator(realKeyA, realKeyA),
		}, groupSep)
	}
	// Here we know that realKeyA==realKeyB

	// TODO: Handle this
	intPartA, errA := getIntegerPart(a)
	intPartB, errB := getIntegerPart(b)
	switch {
	case errA == nil && errB == nil:
		// [Group, key, intA] </>/= [Group, key, intB]
		switch {
		case intPartA < intPartB:
			return bytes.Join([][]byte{
				groupA,
				realKeyA,
				[]byte(strconv.Itoa(intPartB - 1)),
			}, groupSep)
		/*case intPartA > intPartB:
			return a*/
		default:
			return a
		}
	case errA != nil && errB != nil:
		// [Group, key] == [Group, key]
		return a
	case errA != nil:
		// [Group, key, int] > [Group, key]
		return a
	}
	//default: -- must be put outside of switch to avoid compiler
	//error.
	// [Group, key] < [Group, key, int]
	return bytes.Join([][]byte{
		groupA,
		realKeyA,
		[]byte("1"),
	}, groupSep)

	// Unoptimized result that always works.
	return a
}

// Changes 'b' to a short string >= 'b'
//
// Used to minimize the size of index blocks and other data structures.
func (v* EventStreamComparer) Successor(b []byte) []byte {
	groupB := getGroup(b)
	bcomp := comparer.BytesComparer{}
	return bytes.Join([][]byte{
		bcomp.Successor(groupB),
		[]byte{},
	}, groupSep)
}

func (v* EventStreamComparer) Compare(a, b []byte) int {
	groupA := getGroup(a)
	groupB := getGroup(b)
	if c := bytes.Compare(groupA, groupB); c != 0 {
		return c
	}

	realKeyA := getRealKey(a)
	realKeyB := getRealKey(b)
	if c := bytes.Compare(realKeyA, realKeyB); c != 0 {
		return c
	}

	intPartA, errA := getIntegerPart(a)
	intPartB, errB := getIntegerPart(b)
	switch {
	case errA == nil && errB == nil:
		// [Group, key, intA] </>/= [Group, key, intB]
		switch {
		case intPartA < intPartB:
			return -1
		case intPartA > intPartB:
			return 1
		default:
			return 0
		}
	case errA != nil && errB != nil:
		// [Group, key] == [Group, key]
		return 0
	case errA != nil:
		// [Group, key, int] > [Group, key]
		return 1
	}
	//default: -- must be put outside of switch to avoid compiler
	//error.
	// [Group, key] < [Group, key, int]
	return -1
}
<commit_msg>Implementation of `EventStore.Query`<commit_after>// gorewind is an event store server written in Python that talks ZeroMQ.
// Copyright (C) 2013  Jens Rantil
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Deals with persisting events to disk and querying them. No network is
// involved in any of the code in this package.
package server

import (
	"bytes"
	"sync"
	"math/big"
	"strconv"
	//"code.google.com/p/leveldb-go/leveldb"
	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/descriptor"
	"github.com/syndtr/goleveldb/leveldb/comparer"
	iter "github.com/syndtr/goleveldb/leveldb/iterator"
	"github.com/syndtr/goleveldb/leveldb/opt"
)

const (
	EVENT_ID_CHAN_SIZE = 100
)

// Instance of an event store. All of its functions are threadsafe.
type EventStore struct {
	eventPublishersLock sync.RWMutex
	// Using a map to avoid registering a channel multiple times
	eventPublishers map[chan StoredEvent]chan StoredEvent

	// A channel where we can make read event ID:s in a lock-free
	// way.
	eventIdChan chan string

	// Write something to this channel to quit the generator
	eventIdChanGeneratorShutdown chan bool

	db *leveldb.DB
}

// An event that has not yet been persisted to disk.
type UnstoredEvent struct {
	Stream []byte
	Data []byte
}

// An event that has previously been persisted to disk.
type StoredEvent struct {
	Stream []byte
	Id []byte
	Data []byte
}

// Register a channel where are published events will be pushed to.
// Multiple channels can be registered.
func (v *EventStore) RegisterPublishedEventsChannel(publisher chan StoredEvent) {
	// TODO: Implement an UnregisterPublishedEventsChannel.
	v.eventPublishersLock.Lock()
	defer v.eventPublishersLock.Unlock()
	v.eventPublishers[publisher] = publisher
}

var streamPrefix []byte = []byte("stream")
var eventPrefix []byte = []byte("event")

// Store an event to the event store. Returns the unique event id that
// the event was stored under. As long as no error occurred, of course.
func (v *EventStore) Add(event UnstoredEvent) (string, error) {
	newId := <-v.eventIdChan

	batch := new(leveldb.Batch)

	// TODO: Benchmark how much impact this write has. We could also
	// check if it exists and not write it in that case, which is
	// probably faster. Especially if we are using bloom filter.
	// TODO: Rewrite to use eventStoreKey
	streamKeyParts := [][]byte{streamPrefix, event.Stream}
	streamKey := bytes.Join(streamKeyParts, []byte(""))
	batch.Put(streamKey, []byte(""))

	evKeyParts := [][]byte{
		eventPrefix,
		event.Stream,
		[]byte(":"),
		[]byte(newId),
	}
	evKey := bytes.Join(evKeyParts, []byte(""))
	batch.Put(evKey, event.Data)

	wo := &opt.WriteOptions{}
	err := v.db.Write(batch, wo)
	if err != nil {
		return "", err
	}

	storedEvent := StoredEvent{
		Stream: event.Stream,
		Id: []byte(newId),
		Data: event.Data,
	}
	for pubchan := range v.eventPublishers {
		pubchan <- storedEvent
	}
	return newId, nil
}

// Close an open event store. A previously closed event store must never
// be used further.
func (v* EventStore) Close() error {
	v.eventIdChanGeneratorShutdown <- true
	return nil
}

// A query request.
type QueryRequest struct {
	Stream []byte
	FromId []byte
	ToId []byte
}

// Query events from an event store. If the request is malformed in any
// way, an error is returned. Otherwise, the query result is streamed
// through the res channel in chronological order.
//
// Currently this function will make error checks synchronously.  If all
// looks good, streaming the results through `res` is done
// asynchronously. TODO: Also make the error checking asynchronously, to
// minimize IO blocking when calling this function.
func (v *EventStore) Query(req QueryRequest, res chan StoredEvent) error {
	ro := &opt.ReadOptions{}
	it := v.db.NewIterator(ro)

	// To key
	seekKey := eventStoreKey{
		streamPrefix,
		req.Stream
		req.toId,
	}
	toKeyBytes = seekKey.toBytes()
	it.Seek(toKeyBytes)
	if bytes.Compare(toKeyBytes, it.Key() != 0 {
		bToId := []byte(toId)
		msg := fmt.SPrint("to key did not exist:", bToId)
		return errors.New(msg)
	}

	// From key
	seekKey := eventStoreKey{
		streamPrefix,
		req.Stream
		req.fromId,
	}
	fromKeyBytes = seekKey.toBytes()
	it.Seek(fromKeyBytes)
	if bytes.Compare(fromKeyBytes, it.Key() != 0 {
		bFromId := []byte(fromId)
		msg := fmt.SPrint("from key did not exist:", bFromId)
		return errors.New(msg)
	}

	diff := EventStreamComparer.Compare(fromKeyBytes, toKeyBytes)
	if diff >= -1 {
		msg := "The query was done in wrong chronological order."
		return errors.New(msg)
	}

	go safeQuery(it, req, res)

	return nil
}

// Make the actual query. Sanity checks of the iterator i is expected to
// have been done before calling this function.
func safeQuery(i iter.Iterator, req QueryRequest, res chan StoredEvent) error {
	if !i.Next() {
		// Querying should never return the fromKey, but be
		// inclusive when it comes to the last one. This is
		// natural, since the querier is expected to previouslye
		// have seen fromId, and the goal is to reach the state
		// of toId.
		return
	}
	for i.Next() {
		curKey := neweventStoreKey(i.Key())
		if bytes.Compare(curKey.groupKey, streamPrefix) != 0 {
			break
		}

		resEvent := {
			curKey.groupKey,
			curKey.key,
			[]byte(curKey.keyId.String()),
		}
		res <- resEvent

		if bytes.Compare(curKey.key, req.stream) != 0 {
			break
		}
		keyId := []byte(curKey.keyId.String())
		if bytes.Compare(req.toId, keyId) == 0 {
			break
		}
	}

	close(res)
}

func startEventIdGenerator(initId *string) (chan string, chan bool) {
	// TODO: Allow nextId to be set explicitly based on what's
	// previously been stored in the event store.
	nextId := big.NewInt(0)
	if initId != nil {
		nextId.SetString(*initId, 10)
		// We do not care if this succeeded. Instead, we simply
		// initialize with zero (0).
	}
	stopChan := make(chan bool)
	idChan := make(chan string, EVENT_ID_CHAN_SIZE)
	go func() {
		for {
			select {
			case idChan <- nextId.String():
				nextId.Add(nextId, big.NewInt(1))
			case <-stopChan:
				return
			}
		}
	}()
	return idChan, stopChan
}

// Create a new event store instance.
func NewEventStore(desc descriptor.Desc) (*EventStore, error) {
	estore := new(EventStore)

	// TODO: Initialize the eventid generator with maxId+1
	initId := "0"
	idChan, idChanShutdown := startEventIdGenerator(&initId)
	estore.eventIdChan = idChan
	estore.eventIdChanGeneratorShutdown = idChanShutdown

	options := &opt.Options{
		Flag: opt.OFCreateIfMissing,
		Comparer: &EventStreamComparer{},
	}
	db, err := leveldb.Open(desc, options)
	if err != nil {
		return nil, err
	}
	estore.db = db

	return estore, nil
}

// The separator used for separating into the different eventStoreKey
// fields.
var groupSep []byte = []byte(":")

// Represents a leveldb key.
type eventStoreKey struct {
	groupKey []byte
	key []byte
	keyId *big.Int
}

// Convert a eventStoreKey to bytes. The returned byte slice is either
// "groupKey:key:keyId" if keyId is non-nil, or "groupKey:key"
// otherwise.
func (v *eventStoreKey) toBytes() []byte {
	var pieces [][]byte
	if v.keyId != nil {
		pieces = make([][]byte, 3)
		pieces[0] = v.groupKey
		pieces[1] = v.key
		pieces[2] = []byte(v.keyId.String())
	} else {
		pieces = make([][]byte, 2)
		pieces[0] = v.groupKey
		pieces[1] = v.key
	}
	return bytes.Join(pieces, groupSep)

}

// Convert a byte slice to a parsed eventStoreKey.
func neweventStoreKey(data []byte) (*eventStoreKey) {
	res := new(eventStoreKey)
	pieces := bytes.Split(data, groupSep)
	if len(pieces) > 2 {
		possibleId := big.NewInt(0)
		_, success := possibleId.SetString(string(pieces[len(pieces)-1]), 10)
		if success {
			res.keyId = possibleId
		}
	}
	if len(pieces) > 0 {
		res.groupKey = pieces[0]
	}
	if len(pieces) > 1 {
		var upperIndex int
		if res.keyId != nil {
			upperIndex = len(pieces) - 1
		} else {
			upperIndex = len(pieces)
		}
		keyPieces := pieces[1:upperIndex]
		res.key = bytes.Join(pieces, groupSep)
	}
	return res
}

// Compare to another eventStoreKey. Returns -1 if this one is smaller
// than o2, 0 same, or 1 is this one is bigger than the previous one.
func (o1 *eventStoreKey) compare(o2 *eventStoreKey) int {
	if diff := bytes.Compare(o1.groupKey, o2.groupKey); diff != 0 {
		return diff
	}
	if diff := bytes.Compare(o1.key, o2.key); diff != 0 {
		return diff
	}
	switch {
	case o1.keyId != nil && o2.keyId != nil:
		return o1.keyId.Cmp(o2.keyId)
	case o1.keyId != nil:
		return 1
	case o2.keyId != nil:
		return -1
	default:
		return 0
	}
}

// Helper functions for comparer

func getGroup(key []byte) []byte {
	return bytes.SplitN(key, groupSep, 1)[0]
}

func getRealKey(key []byte) []byte {
	pieces := bytes.Split(key, groupSep)
	if _, err := getIntegerPart(key); err != nil {
		return bytes.Join(pieces[1:len(pieces)], groupSep)
	}
	return bytes.Join(pieces[1:len(pieces) - 1], groupSep)
}

func getIntegerPart(key []byte) (int, error) {
	pieces := bytes.Split(key, groupSep)
	lastpiece := pieces[len(pieces) - 1]
	i, err := strconv.Atoi(string(lastpiece))
	if err != nil {
		return 0, err
	}
	return i, nil
}

// Comparer

type EventStreamComparer struct {
}

func (v* EventStreamComparer) Name() string {
	return "rewindd.eventStreamComparer"
}

// If 'a' < 'b', changes 'a' to a short string in [a,b).
//
// Used to minimize the size of index blocks and other data structures.
func (v* EventStreamComparer) Separator(a, b []byte) []byte {
	groupA := getGroup(a)
	groupB := getGroup(b)
	if c := bytes.Compare(groupA, groupB); c != 0 {
		bcomp := comparer.BytesComparer{}
		return bytes.Join([][]byte{
			bcomp.Separator(groupA, groupB),
			[]byte{},
		}, groupSep)
	}
	// Here we know that groupA==groupB

	realKeyA := getRealKey(a)
	realKeyB := getRealKey(b)
	if c := bytes.Compare(realKeyA, realKeyB); c != 0 {
		bcomp := comparer.BytesComparer{}
		return bytes.Join([][]byte{
			groupA,
			bcomp.Separator(realKeyA, realKeyA),
		}, groupSep)
	}
	// Here we know that realKeyA==realKeyB

	// TODO: Handle this
	intPartA, errA := getIntegerPart(a)
	intPartB, errB := getIntegerPart(b)
	switch {
	case errA == nil && errB == nil:
		// [Group, key, intA] </>/= [Group, key, intB]
		switch {
		case intPartA < intPartB:
			return bytes.Join([][]byte{
				groupA,
				realKeyA,
				[]byte(strconv.Itoa(intPartB - 1)),
			}, groupSep)
		/*case intPartA > intPartB:
			return a*/
		default:
			return a
		}
	case errA != nil && errB != nil:
		// [Group, key] == [Group, key]
		return a
	case errA != nil:
		// [Group, key, int] > [Group, key]
		return a
	}
	//default: -- must be put outside of switch to avoid compiler
	//error.
	// [Group, key] < [Group, key, int]
	return bytes.Join([][]byte{
		groupA,
		realKeyA,
		[]byte("1"),
	}, groupSep)

	// Unoptimized result that always works.
	return a
}

// Changes 'b' to a short string >= 'b'
//
// Used to minimize the size of index blocks and other data structures.
func (v* EventStreamComparer) Successor(b []byte) []byte {
	groupB := getGroup(b)
	bcomp := comparer.BytesComparer{}
	return bytes.Join([][]byte{
		bcomp.Successor(groupB),
		[]byte{},
	}, groupSep)
}

func (v* EventStreamComparer) Compare(a, b []byte) int {
	groupA := getGroup(a)
	groupB := getGroup(b)
	if c := bytes.Compare(groupA, groupB); c != 0 {
		return c
	}

	realKeyA := getRealKey(a)
	realKeyB := getRealKey(b)
	if c := bytes.Compare(realKeyA, realKeyB); c != 0 {
		return c
	}

	intPartA, errA := getIntegerPart(a)
	intPartB, errB := getIntegerPart(b)
	switch {
	case errA == nil && errB == nil:
		// [Group, key, intA] </>/= [Group, key, intB]
		switch {
		case intPartA < intPartB:
			return -1
		case intPartA > intPartB:
			return 1
		default:
			return 0
		}
	case errA != nil && errB != nil:
		// [Group, key] == [Group, key]
		return 0
	case errA != nil:
		// [Group, key, int] > [Group, key]
		return 1
	}
	//default: -- must be put outside of switch to avoid compiler
	//error.
	// [Group, key] < [Group, key, int]
	return -1
}
<|endoftext|>


<commit_before>package game

import (
	"encoding/json"
	"io"
	"sync"
)

// Statum is some made up latin.
type Statum int

// Broad states (statums).
const (
	StateLobby Statum = iota
	StateInGame
	StateGameOver
)

// State models the entire game state.
type State struct {
	State     Statum          `json:"state"`
	Players   map[int]*Player `json:"players"`
	Clock     int             `json:"clock"`
	WhoseTurn int             `json:"whose_turn"`

	// Fields for managing the game.
	nextID   int
	baseDeck Deck
	deck     Deck

	// Fields for coordinating state.
	changedNote chan struct{}
	mu          sync.RWMutex
}

// New returns a new game state.
func New(deck Deck) *State {
	return &State{
		Players:     make(map[int]*Player),
		changedNote: make(chan struct{}),
		baseDeck:    deck,
	}
}

// Changed returns a channel closed when the state has changed.
func (s *State) Changed() <-chan struct{} {
	s.RLock()
	defer s.RUnlock()
	return s.changedNote
}

// Dump writes the state to a writer in JSON.
func (s *State) Dump(w io.Writer) error {
	enc := json.NewEncoder(w)
	enc.SetIndent("", "\t")
	s.RLock()
	defer s.RUnlock()
	return enc.Encode(s)
}

// MUST GUARD WITH LOCK
func (s *State) notify() {
	close(s.changedNote)
	s.changedNote = make(chan struct{})
}

func (s *State) Lock()    { s.mu.Lock() }
func (s *State) Unlock()  { s.mu.Unlock() }
func (s *State) RLock()   { s.mu.RLock() }
func (s *State) RUnlock() { s.mu.RUnlock() }

// Player is the state relative to a particular player.
type Player struct {
	Name  string `json:"name"`
	Hand  *Hand  `json:"hand"`
	Score int    `json:"score"`
}

// PersonCard models a game card.
type PersonCard struct {
	Name string `json:"name"`
	// TODO
}

// ActionCard models a game card.
type ActionCard struct {
	Name string `json:"name"`
	// TODO
}
<commit_msg>Fix build<commit_after>package game

import (
	"encoding/json"
	"io"
	"sync"
)

// Statum is some made up latin.
type Statum int

// Broad states (statums).
const (
	StateLobby Statum = iota
	StateInGame
	StateGameOver
)

// State models the entire game state.
type State struct {
	State     Statum          `json:"state"`
	Players   map[int]*Player `json:"players"`
	Clock     int             `json:"clock"`
	WhoseTurn int             `json:"whose_turn"`

	// Fields for managing the game.
	nextID   int
	baseDeck Deck
	deck     Deck

	// Fields for coordinating state.
	changedNote chan struct{}
	mu          sync.RWMutex
}

// New returns a new game state.
func New(deck Deck) *State {
	return &State{
		Players:     make(map[int]*Player),
		changedNote: make(chan struct{}),
		baseDeck:    deck,
	}
}

// Changed returns a channel closed when the state has changed.
func (s *State) Changed() <-chan struct{} {
	s.RLock()
	defer s.RUnlock()
	return s.changedNote
}

// Dump writes the state to a writer in JSON.
func (s *State) Dump(w io.Writer) error {
	enc := json.NewEncoder(w)
	enc.SetIndent("", "\t")
	s.RLock()
	defer s.RUnlock()
	return enc.Encode(s)
}

// MUST GUARD WITH LOCK
func (s *State) notify() {
	close(s.changedNote)
	s.changedNote = make(chan struct{})
}

func (s *State) Lock()    { s.mu.Lock() }
func (s *State) Unlock()  { s.mu.Unlock() }
func (s *State) RLock()   { s.mu.RLock() }
func (s *State) RUnlock() { s.mu.RUnlock() }

// Player is the state relative to a particular player.
type Player struct {
	Name  string `json:"name"`
	Hand  *Hand  `json:"hand"`
	Score int    `json:"score"`
}
<|endoftext|>


<commit_before>package server

import "time"

func calculateArrivalTime(start_point, end_point, []int) time.Time {
	now := time.Now()
}

<commit_msg>Disable missionary for now<commit_after>package server

// import "time"
//
// func calculateArrivalTime(start_point, end_point, []int) time.Time {
// 	now := time.Now()
// }
//
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"github.com/Vladimiroff/vec2d"
	"time"
	"warcluster/db_manager"
	"warcluster/entities"
)

// The CalculateArrivalTime is used by the mission starter (StartMissionary) to calculate the mission duration.
func CalculateArrivalTime(start_point, end_point []int, speed int) time.Duration {
	start_vector := vec2d.New(float64(start_point[0]), float64(start_point[1]))
	end_vector := vec2d.New(float64(end_point[0]), float64(end_point[1]))
	distance := vec2d.Sub(end_vector, start_vector)
	return time.Duration(time.Duration(distance.Length()/float64(speed)) * time.Second)
}

// StartMissionary is used when a call to initiate a new mission is rescived.
// 1. The function gets the mission planet information from the DB and makes basic data checks.
// 2. Calls CalculateArrivalTime and sleeps the thread for the returned ammount of time.
// 3. When the delay ends the thread ends the mission calling EndMission
// 4. The end of the mission is bradcasted to all clients and the mission entry is erased from the DB.
func StartMissionary(mission *entities.Mission) {
	start_entity, err := db_manager.GetEntity(mission.GetStartPlanet())
	end_entity, err := db_manager.GetEntity(mission.EndPlanet)
	start_planet := start_entity.(*entities.Planet)
	end_planet := end_entity.(*entities.Planet)

	speed := mission.GetSpeed()
	time.Sleep(CalculateArrivalTime(start_planet.GetCoords(), end_planet.GetCoords(), speed))

	result := entities.EndMission(end_planet, mission)
	key, serialized_planet, err := result.Serialize()
	if err == nil {
		db_manager.SetEntity(result)
		sessions.Broadcast([]byte(fmt.Sprintf("{\"Command\": \"state_change\", \"Planets\": {\"%s\": %s}}", key, serialized_planet)))
	}
	db_manager.DeleteEntity(mission.GetKey())
}
<commit_msg>Fetch the end_planet after the missionary sleep  in order to know what has changed<commit_after>package server

import (
	"fmt"
	"github.com/Vladimiroff/vec2d"
	"time"
	"warcluster/db_manager"
	"warcluster/entities"
)

// The CalculateArrivalTime is used by the mission starter (StartMissionary) to calculate the mission duration.
func CalculateArrivalTime(start_point, end_point []int, speed int) time.Duration {
	start_vector := vec2d.New(float64(start_point[0]), float64(start_point[1]))
	end_vector := vec2d.New(float64(end_point[0]), float64(end_point[1]))
	distance := vec2d.Sub(end_vector, start_vector)
	return time.Duration(time.Duration(distance.Length()/float64(speed)) * time.Second)
}

// StartMissionary is used when a call to initiate a new mission is rescived.
// 1. The function gets the mission planet information from the DB and makes basic data checks.
// 2. Calls CalculateArrivalTime and sleeps the thread for the returned ammount of time.
// 3. When the delay ends the thread ends the mission calling EndMission
// 4. The end of the mission is bradcasted to all clients and the mission entry is erased from the DB.
func StartMissionary(mission *entities.Mission) {
	start_entity, err := db_manager.GetEntity(mission.GetStartPlanet())
	start_planet := start_entity.(*entities.Planet)
	end_entity, err := db_manager.GetEntity(mission.EndPlanet)
	end_planet := end_entity.(*entities.Planet)

	speed := mission.GetSpeed()
	time.Sleep(CalculateArrivalTime(start_planet.GetCoords(), end_planet.GetCoords(), speed))

	// Fetch the end_planet again in order to know what has changed
	end_entity, err := db_manager.GetEntity(mission.EndPlanet)
	end_planet := end_entity.(*entities.Planet)

	result := entities.EndMission(end_planet, mission)
	key, serialized_planet, err := result.Serialize()
	if err == nil {
		db_manager.SetEntity(result)
		sessions.Broadcast([]byte(fmt.Sprintf("{\"Command\": \"state_change\", \"Planets\": {\"%s\": %s}}", key, serialized_planet)))
	}
	db_manager.DeleteEntity(mission.GetKey())
}
<|endoftext|>


<commit_before>package tls

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"

	"github.com/octavore/naga/service"
	"github.com/octavore/nagax/keystore"
	"github.com/octavore/nagax/logger"
	"github.com/xenolf/lego/acme"

	"github.com/ketchuphq/ketchup/server/config"
	"github.com/ketchuphq/ketchup/server/router"
	"github.com/ketchuphq/ketchup/util/errors"
)

const (
	defaultCAURL        = "https://acme-v01.api.letsencrypt.org/directory"
	defaultStagingCAURL = "https://acme-staging.api.letsencrypt.org/directory"
	challengeBasePath   = "/.well-known/acme-challenge/"
	tlsDir              = "tls"
)

type acmeChallenge struct {
	domain, token, keyAuth string
}

type Module struct {
	Config *config.Module
	Router *router.Module
	Logger *logger.Module

	challenge *acmeChallenge
	keystore  *keystore.KeyStore

	serverStarted bool
}

func (m *Module) Init(c *service.Config) {
	c.AddCommand(&service.Command{
		Keyword:    "tls:provision <example.com> <my@email.com>",
		ShortUsage: `Provision an ssl cert for the given domain and email`,
		Usage: `Provision an ssl cert for the given domain.
Required params: domain to provision a cert for; contact email for Let's Encrypt.`,
		Flags: []*service.Flag{{Key: "agree"}},
		Run: func(ctx *service.CommandContext) {
			ctx.RequireExactlyNArgs(2)
			if !ctx.Flags["agree"].Present() {
				fmt.Print("Please provide the --agree flag to indicate that you agree to Let's Encrypt's TOS. \n")
				return
			}
			err := m.ObtainCert(ctx.Args[1], ctx.Args[0])
			if err != nil {
				fmt.Println(err)
			}
			fmt.Println("success!")
		},
	})
	c.Setup = func() error {
		m.keystore = &keystore.KeyStore{Dir: m.Config.Config.DataDir}

		dir := path.Join(m.Config.Config.DataDir, tlsDir)
		_, err := os.Stat(dir)
		if os.IsNotExist(err) {
			err = os.MkdirAll(dir, 0700)
		}
		m.Router.Handle(challengeBasePath, m)
		return err
	}
	c.Start = func() {
		go func() {
			err := m.StartTLSProxy()
			if err != nil {
				m.Logger.Error(errors.Wrap(err))
			}
		}()
	}
}

func (m *Module) tlsDirPath(file string) string {
	return path.Join(m.Config.Config.DataDir, tlsDir, file)
}

// func (m *Module) Renew(r *Registration) error {}

func (m *Module) ObtainCert(email, domain string) error {
	r, err := m.GetRegistration(domain, true)
	if err != nil {
		return err
	}
	if r == nil {
		r = &Registration{}
	}
	r.Domain = domain
	r.Email = email
	r.AgreedOn = now().Format(time.RFC3339)
	r.Init(m.keystore)
	return m.obtainCert(r)
}

type LetsEncryptError struct{ error }

// ObtainCert obtains a new ssl cert for the given user. Currently uses default
// port 80 and port 443 for challenges.
func (m *Module) obtainCert(r *Registration) error {
	certURL := r.Domain
	// Initialize user and domain
	if certURL == "" {
		return errors.Wrap(fmt.Errorf("no url specified"))
	}
	// hack to URL parse it correctly
	if !strings.HasPrefix(certURL, "https://") && !strings.HasPrefix(certURL, "http://") {
		certURL = "http://" + certURL
	}
	domain, err := url.Parse(certURL)
	if err != nil {
		return err
	}

	if domain.Host == "" {
		return errors.Wrap(fmt.Errorf("no url specified"))
	}

	keyFile := path.Join(tlsDir, domain.Host+".key")
	_, domainKey, err := m.keystore.LoadPrivateKey(keyFile)
	if err != nil {
		return errors.Wrap(err)
	}

	// Initialize the Client
	r.Domain = domain.Host
	c, err := acme.NewClient(defaultCAURL, r, "")
	if err != nil {
		return errors.Wrap(err)
	}

	c.SetChallengeProvider(acme.HTTP01, m)
	registration, err := c.Register()
	if err != nil {
		return errors.Wrap(err)
	}

	r.Registration = registration
	m.SaveRegistration(r)

	err = c.AgreeToTOS()
	if err != nil {
		return errors.Wrap(err)
	}
	cert, errs := c.ObtainCertificate([]string{domain.Host}, true, domainKey, false)
	if len(errs) > 0 {
		lst := []string{}
		for _, e := range errs {
			// todo: check for updated TOS error
			lst = append(lst, e.Error())
		}

		return errors.Wrap(LetsEncryptError{fmt.Errorf(strings.Join(lst, "; "))})
	}

	m.saveCert(cert)
	return errors.Wrap(err)
}

func (m *Module) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if m.challenge == nil {
		router.NotFound(rw)
		return
	}
	// The handler validates the HOST header and request type.
	// For validation it then writes the token the server returned with the challenge
	if strings.HasPrefix(req.Host, m.challenge.domain) &&
		req.URL.Path == acme.HTTP01ChallengePath(m.challenge.token) &&
		req.Method == "GET" {
		rw.Header().Add("Content-Type", "text/plain")
		rw.Write([]byte(m.challenge.keyAuth))
		m.challenge = nil
	} else {
		m.Logger.Warningf("Invalid acme challenge for %s", req.Host)
	}
}

// Present implements the acme.ChallengeProvider.Present
func (m *Module) Present(domain, token, keyAuth string) error {
	if m.challenge != nil {
		m.Logger.Warningf("replacing existing challenge for %s with %s", m.challenge.domain, domain)
	}
	m.challenge = &acmeChallenge{domain: domain, token: token, keyAuth: keyAuth}
	return nil

}

// CleanUp implements the acme.ChallengeProvider.CleanUp
func (m *Module) CleanUp(domain, token, keyAuth string) error {
	m.challenge = nil
	return nil
}
<commit_msg>tls: Automatically renew certs.<commit_after>package tls

import (
	"crypto/x509"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"

	"github.com/octavore/naga/service"
	"github.com/octavore/nagax/keystore"
	"github.com/octavore/nagax/logger"
	"github.com/xenolf/lego/acme"

	"github.com/ketchuphq/ketchup/server/config"
	"github.com/ketchuphq/ketchup/server/router"
	"github.com/ketchuphq/ketchup/util/errors"
)

const (
	defaultCAURL        = "https://acme-v01.api.letsencrypt.org/directory"
	defaultStagingCAURL = "https://acme-staging.api.letsencrypt.org/directory"
	challengeBasePath   = "/.well-known/acme-challenge/"
	tlsDir              = "tls"
)

type acmeChallenge struct {
	domain, token, keyAuth string
}

type Module struct {
	Config *config.Module
	Router *router.Module
	Logger *logger.Module

	challenge *acmeChallenge
	keystore  *keystore.KeyStore

	serverStarted bool
}

func (m *Module) Init(c *service.Config) {
	c.AddCommand(&service.Command{
		Keyword:    "tls:provision <example.com> <my@email.com>",
		ShortUsage: `Provision an ssl cert for the given domain and email`,
		Usage: `Provision an ssl cert for the given domain.
Required params: domain to provision a cert for; contact email for Let's Encrypt.`,
		Flags: []*service.Flag{{Key: "agree"}},
		Run: func(ctx *service.CommandContext) {
			ctx.RequireExactlyNArgs(2)
			if !ctx.Flags["agree"].Present() {
				fmt.Print("Please provide the --agree flag to indicate that you agree to Let's Encrypt's TOS. \n")
				return
			}
			err := m.ObtainCert(ctx.Args[1], ctx.Args[0])
			if err != nil {
				fmt.Println(err)
			}
			fmt.Println("success!")
		},
	})
	c.Setup = func() error {
		m.keystore = &keystore.KeyStore{Dir: m.Config.Config.DataDir}

		dir := path.Join(m.Config.Config.DataDir, tlsDir)
		_, err := os.Stat(dir)
		if os.IsNotExist(err) {
			err = os.MkdirAll(dir, 0700)
		}
		m.Router.Handle(challengeBasePath, m)
		return err
	}
	c.Start = func() {
		go func() {
			err := m.StartTLSProxy()
			if err != nil {
				m.Logger.Error(errors.Wrap(err))
			}
		}()

		go func() {
			for range time.Tick(2 * time.Hour) {
				err := m.renewExpiredCerts()
				if err != nil {
					m.Logger.Error(err)
				}
			}
		}()
	}
}

func (m *Module) tlsDirPath(file string) string {
	return path.Join(m.Config.Config.DataDir, tlsDir, file)
}

func (m *Module) renewExpiredCerts() error {
	tlsConfig, err := m.loadTLSConfig()
	if err != nil {
		return err
	}
	for _, cert := range tlsConfig.Certificates {
		x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
		if err != nil {
			m.Logger.Error(err)
			continue
		}
		if x509Cert.NotAfter.Before(now()) {
			domain := x509Cert.Subject.CommonName
			m.Logger.Infof("expired cert: renewing cert for %s", domain)
			r, err := m.GetRegistration(domain, false)
			if err != nil {
				return err
			}
			err = m.obtainCert(r)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func (m *Module) ObtainCert(email, domain string) error {
	r, err := m.GetRegistration(domain, true)
	if err != nil {
		return err
	}
	if r == nil {
		r = &Registration{}
	}
	r.Domain = domain
	r.Email = email
	r.AgreedOn = now().Format(time.RFC3339)
	r.Init(m.keystore)
	return m.obtainCert(r)
}

type LetsEncryptError struct{ error }

// ObtainCert obtains a new ssl cert for the given user. Currently uses default
// port 80 and port 443 for challenges.
func (m *Module) obtainCert(r *Registration) error {
	certURL := r.Domain
	// Initialize user and domain
	if certURL == "" {
		return errors.Wrap(fmt.Errorf("no url specified"))
	}
	// hack to URL parse it correctly
	if !strings.HasPrefix(certURL, "https://") && !strings.HasPrefix(certURL, "http://") {
		certURL = "http://" + certURL
	}
	domain, err := url.Parse(certURL)
	if err != nil {
		return err
	}

	if domain.Host == "" {
		return errors.Wrap(fmt.Errorf("no url specified"))
	}

	keyFile := path.Join(tlsDir, domain.Host+".key")
	_, domainKey, err := m.keystore.LoadPrivateKey(keyFile)
	if err != nil {
		return errors.Wrap(err)
	}

	// Initialize the Client
	r.Domain = domain.Host
	c, err := acme.NewClient(defaultCAURL, r, "")
	if err != nil {
		return errors.Wrap(err)
	}

	c.SetChallengeProvider(acme.HTTP01, m)
	registration, err := c.Register()
	if err != nil {
		return errors.Wrap(err)
	}

	r.Registration = registration
	m.SaveRegistration(r)

	err = c.AgreeToTOS()
	if err != nil {
		return errors.Wrap(err)
	}
	cert, errs := c.ObtainCertificate([]string{domain.Host}, true, domainKey, false)
	if len(errs) > 0 {
		lst := []string{}
		for _, e := range errs {
			// todo: check for updated TOS error
			lst = append(lst, e.Error())
		}

		return errors.Wrap(LetsEncryptError{fmt.Errorf(strings.Join(lst, "; "))})
	}

	m.saveCert(cert)
	return errors.Wrap(err)
}

func (m *Module) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if m.challenge == nil {
		router.NotFound(rw)
		return
	}
	// The handler validates the HOST header and request type.
	// For validation it then writes the token the server returned with the challenge
	if strings.HasPrefix(req.Host, m.challenge.domain) &&
		req.URL.Path == acme.HTTP01ChallengePath(m.challenge.token) &&
		req.Method == "GET" {
		rw.Header().Add("Content-Type", "text/plain")
		rw.Write([]byte(m.challenge.keyAuth))
		m.challenge = nil
	} else {
		m.Logger.Warningf("Invalid acme challenge for %s", req.Host)
	}
}

// Present implements the acme.ChallengeProvider.Present
func (m *Module) Present(domain, token, keyAuth string) error {
	if m.challenge != nil {
		m.Logger.Warningf("replacing existing challenge for %s with %s", m.challenge.domain, domain)
	}
	m.challenge = &acmeChallenge{domain: domain, token: token, keyAuth: keyAuth}
	return nil

}

// CleanUp implements the acme.ChallengeProvider.CleanUp
func (m *Module) CleanUp(domain, token, keyAuth string) error {
	m.challenge = nil
	return nil
}
<|endoftext|>