<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/grokify/gotilla/fmt/fmtutil"

	"github.com/grokify/chathooks/src/adapters"
	"github.com/grokify/chathooks/src/config"
	"github.com/grokify/chathooks/src/util"
	cc "github.com/grokify/commonchat"
	log "github.com/sirupsen/logrus"
	"github.com/valyala/fasthttp"

	"github.com/grokify/chathooks/src/handlers/aha"
	"github.com/grokify/chathooks/src/handlers/appsignal"
	"github.com/grokify/chathooks/src/handlers/apteligent"
	"github.com/grokify/chathooks/src/handlers/circleci"
	"github.com/grokify/chathooks/src/handlers/codeship"
	"github.com/grokify/chathooks/src/handlers/confluence"
	"github.com/grokify/chathooks/src/handlers/datadog"
	"github.com/grokify/chathooks/src/handlers/deskdotcom"
	"github.com/grokify/chathooks/src/handlers/enchant"
	"github.com/grokify/chathooks/src/handlers/gosquared"
	"github.com/grokify/chathooks/src/handlers/gosquared2"
	"github.com/grokify/chathooks/src/handlers/heroku"
	"github.com/grokify/chathooks/src/handlers/librato"
	"github.com/grokify/chathooks/src/handlers/magnumci"
	"github.com/grokify/chathooks/src/handlers/marketo"
	"github.com/grokify/chathooks/src/handlers/opsgenie"
	"github.com/grokify/chathooks/src/handlers/papertrail"
	"github.com/grokify/chathooks/src/handlers/pingdom"
	"github.com/grokify/chathooks/src/handlers/raygun"
	"github.com/grokify/chathooks/src/handlers/runscope"
	"github.com/grokify/chathooks/src/handlers/semaphore"
	"github.com/grokify/chathooks/src/handlers/statuspage"
	"github.com/grokify/chathooks/src/handlers/travisci"
	"github.com/grokify/chathooks/src/handlers/userlike"
	"github.com/grokify/chathooks/src/handlers/victorops"
)

const (
	GLIP_WEBHOOK_ENV  = "GLIP_WEBHOOK"
	SLACK_WEBHOOK_ENV = "SLACK_WEBHOOK"
)

type Sender struct {
	Adapter adapters.Adapter
}

func (sender *Sender) SendCcMessage(ccMsg cc.Message, err error) {
	if err != nil {
		panic(fmt.Sprintf("Bad Test Message: %v\n", err))
	}
	var resMsg interface{}
	req, resp, err := sender.Adapter.SendMessage(ccMsg, &resMsg)
	fmt.Printf("RESPONSE_STATUS_CODE [%v]\n", resp.StatusCode())
	if err != nil {
		fmt.Printf("ERROR [%v]\n", err)
	}
	fasthttp.ReleaseRequest(req)
	fasthttp.ReleaseResponse(resp)
}

func main() {
	log.SetLevel(log.DebugLevel)

	guidPointer := flag.String("guid", "", "Glip webhook GUID or URL")
	examplePointer := flag.String("example", "", "Example message type")
	adapterType := flag.String("adapter", "", "Adapter")

	flag.Parse()
	webhookURLOrUID := strings.TrimSpace(*guidPointer)
	example := strings.ToLower(strings.TrimSpace(*examplePointer))

	fmt.Printf("LENGUID[%v]\n", len(webhookURLOrUID))
	fmt.Printf("GUID [%v]\n", webhookURLOrUID)
	fmt.Printf("EXAMPLE [%v]\n", example)

	if len(example) < 1 {
		panic("Usage: send_example.go -hook=<GUID> -adapter=glip -example=raygun")
	}

	cfg := config.Configuration{
		IconBaseURL:    "https://grokify.github.io/chathooks/icons/",
		LogrusLogLevel: log.DebugLevel}

	sender := Sender{}
	if *adapterType == "glip" {
		if len(webhookURLOrUID) < 1 {
			webhookURLOrUID = os.Getenv(GLIP_WEBHOOK_ENV)
			fmt.Printf("GLIP_GUID_ENV [%v]\n", webhookURLOrUID)
		}
		adapter, err := adapters.NewGlipAdapter(webhookURLOrUID)
		if err != nil {
			panic("Incorrect Webhook GUID or URL")
		}
		sender.Adapter = adapter
	} else if *adapterType == "slack" {
		if len(webhookURLOrUID) < 1 {
			webhookURLOrUID = os.Getenv(SLACK_WEBHOOK_ENV)
			fmt.Printf("SLACK_GUID_ENV [%v]\n", webhookURLOrUID)
		}
		adapter, err := adapters.NewSlackAdapter(webhookURLOrUID)
		if err != nil {
			panic("Incorrect Webhook GUID or URL")
		}
		sender.Adapter = adapter
	} else {
		panic("Invalid Adapter")
	}

	exampleData, err := util.NewExampleData()
	if err != nil {
		panic(fmt.Sprintf("Invalid Example Data: %v\n", err))
	}
	fmtutil.PrintJSON(exampleData)

	switch example {
	case "aha":
		source := exampleData.Data[aha.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(aha.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "appsignal":
		source := exampleData.Data[appsignal.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(appsignal.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "apteligent":
		source := exampleData.Data[apteligent.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(apteligent.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "circleci":
		sender.SendCcMessage(circleci.ExampleMessage(cfg, exampleData))
	case "codeship":
		sender.SendCcMessage(codeship.ExampleMessage(cfg, exampleData))
	case "confluence":
		source := exampleData.Data[confluence.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(confluence.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "datadog":
		sender.SendCcMessage(datadog.ExampleMessage(cfg, exampleData))
	case "deskdotcom":
		source := exampleData.Data[deskdotcom.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(deskdotcom.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "enchant":
		sender.SendCcMessage(enchant.ExampleMessage(cfg, exampleData))
	case "gosquared":
		source := exampleData.Data[gosquared.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(gosquared.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "gosquared2":
		source := exampleData.Data[gosquared.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(gosquared2.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "heroku":
		sender.SendCcMessage(heroku.ExampleMessage(cfg, exampleData))
	case "librato":
		source := exampleData.Data[librato.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(librato.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "magnumci":
		sender.SendCcMessage(magnumci.ExampleMessage(cfg, exampleData))
	case "marketo":
		source := exampleData.Data[marketo.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(marketo.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "opsgenie":
		source := exampleData.Data[opsgenie.HandlerKey]
		for i, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(opsgenie.ExampleMessage(cfg, exampleData, eventSlug))
			if i == 8 {
				time.Sleep(2000 * time.Millisecond)
			}
		}
	case "papertrail":
		source := exampleData.Data[papertrail.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(papertrail.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "pingdom":
		source := exampleData.Data[pingdom.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(pingdom.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "raygun":
		sender.SendCcMessage(raygun.ExampleMessage(cfg, exampleData))
	case "runscope":
		sender.SendCcMessage(runscope.ExampleMessage(cfg, exampleData))
	case "semaphore":
		source := exampleData.Data[semaphore.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(semaphore.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "statuspage":
		source := exampleData.Data[statuspage.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(statuspage.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "travisci":
		sender.SendCcMessage(travisci.ExampleMessage(cfg, exampleData))
	case "userlike":
		source := exampleData.Data[userlike.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(userlike.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "victorops":
		sender.SendCcMessage(victorops.ExampleMessage(cfg, exampleData))
	default:
		panic(fmt.Sprintf("Unknown webhook source %v\n", example))
	}
}
<commit_msg>update examples/local_send<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/grokify/gotilla/fmt/fmtutil"

	//"github.com/grokify/chathooks/src/adapters"
	"github.com/grokify/chathooks/src/config"
	"github.com/grokify/chathooks/src/util"
	cc "github.com/grokify/commonchat"
	ccglip "github.com/grokify/commonchat/glip"
	ccslack "github.com/grokify/commonchat/slack"
	log "github.com/sirupsen/logrus"
	"github.com/valyala/fasthttp"

	"github.com/grokify/chathooks/src/handlers/aha"
	"github.com/grokify/chathooks/src/handlers/appsignal"
	"github.com/grokify/chathooks/src/handlers/apteligent"
	"github.com/grokify/chathooks/src/handlers/circleci"
	"github.com/grokify/chathooks/src/handlers/codeship"
	"github.com/grokify/chathooks/src/handlers/confluence"
	"github.com/grokify/chathooks/src/handlers/datadog"
	"github.com/grokify/chathooks/src/handlers/deskdotcom"
	"github.com/grokify/chathooks/src/handlers/enchant"
	"github.com/grokify/chathooks/src/handlers/gosquared"
	"github.com/grokify/chathooks/src/handlers/gosquared2"
	"github.com/grokify/chathooks/src/handlers/heroku"
	"github.com/grokify/chathooks/src/handlers/librato"
	"github.com/grokify/chathooks/src/handlers/magnumci"
	"github.com/grokify/chathooks/src/handlers/marketo"
	"github.com/grokify/chathooks/src/handlers/opsgenie"
	"github.com/grokify/chathooks/src/handlers/papertrail"
	"github.com/grokify/chathooks/src/handlers/pingdom"
	"github.com/grokify/chathooks/src/handlers/raygun"
	"github.com/grokify/chathooks/src/handlers/runscope"
	"github.com/grokify/chathooks/src/handlers/semaphore"
	"github.com/grokify/chathooks/src/handlers/statuspage"
	"github.com/grokify/chathooks/src/handlers/travisci"
	"github.com/grokify/chathooks/src/handlers/userlike"
	"github.com/grokify/chathooks/src/handlers/victorops"
)

const (
	GLIP_WEBHOOK_ENV  = "GLIP_WEBHOOK"
	SLACK_WEBHOOK_ENV = "SLACK_WEBHOOK"
)

type Sender struct {
	Adapter cc.Adapter
}

func (sender *Sender) SendCcMessage(ccMsg cc.Message, err error) {
	if err != nil {
		panic(fmt.Sprintf("Bad Test Message: %v\n", err))
	}
	var resMsg interface{}
	req, resp, err := sender.Adapter.SendMessage(ccMsg, &resMsg)
	fmt.Printf("RESPONSE_STATUS_CODE [%v]\n", resp.StatusCode())
	if err != nil {
		fmt.Printf("ERROR [%v]\n", err)
	}
	fasthttp.ReleaseRequest(req)
	fasthttp.ReleaseResponse(resp)
}

func main() {
	log.SetLevel(log.DebugLevel)

	guidPointer := flag.String("guid", "", "Glip webhook GUID or URL")
	examplePointer := flag.String("example", "", "Example message type")
	adapterType := flag.String("adapter", "", "Adapter")

	flag.Parse()
	webhookURLOrUID := strings.TrimSpace(*guidPointer)
	example := strings.ToLower(strings.TrimSpace(*examplePointer))

	fmt.Printf("LENGUID[%v]\n", len(webhookURLOrUID))
	fmt.Printf("GUID [%v]\n", webhookURLOrUID)
	fmt.Printf("EXAMPLE [%v]\n", example)

	if len(example) < 1 {
		panic("Usage: send_example.go -hook=<GUID> -adapter=glip -example=raygun")
	}

	cfg := config.Configuration{
		IconBaseURL:    "https://grokify.github.io/chathooks/icons/",
		LogrusLogLevel: log.DebugLevel}

	sender := Sender{}
	if *adapterType == "glip" {
		if len(webhookURLOrUID) < 1 {
			webhookURLOrUID = os.Getenv(GLIP_WEBHOOK_ENV)
			fmt.Printf("GLIP_GUID_ENV [%v]\n", webhookURLOrUID)
		}
		adapter, err := ccglip.NewGlipAdapter(webhookURLOrUID)
		if err != nil {
			panic("Incorrect Webhook GUID or URL")
		}
		sender.Adapter = adapter
	} else if *adapterType == "slack" {
		if len(webhookURLOrUID) < 1 {
			webhookURLOrUID = os.Getenv(SLACK_WEBHOOK_ENV)
			fmt.Printf("SLACK_GUID_ENV [%v]\n", webhookURLOrUID)
		}
		adapter, err := ccslack.NewSlackAdapter(webhookURLOrUID)
		if err != nil {
			panic("Incorrect Webhook GUID or URL")
		}
		sender.Adapter = adapter
	} else {
		panic("Invalid Adapter")
	}

	exampleData, err := util.NewExampleData()
	if err != nil {
		panic(fmt.Sprintf("Invalid Example Data: %v\n", err))
	}
	fmtutil.PrintJSON(exampleData)

	switch example {
	case "aha":
		source := exampleData.Data[aha.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(aha.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "appsignal":
		source := exampleData.Data[appsignal.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(appsignal.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "apteligent":
		source := exampleData.Data[apteligent.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(apteligent.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "circleci":
		sender.SendCcMessage(circleci.ExampleMessage(cfg, exampleData))
	case "codeship":
		sender.SendCcMessage(codeship.ExampleMessage(cfg, exampleData))
	case "confluence":
		source := exampleData.Data[confluence.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(confluence.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "datadog":
		sender.SendCcMessage(datadog.ExampleMessage(cfg, exampleData))
	case "deskdotcom":
		source := exampleData.Data[deskdotcom.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(deskdotcom.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "enchant":
		sender.SendCcMessage(enchant.ExampleMessage(cfg, exampleData))
	case "gosquared":
		source := exampleData.Data[gosquared.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(gosquared.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "gosquared2":
		source := exampleData.Data[gosquared.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(gosquared2.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "heroku":
		sender.SendCcMessage(heroku.ExampleMessage(cfg, exampleData))
	case "librato":
		source := exampleData.Data[librato.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(librato.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "magnumci":
		sender.SendCcMessage(magnumci.ExampleMessage(cfg, exampleData))
	case "marketo":
		source := exampleData.Data[marketo.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(marketo.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "opsgenie":
		source := exampleData.Data[opsgenie.HandlerKey]
		for i, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(opsgenie.ExampleMessage(cfg, exampleData, eventSlug))
			if i == 8 {
				time.Sleep(2000 * time.Millisecond)
			}
		}
	case "papertrail":
		source := exampleData.Data[papertrail.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(papertrail.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "pingdom":
		source := exampleData.Data[pingdom.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(pingdom.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "raygun":
		sender.SendCcMessage(raygun.ExampleMessage(cfg, exampleData))
	case "runscope":
		sender.SendCcMessage(runscope.ExampleMessage(cfg, exampleData))
	case "semaphore":
		source := exampleData.Data[semaphore.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(semaphore.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "statuspage":
		source := exampleData.Data[statuspage.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(statuspage.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "travisci":
		sender.SendCcMessage(travisci.ExampleMessage(cfg, exampleData))
	case "userlike":
		source := exampleData.Data[userlike.HandlerKey]
		for _, eventSlug := range source.EventSlugs {
			sender.SendCcMessage(userlike.ExampleMessage(cfg, exampleData, eventSlug))
		}
	case "victorops":
		sender.SendCcMessage(victorops.ExampleMessage(cfg, exampleData))
	default:
		panic(fmt.Sprintf("Unknown webhook source %v\n", example))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"

	"github.com/rubblelabs/ripple/data"
	"github.com/rubblelabs/ripple/websockets"
)

func checkErr(err error, quit bool) {
	if err != nil {
		log.Println(err.Error())
		if quit {
			os.Exit(1)
		}
	}
}

var (
	host    = flag.String("host", "wss://s-east.ripple.com:443", "websockets host to connect to")
	account = flag.String("account", "", "optional account to monitor")
)

func main() {
	flag.Parse()
	var (
		filter *data.Account
		err    error
	)
	if len(*account) > 0 {
		filter, err = data.NewAccountFromAddress(*account)
		checkErr(err, true)
	}

	r, err := websockets.NewRemote(*host)
	checkErr(err, true)

	confirmation, err := r.Subscribe(true, true, false, false)
	checkErr(err, true)
	log.Printf("Subscribed at: %d ", confirmation.LedgerSequence)

	for {
		msg, ok := <-r.Incoming
		if !ok {
			return
		}
		switch msg := msg.(type) {
		case *websockets.TransactionStreamMsg:
			trades, err := data.NewTradeSlice(&msg.Transaction)
			checkErr(err, false)
			if filter != nil {
				trades = trades.Filter(*filter)
			}
			for _, trade := range trades {
				log.Println(trade)
			}
		}
	}
}
<commit_msg>Fill in missing LedgerSequence in trades tool<commit_after>package main

import (
	"flag"
	"log"
	"os"

	"github.com/rubblelabs/ripple/data"
	"github.com/rubblelabs/ripple/websockets"
)

func checkErr(err error, quit bool) {
	if err != nil {
		log.Println(err.Error())
		if quit {
			os.Exit(1)
		}
	}
}

var (
	host    = flag.String("host", "wss://s-east.ripple.com:443", "websockets host to connect to")
	account = flag.String("account", "", "optional account to monitor")
)

func main() {
	flag.Parse()
	var (
		filter *data.Account
		err    error
	)
	if len(*account) > 0 {
		filter, err = data.NewAccountFromAddress(*account)
		checkErr(err, true)
	}

	r, err := websockets.NewRemote(*host)
	checkErr(err, true)

	confirmation, err := r.Subscribe(true, true, false, false)
	checkErr(err, true)
	log.Printf("Subscribed at: %d ", confirmation.LedgerSequence)

	for {
		msg, ok := <-r.Incoming
		if !ok {
			return
		}
		switch msg := msg.(type) {
		case *websockets.TransactionStreamMsg:
			msg.Transaction.LedgerSequence = msg.LedgerSequence
			trades, err := data.NewTradeSlice(&msg.Transaction)
			checkErr(err, false)
			if filter != nil {
				trades = trades.Filter(*filter)
			}
			for _, trade := range trades {
				log.Println(trade)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"

	"github.com/rosshendrickson-wf/education/examples/toyserver/message"
)

//1000ms/sec / 900FPS = 1.111.. ms per frame
//1000ms/sec / 450FPS = 2.222.. ms per frame
//Increase in execution time: 1.111.. ms
//
//1000ms/sec / 60FPS = 16.666.. ms per frame
//1000ms/sec / 56.25FPS = 17.777.. ms per frame

// read from the connection ever 5ms and apply the updates
func runShip(address, serverPort, clientPort string) {

	commands := make(chan *message.Vector, 1000)
	ship := &Ship{commands: commands}

	ship.Connect(address, serverPort, clientPort)

	//ApplyUpdates := time.NewTicker(time.Millisecond * 5).C
	SendCommands := time.NewTicker(time.Millisecond * 500).C
	//Display := time.NewTicker(time.Millisecond * 120).C
	DisplayFrames := time.NewTicker(time.Second * 1).C
	Random := time.NewTicker(time.Millisecond * 5).C
	DieTime := time.NewTicker(time.Second * 10).C

OuterLoop:
	for {
		select {
		//	case <-ApplyUpdates:
		//		ship.ApplyUpdates()
		case <-SendCommands:
			ship.SendCommands()
		//	case <-Display:
		//		ship.Display()
		case <-DisplayFrames:
			ship.DisplayFrames()
		case <-Random:
			ship.commands <- RandomMove()
		case <-DieTime:
			ship.Close()
			log.Printf("Ship %+v dead", ship)
			break OuterLoop
		default:
		}
	}
}

type Ship struct {
	xp         int
	yp         int
	health     int
	name       [8]byte
	conn       *net.UDPConn
	sconn      *net.UDPConn
	commands   chan *message.Vector
	updates    chan []byte
	shipTime   int
	serverTime int
	frames     int
	lock       sync.Mutex
	stop       bool
	revision   int
	serverAddr net.Addr
}

func (s *Ship) Connect(address, serverPort, clientPort string) {

	// spin off a goroutine to read from the connection
	addr, err := net.ResolveUDPAddr("udp4", ":"+clientPort)
	if err != nil {
		log.Fatal(err)
	}

	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		log.Fatal(err)
	}

	saddr, err := net.ResolveUDPAddr("udp4", address+":"+serverPort)
	if err != nil {
		log.Fatal(err)
	}
	s.serverAddr = saddr

	sconn, err := net.DialUDP("udp", nil, saddr)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	s.sconn = sconn

	go func() {
		for {
			defer conn.Close()
			connbuf := bufio.NewReader(conn)
			s.handleUpdate(conn, connbuf)
			if s.Stop() {
				return
			}
		}
	}()

	s.conn = conn
}

func (s *Ship) Close() {
	s.lock.Lock()
	s.stop = true
	s.lock.Unlock()
}

func (s *Ship) Stop() bool {
	s.lock.Lock()
	result := s.stop
	s.lock.Unlock()
	return result
}

func (s *Ship) handleUpdate(conn *net.UDPConn, reader *bufio.Reader) {
	var buf []byte = make([]byte, 512)
	conn.ReadFromUDP(buf[0:])
	m := message.PacketToMessage(buf)
	log.Printf("ship got message %+v", m)
	s.frames++
	//	s.updates <- buf
}

func (s *Ship) update(xdir, ydir int) {
	s.xp += xdir
	s.yp += ydir
}

func (s *Ship) DisplayFrames() {
	log.Printf("------------%s:%d", s.name, s.frames)
	s.frames = 0
}

func (s *Ship) Display() {
	fmt.Printf("%s:%d,%d", s.name, s.xp, s.yp)
}

func (s *Ship) gatherCommands() []*message.Vector {

	var stop bool
	time.AfterFunc(time.Millisecond*1, func() {
		stop = true
	})

	var commands []*message.Vector
OuterLoop:
	for {
		select {
		case v := <-s.commands:
			if v != nil {
				commands = append(commands, v)
			}
			if stop {
				break OuterLoop
			}
		default:
			if stop {
				break OuterLoop
			}
		}
	}

	return commands
}

func (s *Ship) SendCommands() {
	s.revision++
	log.Printf("Sending Commands at Rev %d", s.revision)
	ms := message.VectorsToMessages(s.gatherCommands(), s.revision)
	s.sendMessages(ms...)
}

func (s *Ship) sendMessages(ms ...*message.Message) {
	// actuall send the vector over the wire as a command
	for _, m := range ms {
		b := message.MessageToPacket(m)
		s.sconn.Write(b)
	}
}

// reads in as many updates as possible in 1 millisecond
func (s *Ship) gatherUpdates() []*message.Vector {

	var stop bool
	time.AfterFunc(time.Millisecond*1, func() {
		stop = true
	})

	var updates []*message.Vector
OuterLoop:
	for {
		select {
		case b := <-s.updates:
			var m message.Message
			json.Unmarshal(b, m)
			if m.Type != "" {
				println("Ship got message")
				//				updates = append(updates, m.Vectors...)
			}
			if stop {
				break OuterLoop
			}
		default:
		}
	}
	return updates
}

func (s *Ship) ApplyUpdates() {

	// Updates override the position as they are state updates
	for _, update := range s.gatherUpdates() {
		s.xp = update.X
		s.yp = update.Y
	}
	fmt.Printf("%s:%d,%d", s.name, s.xp, s.yp)
	s.frames++
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func RandomMove() *message.Vector {

	xdir := random(1, 6)
	ydir := random(1, 10)

	return &message.Vector{xdir, ydir}
}

func main() {

	runShip("", "10234", "55102")
}

func test() {
	//////////////////////////////////// Sends Random traffic
	addr, err := net.ResolveUDPAddr("udp", ":10234")
	if err != nil {
		log.Fatal(err)
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	defer conn.Close()
	log.Println("Connected to ", addr)
	num := 100000
	vectors := randVectors(num)

	ms := message.VectorsToMessages(vectors, 101)

	for {
		//		newAddr := new(net.UDPAddr)
		//		*newAddr = *addr
		//		newAddr.IP = make(net.IP, len(addr.IP))
		//		copy(newAddr.IP, addr.IP)
		//
		//conn.WriteToUDP(b, newAddr)
		for i, m := range ms {
			m.Revision = i
			p := message.MessageToPacket(m)
			conn.Write(p)
		}

		//	var buf []byte = make([]byte, 512)
		//	n, a, err := conn.ReadFromUDP(buf[0:])
		//	log.Printf("read %s %d", a, n)
		//	if err != nil {
		//		return
		//	}
		//log.Printf("Sent %d vectors in %d", num, len(ms))
		//time.Sleep(time.Second * 1)
		println("Sent Messages")
		time.Sleep(time.Millisecond * 1000)
	}
}

func randVectors(num int) []*message.Vector {

	results := make([]*message.Vector, num)
	for i := range results {

		results[i] = RandomMove()
	}

	return results
}
<commit_msg>Pong message confirmed back, do not need to listen<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"

	"github.com/rosshendrickson-wf/education/examples/toyserver/message"
)

//1000ms/sec / 900FPS = 1.111.. ms per frame
//1000ms/sec / 450FPS = 2.222.. ms per frame
//Increase in execution time: 1.111.. ms
//
//1000ms/sec / 60FPS = 16.666.. ms per frame
//1000ms/sec / 56.25FPS = 17.777.. ms per frame

// read from the connection ever 5ms and apply the updates
func runShip(address, serverPort, clientPort string) {

	commands := make(chan *message.Vector, 1000)
	ship := &Ship{commands: commands}

	ship.Connect(address, serverPort, clientPort)

	//ApplyUpdates := time.NewTicker(time.Millisecond * 5).C
	SendCommands := time.NewTicker(time.Millisecond * 500).C
	//Display := time.NewTicker(time.Millisecond * 120).C
	DisplayFrames := time.NewTicker(time.Second * 1).C
	Random := time.NewTicker(time.Millisecond * 5).C
	DieTime := time.NewTicker(time.Second * 10).C

OuterLoop:
	for {
		select {
		//	case <-ApplyUpdates:
		//		ship.ApplyUpdates()
		case <-SendCommands:
			ship.SendCommands()
		//	case <-Display:
		//		ship.Display()
		case <-DisplayFrames:
			ship.DisplayFrames()
		case <-Random:
			ship.commands <- RandomMove()
		case <-DieTime:
			ship.Close()
			log.Printf("Ship %+v dead", ship)
			break OuterLoop
		default:
		}
	}
}

type Ship struct {
	xp         int
	yp         int
	health     int
	name       [8]byte
	conn       *net.UDPConn
	sconn      *net.UDPConn
	commands   chan *message.Vector
	updates    chan []byte
	shipTime   int
	serverTime int
	frames     int
	lock       sync.Mutex
	stop       bool
	revision   int
	serverAddr net.Addr
}

func (s *Ship) Connect(address, serverPort, clientPort string) {

	// spin off a goroutine to read from the connection
	addr, err := net.ResolveUDPAddr("udp4", ":"+clientPort)
	if err != nil {
		log.Fatal(err)
	}

	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		log.Fatal(err)
	}

	saddr, err := net.ResolveUDPAddr("udp4", address+":"+serverPort)
	if err != nil {
		log.Fatal(err)
	}
	s.serverAddr = saddr

	sconn, err := net.DialUDP("udp", nil, saddr)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	s.sconn = sconn

	go func() {
		for {
			defer conn.Close()
			connbuf := bufio.NewReader(conn)
			s.handleUpdate(sconn, connbuf)
			if s.Stop() {
				return
			}
		}
	}()

	s.conn = conn
}

func (s *Ship) Close() {
	s.lock.Lock()
	s.stop = true
	s.lock.Unlock()
}

func (s *Ship) Stop() bool {
	s.lock.Lock()
	result := s.stop
	s.lock.Unlock()
	return result
}

func (s *Ship) handleUpdate(conn *net.UDPConn, reader *bufio.Reader) {
	var buf []byte = make([]byte, 512)
	conn.ReadFromUDP(buf[0:])
	m := message.PacketToMessage(buf)
	log.Printf("ship got message %+v", m)
	s.frames++
	//	s.updates <- buf
}

func (s *Ship) update(xdir, ydir int) {
	s.xp += xdir
	s.yp += ydir
}

func (s *Ship) DisplayFrames() {
	log.Printf("------------%s:%d", s.name, s.frames)
	s.frames = 0
}

func (s *Ship) Display() {
	fmt.Printf("%s:%d,%d", s.name, s.xp, s.yp)
}

func (s *Ship) gatherCommands() []*message.Vector {

	var stop bool
	time.AfterFunc(time.Millisecond*1, func() {
		stop = true
	})

	var commands []*message.Vector
OuterLoop:
	for {
		select {
		case v := <-s.commands:
			if v != nil {
				commands = append(commands, v)
			}
			if stop {
				break OuterLoop
			}
		default:
			if stop {
				break OuterLoop
			}
		}
	}

	return commands
}

func (s *Ship) SendCommands() {
	s.revision++
	log.Printf("Sending Commands at Rev %d", s.revision)
	ms := message.VectorsToMessages(s.gatherCommands(), s.revision)
	s.sendMessages(ms...)
}

func (s *Ship) sendMessages(ms ...*message.Message) {
	// actuall send the vector over the wire as a command
	for _, m := range ms {
		b := message.MessageToPacket(m)
		s.sconn.Write(b)
	}
}

// reads in as many updates as possible in 1 millisecond
func (s *Ship) gatherUpdates() []*message.Vector {

	var stop bool
	time.AfterFunc(time.Millisecond*1, func() {
		stop = true
	})

	var updates []*message.Vector
OuterLoop:
	for {
		select {
		case b := <-s.updates:
			var m message.Message
			json.Unmarshal(b, m)
			if m.Type != "" {
				println("Ship got message")
				//				updates = append(updates, m.Vectors...)
			}
			if stop {
				break OuterLoop
			}
		default:
		}
	}
	return updates
}

func (s *Ship) ApplyUpdates() {

	// Updates override the position as they are state updates
	for _, update := range s.gatherUpdates() {
		s.xp = update.X
		s.yp = update.Y
	}
	fmt.Printf("%s:%d,%d", s.name, s.xp, s.yp)
	s.frames++
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func RandomMove() *message.Vector {

	xdir := random(1, 6)
	ydir := random(1, 10)

	return &message.Vector{xdir, ydir}
}

func main() {

	runShip("", "10234", "55102")
}

func test() {
	//////////////////////////////////// Sends Random traffic
	addr, err := net.ResolveUDPAddr("udp", ":10234")
	if err != nil {
		log.Fatal(err)
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	defer conn.Close()
	log.Println("Connected to ", addr)
	num := 100000
	vectors := randVectors(num)

	ms := message.VectorsToMessages(vectors, 101)

	for {
		//		newAddr := new(net.UDPAddr)
		//		*newAddr = *addr
		//		newAddr.IP = make(net.IP, len(addr.IP))
		//		copy(newAddr.IP, addr.IP)
		//
		//conn.WriteToUDP(b, newAddr)
		for i, m := range ms {
			m.Revision = i
			p := message.MessageToPacket(m)
			conn.Write(p)
		}

		//	var buf []byte = make([]byte, 512)
		//	n, a, err := conn.ReadFromUDP(buf[0:])
		//	log.Printf("read %s %d", a, n)
		//	if err != nil {
		//		return
		//	}
		//log.Printf("Sent %d vectors in %d", num, len(ms))
		//time.Sleep(time.Second * 1)
		println("Sent Messages")
		time.Sleep(time.Millisecond * 1000)
	}
}

func randVectors(num int) []*message.Vector {

	results := make([]*message.Vector, num)
	for i := range results {

		results[i] = RandomMove()
	}

	return results
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 inode

import (
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/internal/gcsx"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/syncutil"
	"github.com/jacobsa/timeutil"
	"golang.org/x/net/context"
)

type FileInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket
	syncer gcsx.Syncer
	clock  timeutil.Clock

	/////////////////////////
	// Constant data
	/////////////////////////

	id           fuseops.InodeID
	name         string
	attrs        fuseops.InodeAttributes
	gcsChunkSize uint64

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// GUARDED_BY(mu)
	lc lookupCount

	// The source object from which this inode derives.
	//
	// INVARIANT: src.Name == name
	//
	// GUARDED_BY(mu)
	src gcs.Object

	// The current content of this inode, or nil if the source object is still
	// authoritative.
	content gcsx.TempFile

	// Has Destroy been called?
	//
	// GUARDED_BY(mu)
	destroyed bool
}

var _ Inode = &FileInode{}

// Create a file inode for the given object in GCS. The initial lookup count is
// zero.
//
// REQUIRES: o != nil
// REQUIRES: o.Generation > 0
// REQUIRES: len(o.Name) > 0
// REQUIRES: o.Name[len(o.Name)-1] != '/'
func NewFileInode(
	id fuseops.InodeID,
	o *gcs.Object,
	attrs fuseops.InodeAttributes,
	bucket gcs.Bucket,
	syncer gcsx.Syncer,
	clock timeutil.Clock) (f *FileInode) {
	// Set up the basic struct.
	f = &FileInode{
		bucket: bucket,
		syncer: syncer,
		clock:  clock,
		id:     id,
		name:   o.Name,
		attrs:  attrs,
		src:    *o,
	}

	f.lc.Init(id)

	// Set up invariant checking.
	f.mu = syncutil.NewInvariantMutex(f.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) checkInvariants() {
	if f.destroyed {
		return
	}

	// Make sure the name is legal.
	name := f.Name()
	if len(name) == 0 || name[len(name)-1] == '/' {
		panic("Illegal file name: " + name)
	}

	// INVARIANT: src.Name == name
	if f.src.Name != name {
		panic(fmt.Sprintf("Name mismatch: %q vs. %q", f.src.Name, name))
	}

	// INVARIANT: content.CheckInvariants() does not panic
	if f.content != nil {
		f.content.CheckInvariants()
	}
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) clobbered(ctx context.Context) (b bool, err error) {
	// Stat the object in GCS.
	req := &gcs.StatObjectRequest{Name: f.name}
	o, err := f.bucket.StatObject(ctx, req)

	// Special case: "not found" means we have been clobbered.
	if _, ok := err.(*gcs.NotFoundError); ok {
		err = nil
		b = true
		return
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	// We are clobbered iff the generation doesn't match our source generation.
	b = (o.Generation != f.src.Generation)

	return
}

// Ensure that f.content != nil
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) ensureContent(ctx context.Context) (err error) {
	// Is there anything to do?
	if f.content != nil {
		return
	}

	// Open a reader for the generation we care about.
	rc, err := f.bucket.NewReader(
		ctx,
		&gcs.ReadObjectRequest{
			Name:       f.src.Name,
			Generation: f.src.Generation,
		})

	if err != nil {
		err = fmt.Errorf("NewReader: %v", err)
		return
	}

	defer rc.Close()

	// Create a temporary file with its contents.
	tf, err := gcsx.NewTempFile(rc, f.clock)
	if err != nil {
		err = fmt.Errorf("NewTempFile: %v", err)
		return
	}

	// Update state.
	f.content = tf

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (f *FileInode) Lock() {
	f.mu.Lock()
}

func (f *FileInode) Unlock() {
	f.mu.Unlock()
}

func (f *FileInode) ID() fuseops.InodeID {
	return f.id
}

func (f *FileInode) Name() string {
	return f.name
}

// Return a record for the GCS object generation from which this inode is
// branched. The record is guaranteed not to be modified, and users must not
// modify it.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Source() *gcs.Object {
	// Make a copy, since we modify f.src.
	o := f.src
	return &o
}

// If true, it is safe to serve reads directly from the object generation given
// by f.Source(), rather than calling f.ReadAt. Doing so may be more efficient,
// because f.ReadAt may cause the entire object to be faulted in and requires
// the inode to be locked during the read.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) SourceGenerationIsAuthoritative() bool {
	return f.content == nil
}

// Equivalent to f.Source().Generation.
//
// LOCKS_REQUIRED(f)
func (f *FileInode) SourceGeneration() int64 {
	return f.src.Generation
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) IncrementLookupCount() {
	f.lc.Inc()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) DecrementLookupCount(n uint64) (destroy bool) {
	destroy = f.lc.Dec(n)
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Destroy() (err error) {
	f.destroyed = true

	if f.content != nil {
		f.content.Destroy()
	}

	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Attributes(
	ctx context.Context) (attrs fuseops.InodeAttributes, err error) {
	attrs = f.attrs

	// Obtain default information from the source object.
	attrs.Mtime = f.src.Updated
	attrs.Size = uint64(f.src.Size)

	// If GCS is no longer authoritative, stat our local content to obtain size
	// and mtime.
	if f.content != nil {
		var sr gcsx.StatResult
		sr, err = f.content.Stat()
		if err != nil {
			err = fmt.Errorf("Stat: %v", err)
			return
		}

		attrs.Size = uint64(sr.Size)
		if sr.Mtime != nil {
			attrs.Mtime = *sr.Mtime
		}
	}

	// If the object has been clobbered, we reflect that as the inode being
	// unlinked.
	clobbered, err := f.clobbered(ctx)
	if err != nil {
		err = fmt.Errorf("clobbered: %v", err)
		return
	}

	if !clobbered {
		attrs.Nlink = 1
	}

	return
}

// Serve a read for this file with semantics matching io.ReaderAt.
//
// The caller may be better off reading directly from GCS when
// f.SourceGenerationIsAuthoritative() is true.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Read(
	ctx context.Context,
	dst []byte,
	offset int64) (n int, err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Read from the local content, propagating io.EOF.
	n, err = f.content.ReadAt(dst, offset)
	switch {
	case err == io.EOF:
		return

	case err != nil:
		err = fmt.Errorf("content.ReadAt: %v", err)
		return
	}

	return
}

// Serve a write for this file with semantics matching fuseops.WriteFileOp.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Write(
	ctx context.Context,
	data []byte,
	offset int64) (err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Write to the mutable content. Note that io.WriterAt guarantees it returns
	// an error for short writes.
	_, err = f.content.WriteAt(data, offset)

	return
}

// Write out contents to GCS. If this fails due to the generation having been
// clobbered, treat it as a non-error (simulating the inode having been
// unlinked).
//
// After this method succeeds, SourceGeneration will return the new generation
// by which this inode should be known (which may be the same as before). If it
// fails, the generation will not change.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Sync(ctx context.Context) (err error) {
	// If we have not been dirtied, there is nothing to do.
	if f.content == nil {
		return
	}

	// Write out the contents if they are dirty.
	newObj, err := f.syncer.SyncObject(ctx, &f.src, f.content)

	// Special case: a precondition error means we were clobbered, which we treat
	// as being unlinked. There's no reason to return an error in that case.
	if _, ok := err.(*gcs.PreconditionError); ok {
		err = nil
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("SyncObject: %v", err)
		return
	}

	// If we wrote out a new object, we need to update our state.
	if newObj != nil {
		f.src = *newObj
		f.content = nil
	}

	return
}

// Truncate the file to the specified size.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Truncate(
	ctx context.Context,
	size int64) (err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Call through.
	err = f.content.Truncate(size)

	return
}
<commit_msg>Removed an unused field.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 inode

import (
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/internal/gcsx"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/syncutil"
	"github.com/jacobsa/timeutil"
	"golang.org/x/net/context"
)

type FileInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket
	syncer gcsx.Syncer
	clock  timeutil.Clock

	/////////////////////////
	// Constant data
	/////////////////////////

	id    fuseops.InodeID
	name  string
	attrs fuseops.InodeAttributes

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// GUARDED_BY(mu)
	lc lookupCount

	// The source object from which this inode derives.
	//
	// INVARIANT: src.Name == name
	//
	// GUARDED_BY(mu)
	src gcs.Object

	// The current content of this inode, or nil if the source object is still
	// authoritative.
	content gcsx.TempFile

	// Has Destroy been called?
	//
	// GUARDED_BY(mu)
	destroyed bool
}

var _ Inode = &FileInode{}

// Create a file inode for the given object in GCS. The initial lookup count is
// zero.
//
// REQUIRES: o != nil
// REQUIRES: o.Generation > 0
// REQUIRES: len(o.Name) > 0
// REQUIRES: o.Name[len(o.Name)-1] != '/'
func NewFileInode(
	id fuseops.InodeID,
	o *gcs.Object,
	attrs fuseops.InodeAttributes,
	bucket gcs.Bucket,
	syncer gcsx.Syncer,
	clock timeutil.Clock) (f *FileInode) {
	// Set up the basic struct.
	f = &FileInode{
		bucket: bucket,
		syncer: syncer,
		clock:  clock,
		id:     id,
		name:   o.Name,
		attrs:  attrs,
		src:    *o,
	}

	f.lc.Init(id)

	// Set up invariant checking.
	f.mu = syncutil.NewInvariantMutex(f.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) checkInvariants() {
	if f.destroyed {
		return
	}

	// Make sure the name is legal.
	name := f.Name()
	if len(name) == 0 || name[len(name)-1] == '/' {
		panic("Illegal file name: " + name)
	}

	// INVARIANT: src.Name == name
	if f.src.Name != name {
		panic(fmt.Sprintf("Name mismatch: %q vs. %q", f.src.Name, name))
	}

	// INVARIANT: content.CheckInvariants() does not panic
	if f.content != nil {
		f.content.CheckInvariants()
	}
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) clobbered(ctx context.Context) (b bool, err error) {
	// Stat the object in GCS.
	req := &gcs.StatObjectRequest{Name: f.name}
	o, err := f.bucket.StatObject(ctx, req)

	// Special case: "not found" means we have been clobbered.
	if _, ok := err.(*gcs.NotFoundError); ok {
		err = nil
		b = true
		return
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("StatObject: %v", err)
		return
	}

	// We are clobbered iff the generation doesn't match our source generation.
	b = (o.Generation != f.src.Generation)

	return
}

// Ensure that f.content != nil
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) ensureContent(ctx context.Context) (err error) {
	// Is there anything to do?
	if f.content != nil {
		return
	}

	// Open a reader for the generation we care about.
	rc, err := f.bucket.NewReader(
		ctx,
		&gcs.ReadObjectRequest{
			Name:       f.src.Name,
			Generation: f.src.Generation,
		})

	if err != nil {
		err = fmt.Errorf("NewReader: %v", err)
		return
	}

	defer rc.Close()

	// Create a temporary file with its contents.
	tf, err := gcsx.NewTempFile(rc, f.clock)
	if err != nil {
		err = fmt.Errorf("NewTempFile: %v", err)
		return
	}

	// Update state.
	f.content = tf

	return
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (f *FileInode) Lock() {
	f.mu.Lock()
}

func (f *FileInode) Unlock() {
	f.mu.Unlock()
}

func (f *FileInode) ID() fuseops.InodeID {
	return f.id
}

func (f *FileInode) Name() string {
	return f.name
}

// Return a record for the GCS object generation from which this inode is
// branched. The record is guaranteed not to be modified, and users must not
// modify it.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Source() *gcs.Object {
	// Make a copy, since we modify f.src.
	o := f.src
	return &o
}

// If true, it is safe to serve reads directly from the object generation given
// by f.Source(), rather than calling f.ReadAt. Doing so may be more efficient,
// because f.ReadAt may cause the entire object to be faulted in and requires
// the inode to be locked during the read.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) SourceGenerationIsAuthoritative() bool {
	return f.content == nil
}

// Equivalent to f.Source().Generation.
//
// LOCKS_REQUIRED(f)
func (f *FileInode) SourceGeneration() int64 {
	return f.src.Generation
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) IncrementLookupCount() {
	f.lc.Inc()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) DecrementLookupCount(n uint64) (destroy bool) {
	destroy = f.lc.Dec(n)
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Destroy() (err error) {
	f.destroyed = true

	if f.content != nil {
		f.content.Destroy()
	}

	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Attributes(
	ctx context.Context) (attrs fuseops.InodeAttributes, err error) {
	attrs = f.attrs

	// Obtain default information from the source object.
	attrs.Mtime = f.src.Updated
	attrs.Size = uint64(f.src.Size)

	// If GCS is no longer authoritative, stat our local content to obtain size
	// and mtime.
	if f.content != nil {
		var sr gcsx.StatResult
		sr, err = f.content.Stat()
		if err != nil {
			err = fmt.Errorf("Stat: %v", err)
			return
		}

		attrs.Size = uint64(sr.Size)
		if sr.Mtime != nil {
			attrs.Mtime = *sr.Mtime
		}
	}

	// If the object has been clobbered, we reflect that as the inode being
	// unlinked.
	clobbered, err := f.clobbered(ctx)
	if err != nil {
		err = fmt.Errorf("clobbered: %v", err)
		return
	}

	if !clobbered {
		attrs.Nlink = 1
	}

	return
}

// Serve a read for this file with semantics matching io.ReaderAt.
//
// The caller may be better off reading directly from GCS when
// f.SourceGenerationIsAuthoritative() is true.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Read(
	ctx context.Context,
	dst []byte,
	offset int64) (n int, err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Read from the local content, propagating io.EOF.
	n, err = f.content.ReadAt(dst, offset)
	switch {
	case err == io.EOF:
		return

	case err != nil:
		err = fmt.Errorf("content.ReadAt: %v", err)
		return
	}

	return
}

// Serve a write for this file with semantics matching fuseops.WriteFileOp.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Write(
	ctx context.Context,
	data []byte,
	offset int64) (err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Write to the mutable content. Note that io.WriterAt guarantees it returns
	// an error for short writes.
	_, err = f.content.WriteAt(data, offset)

	return
}

// Write out contents to GCS. If this fails due to the generation having been
// clobbered, treat it as a non-error (simulating the inode having been
// unlinked).
//
// After this method succeeds, SourceGeneration will return the new generation
// by which this inode should be known (which may be the same as before). If it
// fails, the generation will not change.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Sync(ctx context.Context) (err error) {
	// If we have not been dirtied, there is nothing to do.
	if f.content == nil {
		return
	}

	// Write out the contents if they are dirty.
	newObj, err := f.syncer.SyncObject(ctx, &f.src, f.content)

	// Special case: a precondition error means we were clobbered, which we treat
	// as being unlinked. There's no reason to return an error in that case.
	if _, ok := err.(*gcs.PreconditionError); ok {
		err = nil
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("SyncObject: %v", err)
		return
	}

	// If we wrote out a new object, we need to update our state.
	if newObj != nil {
		f.src = *newObj
		f.content = nil
	}

	return
}

// Truncate the file to the specified size.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Truncate(
	ctx context.Context,
	size int64) (err error) {
	// Make sure f.content != nil.
	err = f.ensureContent(ctx)
	if err != nil {
		err = fmt.Errorf("ensureContent: %v", err)
		return
	}

	// Call through.
	err = f.content.Truncate(size)

	return
}
<|endoftext|>


<commit_before>package serialapi

import (
	"encoding/hex"
	"fmt"
	"strings"
	"testing"
)

func Test_Decode(t *testing.T) {

	raw := []string{
		"01 04 01 13 01 00",

		"01 11 00 04 00 02 0b 71 05 07 00 00 ff 07 00 01 08 00 00",
		"01 11 00 04 00 02 0b 71 05 07 ff 00 ff 07 08 01 08 00 00",
		"01 11 00 04 00 02 0b 71 05 07 00 00 ff 07 00 01 08 00 00",

		"01 0c 00 04 00 03 06 31 05 03 0a 00 c2 00",
		"01 0c 00 04 00 03 06 31 05 03 0a 00 1d 00",

		"01 09 00 04 00 02 03 20 01 00 00",
		"01 09 00 04 00 02 03 20 01 ff 00",

		"01 08 00 04 04 03 02 84 07 00",
	}

	for _, val := range raw {
		data := []byte{}

		bytes := strings.Split(val, " ")
		for _, b := range bytes {
			dhex, _ := hex.DecodeString(b)
			data = append(data, dhex...)
		}

		msg := CreateMessage(data)
		fmt.Printf("%+v\n\n", msg.Data)
	}

}
<commit_msg>Fix broken serialapi tests<commit_after>package serialapi

import (
	"encoding/hex"
	"fmt"
	"strings"
	"testing"
)

func Test_Decode(t *testing.T) {

	raw := []string{
		"01 04 01 13 01 00",

		"01 11 00 04 00 02 0b 71 05 07 00 00 ff 07 00 01 08 00 00",
		"01 11 00 04 00 02 0b 71 05 07 ff 00 ff 07 08 01 08 00 00",
		"01 11 00 04 00 02 0b 71 05 07 00 00 ff 07 00 01 08 00 00",

		"01 0c 00 04 00 03 06 31 05 03 0a 00 c2 00",
		"01 0c 00 04 00 03 06 31 05 03 0a 00 1d 00",

		"01 09 00 04 00 02 03 20 01 00 00",
		"01 09 00 04 00 02 03 20 01 ff 00",

		"01 08 00 04 04 03 02 84 07 00",
	}

	for _, val := range raw {
		data := []byte{}

		bytes := strings.Split(val, " ")
		for _, b := range bytes {
			dhex, _ := hex.DecodeString(b)
			data = append(data, dhex...)
		}

		msg := NewMessage(data)
		fmt.Printf("%+v\n\n", msg.Data)
	}

}
<|endoftext|>


<commit_before>package git

import (
    "os"
)

type mkMonad struct {
    err error
}

func (m *mkMonad) Mk(name string, perm os.FileMode) {
    if m.err != nil {
        m.err = os.Mkdir(name, 0666)
    }
}

func Init() {
    var m mkMonad
    var list = []string{
        ".git"
        ".git/info"
        ".git/hooks",
        ".git/objects",
        ".git/objects/info",
        ".git/objects/pack",
        ".git/refs",
        ".git/refs/heads",
        ".git/refs/tags",
    }

    for _, path := range list {
        m.MkDir(path, 0666)
    }
}
<commit_msg>init.go: Mk method doesn't exist.<commit_after>package git

import (
	"os"
)

type mkMonad struct {
	err error
}

func (m *mkMonad) MkDir(name string, perm os.FileMode) {
	if m.err != nil {
		m.err = os.Mkdir(name, 0666)
	}
}

func Init() {
	var m mkMonad
	var list = []string{
		".git",
		".git/info",
		".git/hooks",
		".git/objects",
		".git/objects/info",
		".git/objects/pack",
		".git/refs",
		".git/refs/heads",
		".git/refs/tags",
	}

	for _, path := range list {
		m.MkDir(path, 0666)
	}
}
<|endoftext|>


<commit_before>package dog

import "runtime"

// DefaultRunner defines the runner to use in case the task does not specify it.
//
// The value is automatically assigned based on the operating system when the
// package initializes.
var DefaultRunner string

// ProvideExtraInfo specifies if dog needs to provide execution info (duration,
// exit status) after task execution.
var ProvideExtraInfo bool

// deprecation warning flags
var deprecationWarningRun bool
var deprecationWarningExec bool

func init() {
	if runtime.GOOS == "windows" {
		DefaultRunner = "cmd" // not implemented yet
	} else {
		DefaultRunner = "sh"
	}
}
<commit_msg>Remove Windows support, was not implemented anyway<commit_after>package dog

// DefaultRunner defines the runner to use in case the task does not specify it.
var DefaultRunner = "sh"

// ProvideExtraInfo specifies if dog needs to provide execution info (duration,
// exit status) after task execution.
var ProvideExtraInfo bool

// deprecation warning flags
var deprecationWarningRun bool
var deprecationWarningExec bool
<|endoftext|>


<commit_before>package s3api

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

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
	xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
)

type Action string

type Iam interface {
	Check(f http.HandlerFunc, actions ...Action) http.HandlerFunc
}

type IdentityAccessManagement struct {
	identities []*Identity
	domain     string
}

type Identity struct {
	Name        string
	Credentials []*Credential
	Actions     []Action
}

type Credential struct {
	AccessKey string
	SecretKey string
}

func (action Action) isAdmin() bool {
	return strings.HasPrefix(string(action), s3_constants.ACTION_ADMIN)
}

func (action Action) isOwner(bucket string) bool {
	return string(action) == s3_constants.ACTION_ADMIN+":"+bucket
}

func (action Action) overBucket(bucket string) bool {
	return strings.HasSuffix(string(action), ":"+bucket) || strings.HasSuffix(string(action), ":*")
}

func (action Action) getPermission() Permission {
	switch act := strings.Split(string(action), ":")[0]; act {
	case s3_constants.ACTION_ADMIN:
		return Permission("FULL_CONTROL")
	case s3_constants.ACTION_WRITE:
		return Permission("WRITE")
	case s3_constants.ACTION_READ:
		return Permission("READ")
	default:
		return Permission("")
	}
}

func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManagement {
	iam := &IdentityAccessManagement{
		domain: option.DomainName,
	}
	if option.Config != "" {
		if err := iam.loadS3ApiConfigurationFromFile(option.Config); err != nil {
			glog.Fatalf("fail to load config file %s: %v", option.Config, err)
		}
	} else {
		if err := iam.loadS3ApiConfigurationFromFiler(option); err != nil {
			glog.Warningf("fail to load config: %v", err)
		}
	}
	return iam
}

func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFiler(option *S3ApiServerOption) (err error) {
	var content []byte
	err = pb.WithFilerClient(option.Filer, option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
		content, err = filer.ReadInsideFiler(client, filer.IamConfigDirecotry, filer.IamIdentityFile)
		return err
	})
	if err != nil {
		return fmt.Errorf("read S3 config: %v", err)
	}
	return iam.loadS3ApiConfigurationFromBytes(content)
}

func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFile(fileName string) error {
	content, readErr := os.ReadFile(fileName)
	if readErr != nil {
		glog.Warningf("fail to read %s : %v", fileName, readErr)
		return fmt.Errorf("fail to read %s : %v", fileName, readErr)
	}
	return iam.loadS3ApiConfigurationFromBytes(content)
}

func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromBytes(content []byte) error {
	s3ApiConfiguration := &iam_pb.S3ApiConfiguration{}
	if err := filer.ParseS3ConfigurationFromBytes(content, s3ApiConfiguration); err != nil {
		glog.Warningf("unmarshal error: %v", err)
		return fmt.Errorf("unmarshal error: %v", err)
	}
	if err := iam.loadS3ApiConfiguration(s3ApiConfiguration); err != nil {
		return err
	}
	return nil
}

func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error {
	var identities []*Identity
	for _, ident := range config.Identities {
		t := &Identity{
			Name:        ident.Name,
			Credentials: nil,
			Actions:     nil,
		}
		for _, action := range ident.Actions {
			t.Actions = append(t.Actions, Action(action))
		}
		for _, cred := range ident.Credentials {
			t.Credentials = append(t.Credentials, &Credential{
				AccessKey: cred.AccessKey,
				SecretKey: cred.SecretKey,
			})
		}
		identities = append(identities, t)
	}

	// atomically switch
	iam.identities = identities
	return nil
}

func (iam *IdentityAccessManagement) isEnabled() bool {

	return len(iam.identities) > 0
}

func (iam *IdentityAccessManagement) lookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) {

	for _, ident := range iam.identities {
		for _, cred := range ident.Credentials {
			if cred.AccessKey == accessKey {
				return ident, cred, true
			}
		}
	}
	return nil, nil, false
}

func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, found bool) {

	for _, ident := range iam.identities {
		if ident.Name == "anonymous" {
			return ident, true
		}
	}
	return nil, false
}

func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc {

	if !iam.isEnabled() {
		return f
	}

	return func(w http.ResponseWriter, r *http.Request) {
		identity, errCode := iam.authRequest(r, action)
		if errCode == s3err.ErrNone {
			if identity != nil && identity.Name != "" {
				r.Header.Set(xhttp.AmzIdentityId, identity.Name)
				if identity.isAdmin() {
					r.Header.Set(xhttp.AmzIsAdmin, "true")
				}
			}
			f(w, r)
			return
		}
		s3err.WriteErrorResponse(w, r, errCode)
	}
}

// check whether the request has valid access keys
func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) (*Identity, s3err.ErrorCode) {
	var identity *Identity
	var s3Err s3err.ErrorCode
	var found bool
	switch getRequestAuthType(r) {
	case authTypeStreamingSigned:
		return identity, s3err.ErrNone
	case authTypeUnknown:
		glog.V(3).Infof("unknown auth type")
		return identity, s3err.ErrAccessDenied
	case authTypePresignedV2, authTypeSignedV2:
		glog.V(3).Infof("v2 auth type")
		identity, s3Err = iam.isReqAuthenticatedV2(r)
	case authTypeSigned, authTypePresigned:
		glog.V(3).Infof("v4 auth type")
		identity, s3Err = iam.reqSignatureV4Verify(r)
	case authTypePostPolicy:
		glog.V(3).Infof("post policy auth type")
		return identity, s3err.ErrNone
	case authTypeJWT:
		glog.V(3).Infof("jwt auth type")
		return identity, s3err.ErrNotImplemented
	case authTypeAnonymous:
		identity, found = iam.lookupAnonymous()
		if !found {
			return identity, s3err.ErrAccessDenied
		}
	default:
		return identity, s3err.ErrNotImplemented
	}

	if s3Err != s3err.ErrNone {
		return identity, s3Err
	}

	glog.V(3).Infof("user name: %v actions: %v, action: %v", identity.Name, identity.Actions, action)

	bucket, _ := getBucketAndObject(r)

	if !identity.canDo(action, bucket) {
		return identity, s3err.ErrAccessDenied
	}

	return identity, s3err.ErrNone

}

func (iam *IdentityAccessManagement) authUser(r *http.Request) (*Identity, s3err.ErrorCode) {
	var identity *Identity
	var s3Err s3err.ErrorCode
	var found bool
	switch getRequestAuthType(r) {
	case authTypeStreamingSigned:
		return identity, s3err.ErrNone
	case authTypeUnknown:
		glog.V(3).Infof("unknown auth type")
		return identity, s3err.ErrAccessDenied
	case authTypePresignedV2, authTypeSignedV2:
		glog.V(3).Infof("v2 auth type")
		identity, s3Err = iam.isReqAuthenticatedV2(r)
	case authTypeSigned, authTypePresigned:
		glog.V(3).Infof("v4 auth type")
		identity, s3Err = iam.reqSignatureV4Verify(r)
	case authTypePostPolicy:
		glog.V(3).Infof("post policy auth type")
		return identity, s3err.ErrNone
	case authTypeJWT:
		glog.V(3).Infof("jwt auth type")
		return identity, s3err.ErrNotImplemented
	case authTypeAnonymous:
		identity, found = iam.lookupAnonymous()
		if !found {
			return identity, s3err.ErrAccessDenied
		}
	default:
		return identity, s3err.ErrNotImplemented
	}

	glog.V(3).Infof("auth error: %v", s3Err)
	if s3Err != s3err.ErrNone {
		return identity, s3Err
	}
	return identity, s3err.ErrNone
}

func (identity *Identity) canDo(action Action, bucket string) bool {
	if identity.isAdmin() {
		return true
	}
	for _, a := range identity.Actions {
		if a == action {
			return true
		}
	}
	if bucket == "" {
		return false
	}
	limitedByBucket := string(action) + ":" + bucket
	adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket
	for _, a := range identity.Actions {
		act := string(a)
		if strings.HasSuffix(act, "*") {
			if strings.HasPrefix(limitedByBucket, act[:len(act)-1]) {
				return true
			}
			if strings.HasPrefix(adminLimitedByBucket, act[:len(act)-1]) {
				return true
			}
		} else {
			if act == limitedByBucket {
				return true
			}
			if act == adminLimitedByBucket {
				return true
			}
		}
	}
	return false
}

func (identity *Identity) isAdmin() bool {
	for _, a := range identity.Actions {
		if a == "Admin" {
			return true
		}
	}
	return false
}
<commit_msg>log unknown access key<commit_after>package s3api

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

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
	xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
	"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
)

type Action string

type Iam interface {
	Check(f http.HandlerFunc, actions ...Action) http.HandlerFunc
}

type IdentityAccessManagement struct {
	identities []*Identity
	domain     string
}

type Identity struct {
	Name        string
	Credentials []*Credential
	Actions     []Action
}

type Credential struct {
	AccessKey string
	SecretKey string
}

func (action Action) isAdmin() bool {
	return strings.HasPrefix(string(action), s3_constants.ACTION_ADMIN)
}

func (action Action) isOwner(bucket string) bool {
	return string(action) == s3_constants.ACTION_ADMIN+":"+bucket
}

func (action Action) overBucket(bucket string) bool {
	return strings.HasSuffix(string(action), ":"+bucket) || strings.HasSuffix(string(action), ":*")
}

func (action Action) getPermission() Permission {
	switch act := strings.Split(string(action), ":")[0]; act {
	case s3_constants.ACTION_ADMIN:
		return Permission("FULL_CONTROL")
	case s3_constants.ACTION_WRITE:
		return Permission("WRITE")
	case s3_constants.ACTION_READ:
		return Permission("READ")
	default:
		return Permission("")
	}
}

func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManagement {
	iam := &IdentityAccessManagement{
		domain: option.DomainName,
	}
	if option.Config != "" {
		if err := iam.loadS3ApiConfigurationFromFile(option.Config); err != nil {
			glog.Fatalf("fail to load config file %s: %v", option.Config, err)
		}
	} else {
		if err := iam.loadS3ApiConfigurationFromFiler(option); err != nil {
			glog.Warningf("fail to load config: %v", err)
		}
	}
	return iam
}

func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFiler(option *S3ApiServerOption) (err error) {
	var content []byte
	err = pb.WithFilerClient(option.Filer, option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
		content, err = filer.ReadInsideFiler(client, filer.IamConfigDirecotry, filer.IamIdentityFile)
		return err
	})
	if err != nil {
		return fmt.Errorf("read S3 config: %v", err)
	}
	return iam.loadS3ApiConfigurationFromBytes(content)
}

func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFile(fileName string) error {
	content, readErr := os.ReadFile(fileName)
	if readErr != nil {
		glog.Warningf("fail to read %s : %v", fileName, readErr)
		return fmt.Errorf("fail to read %s : %v", fileName, readErr)
	}
	return iam.loadS3ApiConfigurationFromBytes(content)
}

func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromBytes(content []byte) error {
	s3ApiConfiguration := &iam_pb.S3ApiConfiguration{}
	if err := filer.ParseS3ConfigurationFromBytes(content, s3ApiConfiguration); err != nil {
		glog.Warningf("unmarshal error: %v", err)
		return fmt.Errorf("unmarshal error: %v", err)
	}
	if err := iam.loadS3ApiConfiguration(s3ApiConfiguration); err != nil {
		return err
	}
	return nil
}

func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error {
	var identities []*Identity
	for _, ident := range config.Identities {
		t := &Identity{
			Name:        ident.Name,
			Credentials: nil,
			Actions:     nil,
		}
		for _, action := range ident.Actions {
			t.Actions = append(t.Actions, Action(action))
		}
		for _, cred := range ident.Credentials {
			t.Credentials = append(t.Credentials, &Credential{
				AccessKey: cred.AccessKey,
				SecretKey: cred.SecretKey,
			})
		}
		identities = append(identities, t)
	}

	// atomically switch
	iam.identities = identities
	return nil
}

func (iam *IdentityAccessManagement) isEnabled() bool {

	return len(iam.identities) > 0
}

func (iam *IdentityAccessManagement) lookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) {

	for _, ident := range iam.identities {
		for _, cred := range ident.Credentials {
			println("checking", ident.Name, cred.AccessKey)
			if cred.AccessKey == accessKey {
				return ident, cred, true
			}
		}
	}
	glog.V(1).Infof("could not find accessKey %s", accessKey)
	return nil, nil, false
}

func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, found bool) {

	for _, ident := range iam.identities {
		if ident.Name == "anonymous" {
			return ident, true
		}
	}
	return nil, false
}

func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc {

	if !iam.isEnabled() {
		return f
	}

	return func(w http.ResponseWriter, r *http.Request) {
		identity, errCode := iam.authRequest(r, action)
		if errCode == s3err.ErrNone {
			if identity != nil && identity.Name != "" {
				r.Header.Set(xhttp.AmzIdentityId, identity.Name)
				if identity.isAdmin() {
					r.Header.Set(xhttp.AmzIsAdmin, "true")
				}
			}
			f(w, r)
			return
		}
		s3err.WriteErrorResponse(w, r, errCode)
	}
}

// check whether the request has valid access keys
func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) (*Identity, s3err.ErrorCode) {
	var identity *Identity
	var s3Err s3err.ErrorCode
	var found bool
	switch getRequestAuthType(r) {
	case authTypeStreamingSigned:
		return identity, s3err.ErrNone
	case authTypeUnknown:
		glog.V(3).Infof("unknown auth type")
		return identity, s3err.ErrAccessDenied
	case authTypePresignedV2, authTypeSignedV2:
		glog.V(3).Infof("v2 auth type")
		identity, s3Err = iam.isReqAuthenticatedV2(r)
	case authTypeSigned, authTypePresigned:
		glog.V(3).Infof("v4 auth type")
		identity, s3Err = iam.reqSignatureV4Verify(r)
	case authTypePostPolicy:
		glog.V(3).Infof("post policy auth type")
		return identity, s3err.ErrNone
	case authTypeJWT:
		glog.V(3).Infof("jwt auth type")
		return identity, s3err.ErrNotImplemented
	case authTypeAnonymous:
		identity, found = iam.lookupAnonymous()
		if !found {
			return identity, s3err.ErrAccessDenied
		}
	default:
		return identity, s3err.ErrNotImplemented
	}

	if s3Err != s3err.ErrNone {
		return identity, s3Err
	}

	glog.V(3).Infof("user name: %v actions: %v, action: %v", identity.Name, identity.Actions, action)

	bucket, _ := getBucketAndObject(r)

	if !identity.canDo(action, bucket) {
		return identity, s3err.ErrAccessDenied
	}

	return identity, s3err.ErrNone

}

func (iam *IdentityAccessManagement) authUser(r *http.Request) (*Identity, s3err.ErrorCode) {
	var identity *Identity
	var s3Err s3err.ErrorCode
	var found bool
	switch getRequestAuthType(r) {
	case authTypeStreamingSigned:
		return identity, s3err.ErrNone
	case authTypeUnknown:
		glog.V(3).Infof("unknown auth type")
		return identity, s3err.ErrAccessDenied
	case authTypePresignedV2, authTypeSignedV2:
		glog.V(3).Infof("v2 auth type")
		identity, s3Err = iam.isReqAuthenticatedV2(r)
	case authTypeSigned, authTypePresigned:
		glog.V(3).Infof("v4 auth type")
		identity, s3Err = iam.reqSignatureV4Verify(r)
	case authTypePostPolicy:
		glog.V(3).Infof("post policy auth type")
		return identity, s3err.ErrNone
	case authTypeJWT:
		glog.V(3).Infof("jwt auth type")
		return identity, s3err.ErrNotImplemented
	case authTypeAnonymous:
		identity, found = iam.lookupAnonymous()
		if !found {
			return identity, s3err.ErrAccessDenied
		}
	default:
		return identity, s3err.ErrNotImplemented
	}

	glog.V(3).Infof("auth error: %v", s3Err)
	if s3Err != s3err.ErrNone {
		return identity, s3Err
	}
	return identity, s3err.ErrNone
}

func (identity *Identity) canDo(action Action, bucket string) bool {
	if identity.isAdmin() {
		return true
	}
	for _, a := range identity.Actions {
		if a == action {
			return true
		}
	}
	if bucket == "" {
		return false
	}
	limitedByBucket := string(action) + ":" + bucket
	adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket
	for _, a := range identity.Actions {
		act := string(a)
		if strings.HasSuffix(act, "*") {
			if strings.HasPrefix(limitedByBucket, act[:len(act)-1]) {
				return true
			}
			if strings.HasPrefix(adminLimitedByBucket, act[:len(act)-1]) {
				return true
			}
		} else {
			if act == limitedByBucket {
				return true
			}
			if act == adminLimitedByBucket {
				return true
			}
		}
	}
	return false
}

func (identity *Identity) isAdmin() bool {
	for _, a := range identity.Actions {
		if a == "Admin" {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package shell

import (
	"context"
	"sync/atomic"
	"time"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/master_pb"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
)

const (
	RenewInteval     = 4 * time.Second
	SafeRenewInteval = 3 * time.Second
	InitLockInteval  = 1 * time.Second
)

type ExclusiveLocker struct {
	masterClient *wdclient.MasterClient
	token        int64
	lockTsNs     int64
	isLocking    bool
}

func NewExclusiveLocker(masterClient *wdclient.MasterClient) *ExclusiveLocker {
	return &ExclusiveLocker{
		masterClient: masterClient,
	}
}

func (l *ExclusiveLocker) GetToken() (token int64, lockTsNs int64) {
	for time.Unix(0, atomic.LoadInt64(&l.lockTsNs)).Add(SafeRenewInteval).Before(time.Now()) {
		// wait until now is within the safe lock period, no immediate renewal to change the token
		time.Sleep(100 * time.Millisecond)
	}
	return atomic.LoadInt64(&l.token), atomic.LoadInt64(&l.lockTsNs)
}

func (l *ExclusiveLocker) RequestLock() {
	// retry to get the lease
	for {
		if err := l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
			resp, err := client.LeaseAdminToken(context.Background(), &master_pb.LeaseAdminTokenRequest{})
			if err == nil {
				atomic.StoreInt64(&l.token, resp.Token)
				atomic.StoreInt64(&l.lockTsNs, resp.LockTsNs)
			}
			return err
		}); err != nil {
			// println("leasing problem", err.Error())
			time.Sleep(InitLockInteval)
		} else {
			break
		}
	}

	l.isLocking = true

	// start a goroutine to renew the lease
	go func() {
		for l.isLocking {
			if err := l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
				resp, err := client.LeaseAdminToken(context.Background(), &master_pb.LeaseAdminTokenRequest{
					PreviousToken:    atomic.LoadInt64(&l.token),
					PreviousLockTime: atomic.LoadInt64(&l.lockTsNs),
				})
				if err == nil {
					atomic.StoreInt64(&l.token, resp.Token)
					atomic.StoreInt64(&l.lockTsNs, resp.LockTsNs)
					// println("ts", l.lockTsNs, "token", l.token)
				}
				return err
			}); err != nil {
				glog.Error("failed to renew lock: %v", err)
				return
			} else {
				time.Sleep(RenewInteval)
			}

		}
	}()

}

func (l *ExclusiveLocker) ReleaseLock() {
	l.isLocking = false
	l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
		client.ReleaseAdminToken(context.Background(), &master_pb.ReleaseAdminTokenRequest{
			PreviousToken:    atomic.LoadInt64(&l.token),
			PreviousLockTime: atomic.LoadInt64(&l.lockTsNs),
		})
		return nil
	})
	atomic.StoreInt64(&l.token, 0)
	atomic.StoreInt64(&l.lockTsNs, 0)
}
<commit_msg>allow lock with an existing lock<commit_after>package shell

import (
	"context"
	"sync/atomic"
	"time"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/master_pb"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
)

const (
	RenewInteval     = 4 * time.Second
	SafeRenewInteval = 3 * time.Second
	InitLockInteval  = 1 * time.Second
)

type ExclusiveLocker struct {
	masterClient *wdclient.MasterClient
	token        int64
	lockTsNs     int64
	isLocking    bool
}

func NewExclusiveLocker(masterClient *wdclient.MasterClient) *ExclusiveLocker {
	return &ExclusiveLocker{
		masterClient: masterClient,
	}
}

func (l *ExclusiveLocker) GetToken() (token int64, lockTsNs int64) {
	for time.Unix(0, atomic.LoadInt64(&l.lockTsNs)).Add(SafeRenewInteval).Before(time.Now()) {
		// wait until now is within the safe lock period, no immediate renewal to change the token
		time.Sleep(100 * time.Millisecond)
	}
	return atomic.LoadInt64(&l.token), atomic.LoadInt64(&l.lockTsNs)
}

func (l *ExclusiveLocker) RequestLock() {
	// retry to get the lease
	for {
		if err := l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
			resp, err := client.LeaseAdminToken(context.Background(), &master_pb.LeaseAdminTokenRequest{
				PreviousToken:    atomic.LoadInt64(&l.token),
				PreviousLockTime: atomic.LoadInt64(&l.lockTsNs),
			})
			if err == nil {
				atomic.StoreInt64(&l.token, resp.Token)
				atomic.StoreInt64(&l.lockTsNs, resp.LockTsNs)
			}
			return err
		}); err != nil {
			// println("leasing problem", err.Error())
			time.Sleep(InitLockInteval)
		} else {
			break
		}
	}

	l.isLocking = true

	// start a goroutine to renew the lease
	go func() {
		for l.isLocking {
			if err := l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
				resp, err := client.LeaseAdminToken(context.Background(), &master_pb.LeaseAdminTokenRequest{
					PreviousToken:    atomic.LoadInt64(&l.token),
					PreviousLockTime: atomic.LoadInt64(&l.lockTsNs),
				})
				if err == nil {
					atomic.StoreInt64(&l.token, resp.Token)
					atomic.StoreInt64(&l.lockTsNs, resp.LockTsNs)
					// println("ts", l.lockTsNs, "token", l.token)
				}
				return err
			}); err != nil {
				glog.Error("failed to renew lock: %v", err)
				return
			} else {
				time.Sleep(RenewInteval)
			}

		}
	}()

}

func (l *ExclusiveLocker) ReleaseLock() {
	l.isLocking = false
	l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
		client.ReleaseAdminToken(context.Background(), &master_pb.ReleaseAdminTokenRequest{
			PreviousToken:    atomic.LoadInt64(&l.token),
			PreviousLockTime: atomic.LoadInt64(&l.lockTsNs),
		})
		return nil
	})
	atomic.StoreInt64(&l.token, 0)
	atomic.StoreInt64(&l.lockTsNs, 0)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
)

// Functions main purpose is to break a large program into a number of smaller
// tasks (functions). It also helps enforce the D-R-Y (dont repeat yourself)
// principle, the same task can be invoked several times, so a function promotes code reuse.

// There are 3 types of functions in Go:
// - Normal functions with an identifier
// - Anonymous or lamda functions
// - Methods

// Any of these can have parameters and return values. The definition of all the
// function parameters and return values, together with their types, is called
// the function signature.

// The function main is special, go programs begins execution in the function
// named main located in package main.

func main() {

	// You invoke the function by specifying the name of the package it is
	// defined in followed by a . (dot) followed by the name of the function
	// and any parameters within a set of parantheses.
	// However if the function is defined in the current package then it can
	// be invoked by referring to its name and providing the values for
	// all defined parameters, if any.
	Greet()

	x, y := 1, 2
	// You can capture the returned value either assigning it to a variable or
	// passing it as argument to another function
	r := add(x, y)
	fmt.Printf("The sum of %d and %d is %d\n", x, y, r)

	numr, denom := 3, 0
	quot, err := divide(numr, denom)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("Result of %d / %d is %d\n", numr, denom, quot)
	}

	// One or more returned values can be discarded using the blank identifier _ (underscore)

	// When would this be useful?
	// Recall that its illegal to declare a variable and not use it. Now suppose
	// you have function that you wrote or calling a function defined in some
	// package, but you do not want to consume one or more returned values.
	// The only way out, is to use the blank indentifier.

	// For example here the second retured value error is being discarded
	// NOTE: Its considered bad parctice to ignore errors. Please dont do this in normal code.
	// I am ignoring the error returned only to demonstrate how to discard one or more returned values.
	q, _ := divide(numr, denom)
	fmt.Printf("Result of %d / %d is %d\n", numr, denom, q)

	// call the variadic function sum
	n := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	// n... is the shorthand to pass each element of the slice to the function
	s := sum("Sum of input number is:", n...)
	fmt.Printf("%s", s)

	// call recursive function
	fmt.Printf("Fibonacci of 10 is %d\n", fibonacci(10))

	// A function that does not have a name is called anonymous function
	// (also known under the names of a lambda function, a function literal, or a closure).
	// Such a function cannot stand on its own, since complier would throw an error.
	// Hence such functions  must be either assigned to variable or must be
	// directly invoked or returned as output value of a named function

	// The variable prod's value is a function that returns product of the passed numbers
	prod := func(x, y int) int {
		return x * y
	}
	fmt.Printf("%d * %d = %d\n", x, y, prod(x, y))

	// To directly invoke an anonymous function, put pair of () after the closing
	// '}' brackets within which we can list the optional parameters to the function.
	func(x, y int) {
		fmt.Printf("%d %% %d = %d\n", x, y, x%y)
	}(4, 2) // 4,2 are the parameters to the anonymous function
}

// The keyword func introduces a function
// A function can take zero or more paramaters
// One of the functions parameters can accept arbitary number of values
// A function can return zero or more values, unlike other languages
// A function can take other function as paramters or return functions as return values
// A function which starts with capital/uppercase letter is exported (visible/accessible from other packages)
// The order in which functions are defined in go source file is of no
// consequence, however it is idiomatic to define the main function as first function.

// Greet is a function that take no paraters and returns no values. Notice this
// function start with capital letter hence it is said to be a exported function.
func Greet() {
	fmt.Printf("Hello how are you?\n")
}

// add is a function that take two parameters of type int and return a single
// value of the type int. Notice this function start with lowercase letter hence
// it is said to be un-exported function.
func add(x, y int) int {
	return x + y
}

var ErrDivideByZero = errors.New("Division by zero is not allowed")

// A function can return one or more values. This forms very foundations of Go's error
// handling machinary, since unlike other languages go does not support exception handling
func divide(x, y int) (int, error) {
	if y == 0 {
		return 0, ErrDivideByZero
	}
	return x / y, nil
}

// A function parameter can accept arbitary number of values. Such functions are
// called as variadic functions. A variadic parameter is prefixed by ... (three asterisks)
// to its type. There can only be one such parameter. If a function happens to take
// more than one parameter, then the parameter that accepts multiple values
// must be the last one. The following example show one such function.
// The value of the variadic parameter is accessible as a slice within the
// function body, which are be iterated over using the for..range loop
func sum(title string, nums ...int) string {
	fmt.Printf("Data type of nums is %T\n", nums)
	s := 0
	for _, v := range nums {
		s += v
	}
	return fmt.Sprintf("%s %d\n", title, s)
}

// A function that calls itself in the body of the function is called a recursive function.
func fibonacci(num int) int {
	if num == 0 {
		return 0
	} else if num == 1 {
		return 1
	}

	return fibonacci(num-1) + fibonacci(num-2)
}
<commit_msg>add named return<commit_after>package main

import (
	"errors"
	"fmt"
)

// Functions main purpose is to break a large program into a number of smaller
// tasks (functions). It also helps enforce the D-R-Y (dont repeat yourself)
// principle, the same task can be invoked several times, so a function promotes code reuse.

// There are 3 types of functions in Go:
// - Normal functions with an identifier
// - Anonymous or lamda functions
// - Methods

// Any of these can have parameters and return values. The definition of all the
// function parameters and return values, together with their types, is called
// the function signature.

// The function main is special, go programs begins execution in the function
// named main located in package main.

func main() {

	// You invoke the function by specifying the name of the package it is
	// defined in followed by a . (dot) followed by the name of the function
	// and any parameters within a set of parantheses.
	// However if the function is defined in the current package then it can
	// be invoked by referring to its name and providing the values for
	// all defined parameters, if any.
	Greet()

	x, y := 1, 2
	// You can capture the returned value either assigning it to a variable or
	// passing it as argument to another function
	r := add(x, y)
	fmt.Printf("The sum of %d and %d is %d\n", x, y, r)

	numr, denom := 3, 0
	quot, err := divide(numr, denom)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("Result of %d / %d is %d\n", numr, denom, quot)
	}

	// One or more returned values can be discarded using the blank identifier _ (underscore)

	// When would this be useful?
	// Recall that its illegal to declare a variable and not use it. Now suppose
	// you have function that you wrote or calling a function defined in some
	// package, but you do not want to consume one or more returned values.
	// The only way out, is to use the blank indentifier.

	// For example here the second retured value error is being discarded
	// NOTE: Its considered bad parctice to ignore errors. Please dont do this in normal code.
	// I am ignoring the error returned only to demonstrate how to discard one or more returned values.
	q, _ := divide(numr, denom)
	fmt.Printf("Result of %d / %d is %d\n", numr, denom, q)

	// call the variadic function sum
	n := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	// n... is the shorthand to pass each element of the slice to the function
	s := sum("Sum of input number is:", n...)
	fmt.Printf("%s", s)

	// call recursive function
	fmt.Printf("Fibonacci of 10 is %d\n", fibonacci(10))

	// A function that does not have a name is called anonymous function
	// (also known under the names of a lambda function, a function literal, or a closure).
	// Such a function cannot stand on its own, since complier would throw an error.
	// Hence such functions  must be either assigned to variable or must be
	// directly invoked or returned as output value of a named function

	// The variable prod's value is a function that returns product of the passed numbers
	prod := func(x, y int) int {
		return x * y
	}
	fmt.Printf("%d * %d = %d\n", x, y, prod(x, y))

	// To directly invoke an anonymous function, put pair of () after the closing
	// '}' brackets within which we can list the optional parameters to the function.
	func(x, y int) {
		fmt.Printf("%d %% %d = %d\n", x, y, x%y)
	}(4, 2) // 4,2 are the parameters to the anonymous function
}

// The keyword func introduces a function
// A function can take zero or more paramaters
// One of the functions parameters can accept arbitary number of values
// A function can return zero or more values, unlike other languages
// A function can take other function as paramters or return functions as return values
// A function which starts with capital/uppercase letter is exported (visible/accessible from other packages)
// The order in which functions are defined in go source file is of no
// consequence, however it is idiomatic to define the main function as first function.

// Greet is a function that take no paraters and returns no values. Notice this
// function start with capital letter hence it is said to be a exported function.
func Greet() {
	fmt.Printf("Hello how are you?\n")
}

// add is a function that take two parameters of type int and return a single
// value of the type int. Notice this function start with lowercase letter hence
// it is said to be un-exported function.
func add(x, y int) int {
	return x + y
}

var ErrDivideByZero = errors.New("Division by zero is not allowed")

// A function can return one or more values. This forms very foundations of Go's error
// handling machinary, since unlike other languages go does not support exception handling
// NOTE: If you have more than one returned value then they must enclosed in set of parens
func divide(x, y int) (int, error) {
	if y == 0 {
		return 0, ErrDivideByZero
	}
	return x / y, nil
}

// A function parameter can accept arbitary number of values. Such functions are
// called as variadic functions. A variadic parameter is prefixed by ... (three asterisks)
// to its type. There can only be one such parameter. If a function happens to take
// more than one parameter, then the parameter that accepts multiple values
// must be the last one. The following example show one such function.
// The value of the variadic parameter is accessible as a slice within the
// function body, which are be iterated over using the for..range loop
func sum(title string, nums ...int) string {
	fmt.Printf("Data type of nums is %T\n", nums)
	s := 0
	for _, v := range nums {
		s += v
	}
	return fmt.Sprintf("%s %d\n", title, s)
}

// A function that calls itself in the body of the function is called a recursive function.
func fibonacci(num int) int {
	if num == 0 {
		return 0
	} else if num == 1 {
		return 1
	}

	return fibonacci(num-1) + fibonacci(num-2)
}

// A function can have named return values, in which case you can named naked retrun
// statement. The last computed value of the variable would be retuned when the
// function exits
func IsEven(n int) (result bool) {
	result = false
	if n%2 == 0 {
		result = true
	}
	return
}
<|endoftext|>