<commit_before>package test

import (
	"flag"
	"os"
	"path"
	"runtime"
	"strconv"
	"time"

	"github.com/DATA-DOG/godog"
)

var acceptanceRunner *godogRunner

func init() {
	acceptanceRunner = new(godogRunner)
}

type featureContextFunc func(s *godog.Suite)

type godogRunner struct {
	featurePaths []string
	featureContexts []featureContextFunc
}

func (r *godogRunner) registerFeaturePath(featurePath string) {
	if path.IsAbs(featurePath) == false {
		_, filename, _, ok := runtime.Caller(2)
		if !ok {
			panic("cannot determine feature path: no caller information")
		}

		featurePath = path.Clean(path.Join(path.Dir(filename), featurePath))
	}

	r.featurePaths = append(r.featurePaths, featurePath)
}

func (r *godogRunner) registerFeatureContext(ctx func(s *godog.Suite)) {
	r.featureContexts = append(r.featureContexts, ctx)
}

func (r *godogRunner) Run() int {
	format := "progress"
	seed := int64(0)

	// go test transforms -v option
	if verbose := flag.Lookup("test.v"); verbose != nil {
		format = "pretty"
	}

	// Randomize scenario execution order
	if randomize, _ := strconv.ParseBool(os.Getenv("TEST_RANDOMIZE")); randomize {
		seed = time.Now().UTC().UnixNano()
	}

	return godog.RunWithOptions(
		"godog",
		func(s *godog.Suite) {
			for _, featureContext := range r.featureContexts {
				featureContext(s)
			}
		},
		godog.Options{
			Format:    format,
			Paths:     r.featurePaths,
			Randomize: seed,
		},
	)
}

func RegisterFeaturePath(featurePath string) {
	acceptanceRunner.registerFeaturePath(featurePath)
}

func RegisterFeatureContext(ctx func(s *godog.Suite)) {
	acceptanceRunner.registerFeatureContext(ctx)
}
<commit_msg>Remove function type<commit_after>package test

import (
	"flag"
	"os"
	"path"
	"runtime"
	"strconv"
	"time"

	"github.com/DATA-DOG/godog"
)

var acceptanceRunner *godogRunner

func init() {
	acceptanceRunner = new(godogRunner)
}

type godogRunner struct {
	featurePaths []string
	featureContexts []func(s *godog.Suite)
}

func (r *godogRunner) registerFeaturePath(featurePath string) {
	if path.IsAbs(featurePath) == false {
		_, filename, _, ok := runtime.Caller(2)
		if !ok {
			panic("cannot determine feature path: no caller information")
		}

		featurePath = path.Clean(path.Join(path.Dir(filename), featurePath))
	}

	r.featurePaths = append(r.featurePaths, featurePath)
}

func (r *godogRunner) registerFeatureContext(ctx func(s *godog.Suite)) {
	r.featureContexts = append(r.featureContexts, ctx)
}

func (r *godogRunner) Run() int {
	format := "progress"
	seed := int64(0)

	// go test transforms -v option
	if verbose := flag.Lookup("test.v"); verbose != nil {
		format = "pretty"
	}

	// Randomize scenario execution order
	if randomize, _ := strconv.ParseBool(os.Getenv("TEST_RANDOMIZE")); randomize {
		seed = time.Now().UTC().UnixNano()
	}

	return godog.RunWithOptions(
		"godog",
		func(s *godog.Suite) {
			for _, featureContext := range r.featureContexts {
				featureContext(s)
			}
		},
		godog.Options{
			Format:    format,
			Paths:     r.featurePaths,
			Randomize: seed,
		},
	)
}

func RegisterFeaturePath(featurePath string) {
	acceptanceRunner.registerFeaturePath(featurePath)
}

func RegisterFeatureContext(ctx func(s *godog.Suite)) {
	acceptanceRunner.registerFeatureContext(ctx)
}
<|endoftext|>


<commit_before>package gonzo

import "net/http"

type Context struct {
	next func(http.ResponseWriter, *http.Request, *Context)
	r *http.Request
}

func (p Context) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// shadow the request to do actions on
	p.r = r

	// execute next step in the chain
	p.next(w, r, &p)
}

func (p *Context) Principal() string {
	return p.r.Header.Get("X-Principal")
}<commit_msg>renamed handler<commit_after>package gonzo

import "net/http"

type Context struct {
	next ContextHandler
	r *http.Request
}

func (p Context) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// shadow the request to do actions on
	p.r = r

	// execute next step in the chain
	p.next(w, r, &p)
}

func (p *Context) Principal() string {
	return p.r.Header.Get("X-Principal")
}<|endoftext|>


<commit_before>// Copyright 2018 The Ebiten 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.

// +build example jsgo

package main

import (
	"bytes"
	"fmt"
	"image"
	"image/color"
	_ "image/png"
	"log"
	"math"
	"math/rand"
	"runtime"
	"time"

	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/audio"
	"github.com/hajimehoshi/ebiten/audio/vorbis"
	"github.com/hajimehoshi/ebiten/audio/wav"
	"github.com/hajimehoshi/ebiten/ebitenutil"
	raudio "github.com/hajimehoshi/ebiten/examples/resources/audio"
	"github.com/hajimehoshi/ebiten/examples/resources/fonts"
	resources "github.com/hajimehoshi/ebiten/examples/resources/images/flappy"
	"github.com/hajimehoshi/ebiten/inpututil"
	"github.com/hajimehoshi/ebiten/text"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func floorDiv(x, y int) int {
	d := x / y
	if d*y == x || x >= 0 {
		return d
	}
	return d - 1
}

func floorMod(x, y int) int {
	return x - floorDiv(x, y)*y
}

const (
	screenWidth      = 640
	screenHeight     = 480
	tileSize         = 32
	fontSize         = 32
	pipeWidth        = tileSize * 2
	pipeStartOffsetX = 8
	pipeIntervalX    = 8
	pipeGapY         = 5
)

var (
	gopherImage *ebiten.Image
	tilesImage  *ebiten.Image
	arcadeFont  font.Face
)

func init() {
	img, _, err := image.Decode(bytes.NewReader(resources.Gopher_png))
	if err != nil {
		log.Fatal(err)
	}
	gopherImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterDefault)

	img, _, err = image.Decode(bytes.NewReader(resources.Tiles_png))
	if err != nil {
		log.Fatal(err)
	}
	tilesImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterDefault)
}

func init() {
	tt, err := truetype.Parse(fonts.ArcadeN_ttf)
	if err != nil {
		log.Fatal(err)
	}
	const dpi = 72
	arcadeFont = truetype.NewFace(tt, &truetype.Options{
		Size:    fontSize,
		DPI:     dpi,
		Hinting: font.HintingFull,
	})
}

var (
	audioContext *audio.Context
	jumpPlayer   *audio.Player
	hitPlayer    *audio.Player
)

func init() {
	audioContext, _ = audio.NewContext(44100)

	jumpD, err := vorbis.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Jump_ogg))
	if err != nil {
		println("!?")
		log.Fatal(err)
	}
	jumpPlayer, err = audio.NewPlayer(audioContext, jumpD)
	if err != nil {
		log.Fatal(err)
	}

	jabD, err := wav.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Jab_wav))
	if err != nil {
		log.Fatal(err)
	}
	hitPlayer, err = audio.NewPlayer(audioContext, jabD)
	if err != nil {
		log.Fatal(err)
	}
}

type Mode int

const (
	ModeTitle Mode = iota
	ModeGame
	ModeGameOver
)

type Game struct {
	mode Mode

	// The gopher's position
	x16  int
	y16  int
	vy16 int

	// Camera
	cameraX int
	cameraY int

	// Pipes
	pipeTileYs []int

	gameoverCount int
}

func NewGame() *Game {
	g := &Game{}
	g.init()
	return g
}

func (g *Game) init() {
	g.x16 = 0
	g.y16 = 100 * 16
	g.cameraX = -240
	g.cameraY = 0
	g.pipeTileYs = make([]int, 256)
	for i := range g.pipeTileYs {
		g.pipeTileYs[i] = rand.Intn(6) + 2
	}
}

func jump() bool {
	if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
		return true
	}
	if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
		return true
	}
	if len(inpututil.JustPressedTouchIDs()) > 0 {
		return true
	}
	return false
}

func (g *Game) Update(screen *ebiten.Image) error {
	switch g.mode {
	case ModeTitle:
		if jump() {
			g.mode = ModeGame
		}
	case ModeGame:
		g.x16 += 32
		g.cameraX += 2
		if jump() {
			g.vy16 = -96
			jumpPlayer.Rewind()
			jumpPlayer.Play()
		}
		g.y16 += g.vy16

		// Gravity
		g.vy16 += 4
		if g.vy16 > 96 {
			g.vy16 = 96
		}

		if g.hit() {
			hitPlayer.Rewind()
			hitPlayer.Play()
			g.mode = ModeGameOver
			g.gameoverCount = 30
		}
	case ModeGameOver:
		if g.gameoverCount > 0 {
			g.gameoverCount--
		}
		if g.gameoverCount == 0 && jump() {
			g.init()
			g.mode = ModeTitle
		}
	}

	if ebiten.IsRunningSlowly() {
		return nil
	}

	screen.Fill(color.RGBA{0x80, 0xa0, 0xc0, 0xff})
	g.drawTiles(screen)
	if g.mode != ModeTitle {
		g.drawGopher(screen)
	}
	var texts []string
	switch g.mode {
	case ModeTitle:
		texts = []string{"FLAPPY GOPHER", "", "", "", "", "PRESS SPACE KEY", "", "OR TOUCH SCREEN"}
	case ModeGameOver:
		texts = []string{"", "GAMEOVER!"}
	}
	for i, l := range texts {
		x := (screenWidth - len(l)*fontSize) / 2
		text.Draw(screen, l, arcadeFont, x, (i+4)*fontSize, color.White)
	}

	scoreStr := fmt.Sprintf("%04d", g.score())
	text.Draw(screen, scoreStr, arcadeFont, screenWidth-len(scoreStr)*fontSize, fontSize, color.White)
	ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f", ebiten.CurrentFPS()))
	return nil
}

func (g *Game) pipeAt(tileX int) (tileY int, ok bool) {
	if (tileX - pipeStartOffsetX) <= 0 {
		return 0, false
	}
	if floorMod(tileX-pipeStartOffsetX, pipeIntervalX) != 0 {
		return 0, false
	}
	idx := floorDiv(tileX-pipeStartOffsetX, pipeIntervalX)
	return g.pipeTileYs[idx%len(g.pipeTileYs)], true
}

func (g *Game) score() int {
	x := floorDiv(g.x16, 16) / tileSize
	if (x - pipeStartOffsetX) <= 0 {
		return 0
	}
	return floorDiv(x-pipeStartOffsetX, pipeIntervalX)
}

func (g *Game) hit() bool {
	if g.mode != ModeGame {
		return false
	}
	const (
		gopherWidth  = 30
		gopherHeight = 60
	)
	w, h := gopherImage.Size()
	x0 := floorDiv(g.x16, 16) + (w-gopherWidth)/2
	y0 := floorDiv(g.y16, 16) + (h-gopherHeight)/2
	x1 := x0 + gopherWidth
	y1 := y0 + gopherHeight
	if y0 < -tileSize*4 {
		return true
	}
	if y1 >= screenHeight-tileSize {
		return true
	}
	xMin := floorDiv(x0-pipeWidth, tileSize)
	xMax := floorDiv(x0+gopherWidth, tileSize)
	for x := xMin; x <= xMax; x++ {
		y, ok := g.pipeAt(x)
		if !ok {
			continue
		}
		if x0 >= x*tileSize+pipeWidth {
			continue
		}
		if x1 < x*tileSize {
			continue
		}
		if y0 < y*tileSize {
			return true
		}
		if y1 >= (y+pipeGapY)*tileSize {
			return true
		}
	}
	return false
}

func (g *Game) drawTiles(screen *ebiten.Image) {
	const (
		nx           = screenWidth / tileSize
		ny           = screenHeight / tileSize
		pipeTileSrcX = 128
		pipeTileSrcY = 192
	)

	op := &ebiten.DrawImageOptions{}
	for i := -2; i < nx+1; i++ {
		// ground
		op.GeoM.Reset()
		op.GeoM.Translate(float64(i*tileSize-floorMod(g.cameraX, tileSize)),
			float64((ny-1)*tileSize-floorMod(g.cameraY, tileSize)))
		r := image.Rect(0, 0, tileSize, tileSize)
		op.SourceRect = &r
		screen.DrawImage(tilesImage, op)

		// pipe
		if tileY, ok := g.pipeAt(floorDiv(g.cameraX, tileSize) + i); ok {
			for j := 0; j < tileY; j++ {
				op.GeoM.Reset()
				op.GeoM.Scale(1, -1)
				op.GeoM.Translate(float64(i*tileSize-floorMod(g.cameraX, tileSize)),
					float64(j*tileSize-floorMod(g.cameraY, tileSize)))
				op.GeoM.Translate(0, tileSize)
				if j == tileY-1 {
					r := image.Rect(pipeTileSrcX, pipeTileSrcY, pipeTileSrcX+tileSize*2, pipeTileSrcY+tileSize)
					op.SourceRect = &r
				} else {
					r := image.Rect(pipeTileSrcX, pipeTileSrcY+tileSize, pipeTileSrcX+tileSize*2, pipeTileSrcY+tileSize*2)
					op.SourceRect = &r
				}
				screen.DrawImage(tilesImage, op)
			}
			for j := tileY + pipeGapY; j < screenHeight/tileSize-1; j++ {
				op.GeoM.Reset()
				op.GeoM.Translate(float64(i*tileSize-floorMod(g.cameraX, tileSize)),
					float64(j*tileSize-floorMod(g.cameraY, tileSize)))
				if j == tileY+pipeGapY {
					r := image.Rect(pipeTileSrcX, pipeTileSrcY, pipeTileSrcX+pipeWidth, pipeTileSrcY+tileSize)
					op.SourceRect = &r
				} else {
					r := image.Rect(pipeTileSrcX, pipeTileSrcY+tileSize, pipeTileSrcX+pipeWidth, pipeTileSrcY+tileSize+tileSize)
					op.SourceRect = &r
				}
				screen.DrawImage(tilesImage, op)
			}
		}
	}
}

func (g *Game) drawGopher(screen *ebiten.Image) {
	op := &ebiten.DrawImageOptions{}
	w, h := gopherImage.Size()
	op.GeoM.Translate(-float64(w)/2.0, -float64(h)/2.0)
	op.GeoM.Rotate(float64(g.vy16) / 96.0 * math.Pi / 6)
	op.GeoM.Translate(float64(w)/2.0, float64(h)/2.0)
	op.GeoM.Translate(float64(g.x16/16.0)-float64(g.cameraX), float64(g.y16/16.0)-float64(g.cameraY))
	op.Filter = ebiten.FilterLinear
	screen.DrawImage(gopherImage, op)
}

func main() {
	g := NewGame()
	// On browsers, let's use fullscreen so that this is playable on any browsers.
	// It is planned to ignore the given 'scale' apply fullscreen automatically on browsers (#571).
	if runtime.GOARCH == "js" {
		ebiten.SetFullscreen(true)
	}
	if err := ebiten.Run(g.Update, screenWidth, screenHeight, 1, "Flappy Gopher (Ebiten Demo)"); err != nil {
		panic(err)
	}
}
<commit_msg>examples/flappy: Add license comment<commit_after>// Copyright 2018 The Ebiten 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.

// +build example jsgo

package main

import (
	"bytes"
	"fmt"
	"image"
	"image/color"
	_ "image/png"
	"log"
	"math"
	"math/rand"
	"runtime"
	"time"

	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/audio"
	"github.com/hajimehoshi/ebiten/audio/vorbis"
	"github.com/hajimehoshi/ebiten/audio/wav"
	"github.com/hajimehoshi/ebiten/ebitenutil"
	raudio "github.com/hajimehoshi/ebiten/examples/resources/audio"
	"github.com/hajimehoshi/ebiten/examples/resources/fonts"
	resources "github.com/hajimehoshi/ebiten/examples/resources/images/flappy"
	"github.com/hajimehoshi/ebiten/inpututil"
	"github.com/hajimehoshi/ebiten/text"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func floorDiv(x, y int) int {
	d := x / y
	if d*y == x || x >= 0 {
		return d
	}
	return d - 1
}

func floorMod(x, y int) int {
	return x - floorDiv(x, y)*y
}

const (
	screenWidth      = 640
	screenHeight     = 480
	tileSize         = 32
	fontSize         = 32
	smallFontSize    = fontSize / 2
	pipeWidth        = tileSize * 2
	pipeStartOffsetX = 8
	pipeIntervalX    = 8
	pipeGapY         = 5
)

var (
	gopherImage     *ebiten.Image
	tilesImage      *ebiten.Image
	arcadeFont      font.Face
	smallArcadeFont font.Face
)

func init() {
	img, _, err := image.Decode(bytes.NewReader(resources.Gopher_png))
	if err != nil {
		log.Fatal(err)
	}
	gopherImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterDefault)

	img, _, err = image.Decode(bytes.NewReader(resources.Tiles_png))
	if err != nil {
		log.Fatal(err)
	}
	tilesImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterDefault)
}

func init() {
	tt, err := truetype.Parse(fonts.ArcadeN_ttf)
	if err != nil {
		log.Fatal(err)
	}
	const dpi = 72
	arcadeFont = truetype.NewFace(tt, &truetype.Options{
		Size:    fontSize,
		DPI:     dpi,
		Hinting: font.HintingFull,
	})
	smallArcadeFont = truetype.NewFace(tt, &truetype.Options{
		Size:    smallFontSize,
		DPI:     dpi,
		Hinting: font.HintingFull,
	})
}

var (
	audioContext *audio.Context
	jumpPlayer   *audio.Player
	hitPlayer    *audio.Player
)

func init() {
	audioContext, _ = audio.NewContext(44100)

	jumpD, err := vorbis.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Jump_ogg))
	if err != nil {
		println("!?")
		log.Fatal(err)
	}
	jumpPlayer, err = audio.NewPlayer(audioContext, jumpD)
	if err != nil {
		log.Fatal(err)
	}

	jabD, err := wav.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Jab_wav))
	if err != nil {
		log.Fatal(err)
	}
	hitPlayer, err = audio.NewPlayer(audioContext, jabD)
	if err != nil {
		log.Fatal(err)
	}
}

type Mode int

const (
	ModeTitle Mode = iota
	ModeGame
	ModeGameOver
)

type Game struct {
	mode Mode

	// The gopher's position
	x16  int
	y16  int
	vy16 int

	// Camera
	cameraX int
	cameraY int

	// Pipes
	pipeTileYs []int

	gameoverCount int
}

func NewGame() *Game {
	g := &Game{}
	g.init()
	return g
}

func (g *Game) init() {
	g.x16 = 0
	g.y16 = 100 * 16
	g.cameraX = -240
	g.cameraY = 0
	g.pipeTileYs = make([]int, 256)
	for i := range g.pipeTileYs {
		g.pipeTileYs[i] = rand.Intn(6) + 2
	}
}

func jump() bool {
	if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
		return true
	}
	if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
		return true
	}
	if len(inpututil.JustPressedTouchIDs()) > 0 {
		return true
	}
	return false
}

func (g *Game) Update(screen *ebiten.Image) error {
	switch g.mode {
	case ModeTitle:
		if jump() {
			g.mode = ModeGame
		}
	case ModeGame:
		g.x16 += 32
		g.cameraX += 2
		if jump() {
			g.vy16 = -96
			jumpPlayer.Rewind()
			jumpPlayer.Play()
		}
		g.y16 += g.vy16

		// Gravity
		g.vy16 += 4
		if g.vy16 > 96 {
			g.vy16 = 96
		}

		if g.hit() {
			hitPlayer.Rewind()
			hitPlayer.Play()
			g.mode = ModeGameOver
			g.gameoverCount = 30
		}
	case ModeGameOver:
		if g.gameoverCount > 0 {
			g.gameoverCount--
		}
		if g.gameoverCount == 0 && jump() {
			g.init()
			g.mode = ModeTitle
		}
	}

	if ebiten.IsRunningSlowly() {
		return nil
	}

	screen.Fill(color.RGBA{0x80, 0xa0, 0xc0, 0xff})
	g.drawTiles(screen)
	if g.mode != ModeTitle {
		g.drawGopher(screen)
	}
	var texts []string
	switch g.mode {
	case ModeTitle:
		texts = []string{"FLAPPY GOPHER", "", "", "", "", "PRESS SPACE KEY", "", "OR TOUCH SCREEN"}
	case ModeGameOver:
		texts = []string{"", "GAMEOVER!"}
	}
	for i, l := range texts {
		x := (screenWidth - len(l)*fontSize) / 2
		text.Draw(screen, l, arcadeFont, x, (i+4)*fontSize, color.White)
	}

	if g.mode == ModeTitle {
		msg := []string{
			"Go Gopher by Renee French is",
			"licenced under CC BY 3.0.",
		}
		for i, l := range msg {
			x := (screenWidth - len(l)*smallFontSize) / 2
			text.Draw(screen, l, smallArcadeFont, x, screenHeight-4+(i-1)*smallFontSize, color.White)
		}
	}

	scoreStr := fmt.Sprintf("%04d", g.score())
	text.Draw(screen, scoreStr, arcadeFont, screenWidth-len(scoreStr)*fontSize, fontSize, color.White)
	ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f", ebiten.CurrentFPS()))
	return nil
}

func (g *Game) pipeAt(tileX int) (tileY int, ok bool) {
	if (tileX - pipeStartOffsetX) <= 0 {
		return 0, false
	}
	if floorMod(tileX-pipeStartOffsetX, pipeIntervalX) != 0 {
		return 0, false
	}
	idx := floorDiv(tileX-pipeStartOffsetX, pipeIntervalX)
	return g.pipeTileYs[idx%len(g.pipeTileYs)], true
}

func (g *Game) score() int {
	x := floorDiv(g.x16, 16) / tileSize
	if (x - pipeStartOffsetX) <= 0 {
		return 0
	}
	return floorDiv(x-pipeStartOffsetX, pipeIntervalX)
}

func (g *Game) hit() bool {
	if g.mode != ModeGame {
		return false
	}
	const (
		gopherWidth  = 30
		gopherHeight = 60
	)
	w, h := gopherImage.Size()
	x0 := floorDiv(g.x16, 16) + (w-gopherWidth)/2
	y0 := floorDiv(g.y16, 16) + (h-gopherHeight)/2
	x1 := x0 + gopherWidth
	y1 := y0 + gopherHeight
	if y0 < -tileSize*4 {
		return true
	}
	if y1 >= screenHeight-tileSize {
		return true
	}
	xMin := floorDiv(x0-pipeWidth, tileSize)
	xMax := floorDiv(x0+gopherWidth, tileSize)
	for x := xMin; x <= xMax; x++ {
		y, ok := g.pipeAt(x)
		if !ok {
			continue
		}
		if x0 >= x*tileSize+pipeWidth {
			continue
		}
		if x1 < x*tileSize {
			continue
		}
		if y0 < y*tileSize {
			return true
		}
		if y1 >= (y+pipeGapY)*tileSize {
			return true
		}
	}
	return false
}

func (g *Game) drawTiles(screen *ebiten.Image) {
	const (
		nx           = screenWidth / tileSize
		ny           = screenHeight / tileSize
		pipeTileSrcX = 128
		pipeTileSrcY = 192
	)

	op := &ebiten.DrawImageOptions{}
	for i := -2; i < nx+1; i++ {
		// ground
		op.GeoM.Reset()
		op.GeoM.Translate(float64(i*tileSize-floorMod(g.cameraX, tileSize)),
			float64((ny-1)*tileSize-floorMod(g.cameraY, tileSize)))
		r := image.Rect(0, 0, tileSize, tileSize)
		op.SourceRect = &r
		screen.DrawImage(tilesImage, op)

		// pipe
		if tileY, ok := g.pipeAt(floorDiv(g.cameraX, tileSize) + i); ok {
			for j := 0; j < tileY; j++ {
				op.GeoM.Reset()
				op.GeoM.Scale(1, -1)
				op.GeoM.Translate(float64(i*tileSize-floorMod(g.cameraX, tileSize)),
					float64(j*tileSize-floorMod(g.cameraY, tileSize)))
				op.GeoM.Translate(0, tileSize)
				if j == tileY-1 {
					r := image.Rect(pipeTileSrcX, pipeTileSrcY, pipeTileSrcX+tileSize*2, pipeTileSrcY+tileSize)
					op.SourceRect = &r
				} else {
					r := image.Rect(pipeTileSrcX, pipeTileSrcY+tileSize, pipeTileSrcX+tileSize*2, pipeTileSrcY+tileSize*2)
					op.SourceRect = &r
				}
				screen.DrawImage(tilesImage, op)
			}
			for j := tileY + pipeGapY; j < screenHeight/tileSize-1; j++ {
				op.GeoM.Reset()
				op.GeoM.Translate(float64(i*tileSize-floorMod(g.cameraX, tileSize)),
					float64(j*tileSize-floorMod(g.cameraY, tileSize)))
				if j == tileY+pipeGapY {
					r := image.Rect(pipeTileSrcX, pipeTileSrcY, pipeTileSrcX+pipeWidth, pipeTileSrcY+tileSize)
					op.SourceRect = &r
				} else {
					r := image.Rect(pipeTileSrcX, pipeTileSrcY+tileSize, pipeTileSrcX+pipeWidth, pipeTileSrcY+tileSize+tileSize)
					op.SourceRect = &r
				}
				screen.DrawImage(tilesImage, op)
			}
		}
	}
}

func (g *Game) drawGopher(screen *ebiten.Image) {
	op := &ebiten.DrawImageOptions{}
	w, h := gopherImage.Size()
	op.GeoM.Translate(-float64(w)/2.0, -float64(h)/2.0)
	op.GeoM.Rotate(float64(g.vy16) / 96.0 * math.Pi / 6)
	op.GeoM.Translate(float64(w)/2.0, float64(h)/2.0)
	op.GeoM.Translate(float64(g.x16/16.0)-float64(g.cameraX), float64(g.y16/16.0)-float64(g.cameraY))
	op.Filter = ebiten.FilterLinear
	screen.DrawImage(gopherImage, op)
}

func main() {
	g := NewGame()
	// On browsers, let's use fullscreen so that this is playable on any browsers.
	// It is planned to ignore the given 'scale' apply fullscreen automatically on browsers (#571).
	if runtime.GOARCH == "js" {
		ebiten.SetFullscreen(true)
	}
	if err := ebiten.Run(g.Update, screenWidth, screenHeight, 1, "Flappy Gopher (Ebiten Demo)"); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/martini"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	"github.com/tomsteele/dmv"
	"net/http"
)

func main() {
	googleOpts := &dmv.OAuth2Options{
		ClientID:     "CLIENTID",
		ClientSecret: "CLIENTSECRET",
		RedirectURL:  "http://localhost:3000/auth/callback/google",
		Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
	}

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte("secret123"))
	m.Use(sessions.Sessions("my_session", store))
	m.Use(render.Renderer())

	m.Get("/", func(s sessions.Session, r render.Render) {
		if s.Get("userID") == nil {
			r.Redirect("/login", 302)
			return
		}
		r.HTML(200, "home", nil)
	})

	m.Get("/login", func(r render.Render) {
		r.HTML(200, "login", nil)
	})

	m.Get("/auth/google", dmv.AuthGoogle(googleOpts))
	m.Get("/auth/callback/google", dmv.AuthGoogle(googleOpts), func(goog *dmv.Google, r render.Render, s sessions.Session, w http.ResponseWriter) {
		// Handle any errors.
		if len(goog.Errors) > 0 {
			http.Error(w, "Oauth failure", http.StatusInternalServerError)
			return
		}
		// Do something in a database to create or find the user by the Google profile id.
		// for now just pass the google display name
		s.Set("userID", goog.Profile.ID)
		r.HTML(200, "home", goog.Profile.DisplayName)

	})

	m.Run()
}
<commit_msg>Pass userID to template<commit_after>package main

import (
	"github.com/codegangsta/martini"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	"github.com/tomsteele/dmv"
	"net/http"
)

func main() {
	googleOpts := &dmv.OAuth2Options{
		ClientID:     "CLIENTID",
		ClientSecret: "CLIENTSECRET",
		RedirectURL:  "http://localhost:3000/auth/callback/google",
		Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
	}

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte("secret123"))
	m.Use(sessions.Sessions("my_session", store))
	m.Use(render.Renderer())

	m.Get("/", func(s sessions.Session, r render.Render) {
		if uid := s.Get("userID"); uid == nil {
			r.Redirect("/login", 302)
		} else {
			r.HTML(200, "home", uid.(string))
		}
	})

	m.Get("/login", func(r render.Render) {
		r.HTML(200, "login", nil)
	})

	m.Get("/auth/google", dmv.AuthGoogle(googleOpts))
	m.Get("/auth/callback/google", dmv.AuthGoogle(googleOpts), func(goog *dmv.Google, r render.Render, s sessions.Session, w http.ResponseWriter) {
		// Handle any errors.
		if len(goog.Errors) > 0 {
			http.Error(w, "Oauth failure", http.StatusInternalServerError)
			return
		}
		// Do something in a database to create or find the user by the Google profile id.
		// for now just pass the google display name
		s.Set("userID", goog.Profile.ID)
		r.HTML(200, "home", goog.Profile.DisplayName)
	})

	m.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"time"
)

import (
	"github.com/julienschmidt/httprouter"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprint(w, "Welcome!\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

func RecordingsHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	fmt.Fprintln(w, "Recordings handler function")
}

func NewRecordingHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	fmt.Fprintln(w, "
Scheduling New recording
")

	rec := new(Recording)
	rec.Init() // give it a UUID

	// Need to fill this from the posted data (*http.Request)
	//rec.Date = time.Date(2016, time.June, 23, 21, 33, 00, 0000, time.Local)
	rec.Date = time.Now().Add(time.Duration(30) * time.Second) // 30 seconds in the future
	rec.Duration = 15                                          // 60 s/min * 30 min = 1800 sec
	rec.Scheduled = true

	//requestedTime := time.Date(2018, time.June, 13, 19, 00, 00, 0000, time.UTC)

	if rec.Date.Add(time.Duration(rec.Duration) * time.Second).Before(time.Now()) {

		fmt.Fprintln(w, "ERROR: Requested recording window is in the past")

	} else {

		fmt.Fprintf(w, "Scheduled recording on %s for %d seconds\n", rec.Date.Local(), rec.Duration)

		// post rec to database

		// call the scheduler
		ScheduleRecording(rec)
	}
}
<commit_msg>JSON response to new recording request<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

import (
	"github.com/julienschmidt/httprouter"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprint(w, "Welcome!\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

func RecordingsHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	fmt.Fprintln(w, "Recordings handler function")
}

func NewRecordingHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

	type Response struct {
		Status  string `json:"status"`
		Message string `json:"message"`
	}

	header := w.Header()
	header.Set("Content-Type", "application/json")

	//fmt.Fprintln(w, "
Scheduling New recording
")

	rec := new(Recording)
	rec.Init() // give it a UUID

	// Need to fill this from the posted data (*http.Request)
	//rec.Date = time.Date(2016, time.June, 23, 21, 33, 00, 0000, time.Local)
	rec.Date = time.Now().Add(time.Duration(30) * time.Second) // 30 seconds in the future
	rec.Duration = 15                                          // 60 s/min * 30 min = 1800 sec
	rec.Scheduled = true

	//requestedTime := time.Date(2018, time.June, 13, 19, 00, 00, 0000, time.UTC)

	if rec.Date.Add(time.Duration(rec.Duration) * time.Second).Before(time.Now()) {

		resp := Response{"error", "Requested recording window is in the past"}
		jsonresp, _ := json.Marshal(resp)
		w.Write(jsonresp)
		//fmt.Fprintln(w, "ERROR: Requested recording window is in the past")

	} else {

		// post rec to database

		// call the scheduler
		ScheduleRecording(rec)

		msg := fmt.Sprintf("Scheduled recording on %s for %d seconds\n", rec.Date.Local(), rec.Duration)
		resp := Response{"ok", msg}
		jsonresp, _ := json.Marshal(resp)
		w.Write(jsonresp)

	}
}
<|endoftext|>


<commit_before>package huffman

type HuffmanCode struct {
	Code   uint32
	BitLen byte
}

type Node struct {
	Left, Right *Node
	Code        int16
}

var HUFFMAN_TABLE *[257]HuffmanCode = &[257]HuffmanCode{
	{0x1ff8, 13},
	{0x7fffd8, 23},
	{0xfffffe2, 28},
	{0xfffffe3, 28},
	{0xfffffe4, 28},
	{0xfffffe5, 28},
	{0xfffffe6, 28},
	{0xfffffe7, 28},
	{0xfffffe8, 28},
	{0xffffea, 24},
	{0x3ffffffc, 30},
	{0xfffffe9, 28},
	{0xfffffea, 28},
	{0x3ffffffd, 30},
	{0xfffffeb, 28},
	{0xfffffec, 28},
	{0xfffffed, 28},
	{0xfffffee, 28},
	{0xfffffef, 28},
	{0xffffff0, 28},
	{0xffffff1, 28},
	{0xffffff2, 28},
	{0x3ffffffe, 30},
	{0xffffff3, 28},
	{0xffffff4, 28},
	{0xffffff5, 28},
	{0xffffff6, 28},
	{0xffffff7, 28},
	{0xffffff8, 28},
	{0xffffff9, 28},
	{0xffffffa, 28},
	{0xffffffb, 28},
	{0x14, 6},
	{0x3f8, 10},
	{0x3f9, 10},
	{0xffa, 12},
	{0x1ff9, 13},
	{0x15, 6},
	{0xf8, 8},
	{0x7fa, 11},
	{0x3fa, 10},
	{0x3fb, 10},
	{0xf9, 8},
	{0x7fb, 11},
	{0xfa, 8},
	{0x16, 6},
	{0x17, 6},
	{0x18, 6},
	{0x0, 5},
	{0x1, 5},
	{0x2, 5},
	{0x19, 6},
	{0x1a, 6},
	{0x1b, 6},
	{0x1c, 6},
	{0x1d, 6},
	{0x1e, 6},
	{0x1f, 6},
	{0x5c, 7},
	{0xfb, 8},
	{0x7ffc, 15},
	{0x20, 6},
	{0xffb, 12},
	{0x3fc, 10},
	{0x1ffa, 13},
	{0x21, 6},
	{0x5d, 7},
	{0x5e, 7},
	{0x5f, 7},
	{0x60, 7},
	{0x61, 7},
	{0x62, 7},
	{0x63, 7},
	{0x64, 7},
	{0x65, 7},
	{0x66, 7},
	{0x67, 7},
	{0x68, 7},
	{0x69, 7},
	{0x6a, 7},
	{0x6b, 7},
	{0x6c, 7},
	{0x6d, 7},
	{0x6e, 7},
	{0x6f, 7},
	{0x70, 7},
	{0x71, 7},
	{0x72, 7},
	{0xfc, 8},
	{0x73, 7},
	{0xfd, 8},
	{0x1ffb, 13},
	{0x7fff0, 19},
	{0x1ffc, 13},
	{0x3ffc, 14},
	{0x22, 6},
	{0x7ffd, 15},
	{0x3, 5},
	{0x23, 6},
	{0x4, 5},
	{0x24, 6},
	{0x5, 5},
	{0x25, 6},
	{0x26, 6},
	{0x27, 6},
	{0x6, 5},
	{0x74, 7},
	{0x75, 7},
	{0x28, 6},
	{0x29, 6},
	{0x2a, 6},
	{0x7, 5},
	{0x2b, 6},
	{0x76, 7},
	{0x2c, 6},
	{0x8, 5},
	{0x9, 5},
	{0x2d, 6},
	{0x77, 7},
	{0x78, 7},
	{0x79, 7},
	{0x7a, 7},
	{0x7b, 7},
	{0x7ffe, 15},
	{0x7fc, 11},
	{0x3ffd, 14},
	{0x1ffd, 13},
	{0xffffffc, 28},
	{0xfffe6, 20},
	{0x3fffd2, 22},
	{0xfffe7, 20},
	{0xfffe8, 20},
	{0x3fffd3, 22},
	{0x3fffd4, 22},
	{0x3fffd5, 22},
	{0x7fffd9, 23},
	{0x3fffd6, 22},
	{0x7fffda, 23},
	{0x7fffdb, 23},
	{0x7fffdc, 23},
	{0x7fffdd, 23},
	{0x7fffde, 23},
	{0xffffeb, 23},
	{0x7fffdf, 23},
	{0xffffec, 24},
	{0xffffed, 24},
	{0x3fffd7, 22},
	{0x7fffe0, 23},
	{0xffffee, 24},
	{0x7fffe1, 23},
	{0x7fffe2, 23},
	{0x7fffe3, 23},
	{0x7fffe4, 23},
	{0x1fffdc, 21},
	{0x3fffd8, 22},
	{0x7fffe5, 23},
	{0x3fffd9, 22},
	{0x7fffe6, 23},
	{0x7fffe7, 23},
	{0xffffef, 24},
	{0x3fffda, 22},
	{0x1fffdd, 21},
	{0xfffe9, 20},
	{0x3fffdb, 22},
	{0x3fffdc, 22},
	{0x7fffe8, 23},
	{0x7fffe9, 23},
	{0x1fffde, 21},
	{0x7fffde, 23},
	{0x3fffdd, 22},
	{0x3fffde, 22},
	{0xfffff0, 24},
	{0x1fffdf, 21},
	{0x3fffdf, 22},
	{0x7fffeb, 23},
	{0x7fffec, 23},
	{0x1fffe0, 21},
	{0x1fffe1, 21},
	{0x3fffe0, 22},
	{0x1fffe2, 21},
	{0x7fffed, 23},
	{0x3fffe1, 22},
	{0x7fffee, 23},
	{0x7fffef, 23},
	{0xfffea, 20},
	{0x3fffe2, 22},
	{0x3fffe3, 22},
	{0x3fffe4, 22},
	{0x7ffff0, 23},
	{0x3fffe5, 22},
	{0x3fffe6, 22},
	{0x7ffff1, 23},
	{0x3ffffe0, 26},
	{0x3ffffe1, 26},
	{0xfffeb, 20},
	{0x7fff1, 19},
	{0x3fffe7, 22},
	{0x7ffff2, 23},
	{0x3fffe8, 22},
	{0x1ffffec, 25},
	{0x3ffffe2, 26},
	{0x3ffffe3, 26},
	{0x3ffffe4, 26},
	{0x7ffffde, 27},
	{0x7ffffdf, 27},
	{0x3ffffe5, 26},
	{0xfffff1, 24},
	{0x1ffffed, 25},
	{0x7fff2, 19},
	{0x1fffe3, 21},
	{0x3ffffe6, 26},
	{0x7ffffe0, 27},
	{0x7ffffe1, 27},
	{0x3ffffe7, 26},
	{0x3ffffe2, 27},
	{0xfffff2, 24},
	{0x1fffe4, 21},
	{0x1fffe5, 21},
	{0x3ffffe8, 26},
	{0x3ffffe9, 26},
	{0xffffffd, 28},
	{0x7ffffe3, 27},
	{0x7ffffe4, 27},
	{0x7ffffe5, 27},
	{0xfffec, 20},
	{0xfffff3, 24},
	{0xfffed, 20},
	{0x1fffe6, 21},
	{0x3fffe9, 22},
	{0x1fffe7, 21},
	{0x1fffe8, 21},
	{0x7ffff3, 23},
	{0x3fffea, 22},
	{0x3fffeb, 22},
	{0x1ffffee, 25},
	{0x1ffffef, 25},
	{0xfffff4, 24},
	{0xfffff5, 24},
	{0x3ffffea, 26},
	{0x7ffff4, 23},
	{0x3ffffeb, 26},
	{0x7ffffe6, 27},
	{0x3ffffec, 26},
	{0x3ffffed, 26},
	{0x7ffffe7, 27},
	{0x7ffffe8, 27},
	{0x7ffffe9, 27},
	{0x7ffffea, 27},
	{0x7ffffeb, 27},
	{0xffffffe, 28},
	{0x7ffffec, 27},
	{0x7ffffed, 27},
	{0x7ffffee, 27},
	{0x7ffffef, 27},
	{0x7fffff0, 27},
	{0x3ffffee, 26},
	{0x3fffffff, 30},
}

var Root Node = Node{nil, nil, -1}

func (root *Node) CreateTree() {
	for code, huff := range HUFFMAN_TABLE {
		cursor := root
		for i := huff.BitLen; i > 0; i-- {
			if huff.Code&(1<<(i-1)) > 0 {
				if cursor.Right == nil {
					cursor.Right = &Node{nil, nil, -1}
				}
				cursor = cursor.Right
			} else {
				if cursor.Left == nil {
					cursor.Left = &Node{nil, nil, -1}
				}
				cursor = cursor.Left
			}
		}
		cursor.Code = int16(code)
	}
}

func (root *Node) Encode(content string) (dist []byte, length uint32) {
	var buf byte
	var bufRest byte = 8
	asciis := []byte(content)
	for _, ascii := range asciis {
		huff := HUFFMAN_TABLE[ascii]

		for huff.BitLen > 0 {
			if huff.BitLen < bufRest {
				bufRest -= huff.BitLen
				buf |= byte(huff.Code << bufRest)
				huff.BitLen = 0
			} else {
				shift := huff.BitLen - bufRest
				buf |= byte(huff.Code >> shift)
				huff.BitLen -= bufRest
				bufRest = 0
				huff.Code = huff.Code & ((1 << shift) - 1)
			}
			if bufRest == 0 {
				dist = append(dist, buf)
				buf = 0
				bufRest = 8
				length++
			}
		}
	}
	if bufRest > 0 && bufRest < 8 {
		buf |= ((1 << bufRest) - 1)
		dist = append(dist, buf)
		length++
	}

	return
}

func (root *Node) Decode(buf []byte, length uint32) (content string) {
	cursor := root
	for i := 0; uint32(i) < length; i++ {
		for j := 7; j >= 0; j-- {
			if buf[i]&(1<<byte(j)) > 0 {
				cursor = cursor.Right
			} else {
				cursor = cursor.Left
			}
			if cursor.Code >= 0 {
				content += string(cursor.Code)
				cursor = root
			}
		}
	}
	return content
}
<commit_msg>added huffman directory<commit_after><|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/goburrow/serial"
	"github.com/xiegeo/modbusone"
)

var address = flag.String("l", "", "required device location, such as: /dev/ttyS0 in linux or com1 in windows")
var baudRate = flag.Int("r", 19200, "baud rate")

var isClient = flag.Bool("c", false, "true for client, false (default) for server. The client is interactive.")
var slaveID = flag.Uint64("id", 1, "the slaveId of the server for serial communication, 0 for multicast only")
var fillData = flag.String("d", "am3", "data to start with, am3 starts memory "+
	"with bools as address (mod 3) == 0, and registers as address * 3 (mod uint16)")

var verbose = flag.Bool("v", false, "prints debugging information")

func main() {
	flag.Parse()
	if *verbose {
		modbusone.DebugOut = os.Stdout
	}
	var com = &modbusone.SerialPort{}
	err := com.Open(serial.Config{
		Address:  *address,
		BaudRate: *baudRate,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "open serial error: %v\n", err)
		os.Exit(1)
	}
	defer com.Close()

	id, err := modbusone.Uint64ToSlaveID(*slaveID)
	if err != nil {
		fmt.Fprintf(os.Stderr, "set slaveID error: %v\n", err)
		os.Exit(1)
	}
	if *fillData == "am3" {
		fillAm3()
	}
	var device modbusone.Server
	if *isClient {
		client := modbusone.NewRTUCLient(com, id)
		go runClient(client)
		device = client
	} else {
		device = modbusone.NewRTUServer(com, id)
	}
	h := modbusone.SimpleHandler{
		ReadDiscreteInputs: func(address, quantity uint16) ([]bool, error) {
			fmt.Printf("ReadDiscreteInputs from %v, quantity %v\n", address, quantity)
			return discretes[address : address+quantity], nil
		},
		WriteDiscreteInputs: func(address uint16, values []bool) error {
			fmt.Printf("WriteDiscreteInputs from %v, quantity %v\n", address, len(values))
			for i, v := range values {
				discretes[address+uint16(i)] = v
			}
			return nil
		},

		ReadCoils: func(address, quantity uint16) ([]bool, error) {
			fmt.Printf("ReadCoils from %v, quantity %v\n", address, quantity)
			return coils[address : address+quantity], nil
		},
		WriteCoils: func(address uint16, values []bool) error {
			fmt.Printf("WriteCoils from %v, quantity %v\n", address, len(values))
			for i, v := range values {
				coils[address+uint16(i)] = v
			}
			return nil
		},

		ReadInputRegisters: func(address, quantity uint16) ([]uint16, error) {
			fmt.Printf("ReadInputRegisters from %v, quantity %v\n", address, quantity)
			return inputRegisters[address : address+quantity], nil
		},
		WriteInputRegisters: func(address uint16, values []uint16) error {
			fmt.Printf("WriteInputRegisters from %v, quantity %v\n", address, len(values))
			for i, v := range values {
				inputRegisters[address+uint16(i)] = v
			}
			return nil
		},

		ReadHoldingRegisters: func(address, quantity uint16) ([]uint16, error) {
			fmt.Printf("ReadHoldingRegisters from %v, quantity %v\n", address, quantity)
			return holdingRegisters[address : address+quantity], nil
		},
		WriteHoldingRegisters: func(address uint16, values []uint16) error {
			fmt.Printf("WriteHoldingRegisters from %v, quantity %v\n", address, len(values))
			for i, v := range values {
				holdingRegisters[address+uint16(i)] = v
			}
			return nil
		},

		OnErrorImp: func(req modbusone.PDU, errRep modbusone.PDU) {
			fmt.Printf("error received: %v from req:\n", errRep, req)
		},
	}
	err = device.Serve(&h)
	if err != nil {
		fmt.Fprintf(os.Stderr, "serve error: %v\n", err)
		os.Exit(1)
	}
}

const size = 0x10000

var discretes [size]bool
var coils [size]bool
var inputRegisters [size]uint16
var holdingRegisters [size]uint16

func fillAm3() {
	for i := range discretes {
		discretes[i] = i%3 == 0
	}
	for i := range coils {
		coils[i] = i%3 != 0
	}
	for i := range inputRegisters {
		inputRegisters[i] = uint16(i * 3)
	}
	for i := range holdingRegisters {
		holdingRegisters[i] = uint16(0xFFFF - i)
	}
}
<commit_msg>hack for serial issue 5<commit_after>package main

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

	"github.com/goburrow/serial"
	"github.com/xiegeo/modbusone"
)

var address = flag.String("l", "", "required device location, such as: /dev/ttyS0 in linux or com1 in windows")
var baudRate = flag.Int("r", 19200, "baud rate")

var isClient = flag.Bool("c", false, "true for client, false (default) for server. The client is interactive.")
var slaveID = flag.Uint64("id", 1, "the slaveId of the server for serial communication, 0 for multicast only")
var fillData = flag.String("d", "am3", "data to start with, am3 starts memory "+
	"with bools as address (mod 3) == 0, and registers as address * 3 (mod uint16)")

var verbose = flag.Bool("v", false, "prints debugging information")

func main() {
	flag.Parse()
	if *verbose {
		modbusone.DebugOut = os.Stdout
	}
	var com = &modbusone.SerialPort{}
	err := com.Open(serial.Config{
		Address:  *address,
		BaudRate: *baudRate,
		Timeout:  time.Hour, //a hack for https://github.com/goburrow/serial/issues/5
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "open serial error: %v\n", err)
		os.Exit(1)
	}
	defer com.Close()

	id, err := modbusone.Uint64ToSlaveID(*slaveID)
	if err != nil {
		fmt.Fprintf(os.Stderr, "set slaveID error: %v\n", err)
		os.Exit(1)
	}
	if *fillData == "am3" {
		fillAm3()
	}
	var device modbusone.Server
	if *isClient {
		client := modbusone.NewRTUCLient(com, id)
		go runClient(client)
		device = client
	} else {
		device = modbusone.NewRTUServer(com, id)
	}
	h := modbusone.SimpleHandler{
		ReadDiscreteInputs: func(address, quantity uint16) ([]bool, error) {
			fmt.Printf("ReadDiscreteInputs from %v, quantity %v\n", address, quantity)
			return discretes[address : address+quantity], nil
		},
		WriteDiscreteInputs: func(address uint16, values []bool) error {
			fmt.Printf("WriteDiscreteInputs from %v, quantity %v\n", address, len(values))
			for i, v := range values {
				discretes[address+uint16(i)] = v
			}
			return nil
		},

		ReadCoils: func(address, quantity uint16) ([]bool, error) {
			fmt.Printf("ReadCoils from %v, quantity %v\n", address, quantity)
			return coils[address : address+quantity], nil
		},
		WriteCoils: func(address uint16, values []bool) error {
			fmt.Printf("WriteCoils from %v, quantity %v\n", address, len(values))
			for i, v := range values {
				coils[address+uint16(i)] = v
			}
			return nil
		},

		ReadInputRegisters: func(address, quantity uint16) ([]uint16, error) {
			fmt.Printf("ReadInputRegisters from %v, quantity %v\n", address, quantity)
			return inputRegisters[address : address+quantity], nil
		},
		WriteInputRegisters: func(address uint16, values []uint16) error {
			fmt.Printf("WriteInputRegisters from %v, quantity %v\n", address, len(values))
			for i, v := range values {
				inputRegisters[address+uint16(i)] = v
			}
			return nil
		},

		ReadHoldingRegisters: func(address, quantity uint16) ([]uint16, error) {
			fmt.Printf("ReadHoldingRegisters from %v, quantity %v\n", address, quantity)
			return holdingRegisters[address : address+quantity], nil
		},
		WriteHoldingRegisters: func(address uint16, values []uint16) error {
			fmt.Printf("WriteHoldingRegisters from %v, quantity %v\n", address, len(values))
			for i, v := range values {
				holdingRegisters[address+uint16(i)] = v
			}
			return nil
		},

		OnErrorImp: func(req modbusone.PDU, errRep modbusone.PDU) {
			fmt.Printf("error received: %v from req:\n", errRep, req)
		},
	}
	err = device.Serve(&h)
	if err != nil {
		fmt.Fprintf(os.Stderr, "serve error: %v\n", err)
		os.Exit(1)
	}
}

const size = 0x10000

var discretes [size]bool
var coils [size]bool
var inputRegisters [size]uint16
var holdingRegisters [size]uint16

func fillAm3() {
	for i := range discretes {
		discretes[i] = i%3 == 0
	}
	for i := range coils {
		coils[i] = i%3 != 0
	}
	for i := range inputRegisters {
		inputRegisters[i] = uint16(i * 3)
	}
	for i := range holdingRegisters {
		holdingRegisters[i] = uint16(0xFFFF - i)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/ashaniray/go4git"
	"io"
	"os"
	"sort"
)

var offset = flag.Int64("s", -1, "The offset to read from the pack file")
var verbose = flag.Bool("t", false, "Output verbose information")
var verifyPack = flag.Bool("v", true, "Produce output of git pack-verify -v")
var debug = flag.Bool("d", false, "Outputs debug information to debug the code")

func showDebugInfo(inPack io.ReadSeeker, inIdx io.ReadSeeker) {
	indices, err := go4git.GetAllPackedIndex(inIdx)
	if err != nil {
		panic(err)
	}
	sort.Sort(go4git.ByOffset(indices))
	cnt := len(indices)
	o, err := go4git.ReadPackedObjectAtOffset(int64(indices[0].Offset), inPack, inIdx)
	if err != nil {
		panic(err)
	}
	for i := 0; i < cnt-1; i++ {
		next, _ := go4git.ReadPackedObjectAtOffset(int64(indices[i+1].Offset), inPack, inIdx)
		if err != nil {
			panic(err)
		}
		fmt.Fprintf(os.Stdout, "################ Data at %d #############\n", i)
		fmt.Fprintf(os.Stdout, "%s %s %d %d %d %d Data(Below)\n%s\n",
			o.Hash,
			o.Type,
			o.Size,
			next.StartOffset-o.StartOffset,
			o.StartOffset,
			o.RefOffset,
			o.Data,
		)
		o = next
	}
}

func showVerifyPack(inPack io.ReadSeeker, inIdx io.ReadSeeker) {
	indices, err := go4git.GetAllPackedIndex(inIdx)
	if err != nil {
		panic(err)
	}
	sort.Sort(go4git.ByOffset(indices))
	cnt := len(indices)
	o, err := go4git.ReadPackedObjectAtOffset(int64(indices[0].Offset), inPack, inIdx)
	if err != nil {
		panic(err)
	}

	chainLength := make(map[int]int)
	for i := 0; i < cnt-1; i++ {
		next, _ := go4git.ReadPackedObjectAtOffset(int64(indices[i+1].Offset), inPack, inIdx)
		if err != nil {
			panic(err)
		}
		chainLength[next.RefLevel]++
		str := fmt.Sprintf("%s %s\t%d %d %d", o.Hash, o.ActualType, o.Size, next.StartOffset-o.StartOffset, o.StartOffset)
		if o.Type == go4git.REF_DELTA || o.Type == go4git.OFS_DELTA {
			str += fmt.Sprintf(" %d %s", o.RefLevel, o.BaseHash)
		}
		fmt.Fprintf(os.Stdout, "%s\n", str)
		o = next
		if i == cnt-2 {
			end := 0
			str := fmt.Sprintf("%s %s\t%d %d %d", next.Hash, next.ActualType, next.Size, end, next.StartOffset)
			if o.Type == go4git.REF_DELTA || o.Type == go4git.OFS_DELTA {
				str += fmt.Sprintf(" %d %s", o.RefLevel, o.BaseHash)
			}
			fmt.Fprintf(os.Stdout, "%s\n", str)
		}
	}
	fmt.Printf("Stats: %v\n", chainLength)
}

func main() {
	flag.Parse()
	f, err := go4git.GetArgInputFile()
	if err != nil {
		panic(err)
	}
	packFile := f.Name()
	idxName := packFile[:len(packFile)-4] + "idx"
	inIdx, err := os.Open(idxName)
	if err != nil {
		panic(err)
	}

	// TODO: This in only for easy debugging purposes..
	// To be deleted...
	if *debug {
		showDebugInfo(f, inIdx)
		return
	}

	if *offset != -1 {
		p, err := go4git.ReadPackedObjectAtOffset(*offset, f, inIdx)
		if err != nil {
			panic(err)
		}
		if *verbose {
			fmt.Fprintf(os.Stdout, "Object at [%d] => Type: %s, Size: %d\n", *offset, p.Type, p.Size)
			fmt.Fprintf(os.Stdout, "  ObjRef: %s, ObjOffset: %d\n", p.HashOfRef, p.RefOffset)
			fmt.Fprintf(os.Stdout, "  Data(starts below):\n")
		}
		fmt.Fprintf(os.Stdout, "%s", p.Data)
		return
	}

	if *verifyPack {
		showVerifyPack(f, inIdx)
		return
	}

}
<commit_msg>showpack...<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/ashaniray/go4git"
	"io"
	"os"
	"sort"
)

var offset = flag.Int64("s", -1, "The offset to read from the pack file")
var verbose = flag.Bool("t", false, "Output verbose information")
var verifyPack = flag.Bool("v", true, "Produce output of git pack-verify -v")

func showStats(stats map[int]int) {
	maxChain := 0
	for k, _ := range stats {
		if k > maxChain {
			maxChain = k
		}
	}
	fmt.Printf("non delta: %d objects\n", stats[0])
	for i := 1; i <= maxChain; i++ {
		fmt.Printf("chain length = %d: %d object", i, stats[i])
		if stats[i] > 1 {
			fmt.Printf("s")
		}
		fmt.Printf("\n")
	}
}

func showVerifyPack(inPack io.ReadSeeker, inIdx io.ReadSeeker) {
	indices, err := go4git.GetAllPackedIndex(inIdx)
	if err != nil {
		panic(err)
	}
	sort.Sort(go4git.ByOffset(indices))
	cnt := len(indices)

	chainLength := make(map[int]int)

	o, err := go4git.ReadPackedObjectAtOffset(int64(indices[0].Offset), inPack, inIdx)
	if err != nil {
		panic(err)
	}
	chainLength[o.RefLevel]++
	for i := 0; i < cnt-1; i++ {
		next, _ := go4git.ReadPackedObjectAtOffset(int64(indices[i+1].Offset), inPack, inIdx)
		if err != nil {
			panic(err)
		}
		chainLength[next.RefLevel]++
		str := fmt.Sprintf("%s %s %d %d %d", o.Hash, o.ActualType, o.Size, next.StartOffset-o.StartOffset, o.StartOffset)
		if o.Type == go4git.REF_DELTA || o.Type == go4git.OFS_DELTA {
			str += fmt.Sprintf(" %d %s", o.RefLevel, o.BaseHash)
		}
		fmt.Printf("%s\n", str)
		if o.Hash != indices[i].Hash {
			panic(fmt.Sprintf("Hash do not match for index %d offset %d: Expected: %s, Computed: %s",
				i,
				indices[i].Offset,
				indices[i].Hash,
				o.Hash,
			))
		}
		o = next
		if i == cnt-2 {
			end, _ := inPack.Seek(0, os.SEEK_END)
			end -= 20
			str := fmt.Sprintf("%s %s\t%d %d %d", next.Hash, next.ActualType, next.Size, end, next.StartOffset)
			if o.Type == go4git.REF_DELTA || o.Type == go4git.OFS_DELTA {
				str += fmt.Sprintf(" %d %s", o.RefLevel, o.BaseHash)
			}
			fmt.Printf("%s\n", str)
		}
	}
	showStats(chainLength)
}

func main() {
	flag.Parse()
	f, err := go4git.GetArgInputFile()
	if err != nil {
		panic(err)
	}
	packFile := f.Name()
	idxName := packFile[:len(packFile)-4] + "idx"
	inIdx, err := os.Open(idxName)
	if err != nil {
		panic(err)
	}

	if *offset != -1 {
		p, err := go4git.ReadPackedObjectAtOffset(*offset, f, inIdx)
		if err != nil {
			panic(err)
		}
		if *verbose {
			fmt.Printf("Details of Object at offset [%d] \n", *offset)
			fmt.Printf(" Type: %s\n", p.Type)
			fmt.Printf(" HashOfRef: %s\n", p.HashOfRef)
			fmt.Printf(" RefOffset: %d\n", p.RefOffset)
			fmt.Printf(" Size: %d\n", p.Size)
			fmt.Printf(" StartOffset: %d\n", p.StartOffset)
			fmt.Printf(" ActualType: %s\n", p.ActualType)
			fmt.Printf(" Hash: %s\n", p.Hash)
			fmt.Printf(" RefLevel: %d\n", p.RefLevel)
			fmt.Printf(" BaseHash: %s\n", p.BaseHash)
			fmt.Printf(" ---Data(starts below):---\n")
		}
		fmt.Printf("%s", p.Data)
		return
	}

	if *verifyPack {
		showVerifyPack(f, inIdx)
		return
	}
}
<|endoftext|>


<commit_before>/*
Package clerk provides file persistence for your Go structures.

It can replace external databases for small projects that can keep data in memory and don't make a lot of writes.
*/
package clerk

import (
	"encoding/gob"
	"errors"
	"io"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// Errors.
var (
	ErrDBInited     = errors.New("clerk: database inited multiple times")
	ErrDBNotInited  = errors.New("clerk: database not inited")
	ErrOldMigration = errors.New("clerk: database in file is newer than migration timestamp")
)

// DB provides methods to persist your data.
type DB struct {
	UpdatedAt   time.Time // UpdatedAt is the timestamp of the last data modification (by calling DB.Lock).
	filename    string
	tmpFilename string
	parent      interface{}
	mu          sync.RWMutex
}

// DBInterface represents an underlying database.
type DBInterface interface {
	updatedAt() time.Time
	setFilename(string)
	setParent(interface{})
	inited() bool
	Touch()
	Save() error
	Migrate(string, DBInterface, func() error) error
	MigrateOrRebase(string, DBInterface, func() error)
	Rebase() error
	Remove() error
	Lock()
	RLock()
	Unlock()
	RUnlock()
}

// Init sets the filename for source saving.
func Init(filename string, db DBInterface) {
	if db.inited() {
		panic(ErrDBInited)
	}
	db.setFilename(filename)
	db.setParent(db)
}

func (db *DB) updatedAt() time.Time {
	return db.UpdatedAt
}

func (db *DB) setFilename(name string) {
	db.filename = name
	// Set d.tmpFilename
	dir := filepath.Dir(name)
	base := filepath.Base(name)
	db.tmpFilename = filepath.Join(dir, base+"~")
}

func (db *DB) setParent(p interface{}) {
	db.parent = p
}

func (db *DB) inited() bool {
	return db.filename != "" && db.tmpFilename != "" && db.parent != nil
}

// Touch updates the UpdatedAt field.
func (db *DB) Touch() {
	db.UpdatedAt = time.Now()
}

// Save persists the database in the file set on init.
// Be sure the database is locked for writing.
func (db *DB) Save() error {
	if !db.inited() {
		return ErrDBNotInited
	}
	db.Touch()
	tmpFile, err := os.OpenFile(db.tmpFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}
	if err = gob.NewEncoder(tmpFile).Encode(db.parent); err != nil && err != io.EOF {
		tmpFile.Close()
		return err
	}
	if err = tmpFile.Close(); err != nil {
		return err
	}
	if err = os.Rename(db.tmpFilename, db.filename); err != nil {
		return err
	}
	return nil
}

// Rebase replaces the data with the content of the file set on init.
// Be sure the database is locked for writing.
func (db *DB) Rebase() error {
	if !db.inited() {
		return ErrDBNotInited
	}

	file, err := os.OpenFile(db.filename, os.O_RDONLY|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer file.Close()

	if err = gob.NewDecoder(file).Decode(db.parent); err != nil && err != io.EOF {
		return err
	}
	return nil
}

// Migrate runs a migration if provided date is after the one registered in the data file.
// It rebases the database file in oldDB, runs function do and saves database.
// timestamp format is:
//	2006-01-02 15:04:05 -07
func (db *DB) Migrate(timestamp string, oldDB DBInterface, do func() error) error {
	if !db.inited() {
		return ErrDBNotInited
	}
	ts, err := time.Parse("2006-01-02 15:04:05 -07", timestamp)
	if err != nil {
		return err
	}
	Init(db.filename, oldDB)
	if err = oldDB.Rebase(); err != nil {
		return err
	}
	if !ts.After(oldDB.updatedAt()) {
		return ErrOldMigration
	}
	if err = do(); err != nil {
		return err
	}
	return db.Save()
}

// MigrateOrRebase runs Migrate.
// If the migration has not been used, database is rebased.
// It panics on error.
func (db *DB) MigrateOrRebase(timestamp string, oldDB DBInterface, do func() error) {
	if err := db.Migrate(timestamp, oldDB, do); err != nil {
		if err != ErrOldMigration {
			panic(err)
		}
		if err = db.Rebase(); err != nil {
			panic(err)
		}
	}
}

// Remove deletes the database file.
func (db *DB) Remove() error {
	if err := os.Remove(db.filename); !os.IsNotExist(err) {
		return err
	}
	return nil
}

// Lock locks database for reading and writing.
func (db *DB) Lock() {
	db.Touch()
	db.mu.Lock()
}

// RLock locks database for reading.
func (db *DB) RLock() {
	db.mu.RLock()
}

// Unlock unlocks database for reading and writing.
func (db *DB) Unlock() {
	db.mu.Unlock()
}

// RUnlock unlocks database for reading.
func (db *DB) RUnlock() {
	db.mu.RUnlock()
}
<commit_msg>Simplify return<commit_after>/*
Package clerk provides file persistence for your Go structures.

It can replace external databases for small projects that can keep data in memory and don't make a lot of writes.
*/
package clerk

import (
	"encoding/gob"
	"errors"
	"io"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// Errors.
var (
	ErrDBInited     = errors.New("clerk: database inited multiple times")
	ErrDBNotInited  = errors.New("clerk: database not inited")
	ErrOldMigration = errors.New("clerk: database in file is newer than migration timestamp")
)

// DB provides methods to persist your data.
type DB struct {
	UpdatedAt   time.Time // UpdatedAt is the timestamp of the last data modification (by calling DB.Lock).
	filename    string
	tmpFilename string
	parent      interface{}
	mu          sync.RWMutex
}

// DBInterface represents an underlying database.
type DBInterface interface {
	updatedAt() time.Time
	setFilename(string)
	setParent(interface{})
	inited() bool
	Touch()
	Save() error
	Migrate(string, DBInterface, func() error) error
	MigrateOrRebase(string, DBInterface, func() error)
	Rebase() error
	Remove() error
	Lock()
	RLock()
	Unlock()
	RUnlock()
}

// Init sets the filename for source saving.
func Init(filename string, db DBInterface) {
	if db.inited() {
		panic(ErrDBInited)
	}
	db.setFilename(filename)
	db.setParent(db)
}

func (db *DB) updatedAt() time.Time {
	return db.UpdatedAt
}

func (db *DB) setFilename(name string) {
	db.filename = name
	// Set d.tmpFilename
	dir := filepath.Dir(name)
	base := filepath.Base(name)
	db.tmpFilename = filepath.Join(dir, base+"~")
}

func (db *DB) setParent(p interface{}) {
	db.parent = p
}

func (db *DB) inited() bool {
	return db.filename != "" && db.tmpFilename != "" && db.parent != nil
}

// Touch updates the UpdatedAt field.
func (db *DB) Touch() {
	db.UpdatedAt = time.Now()
}

// Save persists the database in the file set on init.
// Be sure the database is locked for writing.
func (db *DB) Save() error {
	if !db.inited() {
		return ErrDBNotInited
	}
	db.Touch()
	tmpFile, err := os.OpenFile(db.tmpFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}
	if err = gob.NewEncoder(tmpFile).Encode(db.parent); err != nil && err != io.EOF {
		tmpFile.Close()
		return err
	}
	if err = tmpFile.Close(); err != nil {
		return err
	}
	return os.Rename(db.tmpFilename, db.filename)
}

// Rebase replaces the data with the content of the file set on init.
// Be sure the database is locked for writing.
func (db *DB) Rebase() error {
	if !db.inited() {
		return ErrDBNotInited
	}

	file, err := os.OpenFile(db.filename, os.O_RDONLY|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer file.Close()

	if err = gob.NewDecoder(file).Decode(db.parent); err != nil && err != io.EOF {
		return err
	}
	return nil
}

// Migrate runs a migration if provided date is after the one registered in the data file.
// It rebases the database file in oldDB, runs function do and saves database.
// timestamp format is:
//	2006-01-02 15:04:05 -07
func (db *DB) Migrate(timestamp string, oldDB DBInterface, do func() error) error {
	if !db.inited() {
		return ErrDBNotInited
	}
	ts, err := time.Parse("2006-01-02 15:04:05 -07", timestamp)
	if err != nil {
		return err
	}
	Init(db.filename, oldDB)
	if err = oldDB.Rebase(); err != nil {
		return err
	}
	if !ts.After(oldDB.updatedAt()) {
		return ErrOldMigration
	}
	if err = do(); err != nil {
		return err
	}
	return db.Save()
}

// MigrateOrRebase runs Migrate.
// If the migration has not been used, database is rebased.
// It panics on error.
func (db *DB) MigrateOrRebase(timestamp string, oldDB DBInterface, do func() error) {
	if err := db.Migrate(timestamp, oldDB, do); err != nil {
		if err != ErrOldMigration {
			panic(err)
		}
		if err = db.Rebase(); err != nil {
			panic(err)
		}
	}
}

// Remove deletes the database file.
func (db *DB) Remove() error {
	if err := os.Remove(db.filename); !os.IsNotExist(err) {
		return err
	}
	return nil
}

// Lock locks database for reading and writing.
func (db *DB) Lock() {
	db.Touch()
	db.mu.Lock()
}

// RLock locks database for reading.
func (db *DB) RLock() {
	db.mu.RLock()
}

// Unlock unlocks database for reading and writing.
func (db *DB) Unlock() {
	db.mu.Unlock()
}

// RUnlock unlocks database for reading.
func (db *DB) RUnlock() {
	db.mu.RUnlock()
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	maxminddb "github.com/oschwald/maxminddb-golang"
)

type DB struct {
	path string
}

func (d *DB) checkForUpdates() (needsUpdate bool, err error) {
	curSeconds := time.Now().UnixNano() / int64(time.Second)
	stat, err := os.Stat(d.path)
	if err != nil {
		if os.IsNotExist(err) {
			return true, nil
		}

		return true, err
	}

	_, err = maxminddb.Open(d.path)
	if err != nil {
		return true, err
	}

	if curSeconds-(stat.ModTime().UnixNano()/int64(time.Second)) < 604800 {
		return false, nil
	}

	return true, nil
}

func (d *DB) update(url string) error {
	started := time.Now()
	debug.Printf("fetching new geoip data from: %s", url)

	// Create or truncate if already exists.
	tmpfile, err := ioutil.TempFile("", "geoipdb-")
	if err != nil {
		return fmt.Errorf("unable to create temp file: %s", err)
	}
	defer func() {
		if err = tmpfile.Close(); err != nil {
			debug.Printf("error while closing %q: %s", tmpfile.Name(), err)
		}
		debug.Printf("deleting: %q", tmpfile.Name())
		if err = os.Remove(tmpfile.Name()); err != nil {
			debug.Printf("error while removing %q: %s", tmpfile.Name(), err)
		}
	}()

	debug.Printf("streaming new database to: %q", tmpfile.Name())
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	if resp.Body != nil {
		defer resp.Body.Close()
	}

	gz, err := gzip.NewReader(resp.Body)
	if err != nil {
		return err
	}

	if _, err = io.Copy(tmpfile, gz); err != nil {
		return err
	}

	if err = gz.Close(); err != nil {
		return err
	}

	debug.Printf("successfully downloaded and decompressed new database to %q, verifying now", tmpfile.Name())
	if _, err = tmpfile.Seek(0, 0); err != nil {
		return err
	}

	db, err := maxminddb.Open(tmpfile.Name())
	if err != nil {
		return fmt.Errorf("error while attempting to verify geoip data: %s", err)
	}

	if err = db.Verify(); err != nil {
		db.Close()
		return fmt.Errorf("error while attempting to verify geoip data: %s", err)
	}
	db.Close()

	debug.Println("verification complete, updating active database")

	file, err := os.Create(d.path)
	if err != nil {
		return err
	}

	var written int64
	written, err = io.Copy(file, tmpfile)
	if err != nil {
		return err
	}

	debug.Printf("successfully wrote %d bytes to %q (took %s)", written, file.Name(), time.Since(started))

	return nil
}

// IPSearch is the struct->tag search query to search through the Maxmind DB.
type IPSearch struct {
	City struct {
		Names map[string]string `maxminddb:"names"`
	} `maxminddb:"city"`
	Country struct {
		Code  string            `maxminddb:"iso_code"`
		Names map[string]string `maxminddb:"names"`
	} `maxminddb:"country"`
	Continent struct {
		Code  string            `maxminddb:"code"`
		Names map[string]string `maxminddb:"names"`
	} `maxminddb:"continent"`
	Location struct {
		Lat      float64 `maxminddb:"latitude"`
		Long     float64 `maxminddb:"longitude"`
		TimeZone string  `maxminddb:"time_zone"`
	} `maxminddb:"location"`
	Postal struct {
		Code string `maxminddb:"code"`
	} `maxminddb:"postal"`
	Subdivisions []struct {
		Code  string            `maxminddb:"iso_code"`
		Names map[string]string `maxminddb:"names"`
	} `maxminddb:"subdivisions"`
	Traits struct {
		Proxy bool `maxminddb:"is_anonymous_proxy"`
	} `maxminddb:"traits"`

	// RegisteredCountry struct {
	// 	Code  string            `maxminddb:"iso_code"`
	// 	Names map[string]string `maxminddb:"names"`
	// } `maxminddb:"registered_country"`
	// RepresentedCountry struct {
	// 	Code  string            `maxminddb:"iso_code"`
	// 	Names map[string]string `maxminddb:"names"`
	// 	Type  string            `maxminddb:"type"`
	// } `maxminddb:"represented_country"`
}

// AddrResult contains the geolocation and host information for an IP/host.
type AddrResult struct {
	City          string
	Subdivision   string
	Country       string
	CountryCode   string
	Continent     string
	ContinentCode string
	Lat           float64
	Long          float64
	Timezone      string
	PostalCode    string
	Proxy         bool
	Hosts         []string
}

// addrLookup does a geoip lookup of an IP address
func addrLookup(path string, addr net.IP) (*AddrResult, error) {
	db, err := maxminddb.Open(path)
	if err != nil {
		return nil, err
	}

	var results IPSearch

	err = db.Lookup(addr, &results)
	db.Close()

	if err != nil {
		return nil, err
	}

	res := &AddrResult{
		City:          results.City.Names["en"],
		Country:       results.Country.Names["en"],
		CountryCode:   results.Country.Code,
		Continent:     results.Continent.Names["en"],
		ContinentCode: results.Continent.Code,
		Lat:           results.Location.Lat,
		Long:          results.Location.Long,
		Timezone:      results.Location.TimeZone,
		PostalCode:    results.Postal.Code,
		Proxy:         results.Traits.Proxy,
	}

	var subdiv []string
	for i := 0; i < len(results.Subdivisions); i++ {
		subdiv = append(subdiv, results.Subdivisions[i].Names["en"])
	}
	res.Subdivision = strings.Join(subdiv, ", ")

	if names, err := net.LookupAddr(addr.String()); err == nil {
		for i := 0; i < len(names); i++ {
			// These are FQDN's where absolute hosts contain a suffixed ".".
			res.Hosts = append(res.Hosts, strings.TrimSuffix(names[i], "."))
		}
	}

	return res, nil
}
<commit_msg>implement metadata cache, add map keys<commit_after>package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	maxminddb "github.com/oschwald/maxminddb-golang"
)

type DB struct {
	path string
}

// Note that cache may not always be filled.
type MetaCache struct {
	sync.RWMutex
	cache *maxminddb.Metadata
}

var mcache = &MetaCache{}

func (d *DB) checkForUpdates() (needsUpdate bool, err error) {
	curSeconds := time.Now().UnixNano() / int64(time.Second)
	stat, err := os.Stat(d.path)
	if err != nil {
		if os.IsNotExist(err) {
			return true, nil
		}

		return true, err
	}

	var db *maxminddb.Reader

	db, err = maxminddb.Open(d.path)
	if err != nil {
		return true, err
	}

	mcache.Lock()
	mcache.cache = &db.Metadata
	mcache.Unlock()

	if curSeconds-(stat.ModTime().UnixNano()/int64(time.Second)) < 604800 {
		return false, nil
	}

	return true, nil
}

func (d *DB) update(url string) error {
	started := time.Now()
	debug.Printf("fetching new geoip data from: %s", url)

	// Create or truncate if already exists.
	tmpfile, err := ioutil.TempFile("", "geoipdb-")
	if err != nil {
		return fmt.Errorf("unable to create temp file: %s", err)
	}
	defer func() {
		if err = tmpfile.Close(); err != nil {
			debug.Printf("error while closing %q: %s", tmpfile.Name(), err)
		}
		debug.Printf("deleting: %q", tmpfile.Name())
		if err = os.Remove(tmpfile.Name()); err != nil {
			debug.Printf("error while removing %q: %s", tmpfile.Name(), err)
		}
	}()

	debug.Printf("streaming new database to: %q", tmpfile.Name())
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	if resp.Body != nil {
		defer resp.Body.Close()
	}

	gz, err := gzip.NewReader(resp.Body)
	if err != nil {
		return err
	}

	if _, err = io.Copy(tmpfile, gz); err != nil {
		return err
	}

	if err = gz.Close(); err != nil {
		return err
	}

	debug.Printf("successfully downloaded and decompressed new database to %q, verifying now", tmpfile.Name())
	if _, err = tmpfile.Seek(0, 0); err != nil {
		return err
	}

	db, err := maxminddb.Open(tmpfile.Name())
	if err != nil {
		return fmt.Errorf("error while attempting to verify geoip data: %s", err)
	}

	if err = db.Verify(); err != nil {
		db.Close()
		return fmt.Errorf("error while attempting to verify geoip data: %s", err)
	}

	mcache.Lock()
	mcache.cache = &db.Metadata
	mcache.Unlock()
	db.Close()

	debug.Println("verification complete, updating active database")

	file, err := os.Create(d.path)
	if err != nil {
		return err
	}

	var written int64
	written, err = io.Copy(file, tmpfile)
	if err != nil {
		return err
	}

	debug.Printf("successfully wrote %d bytes to %q (took %s)", written, file.Name(), time.Since(started))

	return nil
}

// IPSearch is the struct->tag search query to search through the Maxmind DB.
type IPSearch struct {
	City struct {
		Names map[string]string `maxminddb:"names"`
	} `maxminddb:"city"`
	Country struct {
		Code  string            `maxminddb:"iso_code"`
		Names map[string]string `maxminddb:"names"`
	} `maxminddb:"country"`
	Continent struct {
		Code  string            `maxminddb:"code"`
		Names map[string]string `maxminddb:"names"`
	} `maxminddb:"continent"`
	Location struct {
		Lat       float64 `maxminddb:"latitude"`
		Long      float64 `maxminddb:"longitude"`
		MetroCode int     `maxminddb:"metro_code"`
		TimeZone  string  `maxminddb:"time_zone"`
	} `maxminddb:"location"`
	Postal struct {
		Code string `maxminddb:"code"`
	} `maxminddb:"postal"`
	Subdivisions []struct {
		Code  string            `maxminddb:"iso_code"`
		Names map[string]string `maxminddb:"names"`
	} `maxminddb:"subdivisions"`
	Traits struct {
		Proxy bool `maxminddb:"is_anonymous_proxy"`
	} `maxminddb:"traits"`

	// RegisteredCountry struct {
	// 	Code  string            `maxminddb:"iso_code"`
	// 	Names map[string]string `maxminddb:"names"`
	// } `maxminddb:"registered_country"`
	// RepresentedCountry struct {
	// 	Code  string            `maxminddb:"iso_code"`
	// 	Names map[string]string `maxminddb:"names"`
	// 	Type  string            `maxminddb:"type"`
	// } `maxminddb:"represented_country"`
}

// AddrResult contains the geolocation and host information for an IP/host.
type AddrResult struct {
	IP            net.IP   `json:"ip"`
	City          string   `json:"city"`
	Subdivision   string   `json:"subdivision"`
	Country       string   `json:"country"`
	CountryCode   string   `json:"country_abbr"`
	Continent     string   `json:"continent"`
	ContinentCode string   `json:"continent_abbr"`
	Lat           float64  `json:"latitude"`
	Long          float64  `json:"longitude"`
	Timezone      string   `json:"timezone"`
	PostalCode    string   `json:"postal_code"`
	Proxy         bool     `json:"proxy"`
	Hosts         []string `json:"hosts"`
	MapURL        string   `json:"map_url"`
	MapEmbedURL   string   `json:"map_embed_url"`
}

const mapURI = "https://maps.google.com/maps?f=q&ie=UTF8&iwloc=A&z=0&q=%s"
const mapEmbedURI = "https://maps.google.com/maps?f=q&ie=UTF8&iwloc=A&output=embed&z=%d&q=%s"

// addrLookup does a geoip lookup of an IP address
func addrLookup(path string, addr net.IP) (*AddrResult, error) {
	db, err := maxminddb.Open(path)
	if err != nil {
		return nil, err
	}

	var query IPSearch

	err = db.Lookup(addr, &query)
	db.Close()

	if err != nil {
		return nil, err
	}

	result := &AddrResult{
		IP:            addr,
		City:          query.City.Names["en"],
		Country:       query.Country.Names["en"],
		CountryCode:   query.Country.Code,
		Continent:     query.Continent.Names["en"],
		ContinentCode: query.Continent.Code,
		Lat:           query.Location.Lat,
		Long:          query.Location.Long,
		Timezone:      query.Location.TimeZone,
		PostalCode:    query.Postal.Code,
		Proxy:         query.Traits.Proxy,
	}

	var subdiv []string
	for i := 0; i < len(query.Subdivisions); i++ {
		subdiv = append(subdiv, query.Subdivisions[i].Names["en"])
	}
	result.Subdivision = strings.Join(subdiv, ", ")

	mapZoom := 2
	var mapQuery string
	switch {
	case result.City != "":
		mapZoom = 6
	case result.Subdivision != "":
		mapZoom = 4
	case result.Country != "":
		mapZoom = 3
	}

	switch {
	case result.Lat != 0 || result.Long != 0:
		mapQuery = fmt.Sprintf("%f,%f", result.Lat, result.Long)
	case result.City != "" && result.Subdivision != "" && result.CountryCode != "":
		mapQuery = fmt.Sprintf("%s, %s, %s", result.City, result.Subdivision, result.CountryCode)
	case result.Subdivision != "" && result.CountryCode != "":
		mapQuery = fmt.Sprintf("%s, %s", result.Subdivision, result.CountryCode)
	default:
		mapQuery = result.Country
	}
	result.MapURL = fmt.Sprintf(mapURI, url.QueryEscape(mapQuery))
	result.MapEmbedURL = fmt.Sprintf(mapEmbedURI, mapZoom, url.QueryEscape(mapQuery))

	if names, err := net.LookupAddr(addr.String()); err == nil {
		for i := 0; i < len(names); i++ {
			// These are FQDN's where absolute hosts contain a suffixed ".".
			result.Hosts = append(result.Hosts, strings.TrimSuffix(names[i], "."))
		}
	}

	return result, nil
}
<|endoftext|>


<commit_before>package servicing

import (
	"time"

	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"k8s.io/client-go/tools/record"

	"github.com/sapcc/kubernikus/pkg/api/models"
	v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/controller/config"
	client "github.com/sapcc/kubernikus/pkg/generated/clientset/typed/kubernikus/v1"
	listers_kubernikus_v1 "github.com/sapcc/kubernikus/pkg/generated/listers/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/util"
)

const (
	// AnnotationServicingTimestamp show when the kluster was last serviced
	AnnotationServicingTimestamp = "kubernikus.cloud.sap/servicingTimestamp"

	// AnnotationUpdateTImestamp shows when a node update was started
	AnnotationUpdateTimestamp = "kubernikus.cloud.sap/updateTimestamp"
)

var (
	// ServiceInterval defines how often a kluster is serviced
	ServiceInterval = 1 * time.Hour
	UpdateTimeout   = 10 * time.Minute
)

type (
	// Reconciler checks a specific kluster's nodes for updates/upgrades
	Reconciler interface {
		Do() error
	}

	// ReconcilerFactory produces a Reconciler
	ReconcilerFactory interface {
		Make(*v1.Kluster) (Reconciler, error)
	}

	// KlusterReconcilerFactory produces a Reconciler for a specific kluster
	KlusterReconcilerFactory struct {
		Logger            log.Logger
		ListerFactory     ListerFactory
		LifeCyclerFactory LifeCyclerFactory
		KlusterLister     listers_kubernikus_v1.KlusterLister
		KubernikusClient  client.KubernikusV1Interface
	}

	// KlusterReconciler is a concrete implementation of a Reconciler
	KlusterReconciler struct {
		Logger           log.Logger
		Kluster          *v1.Kluster
		Lister           Lister
		LifeCycler       LifeCycler
		KlusterLister    listers_kubernikus_v1.KlusterLister
		KubernikusClient client.KubernikusV1Interface
	}

	// LoggingReconciler decorates a Reconciler with log messages
	LoggingReconciler struct {
		Logger     log.Logger
		Reconciler Reconciler
	}
)

// NewKlusterReconcilerFactory produces a new Factory
func NewKlusterReconcilerFactory(logger log.Logger, recorder record.EventRecorder, factories config.Factories, clients config.Clients) ReconcilerFactory {
	return &KlusterReconcilerFactory{
		Logger:            logger,
		ListerFactory:     NewNodeListerFactory(logger, recorder, factories, clients),
		LifeCyclerFactory: NewNodeLifeCyclerFactory(logger, recorder, factories, clients),
		KlusterLister:     factories.Kubernikus.Kubernikus().V1().Klusters().Lister(),
		KubernikusClient:  clients.Kubernikus.Kubernikus(),
	}
}

// Make a new Factory
func (f *KlusterReconcilerFactory) Make(k *v1.Kluster) (Reconciler, error) {
	logger := log.With(f.Logger, "kluster", k.Spec.Name, "project", k.Account())
	lister, err := f.ListerFactory.Make(k)
	if err != nil {
		return nil, err
	}

	cycler, err := f.LifeCyclerFactory.Make(k)
	if err != nil {
		return nil, errors.Wrap(err, "Couldn't make LifeCyclerFactory")
	}

	var reconciler Reconciler
	reconciler = &KlusterReconciler{
		Logger:           logger,
		Kluster:          k,
		Lister:           lister,
		LifeCycler:       cycler,
		KlusterLister:    f.KlusterLister,
		KubernikusClient: f.KubernikusClient,
	}

	reconciler = &LoggingReconciler{
		Logger:     logger,
		Reconciler: reconciler,
	}

	return reconciler, nil
}

// Do it
func (r *KlusterReconciler) Do() error {
	r.Logger.Log("msg", "reconciling", "v", 2)

	if r.Kluster.Status.Phase != models.KlusterPhaseRunning {
		r.Logger.Log("msg", "skipped upgrades because kluster is not running", "v", 2)
		return nil
	}

	// Default to skip klusters without the servicing annotation
	if !util.EnabledValue(r.Kluster.ObjectMeta.Annotations[AnnotationServicingSafeguard]) {
		r.Logger.Log("msg", "Skippig upgrades. Manually disabled with safeguard annotation.")
		return nil
	}

	for _, node := range r.Lister.Successful() {
		if err := r.LifeCycler.Uncordon(node); err != nil {
			return errors.Wrap(err, "Failed to uncordon successfully updated node.")
		}
	}

	if len(r.Lister.Failed()) > 0 {
		r.Logger.Log("msg", "skipped upgrades because there is a failed upgrade")
		return nil
	}

	if len(r.Lister.Updating()) > 0 {
		r.Logger.Log("msg", "skipped upgrades because there is sitll nodes being updated", "v", 2)
		return nil
	}

	if !r.isServiceIntervalElapsed() {
		r.Logger.Log("msg", "skipped upgrades because kluster service interval not elapsed yet", "v", 2)
		return nil
	}

	if len(r.Lister.NotReady()) > 0 {
		r.Logger.Log("msg", "skipped upgrades because kluster is not healthy", "v", 2)
		return nil
	}

	replace := r.Lister.Replace()
	reboot := r.Lister.Reboot()
	if len(replace) > 0 {
		if err := r.LifeCycler.Drain(replace[0]); err != nil {
			return errors.Wrap(err, "Failed to drain node that is about to be replaces")
		}

		if err := r.LifeCycler.Replace(replace[0]); err != nil {
			return errors.Wrap(err, "Failed to replace node")
		}
	} else if len(reboot) > 0 {
		if err := r.LifeCycler.Drain(reboot[0]); err != nil {
			return errors.Wrap(err, "Failed to drain node that is about to be rebooted")
		}

		if err := r.LifeCycler.Reboot(reboot[0]); err != nil {
			return errors.Wrap(err, "Failed to reboot node")
		}
	}

	return r.updateLastServicingTime()
}

// Do log it
func (r *LoggingReconciler) Do() (err error) {
	defer func(begin time.Time) {
		r.Logger.Log(
			"msg", "reconciled",
			"took", time.Since(begin),
			"v", 2,
			"err", err,
		)
	}(time.Now())
	return r.Reconciler.Do()
}

func (r *KlusterReconciler) updateLastServicingTime() error {
	client := r.KubernikusClient.Klusters(r.Kluster.Namespace)
	lister := r.KlusterLister.Klusters(r.Kluster.Namespace)
	_, err := util.UpdateKlusterWithRetries(client, lister, r.Kluster.Name, func(kluster *v1.Kluster) error {
		kluster.ObjectMeta.Annotations[AnnotationServicingTimestamp] = Now().UTC().Format(time.RFC3339)
		return nil
	})
	return err
}

func (r *KlusterReconciler) getLastServicingTime(annotations map[string]string) time.Time {
	t, ok := annotations[AnnotationServicingTimestamp]
	if !ok {
		return time.Unix(0, 0)
	}

	pt, err := time.Parse(time.RFC3339, t)
	if err != nil {
		return time.Unix(0, 0)
	}

	return pt
}

func (r *KlusterReconciler) isServiceIntervalElapsed() bool {
	nextServiceTime := r.getLastServicingTime(r.Kluster.ObjectMeta.GetAnnotations()).Add(ServiceInterval)
	return Now().After(nextServiceTime)
}
<commit_msg>enable upgrades<commit_after>package servicing

import (
	"time"

	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"k8s.io/client-go/tools/record"

	"github.com/sapcc/kubernikus/pkg/api/models"
	v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/controller/config"
	client "github.com/sapcc/kubernikus/pkg/generated/clientset/typed/kubernikus/v1"
	listers_kubernikus_v1 "github.com/sapcc/kubernikus/pkg/generated/listers/kubernikus/v1"
	"github.com/sapcc/kubernikus/pkg/util"
)

const (
	// AnnotationServicingTimestamp show when the kluster was last serviced
	AnnotationServicingTimestamp = "kubernikus.cloud.sap/servicingTimestamp"

	// AnnotationUpdateTImestamp shows when a node update was started
	AnnotationUpdateTimestamp = "kubernikus.cloud.sap/updateTimestamp"
)

var (
	// ServiceInterval defines how often a kluster is serviced
	ServiceInterval = 1 * time.Hour
	UpdateTimeout   = 10 * time.Minute
)

type (
	// Reconciler checks a specific kluster's nodes for updates/upgrades
	Reconciler interface {
		Do() error
	}

	// ReconcilerFactory produces a Reconciler
	ReconcilerFactory interface {
		Make(*v1.Kluster) (Reconciler, error)
	}

	// KlusterReconcilerFactory produces a Reconciler for a specific kluster
	KlusterReconcilerFactory struct {
		Logger            log.Logger
		ListerFactory     ListerFactory
		LifeCyclerFactory LifeCyclerFactory
		KlusterLister     listers_kubernikus_v1.KlusterLister
		KubernikusClient  client.KubernikusV1Interface
	}

	// KlusterReconciler is a concrete implementation of a Reconciler
	KlusterReconciler struct {
		Logger           log.Logger
		Kluster          *v1.Kluster
		Lister           Lister
		LifeCycler       LifeCycler
		KlusterLister    listers_kubernikus_v1.KlusterLister
		KubernikusClient client.KubernikusV1Interface
	}

	// LoggingReconciler decorates a Reconciler with log messages
	LoggingReconciler struct {
		Logger     log.Logger
		Reconciler Reconciler
	}
)

// NewKlusterReconcilerFactory produces a new Factory
func NewKlusterReconcilerFactory(logger log.Logger, recorder record.EventRecorder, factories config.Factories, clients config.Clients) ReconcilerFactory {
	return &KlusterReconcilerFactory{
		Logger:            logger,
		ListerFactory:     NewNodeListerFactory(logger, recorder, factories, clients),
		LifeCyclerFactory: NewNodeLifeCyclerFactory(logger, recorder, factories, clients),
		KlusterLister:     factories.Kubernikus.Kubernikus().V1().Klusters().Lister(),
		KubernikusClient:  clients.Kubernikus.Kubernikus(),
	}
}

// Make a new Factory
func (f *KlusterReconcilerFactory) Make(k *v1.Kluster) (Reconciler, error) {
	logger := log.With(f.Logger, "kluster", k.Spec.Name, "project", k.Account())
	lister, err := f.ListerFactory.Make(k)
	if err != nil {
		return nil, err
	}

	cycler, err := f.LifeCyclerFactory.Make(k)
	if err != nil {
		return nil, errors.Wrap(err, "Couldn't make LifeCyclerFactory")
	}

	var reconciler Reconciler
	reconciler = &KlusterReconciler{
		Logger:           logger,
		Kluster:          k,
		Lister:           lister,
		LifeCycler:       cycler,
		KlusterLister:    f.KlusterLister,
		KubernikusClient: f.KubernikusClient,
	}

	reconciler = &LoggingReconciler{
		Logger:     logger,
		Reconciler: reconciler,
	}

	return reconciler, nil
}

// Do it
func (r *KlusterReconciler) Do() error {
	r.Logger.Log("msg", "reconciling", "v", 2)

	if r.Kluster.Status.Phase != models.KlusterPhaseRunning {
		r.Logger.Log("msg", "skipped upgrades because kluster is not running", "v", 2)
		return nil
	}

	if util.DisabledValue(r.Kluster.ObjectMeta.Annotations[AnnotationServicingSafeguard]) {
		r.Logger.Log("msg", "Skippig upgrades. Manually disabled with safeguard annotation.")
		return nil
	}

	for _, node := range r.Lister.Successful() {
		if err := r.LifeCycler.Uncordon(node); err != nil {
			return errors.Wrap(err, "Failed to uncordon successfully updated node.")
		}
	}

	if len(r.Lister.Failed()) > 0 {
		r.Logger.Log("msg", "skipped upgrades because there is a failed upgrade")
		return nil
	}

	if len(r.Lister.Updating()) > 0 {
		r.Logger.Log("msg", "skipped upgrades because there is sitll nodes being updated", "v", 2)
		return nil
	}

	if !r.isServiceIntervalElapsed() {
		r.Logger.Log("msg", "skipped upgrades because kluster service interval not elapsed yet", "v", 2)
		return nil
	}

	if len(r.Lister.NotReady()) > 0 {
		r.Logger.Log("msg", "skipped upgrades because kluster is not healthy", "v", 2)
		return nil
	}

	replace := r.Lister.Replace()
	reboot := r.Lister.Reboot()
	if len(replace) > 0 {
		if err := r.LifeCycler.Drain(replace[0]); err != nil {
			return errors.Wrap(err, "Failed to drain node that is about to be replaces")
		}

		if err := r.LifeCycler.Replace(replace[0]); err != nil {
			return errors.Wrap(err, "Failed to replace node")
		}
	} else if len(reboot) > 0 {
		if err := r.LifeCycler.Drain(reboot[0]); err != nil {
			return errors.Wrap(err, "Failed to drain node that is about to be rebooted")
		}

		if err := r.LifeCycler.Reboot(reboot[0]); err != nil {
			return errors.Wrap(err, "Failed to reboot node")
		}
	}

	return r.updateLastServicingTime()
}

// Do log it
func (r *LoggingReconciler) Do() (err error) {
	defer func(begin time.Time) {
		r.Logger.Log(
			"msg", "reconciled",
			"took", time.Since(begin),
			"v", 2,
			"err", err,
		)
	}(time.Now())
	return r.Reconciler.Do()
}

func (r *KlusterReconciler) updateLastServicingTime() error {
	client := r.KubernikusClient.Klusters(r.Kluster.Namespace)
	lister := r.KlusterLister.Klusters(r.Kluster.Namespace)
	_, err := util.UpdateKlusterWithRetries(client, lister, r.Kluster.Name, func(kluster *v1.Kluster) error {
		kluster.ObjectMeta.Annotations[AnnotationServicingTimestamp] = Now().UTC().Format(time.RFC3339)
		return nil
	})
	return err
}

func (r *KlusterReconciler) getLastServicingTime(annotations map[string]string) time.Time {
	t, ok := annotations[AnnotationServicingTimestamp]
	if !ok {
		return time.Unix(0, 0)
	}

	pt, err := time.Parse(time.RFC3339, t)
	if err != nil {
		return time.Unix(0, 0)
	}

	return pt
}

func (r *KlusterReconciler) isServiceIntervalElapsed() bool {
	nextServiceTime := r.getLastServicingTime(r.Kluster.ObjectMeta.GetAnnotations()).Add(ServiceInterval)
	return Now().After(nextServiceTime)
}
<|endoftext|>


<commit_before>package data

import (
	"crypto/sha256"
	"crypto/sha512"
	"crypto/subtle"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/go/canonical/json"
	"github.com/docker/notary"
)

// SigAlgorithm for types of signatures
type SigAlgorithm string

func (k SigAlgorithm) String() string {
	return string(k)
}

const defaultHashAlgorithm = "sha256"

// Signature types
const (
	EDDSASignature       SigAlgorithm = "eddsa"
	RSAPSSSignature      SigAlgorithm = "rsapss"
	RSAPKCS1v15Signature SigAlgorithm = "rsapkcs1v15"
	ECDSASignature       SigAlgorithm = "ecdsa"
	PyCryptoSignature    SigAlgorithm = "pycrypto-pkcs#1 pss"
)

// Key types
const (
	ED25519Key   = "ed25519"
	RSAKey       = "rsa"
	RSAx509Key   = "rsa-x509"
	ECDSAKey     = "ecdsa"
	ECDSAx509Key = "ecdsa-x509"
)

// TUFTypes is the set of metadata types
var TUFTypes = map[string]string{
	CanonicalRootRole:      "Root",
	CanonicalTargetsRole:   "Targets",
	CanonicalSnapshotRole:  "Snapshot",
	CanonicalTimestampRole: "Timestamp",
}

// SetTUFTypes allows one to override some or all of the default
// type names in TUF.
func SetTUFTypes(ts map[string]string) {
	for k, v := range ts {
		TUFTypes[k] = v
	}
}

// ValidTUFType checks if the given type is valid for the role
func ValidTUFType(typ, role string) bool {
	if ValidRole(role) {
		// All targets delegation roles must have
		// the valid type is for targets.
		if role == "" {
			// role is unknown and does not map to
			// a type
			return false
		}
		if strings.HasPrefix(role, CanonicalTargetsRole+"/") {
			role = CanonicalTargetsRole
		}
	}
	// most people will just use the defaults so have this optimal check
	// first. Do comparison just in case there is some unknown vulnerability
	// if a key and value in the map differ.
	if v, ok := TUFTypes[role]; ok {
		return typ == v
	}
	return false
}

// Signed is the high level, partially deserialized metadata object
// used to verify signatures before fully unpacking, or to add signatures
// before fully packing
type Signed struct {
	Signed     *json.RawMessage `json:"signed"`
	Signatures []Signature      `json:"signatures"`
}

// SignedCommon contains the fields common to the Signed component of all
// TUF metadata files
type SignedCommon struct {
	Type    string    `json:"_type"`
	Expires time.Time `json:"expires"`
	Version int       `json:"version"`
}

// SignedMeta is used in server validation where we only need signatures
// and common fields
type SignedMeta struct {
	Signed     SignedCommon `json:"signed"`
	Signatures []Signature  `json:"signatures"`
}

// Signature is a signature on a piece of metadata
type Signature struct {
	KeyID     string       `json:"keyid"`
	Method    SigAlgorithm `json:"method"`
	Signature []byte       `json:"sig"`
}

// Files is the map of paths to file meta container in targets and delegations
// metadata files
type Files map[string]FileMeta

// Hashes is the map of hash type to digest created for each metadata
// and target file
type Hashes map[string][]byte

// NotaryDefaultHashes contains the default supported hash algorithms.
var NotaryDefaultHashes = []string{notary.SHA256, notary.SHA512}

// FileMeta contains the size and hashes for a metadata or target file. Custom
// data can be optionally added.
type FileMeta struct {
	Length int64            `json:"length"`
	Hashes Hashes           `json:"hashes"`
	Custom *json.RawMessage `json:"custom,omitempty"`
}

// CheckHashes verifies all the checksums specified by the "hashes" of the payload.
func CheckHashes(payload []byte, hashes Hashes) error {
	cnt := 0

	// k, v indicate the hash algorithm and the corresponding value
	for k, v := range hashes {
		switch k {
		case notary.SHA256:
			checksum := sha256.Sum256(payload)
			if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
				return fmt.Errorf("%s checksum mismatched", k)
			}
			cnt++
		case notary.SHA512:
			checksum := sha512.Sum512(payload)
			if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
				return fmt.Errorf("%s checksum mismatched", k)
			}
			cnt++
		}
	}

	if cnt == 0 {
		return fmt.Errorf("at least one supported hash needed")
	}

	return nil
}

// CheckValidHashStructures returns an error, or nil, depending on whether
// the content of the hashes is valid or not.
func CheckValidHashStructures(hashes Hashes) error {
	cnt := 0

	for k, v := range hashes {
		switch k {
		case notary.SHA256:
			if len(v) != sha256.Size {
				return fmt.Errorf("invalid %s checksum", notary.SHA256)
			}
			cnt++
		case notary.SHA512:
			if len(v) != sha512.Size {
				return fmt.Errorf("invalid %s checksum", notary.SHA512)
			}
			cnt++
		}
	}

	if cnt == 0 {
		return fmt.Errorf("at least one supported hash needed")
	}

	return nil
}

// NewFileMeta generates a FileMeta object from the reader, using the
// hash algorithms provided
func NewFileMeta(r io.Reader, hashAlgorithms ...string) (FileMeta, error) {
	if len(hashAlgorithms) == 0 {
		hashAlgorithms = []string{defaultHashAlgorithm}
	}
	hashes := make(map[string]hash.Hash, len(hashAlgorithms))
	for _, hashAlgorithm := range hashAlgorithms {
		var h hash.Hash
		switch hashAlgorithm {
		case notary.SHA256:
			h = sha256.New()
		case notary.SHA512:
			h = sha512.New()
		default:
			return FileMeta{}, fmt.Errorf("Unknown hash algorithm: %s", hashAlgorithm)
		}
		hashes[hashAlgorithm] = h
		r = io.TeeReader(r, h)
	}
	n, err := io.Copy(ioutil.Discard, r)
	if err != nil {
		return FileMeta{}, err
	}
	m := FileMeta{Length: n, Hashes: make(Hashes, len(hashes))}
	for hashAlgorithm, h := range hashes {
		m.Hashes[hashAlgorithm] = h.Sum(nil)
	}
	return m, nil
}

// Delegations holds a tier of targets delegations
type Delegations struct {
	Keys  Keys    `json:"keys"`
	Roles []*Role `json:"roles"`
}

// NewDelegations initializes an empty Delegations object
func NewDelegations() *Delegations {
	return &Delegations{
		Keys:  make(map[string]PublicKey),
		Roles: make([]*Role, 0),
	}
}

// These values are recommended TUF expiry times.
var defaultExpiryTimes = map[string]time.Duration{
	CanonicalRootRole:      notary.Year,
	CanonicalTargetsRole:   90 * notary.Day,
	CanonicalSnapshotRole:  7 * notary.Day,
	CanonicalTimestampRole: notary.Day,
}

// SetDefaultExpiryTimes allows one to change the default expiries.
func SetDefaultExpiryTimes(times map[string]time.Duration) {
	for key, value := range times {
		if _, ok := defaultExpiryTimes[key]; !ok {
			logrus.Errorf("Attempted to set default expiry for an unknown role: %s", key)
			continue
		}
		defaultExpiryTimes[key] = value
	}
}

// DefaultExpires gets the default expiry time for the given role
func DefaultExpires(role string) time.Time {
	if d, ok := defaultExpiryTimes[role]; ok {
		return time.Now().Add(d)
	}
	var t time.Time
	return t.UTC().Round(time.Second)
}

type unmarshalledSignature Signature

// UnmarshalJSON does a custom unmarshalling of the signature JSON
func (s *Signature) UnmarshalJSON(data []byte) error {
	uSignature := unmarshalledSignature{}
	err := json.Unmarshal(data, &uSignature)
	if err != nil {
		return err
	}
	uSignature.Method = SigAlgorithm(strings.ToLower(string(uSignature.Method)))
	*s = Signature(uSignature)
	return nil
}
<commit_msg>Use concrete error types rather than generic errors<commit_after>package data

import (
	"crypto/sha256"
	"crypto/sha512"
	"crypto/subtle"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/go/canonical/json"
	"github.com/docker/notary"
)

// SigAlgorithm for types of signatures
type SigAlgorithm string

func (k SigAlgorithm) String() string {
	return string(k)
}

const defaultHashAlgorithm = "sha256"

// Signature types
const (
	EDDSASignature       SigAlgorithm = "eddsa"
	RSAPSSSignature      SigAlgorithm = "rsapss"
	RSAPKCS1v15Signature SigAlgorithm = "rsapkcs1v15"
	ECDSASignature       SigAlgorithm = "ecdsa"
	PyCryptoSignature    SigAlgorithm = "pycrypto-pkcs#1 pss"
)

// Key types
const (
	ED25519Key   = "ed25519"
	RSAKey       = "rsa"
	RSAx509Key   = "rsa-x509"
	ECDSAKey     = "ecdsa"
	ECDSAx509Key = "ecdsa-x509"
)

// TUFTypes is the set of metadata types
var TUFTypes = map[string]string{
	CanonicalRootRole:      "Root",
	CanonicalTargetsRole:   "Targets",
	CanonicalSnapshotRole:  "Snapshot",
	CanonicalTimestampRole: "Timestamp",
}

// SetTUFTypes allows one to override some or all of the default
// type names in TUF.
func SetTUFTypes(ts map[string]string) {
	for k, v := range ts {
		TUFTypes[k] = v
	}
}

// ValidTUFType checks if the given type is valid for the role
func ValidTUFType(typ, role string) bool {
	if ValidRole(role) {
		// All targets delegation roles must have
		// the valid type is for targets.
		if role == "" {
			// role is unknown and does not map to
			// a type
			return false
		}
		if strings.HasPrefix(role, CanonicalTargetsRole+"/") {
			role = CanonicalTargetsRole
		}
	}
	// most people will just use the defaults so have this optimal check
	// first. Do comparison just in case there is some unknown vulnerability
	// if a key and value in the map differ.
	if v, ok := TUFTypes[role]; ok {
		return typ == v
	}
	return false
}

// Signed is the high level, partially deserialized metadata object
// used to verify signatures before fully unpacking, or to add signatures
// before fully packing
type Signed struct {
	Signed     *json.RawMessage `json:"signed"`
	Signatures []Signature      `json:"signatures"`
}

// SignedCommon contains the fields common to the Signed component of all
// TUF metadata files
type SignedCommon struct {
	Type    string    `json:"_type"`
	Expires time.Time `json:"expires"`
	Version int       `json:"version"`
}

// SignedMeta is used in server validation where we only need signatures
// and common fields
type SignedMeta struct {
	Signed     SignedCommon `json:"signed"`
	Signatures []Signature  `json:"signatures"`
}

// Signature is a signature on a piece of metadata
type Signature struct {
	KeyID     string       `json:"keyid"`
	Method    SigAlgorithm `json:"method"`
	Signature []byte       `json:"sig"`
}

// Files is the map of paths to file meta container in targets and delegations
// metadata files
type Files map[string]FileMeta

// Hashes is the map of hash type to digest created for each metadata
// and target file
type Hashes map[string][]byte

// NotaryDefaultHashes contains the default supported hash algorithms.
var NotaryDefaultHashes = []string{notary.SHA256, notary.SHA512}

// FileMeta contains the size and hashes for a metadata or target file. Custom
// data can be optionally added.
type FileMeta struct {
	Length int64            `json:"length"`
	Hashes Hashes           `json:"hashes"`
	Custom *json.RawMessage `json:"custom,omitempty"`
}

// CheckHashes verifies all the checksums specified by the "hashes" of the payload.
func CheckHashes(payload []byte, hashes Hashes) error {
	cnt := 0

	// k, v indicate the hash algorithm and the corresponding value
	for k, v := range hashes {
		switch k {
		case notary.SHA256:
			checksum := sha256.Sum256(payload)
			if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
				return ErrMismatchedChecksum{alg: notary.SHA256}
			}
			cnt++
		case notary.SHA512:
			checksum := sha512.Sum512(payload)
			if subtle.ConstantTimeCompare(checksum[:], v) == 0 {
				return ErrMismatchedChecksum{alg: notary.SHA512}
			}
			cnt++
		}
	}

	if cnt == 0 {
		return fmt.Errorf("at least one supported hash needed")
	}

	return nil
}

// CheckValidHashStructures returns an error, or nil, depending on whether
// the content of the hashes is valid or not.
func CheckValidHashStructures(hashes Hashes) error {
	cnt := 0

	for k, v := range hashes {
		switch k {
		case notary.SHA256:
			if len(v) != sha256.Size {
				return ErrInvalidChecksum{alg: notary.SHA256}
			}
			cnt++
		case notary.SHA512:
			if len(v) != sha512.Size {
				return ErrInvalidChecksum{alg: notary.SHA512}
			}
			cnt++
		}
	}

	if cnt == 0 {
		return fmt.Errorf("at least one supported hash needed")
	}

	return nil
}

// NewFileMeta generates a FileMeta object from the reader, using the
// hash algorithms provided
func NewFileMeta(r io.Reader, hashAlgorithms ...string) (FileMeta, error) {
	if len(hashAlgorithms) == 0 {
		hashAlgorithms = []string{defaultHashAlgorithm}
	}
	hashes := make(map[string]hash.Hash, len(hashAlgorithms))
	for _, hashAlgorithm := range hashAlgorithms {
		var h hash.Hash
		switch hashAlgorithm {
		case notary.SHA256:
			h = sha256.New()
		case notary.SHA512:
			h = sha512.New()
		default:
			return FileMeta{}, fmt.Errorf("Unknown hash algorithm: %s", hashAlgorithm)
		}
		hashes[hashAlgorithm] = h
		r = io.TeeReader(r, h)
	}
	n, err := io.Copy(ioutil.Discard, r)
	if err != nil {
		return FileMeta{}, err
	}
	m := FileMeta{Length: n, Hashes: make(Hashes, len(hashes))}
	for hashAlgorithm, h := range hashes {
		m.Hashes[hashAlgorithm] = h.Sum(nil)
	}
	return m, nil
}

// Delegations holds a tier of targets delegations
type Delegations struct {
	Keys  Keys    `json:"keys"`
	Roles []*Role `json:"roles"`
}

// NewDelegations initializes an empty Delegations object
func NewDelegations() *Delegations {
	return &Delegations{
		Keys:  make(map[string]PublicKey),
		Roles: make([]*Role, 0),
	}
}

// These values are recommended TUF expiry times.
var defaultExpiryTimes = map[string]time.Duration{
	CanonicalRootRole:      notary.Year,
	CanonicalTargetsRole:   90 * notary.Day,
	CanonicalSnapshotRole:  7 * notary.Day,
	CanonicalTimestampRole: notary.Day,
}

// SetDefaultExpiryTimes allows one to change the default expiries.
func SetDefaultExpiryTimes(times map[string]time.Duration) {
	for key, value := range times {
		if _, ok := defaultExpiryTimes[key]; !ok {
			logrus.Errorf("Attempted to set default expiry for an unknown role: %s", key)
			continue
		}
		defaultExpiryTimes[key] = value
	}
}

// DefaultExpires gets the default expiry time for the given role
func DefaultExpires(role string) time.Time {
	if d, ok := defaultExpiryTimes[role]; ok {
		return time.Now().Add(d)
	}
	var t time.Time
	return t.UTC().Round(time.Second)
}

type unmarshalledSignature Signature

// UnmarshalJSON does a custom unmarshalling of the signature JSON
func (s *Signature) UnmarshalJSON(data []byte) error {
	uSignature := unmarshalledSignature{}
	err := json.Unmarshal(data, &uSignature)
	if err != nil {
		return err
	}
	uSignature.Method = SigAlgorithm(strings.ToLower(string(uSignature.Method)))
	*s = Signature(uSignature)
	return nil
}
<|endoftext|>