<commit_before>package main

import (
	"fmt"
	"github.com/fzzy/radix/redis"
	"math/rand"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var cmdDb *redis.Client

//used for calling functions using string variable contents
type command func(chan string, string, string, string, []string)

func initMap() map[string]command {
	return map[string]command{
		"source":   command(source),
		"botsnack": command(botsnack),
		"register": command(register),
		"uptime":   command(uptime),
		"web":      command(web),
		"login":    command(login),
		"verify":   command(verify),
		"verified": command(verified),
		"help":     command(help),
		"commands": command(commands),
		"kick":     command(kick),
		"wc":       command(wc),
		"top5":     command(top5),
	}
}

func initCmdRedis() {
	var err error
	cmdDb, err = redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func source(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :https://github.com/heydabop/yaircb"
	fmt.Println(message)
	srvChan <- message
}

func botsnack(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :Kisses commend. Perplexities deprave."
	fmt.Println(message)
	srvChan <- message
}

func register(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :https://anex.us/register/"
	fmt.Println(message)
	srvChan <- message
}

func uptime(srvChan chan string, channel, nick, hostname string, args []string) {
	out, err := exec.Command("uptime").Output()
	message := "PRIVMSG " + channel + " :" + strings.TrimSpace(string(out))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(message)
	srvChan <- message
}

func web(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :https://anex.us/"
	fmt.Println(message)
	srvChan <- message
}

func login(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :https://anex.us/login/"
	fmt.Println(message)
	srvChan <- message
}

func verify(srvChan chan string, channel, nick, hostname string, args []string) {
	var message string
	if len(args) != 2 {
		message = "PRIVMSG " + channel + " :ERROR: Invalid number of arguments"
	} else {
		uname := args[0]
		pin := args[1]
		reply := cmdDb.Cmd("get", uname+"Pin")
		pinDb, err := (reply.Bytes())
		if err != nil {
			message = "PRIVMSG " + channel + " :" + fmt.Sprintf("%s", err)
		} else {
			if string(pinDb) == pin {
				message = "PRIVMSG " + channel + " :You are now verified as " + uname
				cmdDb.Cmd("set", uname+"Host", hostname)
				cmdDb.Cmd("set", uname+"Pin", fmt.Sprintf("%06d", rand.Intn(1000000)))
			} else {
				message = "PRIVMSG " + channel + " :PIN does not match that of " + uname
			}
		}
	}
	fmt.Println(message)
	srvChan <- message
}

func verified(srvChan chan string, channel, nick, hostname string, args []string) {
	var message string
	if len(args) != 1 {
		message = "PRIVMSG " + channel + " :ERROR: Invalid number of arguments"
	} else {
		uname := args[0]
		reply := cmdDb.Cmd("get", uname+"Host")
		hostnameDb, err := reply.Bytes()
		if err != nil {
			message = "PRIVMSG " + channel + " :" + fmt.Sprintf("%s", err)
		} else {
			if hostname == string(hostnameDb) {
				message = "PRIVMSG " + channel + " :You are " + uname + " at " + hostname
			} else {
				message = "PRIVMSG " + channel + " :You are not " + uname
			}
		}
	}
	fmt.Println(message)
	srvChan <- message
}

func help(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :8)"
	fmt.Println(message)
	srvChan <- message
}

func commands(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :"
	for command := range funcMap {
		message += command + " "
	}
	fmt.Println(message)
	srvChan <- message
}

func kick(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "KICK " + channel + " " + nick + " :You don't tell me what to do."
	fmt.Println(message)
	srvChan <- message

	message = "KICK " + channel
	if len(args) < 1 {
		message = "PRIVMSG " + channel + " :ERROR: Invalid number of arguments"
	} else {
		if args[0] == config.Nick {
			return
		}
		message += " " + args[0]
	}
	if len(args) >= 2 {
		message += " :" + strings.Join(args[1:], " ")
	}
	fmt.Println(message)
	srvChan <- message
}

func wc(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :"
	if len(args) != 1 {
		message += "ERROR: Invalid number of arguments"
	} else {
		logFile, err := os.Open(`/home/ross/irclogs/freenode/` + channel + `.log`)
		if err != nil {
			message += fmt.Sprintf("%s", err)
		} else {
			fileStat, err := logFile.Stat()
			if err != nil {
				message += fmt.Sprintf("%s", err)
			} else {
				log := make([]byte, fileStat.Size())
				_, err = logFile.Read(log)
				if err != nil {
					message += fmt.Sprintf("%s", err)
				} else {
					logLines := strings.Split(string(log),"\n")
					nickLine := regexp.MustCompile(`^\d\d:\d\d <[@\+\s]?` + args[0] + `>`)
					matches := 0
					for _, line := range logLines {
						if match := nickLine.FindStringSubmatch(line); match != nil {
							matches++
						}
					}
					message += args[0] + ": " + fmt.Sprintf("%d", matches) + " lines"
				}
			}
		}
	}
	fmt.Println(message)
	srvChan <- message
}

func top5(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :"
	logFile, err := os.Open(`/home/ross/irclogs/freenode/` + channel + `.log`)
	if err != nil {
		message += fmt.Sprintf("%s", err)
	} else {
		fileStat, err := logFile.Stat()
		if err != nil {
			message += fmt.Sprintf("%s", err)
		} else {
			log := make([]byte, fileStat.Size())
			_, err = logFile.Read(log)
			if err != nil {
				message += fmt.Sprintf("%s", err)
			} else {
				logLines := strings.Split(string(log),"\n")
				nickLine := regexp.MustCompile(`^\d\d:\d\d <[@\+\s]?(\S*?)>`)
				matches := make(map[string]uint)
				for _, line := range logLines {
					if match := nickLine.FindStringSubmatch(line); match != nil {
						matches[match[1]]++
					}
				}
				for i := 0; i < 5; i++ {
					maxLines := uint(0)
					var maxNick string
					for nick, lines := range matches {
						if lines > maxLines {
							maxLines = lines
							maxNick = nick
						}
					}
					if maxLines < 1 {
						break
					}
					message += maxNick + ": " + fmt.Sprintf("%d", maxLines) + " lines || "
					delete(matches, maxNick)
				}
			}
		}
	}
	fmt.Println(message)
	srvChan <- message
}
<commit_msg>make top5 case insensitive, pust ZWSP in nicks to avoid excessive client mention detection<commit_after>package main

import (
	"fmt"
	"github.com/fzzy/radix/redis"
	"math/rand"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var cmdDb *redis.Client

//used for calling functions using string variable contents
type command func(chan string, string, string, string, []string)

func initMap() map[string]command {
	return map[string]command{
		"source":   command(source),
		"botsnack": command(botsnack),
		"register": command(register),
		"uptime":   command(uptime),
		"web":      command(web),
		"login":    command(login),
		"verify":   command(verify),
		"verified": command(verified),
		"help":     command(help),
		"commands": command(commands),
		"kick":     command(kick),
		"wc":       command(wc),
		"top5":     command(top5),
	}
}

func initCmdRedis() {
	var err error
	cmdDb, err = redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func source(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :https://github.com/heydabop/yaircb"
	fmt.Println(message)
	srvChan <- message
}

func botsnack(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :Kisses commend. Perplexities deprave."
	fmt.Println(message)
	srvChan <- message
}

func register(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :https://anex.us/register/"
	fmt.Println(message)
	srvChan <- message
}

func uptime(srvChan chan string, channel, nick, hostname string, args []string) {
	out, err := exec.Command("uptime").Output()
	message := "PRIVMSG " + channel + " :" + strings.TrimSpace(string(out))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(message)
	srvChan <- message
}

func web(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :https://anex.us/"
	fmt.Println(message)
	srvChan <- message
}

func login(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :https://anex.us/login/"
	fmt.Println(message)
	srvChan <- message
}

func verify(srvChan chan string, channel, nick, hostname string, args []string) {
	var message string
	if len(args) != 2 {
		message = "PRIVMSG " + channel + " :ERROR: Invalid number of arguments"
	} else {
		uname := args[0]
		pin := args[1]
		reply := cmdDb.Cmd("get", uname+"Pin")
		pinDb, err := (reply.Bytes())
		if err != nil {
			message = "PRIVMSG " + channel + " :" + fmt.Sprintf("%s", err)
		} else {
			if string(pinDb) == pin {
				message = "PRIVMSG " + channel + " :You are now verified as " + uname
				cmdDb.Cmd("set", uname+"Host", hostname)
				cmdDb.Cmd("set", uname+"Pin", fmt.Sprintf("%06d", rand.Intn(1000000)))
			} else {
				message = "PRIVMSG " + channel + " :PIN does not match that of " + uname
			}
		}
	}
	fmt.Println(message)
	srvChan <- message
}

func verified(srvChan chan string, channel, nick, hostname string, args []string) {
	var message string
	if len(args) != 1 {
		message = "PRIVMSG " + channel + " :ERROR: Invalid number of arguments"
	} else {
		uname := args[0]
		reply := cmdDb.Cmd("get", uname+"Host")
		hostnameDb, err := reply.Bytes()
		if err != nil {
			message = "PRIVMSG " + channel + " :" + fmt.Sprintf("%s", err)
		} else {
			if hostname == string(hostnameDb) {
				message = "PRIVMSG " + channel + " :You are " + uname + " at " + hostname
			} else {
				message = "PRIVMSG " + channel + " :You are not " + uname
			}
		}
	}
	fmt.Println(message)
	srvChan <- message
}

func help(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :8)"
	fmt.Println(message)
	srvChan <- message
}

func commands(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :"
	for command := range funcMap {
		message += command + " "
	}
	fmt.Println(message)
	srvChan <- message
}

func kick(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "KICK " + channel + " " + nick + " :You don't tell me what to do."
	fmt.Println(message)
	srvChan <- message

	message = "KICK " + channel
	if len(args) < 1 {
		message = "PRIVMSG " + channel + " :ERROR: Invalid number of arguments"
	} else {
		if args[0] == config.Nick {
			return
		}
		message += " " + args[0]
	}
	if len(args) >= 2 {
		message += " :" + strings.Join(args[1:], " ")
	}
	fmt.Println(message)
	srvChan <- message
}

func wc(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :"
	if len(args) != 1 {
		message += "ERROR: Invalid number of arguments"
	} else {
		logFile, err := os.Open(`/home/ross/irclogs/freenode/` + channel + `.log`)
		if err != nil {
			message += fmt.Sprintf("%s", err)
		} else {
			fileStat, err := logFile.Stat()
			if err != nil {
				message += fmt.Sprintf("%s", err)
			} else {
				log := make([]byte, fileStat.Size())
				_, err = logFile.Read(log)
				if err != nil {
					message += fmt.Sprintf("%s", err)
				} else {
					logLines := strings.Split(string(log),"\n")
					nickLine := regexp.MustCompile(`^\d\d:\d\d <[@\+\s]?` + args[0] + `>`)
					matches := 0
					for _, line := range logLines {
						if match := nickLine.FindStringSubmatch(line); match != nil {
							matches++
						}
					}
					message += args[0] + ": " + fmt.Sprintf("%d", matches) + " lines"
				}
			}
		}
	}
	fmt.Println(message)
	srvChan <- message
}

func top5(srvChan chan string, channel, nick, hostname string, args []string) {
	message := "PRIVMSG " + channel + " :"
	logFile, err := os.Open(`/home/ross/irclogs/freenode/` + channel + `.log`)
	if err != nil {
		message += fmt.Sprintf("%s", err)
	} else {
		fileStat, err := logFile.Stat()
		if err != nil {
			message += fmt.Sprintf("%s", err)
		} else {
			log := make([]byte, fileStat.Size())
			_, err = logFile.Read(log)
			if err != nil {
				message += fmt.Sprintf("%s", err)
			} else {
				logLines := strings.Split(string(log),"\n")
				nickLine := regexp.MustCompile(`^\d\d:\d\d <[@\+\s]?(\S*?)>`)
				matches := make(map[string]uint)
				for _, line := range logLines {
					if match := nickLine.FindStringSubmatch(line); match != nil {
						matches[strings.ToLower(match[1])]++
					}
				}
				for i := 0; i < 5; i++ {
					maxLines := uint(0)
					var maxNick string
					for nick, lines := range matches {
						if lines > maxLines {
							maxLines = lines
							maxNick = nick
						}
					}
					if maxLines < 1 {
						break
					}
					message += string(maxNick[0]) + string('\u200B') + maxNick[1:] + ": " + fmt.Sprintf("%d", maxLines) + " lines || "
					delete(matches, maxNick)
				}
			}
		}
	}
	fmt.Println(message)
	srvChan <- message
}
<|endoftext|>


<commit_before>package redisbroker

import (
	"encoding/json"
	"fmt"
	"log"
	"testing"
	"time"

	"github.com/PuerkitoBio/exp/juggler/internal/redistest"
	"github.com/PuerkitoBio/exp/juggler/msg"
	"github.com/garyburd/redigo/redis"
	"github.com/pborman/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const cap = 2

func testBrokerCallOrRes(t *testing.T, keyFmt string, run func(*Broker, uuid.UUID) (uuid.UUID, error)) {
	cmd, port := redistest.StartServer(t, nil)
	defer cmd.Process.Kill()

	pool := redistest.NewPool(t, ":"+port)
	broker := &Broker{
		Pool:      pool,
		LogFunc:   logIfVerbose,
		CallCap:   cap,
		ResultCap: cap,
	}

	var uuids []uuid.UUID
	// run all on same key
	keyUUID := uuid.NewRandom()
	for i := 0; i <= cap; i++ {
		uid, err := run(broker, keyUUID)
		uuids = append(uuids, uid)
		if i < cap {
			assert.NoError(t, err, "Call %d", i)
		} else {
			assert.Error(t, err, "Call %d", i)
			assert.Contains(t, err.Error(), "list capacity exceeded", "error has expected message")
		}
	}

	// the first 2 msg uuids should be present, in inverted order (LPUSH)
	key := fmt.Sprintf(keyFmt, keyUUID)
	expectUUIDs(t, pool.Get(), key, uuids[1], uuids[0])

	// call on a different URI works fine
	diffKeyUUID := uuid.NewRandom()
	_, err := run(broker, diffKeyUUID)
	assert.NoError(t, err, "Call on different key")

	// popping a value should pop uuids[0]
	rc := pool.Get()
	defer rc.Close()
	_, err = rc.Do("RPOP", key)
	require.NoError(t, err, "RPOP")

	expectUUIDs(t, pool.Get(), key, uuids[1])

	// call should now work on original key
	uid, err := run(broker, keyUUID)
	uuids = append(uuids, uid)
	assert.NoError(t, err, "Call after RPOP")

	expectUUIDs(t, pool.Get(), key, uuids[3], uuids[1])
}

func TestBrokerCall(t *testing.T) {
	connUUID := uuid.NewRandom()
	testBrokerCallOrRes(t, callKey, func(b *Broker, keyParm uuid.UUID) (uuid.UUID, error) {
		cp := &msg.CallPayload{
			ConnUUID: connUUID,
			MsgUUID:  uuid.NewRandom(),
			URI:      keyParm.String(),
		}
		err := b.Call(cp, time.Second)
		return cp.MsgUUID, err
	})
}

func TestBrokerResult(t *testing.T) {
	testBrokerCallOrRes(t, resKey, func(b *Broker, keyParm uuid.UUID) (uuid.UUID, error) {
		rp := &msg.ResPayload{
			ConnUUID: keyParm,
			MsgUUID:  uuid.NewRandom(),
			URI:      "z",
		}
		err := b.Result(rp, time.Second)
		return rp.MsgUUID, err
	})
}

func expectUUIDs(t *testing.T, rc redis.Conn, key string, uuids ...uuid.UUID) {
	defer rc.Close()
	vals, err := redis.ByteSlices(rc.Do("LRANGE", key, 0, -1))
	require.NoError(t, err, "LRANGE")

	if assert.Equal(t, len(uuids), len(vals), "number of items") {
		for i, v := range vals {
			var cp msg.CallPayload
			require.NoError(t, json.Unmarshal(v, &cp), "unmarshal into CallPayload")
			assert.Equal(t, uuids[i], cp.MsgUUID, "expected MsgUUID at %d", i)
		}
	}
}

func logIfVerbose(s string, args ...interface{}) {
	if testing.Verbose() {
		log.Printf(s, args...)
	}
}
<commit_msg>juggler/broker/redisbroker: test publish events<commit_after>package redisbroker

import (
	"encoding/json"
	"fmt"
	"log"
	"sync"
	"testing"
	"time"

	"github.com/PuerkitoBio/exp/juggler/broker"
	"github.com/PuerkitoBio/exp/juggler/internal/redistest"
	"github.com/PuerkitoBio/exp/juggler/msg"
	"github.com/garyburd/redigo/redis"
	"github.com/pborman/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const cap = 2

func testBrokerCallOrRes(t *testing.T, keyFmt string, run func(*Broker, uuid.UUID) (uuid.UUID, error)) {
	cmd, port := redistest.StartServer(t, nil)
	defer cmd.Process.Kill()

	pool := redistest.NewPool(t, ":"+port)
	broker := &Broker{
		Pool:      pool,
		LogFunc:   logIfVerbose,
		CallCap:   cap,
		ResultCap: cap,
	}

	var uuids []uuid.UUID
	// run all on same key
	keyUUID := uuid.NewRandom()
	for i := 0; i <= cap; i++ {
		uid, err := run(broker, keyUUID)
		uuids = append(uuids, uid)
		if i < cap {
			assert.NoError(t, err, "Call %d", i)
		} else {
			assert.Error(t, err, "Call %d", i)
			assert.Contains(t, err.Error(), "list capacity exceeded", "error has expected message")
		}
	}

	// the first 2 msg uuids should be present, in inverted order (LPUSH)
	key := fmt.Sprintf(keyFmt, keyUUID)
	expectUUIDs(t, pool.Get(), key, uuids[1], uuids[0])

	// call on a different URI works fine
	diffKeyUUID := uuid.NewRandom()
	_, err := run(broker, diffKeyUUID)
	assert.NoError(t, err, "Call on different key")

	// popping a value should pop uuids[0]
	rc := pool.Get()
	defer rc.Close()
	_, err = rc.Do("RPOP", key)
	require.NoError(t, err, "RPOP")

	expectUUIDs(t, pool.Get(), key, uuids[1])

	// call should now work on original key
	uid, err := run(broker, keyUUID)
	uuids = append(uuids, uid)
	assert.NoError(t, err, "Call after RPOP")

	expectUUIDs(t, pool.Get(), key, uuids[3], uuids[1])
}

func TestBrokerCall(t *testing.T) {
	connUUID := uuid.NewRandom()
	testBrokerCallOrRes(t, callKey, func(b *Broker, keyParm uuid.UUID) (uuid.UUID, error) {
		cp := &msg.CallPayload{
			ConnUUID: connUUID,
			MsgUUID:  uuid.NewRandom(),
			URI:      keyParm.String(),
		}
		err := b.Call(cp, time.Second)
		return cp.MsgUUID, err
	})
}

func TestBrokerResult(t *testing.T) {
	testBrokerCallOrRes(t, resKey, func(b *Broker, keyParm uuid.UUID) (uuid.UUID, error) {
		rp := &msg.ResPayload{
			ConnUUID: keyParm,
			MsgUUID:  uuid.NewRandom(),
			URI:      "z",
		}
		err := b.Result(rp, time.Second)
		return rp.MsgUUID, err
	})
}

func TestPublish(t *testing.T) {
	cmd, port := redistest.StartServer(t, nil)
	defer cmd.Process.Kill()

	pool := redistest.NewPool(t, ":"+port)
	brk := broker.PubSubBroker(&Broker{
		Pool:    pool,
		Dial:    pool.Dial,
		LogFunc: logIfVerbose,
	})

	psc, err := brk.PubSub()
	require.NoError(t, err, "get PubSubConn")

	// subscribe to channel "a"
	require.NoError(t, psc.Subscribe("a", false), "Subscribe")

	// listen to events on "a"
	var cnt int
	expPlds := []string{`"abc"`, `{"v":3}`}
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer wg.Done()
		for ev := range psc.Events() {
			var want string

			if cnt < len(expPlds) {
				want = expPlds[cnt]
			}
			assert.Equal(t, "a", ev.Channel, "event is from the subscribed channel")
			assert.Equal(t, want, string(ev.Args), "event payload")
			cnt++
		}
	}()

	cases := []struct {
		v  interface{}
		ch string
	}{
		{"abc", "a"},
		{"def", "b"},
		{map[string]interface{}{"v": 3}, "a"},
		{5, "c"},
	}
	for i, c := range cases {
		b, err := json.Marshal(c.v)
		require.NoError(t, err, "marshal case %d", i)
		pp := &msg.PubPayload{MsgUUID: uuid.NewRandom(), Args: b}
		require.NoError(t, brk.Publish(c.ch, pp), "Publish event %d", i)
	}

	require.NoError(t, psc.Close(), "close subscribed connection")
	wg.Wait()
	assert.Equal(t, 2, cnt, "number of events received")
}

func expectUUIDs(t *testing.T, rc redis.Conn, key string, uuids ...uuid.UUID) {
	defer rc.Close()
	vals, err := redis.ByteSlices(rc.Do("LRANGE", key, 0, -1))
	require.NoError(t, err, "LRANGE")

	if assert.Equal(t, len(uuids), len(vals), "number of items") {
		for i, v := range vals {
			var cp msg.CallPayload
			require.NoError(t, json.Unmarshal(v, &cp), "unmarshal into CallPayload")
			assert.Equal(t, uuids[i], cp.MsgUUID, "expected MsgUUID at %d", i)
		}
	}
}

func logIfVerbose(s string, args ...interface{}) {
	if testing.Verbose() {
		log.Printf(s, args...)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 CNI 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 allocator

import (
	"fmt"
	"log"
	"net"
	"os"

	"github.com/containernetworking/cni/pkg/ip"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/containernetworking/cni/plugins/ipam/host-local/backend"
)

type IPAllocator struct {
	// start is inclusive and may be allocated
	start net.IP
	// end is inclusive and may be allocated
	end   net.IP
	conf  *IPAMConfig
	store backend.Store
}

func NewIPAllocator(conf *IPAMConfig, store backend.Store) (*IPAllocator, error) {
	// Can't create an allocator for a network with no addresses, eg
	// a /32 or /31
	ones, masklen := conf.Subnet.Mask.Size()
	if ones > masklen-2 {
		return nil, fmt.Errorf("Network %v too small to allocate from", conf.Subnet)
	}

	var (
		start net.IP
		end   net.IP
		err   error
	)
	start, end, err = networkRange((*net.IPNet)(&conf.Subnet))
	if err != nil {
		return nil, err
	}

	// skip the .0 address
	start = ip.NextIP(start)

	if conf.RangeStart != nil {
		if err := validateRangeIP(conf.RangeStart, (*net.IPNet)(&conf.Subnet), start, end); err != nil {
			return nil, err
		}
		start = conf.RangeStart
	}
	if conf.RangeEnd != nil {
		if err := validateRangeIP(conf.RangeEnd, (*net.IPNet)(&conf.Subnet), start, end); err != nil {
			return nil, err
		}
		end = conf.RangeEnd
	}
	return &IPAllocator{start, end, conf, store}, nil
}

func canonicalizeIP(ip net.IP) (net.IP, error) {
	if ip.To4() != nil {
		return ip.To4(), nil
	} else if ip.To16() != nil {
		return ip.To16(), nil
	}
	return nil, fmt.Errorf("IP %s not v4 nor v6", ip)
}

// Ensures @ip is within @ipnet, and (if given) inclusive of @start and @end
func validateRangeIP(ip net.IP, ipnet *net.IPNet, start net.IP, end net.IP) error {
	var err error

	// Make sure we can compare IPv4 addresses directly
	ip, err = canonicalizeIP(ip)
	if err != nil {
		return err
	}

	if !ipnet.Contains(ip) {
		return fmt.Errorf("%s not in network: %s", ip, ipnet)
	}

	if start != nil {
		start, err = canonicalizeIP(start)
		if err != nil {
			return err
		}
		if len(ip) != len(start) {
			return fmt.Errorf("%s %d not same size IP address as start %s %d", ip, len(ip), start, len(start))
		}
		for i := 0; i < len(ip); i++ {
			if ip[i] > start[i] {
				break
			} else if ip[i] < start[i] {
				return fmt.Errorf("%s outside of network %s with start %s", ip, ipnet, start)
			}
		}
	}

	if end != nil {
		end, err = canonicalizeIP(end)
		if err != nil {
			return err
		}
		if len(ip) != len(end) {
			return fmt.Errorf("%s %d not same size IP address as end %s %d", ip, len(ip), end, len(end))
		}
		for i := 0; i < len(ip); i++ {
			if ip[i] < end[i] {
				break
			} else if ip[i] > end[i] {
				return fmt.Errorf("%s outside of network %s with end %s", ip, ipnet, end)
			}
		}
	}
	return nil
}

// Returns newly allocated IP along with its config
func (a *IPAllocator) Get(id string) (*current.IPConfig, []*types.Route, error) {
	a.store.Lock()
	defer a.store.Unlock()

	gw := a.conf.Gateway
	if gw == nil {
		gw = ip.NextIP(a.conf.Subnet.IP)
	}

	var requestedIP net.IP
	if a.conf.Args != nil {
		requestedIP = a.conf.Args.IP
	}

	if requestedIP != nil {
		if gw != nil && gw.Equal(a.conf.Args.IP) {
			return nil, nil, fmt.Errorf("requested IP must differ gateway IP")
		}

		subnet := net.IPNet{
			IP:   a.conf.Subnet.IP,
			Mask: a.conf.Subnet.Mask,
		}
		err := validateRangeIP(requestedIP, &subnet, a.start, a.end)
		if err != nil {
			return nil, nil, err
		}

		reserved, err := a.store.Reserve(id, requestedIP)
		if err != nil {
			return nil, nil, err
		}

		if reserved {
			ipConfig := &current.IPConfig{
				Version: "4",
				Address: net.IPNet{IP: requestedIP, Mask: a.conf.Subnet.Mask},
				Gateway: gw,
			}
			routes := convertRoutesToCurrent(a.conf.Routes)
			return ipConfig, routes, nil
		}
		return nil, nil, fmt.Errorf("requested IP address %q is not available in network: %s", requestedIP, a.conf.Name)
	}

	startIP, endIP := a.getSearchRange()
	for cur := startIP; ; cur = a.nextIP(cur) {
		// don't allocate gateway IP
		if gw != nil && cur.Equal(gw) {
			continue
		}

		reserved, err := a.store.Reserve(id, cur)
		if err != nil {
			return nil, nil, err
		}
		if reserved {
			ipConfig := &current.IPConfig{
				Version: "4",
				Address: net.IPNet{IP: cur, Mask: a.conf.Subnet.Mask},
				Gateway: gw,
			}
			routes := convertRoutesToCurrent(a.conf.Routes)
			return ipConfig, routes, nil
		}
		// break here to complete the loop
		if cur.Equal(endIP) {
			break
		}
	}
	return nil, nil, fmt.Errorf("no IP addresses available in network: %s", a.conf.Name)
}

// Releases all IPs allocated for the container with given ID
func (a *IPAllocator) Release(id string) error {
	a.store.Lock()
	defer a.store.Unlock()

	return a.store.ReleaseByID(id)
}

// Return the start and end IP addresses of a given subnet, excluding
// the broadcast address (eg, 192.168.1.255)
func networkRange(ipnet *net.IPNet) (net.IP, net.IP, error) {
	if ipnet.IP == nil {
		return nil, nil, fmt.Errorf("missing field %q in IPAM configuration", "subnet")
	}
	ip, err := canonicalizeIP(ipnet.IP)
	if err != nil {
		return nil, nil, fmt.Errorf("IP not v4 nor v6")
	}

	if len(ip) != len(ipnet.Mask) {
		return nil, nil, fmt.Errorf("IPNet IP and Mask version mismatch")
	}

	var end net.IP
	for i := 0; i < len(ip); i++ {
		end = append(end, ip[i]|^ipnet.Mask[i])
	}

	// Exclude the broadcast address for IPv4
	if ip.To4() != nil {
		end[3]--
	}

	return ipnet.IP, end, nil
}

// nextIP returns the next ip of curIP within ipallocator's subnet
func (a *IPAllocator) nextIP(curIP net.IP) net.IP {
	if curIP.Equal(a.end) {
		return a.start
	}
	return ip.NextIP(curIP)
}

// getSearchRange returns the start and end ip based on the last reserved ip
func (a *IPAllocator) getSearchRange() (net.IP, net.IP) {
	var startIP net.IP
	var endIP net.IP
	startFromLastReservedIP := false
	lastReservedIP, err := a.store.LastReservedIP()
	if err != nil && !os.IsNotExist(err) {
		log.Printf("Error retriving last reserved ip: %v", err)
	} else if lastReservedIP != nil {
		subnet := net.IPNet{
			IP:   a.conf.Subnet.IP,
			Mask: a.conf.Subnet.Mask,
		}
		err := validateRangeIP(lastReservedIP, &subnet, a.start, a.end)
		if err == nil {
			startFromLastReservedIP = true
		}
	}
	if startFromLastReservedIP {
		startIP = a.nextIP(lastReservedIP)
		endIP = lastReservedIP
	} else {
		startIP = a.start
		endIP = a.end
	}
	return startIP, endIP
}
<commit_msg>修改引用路径<commit_after>// Copyright 2015 CNI 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 allocator

import (
	"fmt"
	"log"
	"net"
	"os"

	"github.com/containernetworking/cni/pkg/ip"
	"github.com/containernetworking/cni/pkg/types"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/shwinpiocess/bonding-consul/backend"
)

type IPAllocator struct {
	// start is inclusive and may be allocated
	start net.IP
	// end is inclusive and may be allocated
	end   net.IP
	conf  *IPAMConfig
	store backend.Store
}

func NewIPAllocator(conf *IPAMConfig, store backend.Store) (*IPAllocator, error) {
	// Can't create an allocator for a network with no addresses, eg
	// a /32 or /31
	ones, masklen := conf.Subnet.Mask.Size()
	if ones > masklen-2 {
		return nil, fmt.Errorf("Network %v too small to allocate from", conf.Subnet)
	}

	var (
		start net.IP
		end   net.IP
		err   error
	)
	start, end, err = networkRange((*net.IPNet)(&conf.Subnet))
	if err != nil {
		return nil, err
	}

	// skip the .0 address
	start = ip.NextIP(start)

	if conf.RangeStart != nil {
		if err := validateRangeIP(conf.RangeStart, (*net.IPNet)(&conf.Subnet), start, end); err != nil {
			return nil, err
		}
		start = conf.RangeStart
	}
	if conf.RangeEnd != nil {
		if err := validateRangeIP(conf.RangeEnd, (*net.IPNet)(&conf.Subnet), start, end); err != nil {
			return nil, err
		}
		end = conf.RangeEnd
	}
	return &IPAllocator{start, end, conf, store}, nil
}

func canonicalizeIP(ip net.IP) (net.IP, error) {
	if ip.To4() != nil {
		return ip.To4(), nil
	} else if ip.To16() != nil {
		return ip.To16(), nil
	}
	return nil, fmt.Errorf("IP %s not v4 nor v6", ip)
}

// Ensures @ip is within @ipnet, and (if given) inclusive of @start and @end
func validateRangeIP(ip net.IP, ipnet *net.IPNet, start net.IP, end net.IP) error {
	var err error

	// Make sure we can compare IPv4 addresses directly
	ip, err = canonicalizeIP(ip)
	if err != nil {
		return err
	}

	if !ipnet.Contains(ip) {
		return fmt.Errorf("%s not in network: %s", ip, ipnet)
	}

	if start != nil {
		start, err = canonicalizeIP(start)
		if err != nil {
			return err
		}
		if len(ip) != len(start) {
			return fmt.Errorf("%s %d not same size IP address as start %s %d", ip, len(ip), start, len(start))
		}
		for i := 0; i < len(ip); i++ {
			if ip[i] > start[i] {
				break
			} else if ip[i] < start[i] {
				return fmt.Errorf("%s outside of network %s with start %s", ip, ipnet, start)
			}
		}
	}

	if end != nil {
		end, err = canonicalizeIP(end)
		if err != nil {
			return err
		}
		if len(ip) != len(end) {
			return fmt.Errorf("%s %d not same size IP address as end %s %d", ip, len(ip), end, len(end))
		}
		for i := 0; i < len(ip); i++ {
			if ip[i] < end[i] {
				break
			} else if ip[i] > end[i] {
				return fmt.Errorf("%s outside of network %s with end %s", ip, ipnet, end)
			}
		}
	}
	return nil
}

// Returns newly allocated IP along with its config
func (a *IPAllocator) Get(id string) (*current.IPConfig, []*types.Route, error) {
	a.store.Lock()
	defer a.store.Unlock()

	gw := a.conf.Gateway
	if gw == nil {
		gw = ip.NextIP(a.conf.Subnet.IP)
	}

	var requestedIP net.IP
	if a.conf.Args != nil {
		requestedIP = a.conf.Args.IP
	}

	if requestedIP != nil {
		if gw != nil && gw.Equal(a.conf.Args.IP) {
			return nil, nil, fmt.Errorf("requested IP must differ gateway IP")
		}

		subnet := net.IPNet{
			IP:   a.conf.Subnet.IP,
			Mask: a.conf.Subnet.Mask,
		}
		err := validateRangeIP(requestedIP, &subnet, a.start, a.end)
		if err != nil {
			return nil, nil, err
		}

		reserved, err := a.store.Reserve(id, requestedIP)
		if err != nil {
			return nil, nil, err
		}

		if reserved {
			ipConfig := &current.IPConfig{
				Version: "4",
				Address: net.IPNet{IP: requestedIP, Mask: a.conf.Subnet.Mask},
				Gateway: gw,
			}
			routes := convertRoutesToCurrent(a.conf.Routes)
			return ipConfig, routes, nil
		}
		return nil, nil, fmt.Errorf("requested IP address %q is not available in network: %s", requestedIP, a.conf.Name)
	}

	startIP, endIP := a.getSearchRange()
	for cur := startIP; ; cur = a.nextIP(cur) {
		// don't allocate gateway IP
		if gw != nil && cur.Equal(gw) {
			continue
		}

		reserved, err := a.store.Reserve(id, cur)
		if err != nil {
			return nil, nil, err
		}
		if reserved {
			ipConfig := &current.IPConfig{
				Version: "4",
				Address: net.IPNet{IP: cur, Mask: a.conf.Subnet.Mask},
				Gateway: gw,
			}
			routes := convertRoutesToCurrent(a.conf.Routes)
			return ipConfig, routes, nil
		}
		// break here to complete the loop
		if cur.Equal(endIP) {
			break
		}
	}
	return nil, nil, fmt.Errorf("no IP addresses available in network: %s", a.conf.Name)
}

// Releases all IPs allocated for the container with given ID
func (a *IPAllocator) Release(id string) error {
	a.store.Lock()
	defer a.store.Unlock()

	return a.store.ReleaseByID(id)
}

// Return the start and end IP addresses of a given subnet, excluding
// the broadcast address (eg, 192.168.1.255)
func networkRange(ipnet *net.IPNet) (net.IP, net.IP, error) {
	if ipnet.IP == nil {
		return nil, nil, fmt.Errorf("missing field %q in IPAM configuration", "subnet")
	}
	ip, err := canonicalizeIP(ipnet.IP)
	if err != nil {
		return nil, nil, fmt.Errorf("IP not v4 nor v6")
	}

	if len(ip) != len(ipnet.Mask) {
		return nil, nil, fmt.Errorf("IPNet IP and Mask version mismatch")
	}

	var end net.IP
	for i := 0; i < len(ip); i++ {
		end = append(end, ip[i]|^ipnet.Mask[i])
	}

	// Exclude the broadcast address for IPv4
	if ip.To4() != nil {
		end[3]--
	}

	return ipnet.IP, end, nil
}

// nextIP returns the next ip of curIP within ipallocator's subnet
func (a *IPAllocator) nextIP(curIP net.IP) net.IP {
	if curIP.Equal(a.end) {
		return a.start
	}
	return ip.NextIP(curIP)
}

// getSearchRange returns the start and end ip based on the last reserved ip
func (a *IPAllocator) getSearchRange() (net.IP, net.IP) {
	var startIP net.IP
	var endIP net.IP
	startFromLastReservedIP := false
	lastReservedIP, err := a.store.LastReservedIP()
	if err != nil && !os.IsNotExist(err) {
		log.Printf("Error retriving last reserved ip: %v", err)
	} else if lastReservedIP != nil {
		subnet := net.IPNet{
			IP:   a.conf.Subnet.IP,
			Mask: a.conf.Subnet.Mask,
		}
		err := validateRangeIP(lastReservedIP, &subnet, a.start, a.end)
		if err == nil {
			startFromLastReservedIP = true
		}
	}
	if startFromLastReservedIP {
		startIP = a.nextIP(lastReservedIP)
		endIP = lastReservedIP
	} else {
		startIP = a.start
		endIP = a.end
	}
	return startIP, endIP
}
<|endoftext|>


<commit_before>// Copyright (C) 2013 Space Monkey, Inc.

package errors

import (
    "fmt"
)

type ErrorClass struct {
    parent *ErrorClass
    name   string
}

var (
    // base error classes. To construct your own error class, use New.
    SystemError       = &ErrorClass{parent: nil, name: "System Error"}
    HierarchicalError = &ErrorClass{parent: nil, name: "Error"}
)

func New(ec *ErrorClass, name string) *ErrorClass {
    if ec == nil {
        ec = HierarchicalError
    }
    return &ErrorClass{parent: ec, name: name}
}

func (e *ErrorClass) Is(parent *ErrorClass) bool {
    for check := e; check != nil; check = check.parent {
        if check == parent {
            return true
        }
    }
    return false
}

type Error struct {
    err   error
    class *ErrorClass
}

func (e *ErrorClass) Wrap(err error) error {
    if err == nil {
        return nil
    }
    return &Error{err: err, class: e}
}

func (e *ErrorClass) New(format string, args ...interface{}) error {
    return e.Wrap(fmt.Errorf(format, args...))
}

func (e *Error) Error() string {
    return fmt.Sprintf("%s: %s", e.class.name, e.err.Error())
}

func (e *Error) WrappedErr() error {
    return e.err
}

func WrappedErr(err error) error {
    cast, ok := err.(*Error)
    if !ok {
        return err
    }
    return cast.WrappedErr()
}

func (e *Error) Is(ec *ErrorClass) bool {
    return e.class.Is(ec)
}

func (e *ErrorClass) Contains(err error) bool {
    cast, ok := err.(*Error)
    if !ok {
        return SystemError == e
    }
    return cast.Is(e)
}

var (
    // useful error classes
    NotImplementedError = New(nil, "Not Implemented Error")
)
<commit_msg>space monkey internal commit export<commit_after>// Copyright (C) 2013 Space Monkey, Inc.

package errors

import (
    "fmt"
)

type ErrorClass struct {
    parent *ErrorClass
    name   string
}

var (
    // base error classes. To construct your own error class, use New.
    SystemError       = &ErrorClass{parent: nil, name: "System Error"}
    HierarchicalError = &ErrorClass{parent: nil, name: "Error"}
)

func New(ec *ErrorClass, name string) *ErrorClass {
    if ec == nil {
        ec = HierarchicalError
    }
    return &ErrorClass{parent: ec, name: name}
}

func (e *ErrorClass) Is(parent *ErrorClass) bool {
    for check := e; check != nil; check = check.parent {
        if check == parent {
            return true
        }
    }
    return false
}

type Error struct {
    err   error
    class *ErrorClass
}

func (e *ErrorClass) Wrap(err error, classes ...*ErrorClass) error {
    if err == nil {
        return nil
    }
    ec, ok := err.(*Error)
    if !ok {
        return &Error{err: err, class: e}
    }
    if ec.Is(e) {
        return err
    }
    for _, class := range classes {
        if ec.Is(class) {
            return err
        }
    }
    return &Error{err: err, class: e}
}

func (e *ErrorClass) New(format string, args ...interface{}) error {
    return e.Wrap(fmt.Errorf(format, args...))
}

func (e *Error) Error() string {
    return fmt.Sprintf("%s: %s", e.class.name, e.err.Error())
}

func (e *Error) WrappedErr() error {
    return e.err
}

func (e *Error) Class() *ErrorClass {
    return e.class
}

func WrappedErr(err error) error {
    cast, ok := err.(*Error)
    if !ok {
        return err
    }
    return cast.WrappedErr()
}

func (e *Error) Is(ec *ErrorClass) bool {
    return e.class.Is(ec)
}

func (e *ErrorClass) Contains(err error) bool {
    cast, ok := err.(*Error)
    if !ok {
        return SystemError == e
    }
    return cast.Is(e)
}

var (
    // useful error classes
    NotImplementedError = New(nil, "Not Implemented Error")
)
<|endoftext|>


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

package libkbfs

import (
	"testing"

	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/kbfs/kbfscodec"
	"github.com/keybase/kbfs/kbfscrypto"
	"github.com/keybase/kbfs/tlf"
	"github.com/stretchr/testify/require"
)

func TestRootMetadataVersionV3(t *testing.T) {
	tlfID := tlf.FakeID(1, false)

	// All V3 objects should have SegregatedKeyBundlesVer.

	uid := keybase1.MakeTestUID(1)
	bh, err := MakeBareTlfHandle([]keybase1.UID{uid}, nil, nil, nil, nil)
	require.NoError(t, err)

	rmd, err := MakeInitialBareRootMetadataV3(tlfID, bh)
	require.NoError(t, err)

	require.Equal(t, SegregatedKeyBundlesVer, rmd.Version())
}

func TestIsValidRekeyRequestBasicV3(t *testing.T) {
	tlfID := tlf.FakeID(1, false)

	uid := keybase1.MakeTestUID(1)
	bh, err := MakeBareTlfHandle([]keybase1.UID{uid}, nil, nil, nil, nil)
	require.NoError(t, err)

	codec := kbfscodec.NewMsgpack()
	crypto := MakeCryptoCommon(kbfscodec.NewMsgpack())

	brmd, err := MakeInitialBareRootMetadataV3(tlfID, bh)
	extra, err := FakeInitialRekey(
		brmd, crypto, bh, kbfscrypto.TLFPublicKey{})

	newBrmd, err := MakeInitialBareRootMetadataV3(tlfID, bh)
	require.NoError(t, err)
	newExtra, err := FakeInitialRekey(
		newBrmd, crypto, bh, kbfscrypto.TLFPublicKey{})

	ok, err := newBrmd.IsValidRekeyRequest(
		codec, brmd, newBrmd.LastModifyingWriter(), extra, newExtra)
	require.NoError(t, err)
	// Should fail because the copy bit is unset.
	require.False(t, ok)

	// Set the copy bit; note the writer metadata is the same.
	newBrmd.SetWriterMetadataCopiedBit()

	// There's no internal signature to compare, so this should
	// then work.

	ok, err = newBrmd.IsValidRekeyRequest(
		codec, brmd, newBrmd.LastModifyingWriter(), extra, newExtra)
	require.NoError(t, err)
	require.True(t, ok)
}
<commit_msg>bare_root_metadata_v3_test: add TestRootMetadataPublicVersionV3<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"testing"

	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/kbfs/kbfscodec"
	"github.com/keybase/kbfs/kbfscrypto"
	"github.com/keybase/kbfs/tlf"
	"github.com/stretchr/testify/require"
)

func TestRootMetadataVersionV3(t *testing.T) {
	tlfID := tlf.FakeID(1, false)

	// All V3 objects should have SegregatedKeyBundlesVer.

	uid := keybase1.MakeTestUID(1)
	bh, err := MakeBareTlfHandle([]keybase1.UID{uid}, nil, nil, nil, nil)
	require.NoError(t, err)

	rmd, err := MakeInitialBareRootMetadataV3(tlfID, bh)
	require.NoError(t, err)

	require.Equal(t, SegregatedKeyBundlesVer, rmd.Version())
}

func TestIsValidRekeyRequestBasicV3(t *testing.T) {
	tlfID := tlf.FakeID(1, false)

	uid := keybase1.MakeTestUID(1)
	bh, err := MakeBareTlfHandle([]keybase1.UID{uid}, nil, nil, nil, nil)
	require.NoError(t, err)

	codec := kbfscodec.NewMsgpack()
	crypto := MakeCryptoCommon(kbfscodec.NewMsgpack())

	brmd, err := MakeInitialBareRootMetadataV3(tlfID, bh)
	extra, err := FakeInitialRekey(
		brmd, crypto, bh, kbfscrypto.TLFPublicKey{})

	newBrmd, err := MakeInitialBareRootMetadataV3(tlfID, bh)
	require.NoError(t, err)
	newExtra, err := FakeInitialRekey(
		newBrmd, crypto, bh, kbfscrypto.TLFPublicKey{})

	ok, err := newBrmd.IsValidRekeyRequest(
		codec, brmd, newBrmd.LastModifyingWriter(), extra, newExtra)
	require.NoError(t, err)
	// Should fail because the copy bit is unset.
	require.False(t, ok)

	// Set the copy bit; note the writer metadata is the same.
	newBrmd.SetWriterMetadataCopiedBit()

	// There's no internal signature to compare, so this should
	// then work.

	ok, err = newBrmd.IsValidRekeyRequest(
		codec, brmd, newBrmd.LastModifyingWriter(), extra, newExtra)
	require.NoError(t, err)
	require.True(t, ok)
}

func TestRootMetadataPublicVersionV3(t *testing.T) {
	tlfID := tlf.FakeID(1, true)

	uid := keybase1.MakeTestUID(1)
	bh, err := MakeBareTlfHandle([]keybase1.UID{uid}, []keybase1.UID{keybase1.PublicUID}, nil, nil, nil)
	require.NoError(t, err)

	rmd, err := MakeInitialBareRootMetadataV3(tlfID, bh)
	require.NoError(t, err)
	require.Equal(t, SegregatedKeyBundlesVer, rmd.Version())

	bh2, err := rmd.MakeBareTlfHandle(nil)
	require.Equal(t, bh, bh2)
}
<|endoftext|>


<commit_before>package chat

import (
	"fmt"
	"time"

	"github.com/keybase/client/go/chat/globals"
	"github.com/keybase/client/go/chat/types"
	"github.com/keybase/client/go/chat/utils"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/gregor1"
	context "golang.org/x/net/context"
)

type supersedesTransform interface {
	Run(ctx context.Context,
		conv types.UnboxConversationInfo, uid gregor1.UID, originalMsgs []chat1.MessageUnboxed) ([]chat1.MessageUnboxed, error)
}

type getMessagesFunc func(context.Context, types.UnboxConversationInfo, gregor1.UID, []chat1.MessageID,
	*chat1.GetThreadReason) ([]chat1.MessageUnboxed, error)

type basicSupersedesTransformOpts struct {
	UseDeletePlaceholders bool
}

type basicSupersedesTransform struct {
	globals.Contextified
	utils.DebugLabeler

	messagesFunc getMessagesFunc
	opts         basicSupersedesTransformOpts
}

var _ supersedesTransform = (*basicSupersedesTransform)(nil)

func newBasicSupersedesTransform(g *globals.Context, opts basicSupersedesTransformOpts) *basicSupersedesTransform {
	return &basicSupersedesTransform{
		Contextified: globals.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "supersedesTransform", false),
		messagesFunc: g.ConvSource.GetMessages,
		opts:         opts,
	}
}

// This is only relevant for ephemeralMessages that are deleted since we want
// these to show up in the gui as "explode now"
func (t *basicSupersedesTransform) transformDelete(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	mvalid := msg.Valid()
	if !mvalid.IsEphemeral() {
		return nil
	}
	explodedBy := superMsg.Valid().SenderUsername
	mvalid.ClientHeader.EphemeralMetadata.ExplodedBy = &explodedBy
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transformEdit(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	mvalid := msg.Valid()
	var payments []chat1.TextPayment
	if mvalid.ClientHeader.MessageType == chat1.MessageType_TEXT {
		payments = mvalid.MessageBody.Text().Payments
	}
	mvalid.MessageBody = chat1.NewMessageBodyWithText(chat1.MessageText{
		Body:     superMsg.Valid().MessageBody.Edit().Body,
		Payments: payments,
	})
	mvalid.AtMentions = superMsg.Valid().AtMentions
	mvalid.AtMentionUsernames = superMsg.Valid().AtMentionUsernames
	mvalid.ChannelMention = superMsg.Valid().ChannelMention
	mvalid.ChannelNameMentions = superMsg.Valid().ChannelNameMentions
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transformAttachment(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	mvalid := msg.Valid()
	uploaded := superMsg.Valid().MessageBody.Attachmentuploaded()
	attachment := chat1.MessageAttachment{
		Object:   uploaded.Object,
		Previews: uploaded.Previews,
		Metadata: uploaded.Metadata,
		Uploaded: true,
	}
	if len(uploaded.Previews) > 0 {
		attachment.Preview = &uploaded.Previews[0]
	}
	mvalid.MessageBody = chat1.NewMessageBodyWithAttachment(attachment)
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transformReaction(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	if superMsg.Valid().MessageBody.IsNil() {
		return &msg
	}

	reactionMap := msg.Valid().Reactions
	if reactionMap.Reactions == nil {
		reactionMap.Reactions = map[string]map[string]chat1.Reaction{}
	}

	reactionText := superMsg.Valid().MessageBody.Reaction().Body
	reactions, ok := reactionMap.Reactions[reactionText]
	if !ok {
		reactions = map[string]chat1.Reaction{}
	}
	reactions[superMsg.Valid().SenderUsername] = chat1.Reaction{
		ReactionMsgID: superMsg.GetMessageID(),
		Ctime:         superMsg.Valid().ServerHeader.Ctime,
	}
	reactionMap.Reactions[reactionText] = reactions

	mvalid := msg.Valid()
	mvalid.Reactions = reactionMap
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transformUnfurl(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	if superMsg.Valid().MessageBody.IsNil() {
		return &msg
	}
	mvalid := msg.Valid()
	utils.SetUnfurl(&mvalid, superMsg.GetMessageID(), superMsg.Valid().MessageBody.Unfurl().Unfurl)
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transform(ctx context.Context, msg chat1.MessageUnboxed,
	superMsgs []chat1.MessageUnboxed) *chat1.MessageUnboxed {

	newMsg := &msg
	for _, superMsg := range superMsgs {
		if !superMsg.IsValidFull() {
			continue
		} else if newMsg == nil {
			return nil
		}

		switch superMsg.GetMessageType() {
		case chat1.MessageType_DELETE:
			newMsg = t.transformDelete(*newMsg, superMsg)
		case chat1.MessageType_DELETEHISTORY:
			return nil
		case chat1.MessageType_EDIT:
			newMsg = t.transformEdit(*newMsg, superMsg)
		case chat1.MessageType_ATTACHMENTUPLOADED:
			newMsg = t.transformAttachment(*newMsg, superMsg)
		case chat1.MessageType_REACTION:
			newMsg = t.transformReaction(*newMsg, superMsg)
		case chat1.MessageType_UNFURL:
			newMsg = t.transformUnfurl(*newMsg, superMsg)
		}

		t.Debug(ctx, "transformed: original:%v super:%v -> %v",
			newMsg.DebugString(), superMsg.DebugString(), newMsg.DebugString())
	}
	return newMsg
}

func (t *basicSupersedesTransform) SetMessagesFunc(f getMessagesFunc) {
	t.messagesFunc = f
}

func (t *basicSupersedesTransform) Run(ctx context.Context,
	conv types.UnboxConversationInfo, uid gregor1.UID, originalMsgs []chat1.MessageUnboxed) (res []chat1.MessageUnboxed, err error) {
	defer t.Trace(ctx, func() error { return err }, fmt.Sprintf("Run(%s)", conv.GetConvID()))()

	// MessageIDs that supersede
	var superMsgIDs []chat1.MessageID
	// Map from a MessageID to the message that supersedes it It's possible
	// that a message can be 'superseded' my multiple messages, by multiple
	// reactions.
	smap := make(map[chat1.MessageID][]chat1.MessageUnboxed)

	// Collect all superseder messages for messages in the current thread view
	for _, msg := range originalMsgs {
		if msg.IsValid() {
			supersededBy := msg.Valid().ServerHeader.SupersededBy
			if supersededBy > 0 {
				superMsgIDs = append(superMsgIDs, supersededBy)
			}
			superMsgIDs = append(superMsgIDs, msg.Valid().ServerHeader.ReactionIDs...)
			superMsgIDs = append(superMsgIDs, msg.Valid().ServerHeader.UnfurlIDs...)
		}
	}

	// Get superseding messages
	var deleteHistoryUpto chat1.MessageID
	// If there are no superseding messages we still need to run
	// the bottom loop to filter out messages deleted by retention.
	if len(superMsgIDs) > 0 {
		msgs, err := t.messagesFunc(ctx, conv, uid, superMsgIDs, nil)
		if err != nil {
			return nil, err
		}
		for _, m := range msgs {
			if m.IsValid() {
				supersedes, err := utils.GetSupersedes(m)
				if err != nil {
					continue
				}
				for _, super := range supersedes {
					supers, ok := smap[super]
					if !ok {
						supers = []chat1.MessageUnboxed{}
					}
					supers = append(supers, m)
					smap[super] = supers
				}

				delh, err := m.Valid().AsDeleteHistory()
				if err == nil {
					if delh.Upto > deleteHistoryUpto {
						t.Debug(ctx, "found delete history: id: %v", m.GetMessageID())
						deleteHistoryUpto = delh.Upto
					}
				}
			}
		}
	}

	// Run through all messages and transform superseded messages into final state
	var newMsgs []chat1.MessageUnboxed
	xformDelete := func(msgID chat1.MessageID) {
		if t.opts.UseDeletePlaceholders {
			newMsgs = append(newMsgs, utils.CreateHiddenPlaceholder(msgID))
		}
	}
	for i, msg := range originalMsgs {
		if msg.IsValid() {
			newMsg := &originalMsgs[i]
			// If the message is superseded, then transform it and add that
			if superMsgs, ok := smap[msg.GetMessageID()]; ok {
				newMsg = t.transform(ctx, msg, superMsgs)
			}
			if newMsg == nil {
				// Transform might return nil in case of a delete.
				t.Debug(ctx, "skipping: %d because it was deleted", msg.GetMessageID())
				xformDelete(msg.GetMessageID())
				continue
			}
			if newMsg.GetMessageID() < deleteHistoryUpto &&
				chat1.IsDeletableByDeleteHistory(newMsg.GetMessageType()) {
				xformDelete(msg.GetMessageID())
				continue
			}
			if !newMsg.IsValidFull() {
				// Drop the message unless it is ephemeral. It has been deleted
				// locally but not superseded by anything.  Could have been
				// deleted by a delete-history, retention expunge, or was an
				// exploding message.
				mvalid := newMsg.Valid()
				if !mvalid.IsEphemeral() || mvalid.HideExplosion(conv.GetMaxDeletedUpTo(), time.Now()) {
					t.Debug(ctx, "skipping: %d because not valid full", msg.GetMessageID())
					xformDelete(msg.GetMessageID())
					continue
				}
			}
			newMsgs = append(newMsgs, *newMsg)
		} else {
			newMsgs = append(newMsgs, msg)
		}
	}

	return newMsgs, nil
}
<commit_msg>fix crasher transforming edit (#15494)<commit_after>package chat

import (
	"fmt"
	"time"

	"github.com/keybase/client/go/chat/globals"
	"github.com/keybase/client/go/chat/types"
	"github.com/keybase/client/go/chat/utils"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/gregor1"
	context "golang.org/x/net/context"
)

type supersedesTransform interface {
	Run(ctx context.Context,
		conv types.UnboxConversationInfo, uid gregor1.UID, originalMsgs []chat1.MessageUnboxed) ([]chat1.MessageUnboxed, error)
}

type getMessagesFunc func(context.Context, types.UnboxConversationInfo, gregor1.UID, []chat1.MessageID,
	*chat1.GetThreadReason) ([]chat1.MessageUnboxed, error)

type basicSupersedesTransformOpts struct {
	UseDeletePlaceholders bool
}

type basicSupersedesTransform struct {
	globals.Contextified
	utils.DebugLabeler

	messagesFunc getMessagesFunc
	opts         basicSupersedesTransformOpts
}

var _ supersedesTransform = (*basicSupersedesTransform)(nil)

func newBasicSupersedesTransform(g *globals.Context, opts basicSupersedesTransformOpts) *basicSupersedesTransform {
	return &basicSupersedesTransform{
		Contextified: globals.NewContextified(g),
		DebugLabeler: utils.NewDebugLabeler(g.GetLog(), "supersedesTransform", false),
		messagesFunc: g.ConvSource.GetMessages,
		opts:         opts,
	}
}

// This is only relevant for ephemeralMessages that are deleted since we want
// these to show up in the gui as "explode now"
func (t *basicSupersedesTransform) transformDelete(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	mvalid := msg.Valid()
	if !mvalid.IsEphemeral() {
		return nil
	}
	explodedBy := superMsg.Valid().SenderUsername
	mvalid.ClientHeader.EphemeralMetadata.ExplodedBy = &explodedBy
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transformEdit(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	mvalid := msg.Valid()
	var payments []chat1.TextPayment
	if mvalid.MessageBody.IsType(chat1.MessageType_TEXT) {
		payments = mvalid.MessageBody.Text().Payments
	}
	mvalid.MessageBody = chat1.NewMessageBodyWithText(chat1.MessageText{
		Body:     superMsg.Valid().MessageBody.Edit().Body,
		Payments: payments,
	})
	mvalid.AtMentions = superMsg.Valid().AtMentions
	mvalid.AtMentionUsernames = superMsg.Valid().AtMentionUsernames
	mvalid.ChannelMention = superMsg.Valid().ChannelMention
	mvalid.ChannelNameMentions = superMsg.Valid().ChannelNameMentions
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transformAttachment(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	mvalid := msg.Valid()
	uploaded := superMsg.Valid().MessageBody.Attachmentuploaded()
	attachment := chat1.MessageAttachment{
		Object:   uploaded.Object,
		Previews: uploaded.Previews,
		Metadata: uploaded.Metadata,
		Uploaded: true,
	}
	if len(uploaded.Previews) > 0 {
		attachment.Preview = &uploaded.Previews[0]
	}
	mvalid.MessageBody = chat1.NewMessageBodyWithAttachment(attachment)
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transformReaction(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	if superMsg.Valid().MessageBody.IsNil() {
		return &msg
	}

	reactionMap := msg.Valid().Reactions
	if reactionMap.Reactions == nil {
		reactionMap.Reactions = map[string]map[string]chat1.Reaction{}
	}

	reactionText := superMsg.Valid().MessageBody.Reaction().Body
	reactions, ok := reactionMap.Reactions[reactionText]
	if !ok {
		reactions = map[string]chat1.Reaction{}
	}
	reactions[superMsg.Valid().SenderUsername] = chat1.Reaction{
		ReactionMsgID: superMsg.GetMessageID(),
		Ctime:         superMsg.Valid().ServerHeader.Ctime,
	}
	reactionMap.Reactions[reactionText] = reactions

	mvalid := msg.Valid()
	mvalid.Reactions = reactionMap
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transformUnfurl(msg chat1.MessageUnboxed, superMsg chat1.MessageUnboxed) *chat1.MessageUnboxed {
	if superMsg.Valid().MessageBody.IsNil() {
		return &msg
	}
	mvalid := msg.Valid()
	utils.SetUnfurl(&mvalid, superMsg.GetMessageID(), superMsg.Valid().MessageBody.Unfurl().Unfurl)
	newMsg := chat1.NewMessageUnboxedWithValid(mvalid)
	return &newMsg
}

func (t *basicSupersedesTransform) transform(ctx context.Context, msg chat1.MessageUnboxed,
	superMsgs []chat1.MessageUnboxed) *chat1.MessageUnboxed {

	newMsg := &msg
	for _, superMsg := range superMsgs {
		if !superMsg.IsValidFull() {
			continue
		} else if newMsg == nil {
			return nil
		}

		switch superMsg.GetMessageType() {
		case chat1.MessageType_DELETE:
			newMsg = t.transformDelete(*newMsg, superMsg)
		case chat1.MessageType_DELETEHISTORY:
			return nil
		case chat1.MessageType_EDIT:
			newMsg = t.transformEdit(*newMsg, superMsg)
		case chat1.MessageType_ATTACHMENTUPLOADED:
			newMsg = t.transformAttachment(*newMsg, superMsg)
		case chat1.MessageType_REACTION:
			newMsg = t.transformReaction(*newMsg, superMsg)
		case chat1.MessageType_UNFURL:
			newMsg = t.transformUnfurl(*newMsg, superMsg)
		}

		t.Debug(ctx, "transformed: original:%v super:%v -> %v",
			newMsg.DebugString(), superMsg.DebugString(), newMsg.DebugString())
	}
	return newMsg
}

func (t *basicSupersedesTransform) SetMessagesFunc(f getMessagesFunc) {
	t.messagesFunc = f
}

func (t *basicSupersedesTransform) Run(ctx context.Context,
	conv types.UnboxConversationInfo, uid gregor1.UID, originalMsgs []chat1.MessageUnboxed) (res []chat1.MessageUnboxed, err error) {
	defer t.Trace(ctx, func() error { return err }, fmt.Sprintf("Run(%s)", conv.GetConvID()))()

	// MessageIDs that supersede
	var superMsgIDs []chat1.MessageID
	// Map from a MessageID to the message that supersedes it It's possible
	// that a message can be 'superseded' my multiple messages, by multiple
	// reactions.
	smap := make(map[chat1.MessageID][]chat1.MessageUnboxed)

	// Collect all superseder messages for messages in the current thread view
	for _, msg := range originalMsgs {
		if msg.IsValid() {
			supersededBy := msg.Valid().ServerHeader.SupersededBy
			if supersededBy > 0 {
				superMsgIDs = append(superMsgIDs, supersededBy)
			}
			superMsgIDs = append(superMsgIDs, msg.Valid().ServerHeader.ReactionIDs...)
			superMsgIDs = append(superMsgIDs, msg.Valid().ServerHeader.UnfurlIDs...)
		}
	}

	// Get superseding messages
	var deleteHistoryUpto chat1.MessageID
	// If there are no superseding messages we still need to run
	// the bottom loop to filter out messages deleted by retention.
	if len(superMsgIDs) > 0 {
		msgs, err := t.messagesFunc(ctx, conv, uid, superMsgIDs, nil)
		if err != nil {
			return nil, err
		}
		for _, m := range msgs {
			if m.IsValid() {
				supersedes, err := utils.GetSupersedes(m)
				if err != nil {
					continue
				}
				for _, super := range supersedes {
					supers, ok := smap[super]
					if !ok {
						supers = []chat1.MessageUnboxed{}
					}
					supers = append(supers, m)
					smap[super] = supers
				}

				delh, err := m.Valid().AsDeleteHistory()
				if err == nil {
					if delh.Upto > deleteHistoryUpto {
						t.Debug(ctx, "found delete history: id: %v", m.GetMessageID())
						deleteHistoryUpto = delh.Upto
					}
				}
			}
		}
	}

	// Run through all messages and transform superseded messages into final state
	var newMsgs []chat1.MessageUnboxed
	xformDelete := func(msgID chat1.MessageID) {
		if t.opts.UseDeletePlaceholders {
			newMsgs = append(newMsgs, utils.CreateHiddenPlaceholder(msgID))
		}
	}
	for i, msg := range originalMsgs {
		if msg.IsValid() {
			newMsg := &originalMsgs[i]
			// If the message is superseded, then transform it and add that
			if superMsgs, ok := smap[msg.GetMessageID()]; ok {
				newMsg = t.transform(ctx, msg, superMsgs)
			}
			if newMsg == nil {
				// Transform might return nil in case of a delete.
				t.Debug(ctx, "skipping: %d because it was deleted", msg.GetMessageID())
				xformDelete(msg.GetMessageID())
				continue
			}
			if newMsg.GetMessageID() < deleteHistoryUpto &&
				chat1.IsDeletableByDeleteHistory(newMsg.GetMessageType()) {
				xformDelete(msg.GetMessageID())
				continue
			}
			if !newMsg.IsValidFull() {
				// Drop the message unless it is ephemeral. It has been deleted
				// locally but not superseded by anything.  Could have been
				// deleted by a delete-history, retention expunge, or was an
				// exploding message.
				mvalid := newMsg.Valid()
				if !mvalid.IsEphemeral() || mvalid.HideExplosion(conv.GetMaxDeletedUpTo(), time.Now()) {
					t.Debug(ctx, "skipping: %d because not valid full", msg.GetMessageID())
					xformDelete(msg.GetMessageID())
					continue
				}
			}
			newMsgs = append(newMsgs, *newMsg)
		} else {
			newMsgs = append(newMsgs, msg)
		}
	}

	return newMsgs, nil
}
<|endoftext|>


<commit_before>//
// Copyright 2015, Sander van Harmelen
//
// 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 gitlab

// PushEvent represents a push event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#push-events
type PushEvent struct {
	ObjectKind  string `json:"object_kind"`
	Before      string `json:"before"`
	After       string `json:"after"`
	Ref         string `json:"ref"`
	CheckoutSha string `json:"checkout_sha"`
	UserID      int    `json:"user_id"`
	UserName    string `json:"user_name"`
	UserEmail   string `json:"user_email"`
	UserAvatar  string `json:"user_avatar"`
	ProjectID   int    `json:"project_id"`
	Project     struct {
		Name              string               `json:"name"`
		Description       string               `json:"description"`
		AvatarURL         string               `json:"avatar_url"`
		GitSSHURL         string               `json:"git_ssh_url"`
		GitHTTPURL        string               `json:"git_http_url"`
		Namespace         string               `json:"namespace"`
		PathWithNamespace string               `json:"path_with_namespace"`
		DefaultBranch     string               `json:"default_branch"`
		Homepage          string               `json:"homepage"`
		URL               string               `json:"url"`
		SSHURL            string               `json:"ssh_url"`
		HTTPURL           string               `json:"http_url"`
		WebURL            string               `json:"web_url"`
		VisibilityLevel   VisibilityLevelValue `json:"visibility_level"`
	} `json:"project"`
	Repository        *Repository `json:"repository"`
	Commits           []*Commit   `json:"commits"`
	TotalCommitsCount int         `json:"total_commits_count"`
}

// TagEvent represents a tag event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#tag-events
type TagEvent struct {
	ObjectKind        string      `json:"object_kind"`
	Before            string      `json:"before"`
	After             string      `json:"after"`
	Ref               string      `json:"ref"`
	CheckoutSha       string      `json:"checkout_sha"`
	UserID            int         `json:"user_id"`
	UserName          string      `json:"user_name"`
	UserAvatar        string      `json:"user_avatar"`
	ProjectID         int         `json:"project_id"`
	Project           *Project    `json:"project"`
	Repository        *Repository `json:"repository"`
	Commits           []*Commit   `json:"commits"`
	TotalCommitsCount int         `json:"total_commits_count"`
}

// IssueEvent represents a issue event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#issues-events
type IssueEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID          int    `json:"id"`
		Title       string `json:"title"`
		AssigneeID  int    `json:"assignee_id"`
		AuthorID    int    `json:"author_id"`
		ProjectID   int    `json:"project_id"`
		CreatedAt   string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
		UpdatedAt   string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
		Position    int    `json:"position"`
		BranchName  string `json:"branch_name"`
		Description string `json:"description"`
		MilestoneID int    `json:"milestone_id"`
		State       string `json:"state"`
		Iid         int    `json:"iid"`
		URL         string `json:"url"`
		Action      string `json:"action"`
	} `json:"object_attributes"`
	Assignee struct {
		Name      string `json:"name"`
		Username  string `json:"username"`
		AvatarURL string `json:"avatar_url"`
	} `json:"assignee"`
}

// CommitCommentEvent represents a comment on a commit event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-commit
type CommitCommentEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	ProjectID        int         `json:"project_id"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int    `json:"id"`
		Note         string `json:"note"`
		NoteableType string `json:"noteable_type"`
		AuthorID     int    `json:"author_id"`
		CreatedAt    string `json:"created_at"`
		UpdatedAt    string `json:"updated_at"`
		ProjectID    int    `json:"project_id"`
		Attachment   string `json:"attachment"`
		LineCode     string `json:"line_code"`
		CommitID     string `json:"commit_id"`
		NoteableID   int    `json:"noteable_id"`
		System       bool   `json:"system"`
		StDiff       struct {
			Diff        string `json:"diff"`
			NewPath     string `json:"new_path"`
			OldPath     string `json:"old_path"`
			AMode       string `json:"a_mode"`
			BMode       string `json:"b_mode"`
			NewFile     bool   `json:"new_file"`
			RenamedFile bool   `json:"renamed_file"`
			DeletedFile bool   `json:"deleted_file"`
		} `json:"st_diff"`
	} `json:"object_attributes"`
	Commit *Commit `json:"commit"`
}

// MergeCommentEvent represents a comment on a merge event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-merge-request
type MergeCommentEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	ProjectID        int         `json:"project_id"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int    `json:"id"`
		Note         string `json:"note"`
		NoteableType string `json:"noteable_type"`
		AuthorID     int    `json:"author_id"`
		CreatedAt    string `json:"created_at"`
		UpdatedAt    string `json:"updated_at"`
		ProjectID    int    `json:"project_id"`
		Attachment   string `json:"attachment"`
		LineCode     string `json:"line_code"`
		CommitID     string `json:"commit_id"`
		NoteableID   int    `json:"noteable_id"`
		System       bool   `json:"system"`
		StDiff       *Diff  `json:"st_diff"`
		URL          string `json:"url"`
	} `json:"object_attributes"`
	MergeRequest *MergeRequest `json:"merge_request"`
}

// IssueCommentEvent represents a comment on an issue event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-issue
type IssueCommentEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	ProjectID        int         `json:"project_id"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int     `json:"id"`
		Note         string  `json:"note"`
		NoteableType string  `json:"noteable_type"`
		AuthorID     int     `json:"author_id"`
		CreatedAt    string  `json:"created_at"`
		UpdatedAt    string  `json:"updated_at"`
		ProjectID    int     `json:"project_id"`
		Attachment   string  `json:"attachment"`
		LineCode     string  `json:"line_code"`
		CommitID     string  `json:"commit_id"`
		NoteableID   int     `json:"noteable_id"`
		System       bool    `json:"system"`
		StDiff       []*Diff `json:"st_diff"`
		URL          string  `json:"url"`
	} `json:"object_attributes"`
	Issue *Issue `json:"issue"`
}

// SnippetCommentEvent represents a comment on a snippet event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-code-snippet
type SnippetCommentEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	ProjectID        int         `json:"project_id"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int    `json:"id"`
		Note         string `json:"note"`
		NoteableType string `json:"noteable_type"`
		AuthorID     int    `json:"author_id"`
		CreatedAt    string `json:"created_at"`
		UpdatedAt    string `json:"updated_at"`
		ProjectID    int    `json:"project_id"`
		Attachment   string `json:"attachment"`
		LineCode     string `json:"line_code"`
		CommitID     string `json:"commit_id"`
		NoteableID   int    `json:"noteable_id"`
		System       bool   `json:"system"`
		StDiff       *Diff  `json:"st_diff"`
		URL          string `json:"url"`
	} `json:"object_attributes"`
	Snippet *Snippet `json:"snippet"`
}

// MergeEvent represents a merge event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#merge-request-events
type MergeEvent struct {
	ObjectKind       string `json:"object_kind"`
	User             *User  `json:"user"`
	ObjectAttributes struct {
		ID              int         `json:"id"`
		TargetBranch    string      `json:"target_branch"`
		SourceBranch    string      `json:"source_branch"`
		SourceProjectID int         `json:"source_project_id"`
		AuthorID        int         `json:"author_id"`
		AssigneeID      int         `json:"assignee_id"`
		Title           string      `json:"title"`
		CreatedAt       string      `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
		UpdatedAt       string      `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
		StCommits       []*Commit   `json:"st_commits"`
		StDiffs         []*Diff     `json:"st_diffs"`
		MilestoneID     int         `json:"milestone_id"`
		State           string      `json:"state"`
		MergeStatus     string      `json:"merge_status"`
		TargetProjectID int         `json:"target_project_id"`
		Iid             int         `json:"iid"`
		Description     string      `json:"description"`
		Source          *Repository `json:"source"`
		Target          *Repository `json:"target"`
		LastCommit      *Commit     `json:"last_commit"`
		WorkInProgress  bool        `json:"work_in_progress"`
		URL             string      `json:"url"`
		Action          string      `json:"action"`
		Assignee        struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			AvatarURL string `json:"avatar_url"`
		} `json:"assignee"`
	} `json:"object_attributes"`
}

// WikiPageEvent represents a wiki page event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#wiki-page-events
type WikiPageEvent struct {
	ObjectKind string   `json:"object_kind"`
	User       *User    `json:"user"`
	Project    *Project `json:"project"`
	Wiki       struct {
		WebURL            string `json:"web_url"`
		GitSSHURL         string `json:"git_ssh_url"`
		GitHTTPURL        string `json:"git_http_url"`
		PathWithNamespace string `json:"path_with_namespace"`
		DefaultBranch     string `json:"default_branch"`
	} `json:"wiki"`
	ObjectAttributes struct {
		Title   string `json:"title"`
		Content string `json:"content"`
		Format  string `json:"format"`
		Message string `json:"message"`
		Slug    string `json:"slug"`
		URL     string `json:"url"`
		Action  string `json:"action"`
	} `json:"object_attributes"`
}

// PipelineEvent represents a pipline event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#pipeline-events
type PipelineEvent struct {
	ObjectKind       string `json:"object_kind"`
	ObjectAttributes struct {
		ID         int      `json:"id"`
		Ref        string   `json:"ref"`
		Tag        bool     `json:"tag"`
		Sha        string   `json:"sha"`
		BeforeSha  string   `json:"before_sha"`
		Status     string   `json:"status"`
		Stages     []string `json:"stages"`
		CreatedAt  string   `json:"created_at"`
		FinishedAt string   `json:"finished_at"`
		Duration   int      `json:"duration"`
	} `json:"object_attributes"`
	User    *User    `json:"user"`
	Project *Project `json:"project"`
	Commit  *Commit  `json:"commit"`
	Builds  []*Build `json:"builds"`
}
<commit_msg>PipelineEvent type changed<commit_after>//
// Copyright 2015, Sander van Harmelen
//
// 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 gitlab

import "time"

// PushEvent represents a push event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#push-events
type PushEvent struct {
	ObjectKind  string `json:"object_kind"`
	Before      string `json:"before"`
	After       string `json:"after"`
	Ref         string `json:"ref"`
	CheckoutSha string `json:"checkout_sha"`
	UserID      int    `json:"user_id"`
	UserName    string `json:"user_name"`
	UserEmail   string `json:"user_email"`
	UserAvatar  string `json:"user_avatar"`
	ProjectID   int    `json:"project_id"`
	Project     struct {
		Name              string               `json:"name"`
		Description       string               `json:"description"`
		AvatarURL         string               `json:"avatar_url"`
		GitSSHURL         string               `json:"git_ssh_url"`
		GitHTTPURL        string               `json:"git_http_url"`
		Namespace         string               `json:"namespace"`
		PathWithNamespace string               `json:"path_with_namespace"`
		DefaultBranch     string               `json:"default_branch"`
		Homepage          string               `json:"homepage"`
		URL               string               `json:"url"`
		SSHURL            string               `json:"ssh_url"`
		HTTPURL           string               `json:"http_url"`
		WebURL            string               `json:"web_url"`
		VisibilityLevel   VisibilityLevelValue `json:"visibility_level"`
	} `json:"project"`
	Repository        *Repository `json:"repository"`
	Commits           []*Commit   `json:"commits"`
	TotalCommitsCount int         `json:"total_commits_count"`
}

// TagEvent represents a tag event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#tag-events
type TagEvent struct {
	ObjectKind        string      `json:"object_kind"`
	Before            string      `json:"before"`
	After             string      `json:"after"`
	Ref               string      `json:"ref"`
	CheckoutSha       string      `json:"checkout_sha"`
	UserID            int         `json:"user_id"`
	UserName          string      `json:"user_name"`
	UserAvatar        string      `json:"user_avatar"`
	ProjectID         int         `json:"project_id"`
	Project           *Project    `json:"project"`
	Repository        *Repository `json:"repository"`
	Commits           []*Commit   `json:"commits"`
	TotalCommitsCount int         `json:"total_commits_count"`
}

// IssueEvent represents a issue event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#issues-events
type IssueEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID          int    `json:"id"`
		Title       string `json:"title"`
		AssigneeID  int    `json:"assignee_id"`
		AuthorID    int    `json:"author_id"`
		ProjectID   int    `json:"project_id"`
		CreatedAt   string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
		UpdatedAt   string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
		Position    int    `json:"position"`
		BranchName  string `json:"branch_name"`
		Description string `json:"description"`
		MilestoneID int    `json:"milestone_id"`
		State       string `json:"state"`
		Iid         int    `json:"iid"`
		URL         string `json:"url"`
		Action      string `json:"action"`
	} `json:"object_attributes"`
	Assignee struct {
		Name      string `json:"name"`
		Username  string `json:"username"`
		AvatarURL string `json:"avatar_url"`
	} `json:"assignee"`
}

// CommitCommentEvent represents a comment on a commit event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-commit
type CommitCommentEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	ProjectID        int         `json:"project_id"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int    `json:"id"`
		Note         string `json:"note"`
		NoteableType string `json:"noteable_type"`
		AuthorID     int    `json:"author_id"`
		CreatedAt    string `json:"created_at"`
		UpdatedAt    string `json:"updated_at"`
		ProjectID    int    `json:"project_id"`
		Attachment   string `json:"attachment"`
		LineCode     string `json:"line_code"`
		CommitID     string `json:"commit_id"`
		NoteableID   int    `json:"noteable_id"`
		System       bool   `json:"system"`
		StDiff       struct {
			Diff        string `json:"diff"`
			NewPath     string `json:"new_path"`
			OldPath     string `json:"old_path"`
			AMode       string `json:"a_mode"`
			BMode       string `json:"b_mode"`
			NewFile     bool   `json:"new_file"`
			RenamedFile bool   `json:"renamed_file"`
			DeletedFile bool   `json:"deleted_file"`
		} `json:"st_diff"`
	} `json:"object_attributes"`
	Commit *Commit `json:"commit"`
}

// MergeCommentEvent represents a comment on a merge event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-merge-request
type MergeCommentEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	ProjectID        int         `json:"project_id"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int    `json:"id"`
		Note         string `json:"note"`
		NoteableType string `json:"noteable_type"`
		AuthorID     int    `json:"author_id"`
		CreatedAt    string `json:"created_at"`
		UpdatedAt    string `json:"updated_at"`
		ProjectID    int    `json:"project_id"`
		Attachment   string `json:"attachment"`
		LineCode     string `json:"line_code"`
		CommitID     string `json:"commit_id"`
		NoteableID   int    `json:"noteable_id"`
		System       bool   `json:"system"`
		StDiff       *Diff  `json:"st_diff"`
		URL          string `json:"url"`
	} `json:"object_attributes"`
	MergeRequest *MergeRequest `json:"merge_request"`
}

// IssueCommentEvent represents a comment on an issue event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-issue
type IssueCommentEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	ProjectID        int         `json:"project_id"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int     `json:"id"`
		Note         string  `json:"note"`
		NoteableType string  `json:"noteable_type"`
		AuthorID     int     `json:"author_id"`
		CreatedAt    string  `json:"created_at"`
		UpdatedAt    string  `json:"updated_at"`
		ProjectID    int     `json:"project_id"`
		Attachment   string  `json:"attachment"`
		LineCode     string  `json:"line_code"`
		CommitID     string  `json:"commit_id"`
		NoteableID   int     `json:"noteable_id"`
		System       bool    `json:"system"`
		StDiff       []*Diff `json:"st_diff"`
		URL          string  `json:"url"`
	} `json:"object_attributes"`
	Issue *Issue `json:"issue"`
}

// SnippetCommentEvent represents a comment on a snippet event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-code-snippet
type SnippetCommentEvent struct {
	ObjectKind       string      `json:"object_kind"`
	User             *User       `json:"user"`
	ProjectID        int         `json:"project_id"`
	Project          *Project    `json:"project"`
	Repository       *Repository `json:"repository"`
	ObjectAttributes struct {
		ID           int    `json:"id"`
		Note         string `json:"note"`
		NoteableType string `json:"noteable_type"`
		AuthorID     int    `json:"author_id"`
		CreatedAt    string `json:"created_at"`
		UpdatedAt    string `json:"updated_at"`
		ProjectID    int    `json:"project_id"`
		Attachment   string `json:"attachment"`
		LineCode     string `json:"line_code"`
		CommitID     string `json:"commit_id"`
		NoteableID   int    `json:"noteable_id"`
		System       bool   `json:"system"`
		StDiff       *Diff  `json:"st_diff"`
		URL          string `json:"url"`
	} `json:"object_attributes"`
	Snippet *Snippet `json:"snippet"`
}

// MergeEvent represents a merge event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#merge-request-events
type MergeEvent struct {
	ObjectKind       string `json:"object_kind"`
	User             *User  `json:"user"`
	ObjectAttributes struct {
		ID              int         `json:"id"`
		TargetBranch    string      `json:"target_branch"`
		SourceBranch    string      `json:"source_branch"`
		SourceProjectID int         `json:"source_project_id"`
		AuthorID        int         `json:"author_id"`
		AssigneeID      int         `json:"assignee_id"`
		Title           string      `json:"title"`
		CreatedAt       string      `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
		UpdatedAt       string      `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
		StCommits       []*Commit   `json:"st_commits"`
		StDiffs         []*Diff     `json:"st_diffs"`
		MilestoneID     int         `json:"milestone_id"`
		State           string      `json:"state"`
		MergeStatus     string      `json:"merge_status"`
		TargetProjectID int         `json:"target_project_id"`
		Iid             int         `json:"iid"`
		Description     string      `json:"description"`
		Source          *Repository `json:"source"`
		Target          *Repository `json:"target"`
		LastCommit      *Commit     `json:"last_commit"`
		WorkInProgress  bool        `json:"work_in_progress"`
		URL             string      `json:"url"`
		Action          string      `json:"action"`
		Assignee        struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			AvatarURL string `json:"avatar_url"`
		} `json:"assignee"`
	} `json:"object_attributes"`
}

// WikiPageEvent represents a wiki page event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#wiki-page-events
type WikiPageEvent struct {
	ObjectKind string   `json:"object_kind"`
	User       *User    `json:"user"`
	Project    *Project `json:"project"`
	Wiki       struct {
		WebURL            string `json:"web_url"`
		GitSSHURL         string `json:"git_ssh_url"`
		GitHTTPURL        string `json:"git_http_url"`
		PathWithNamespace string `json:"path_with_namespace"`
		DefaultBranch     string `json:"default_branch"`
	} `json:"wiki"`
	ObjectAttributes struct {
		Title   string `json:"title"`
		Content string `json:"content"`
		Format  string `json:"format"`
		Message string `json:"message"`
		Slug    string `json:"slug"`
		URL     string `json:"url"`
		Action  string `json:"action"`
	} `json:"object_attributes"`
}

// PipelineEvent represents a pipline event.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#pipeline-events
type PipelineEvent struct {
	ObjectKind       string `json:"object_kind"`
	ObjectAttributes struct {
		ID         int      `json:"id"`
		Ref        string   `json:"ref"`
		Tag        bool     `json:"tag"`
		Sha        string   `json:"sha"`
		BeforeSha  string   `json:"before_sha"`
		Status     string   `json:"status"`
		Stages     []string `json:"stages"`
		CreatedAt  string   `json:"created_at"`
		FinishedAt string   `json:"finished_at"`
		Duration   int      `json:"duration"`
	} `json:"object_attributes"`
	User struct {
		Name      string `json:"name"`
		Username  string `json:"username"`
		AvatarURL string `json:"avatar_url"`
	} `json:"user"`
	Project struct {
		Name              string      `json:"name"`
		Description       string      `json:"description"`
		WebURL            string      `json:"web_url"`
		AvatarURL         interface{} `json:"avatar_url"`
		GitSSHURL         string      `json:"git_ssh_url"`
		GitHTTPURL        string      `json:"git_http_url"`
		Namespace         string      `json:"namespace"`
		VisibilityLevel   int         `json:"visibility_level"`
		PathWithNamespace string      `json:"path_with_namespace"`
		DefaultBranch     string      `json:"default_branch"`
	} `json:"project"`
	Commit struct {
		ID        string    `json:"id"`
		Message   string    `json:"message"`
		Timestamp time.Time `json:"timestamp"`
		URL       string    `json:"url"`
		Author    struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		} `json:"author"`
	} `json:"commit"`
	Builds []struct {
		ID         int    `json:"id"`
		Stage      string `json:"stage"`
		Name       string `json:"name"`
		Status     string `json:"status"`
		CreatedAt  string `json:"created_at"`
		StartedAt  string `json:"started_at"`
		FinishedAt string `json:"finished_at"`
		When       string `json:"when"`
		Manual     bool   `json:"manual"`
		User       struct {
			Name      string `json:"name"`
			Username  string `json:"username"`
			AvatarURL string `json:"avatar_url"`
		} `json:"user"`
		Runner        string `json:"runner"`
		ArtifactsFile struct {
			Filename string `json:"filename"`
			Size     string `json:"size"`
		} `json:"artifacts_file"`
	} `json:"builds"`
}
<|endoftext|>


<commit_before>package diamond

func Gen(char byte) (string, error) {
	panic("Please implement the Gen function")
}
<commit_msg>Handle error<commit_after>package diamond

import "fmt"

func Gen(char byte) (string, error) {
	if char < 'A' || char > 'Z' {
		return "", fmt.Errorf("char %v is not a valid capital letter", char)
	}
	return "A", nil
}
<|endoftext|>


<commit_before>package srv

import (
	"circuit/exp/file"
	"circuit/use/circuit"
	"os"
)

type App struct{}

func init() {
	circuit.RegisterFunc(App{})
}

func (App) Open(filepath string) circuit.X {
	f, err := os.Open(filepath)
	if err != nil {
		return nil
	}
	return circuit.Ref(file.NewFileServer(f))
}
<commit_msg>telefile<commit_after>package srv

import (
	"circuit/exp/file"
	"circuit/use/circuit"
	"os"
	"time"
)

type App struct{}

func init() {
	circuit.RegisterFunc(App{})
}

func (App) Open(filepath string) circuit.X {
	f, err := os.Open(filepath)
	if err != nil {
		return nil
	}
	circuit.Daemonize(func() { time.Sleep(5*time.Second) })
	return circuit.Ref(file.NewFileServer(f))
}
<|endoftext|>


<commit_before>package conf

import (
	"strings"
	"time"
)

const (
	AppName = "jiacrontab"
	Version = "v1.4.3"
)

var (
	AppService *App
	starTime   = time.Now()
)

type App struct {
	Debug           bool      `json:"debug"`
	HttpListenAddr  string    `json:"http_listen_addr"`
	StaticDir       string    `json:"static_dir"`
	DataFile        string    `json:"data_file"`
	RpcListenAddr   string    `json:"rpc_listen_addr"`
	ServerStartTime time.Time `json:"-"`
	TplDir          string    `json:"tpl_dir"`
	TplExt          string    `json:"tpl_ext"`

	User          string   `json:"user"`
	Passwd        string   `json:"-"`
	AllowCommands []string `json:"allow_commands"`
}

func LoadAppService() {
	app := cf.Section("app")
	AppService = &App{
		Debug:           app.Key("debug").MustBool(false),
		HttpListenAddr:  app.Key("http_listen_addr").MustString("0.0.0.0:20000"),
		StaticDir:       app.Key("static_dir").MustString("/static"),
		TplExt:          ".html",
		TplDir:          "template",
		DataFile:        app.Key("data_file").MustString("data.json"),
		RpcListenAddr:   app.Key("listen").MustString(":20003"),
		User:            app.Key("app_user").MustString("admin"),
		Passwd:          app.Key("app_passwd").MustString("123456"),
		ServerStartTime: starTime,
		AllowCommands:   strings.Split(app.Key("allow_commands").MustString("php,python,node,curl,wget,lua"), ","),
	}
}
<commit_msg>Update app.go<commit_after>package conf

import (
	"strings"
	"time"
)

const (
	AppName = "jiacrontab"
	Version = "v1.4.4"
)

var (
	AppService *App
	starTime   = time.Now()
)

type App struct {
	Debug           bool      `json:"debug"`
	HttpListenAddr  string    `json:"http_listen_addr"`
	StaticDir       string    `json:"static_dir"`
	DataFile        string    `json:"data_file"`
	RpcListenAddr   string    `json:"rpc_listen_addr"`
	ServerStartTime time.Time `json:"-"`
	TplDir          string    `json:"tpl_dir"`
	TplExt          string    `json:"tpl_ext"`

	User          string   `json:"user"`
	Passwd        string   `json:"-"`
	AllowCommands []string `json:"allow_commands"`
}

func LoadAppService() {
	app := cf.Section("app")
	AppService = &App{
		Debug:           app.Key("debug").MustBool(false),
		HttpListenAddr:  app.Key("http_listen_addr").MustString("0.0.0.0:20000"),
		StaticDir:       app.Key("static_dir").MustString("/static"),
		TplExt:          ".html",
		TplDir:          "template",
		DataFile:        app.Key("data_file").MustString("data.json"),
		RpcListenAddr:   app.Key("listen").MustString(":20003"),
		User:            app.Key("app_user").MustString("admin"),
		Passwd:          app.Key("app_passwd").MustString("123456"),
		ServerStartTime: starTime,
		AllowCommands:   strings.Split(app.Key("allow_commands").MustString("php,python,node,curl,wget,lua"), ","),
	}
}
<|endoftext|>


<commit_before>// Package tracing consolidates the setup logic for using opencensus tracing and exporting the
// metrics.
package tracing

import (
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"go.opencensus.io/trace"
	"go.skia.org/infra/go/skerr"
)

// Initialize sets up trace options and exporting for this application. It will sample the given
// proportion of traces. All traces will have the given key-value pairs attached.
func Initialize(traceSampleProportion float64, projectID string, defaultAttrs map[string]interface{}) error {
	exporter, err := stackdriver.NewExporter(stackdriver.Options{
		ProjectID: projectID,
		// Use 10 times the default
		TraceSpansBufferMaxBytes: 80_000_000,
		// It is not clear what the default interval is. One minute seems to be a good value since
		// that is the same as our Prometheus metrics are reported.
		ReportingInterval:      time.Minute,
		DefaultTraceAttributes: defaultAttrs,
	})
	if err != nil {
		return skerr.Wrap(err)
	}

	trace.RegisterExporter(exporter)
	sampler := trace.ProbabilitySampler(traceSampleProportion)
	trace.ApplyConfig(trace.Config{DefaultSampler: sampler})
	return nil
}
<commit_msg>[infra] Add required service account permission to docs<commit_after>// Package tracing consolidates the setup logic for using opencensus tracing and exporting the
// metrics to https://cloud.google.com/trace. In order to authenticate to the correct API, any
// service account that uses this package must have the Cloud Trace Agent Role in gcp.
package tracing

import (
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"go.opencensus.io/trace"
	"go.skia.org/infra/go/skerr"
)

// Initialize sets up trace options and exporting for this application. It will sample the given
// proportion of traces. All traces will have the given key-value pairs attached.
func Initialize(traceSampleProportion float64, projectID string, defaultAttrs map[string]interface{}) error {
	exporter, err := stackdriver.NewExporter(stackdriver.Options{
		ProjectID: projectID,
		// Use 10 times the default
		TraceSpansBufferMaxBytes: 80_000_000,
		// It is not clear what the default interval is. One minute seems to be a good value since
		// that is the same as our Prometheus metrics are reported.
		ReportingInterval:      time.Minute,
		DefaultTraceAttributes: defaultAttrs,
	})
	if err != nil {
		return skerr.Wrap(err)
	}

	trace.RegisterExporter(exporter)
	sampler := trace.ProbabilitySampler(traceSampleProportion)
	trace.ApplyConfig(trace.Config{DefaultSampler: sampler})
	return nil
}
<|endoftext|>