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

package bot

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

	"github.com/cloudflare/gokabinet/kt"

	cfg "github.com/arachnist/gorepost/config"
	"github.com/arachnist/gorepost/irc"
)

var k *kt.Conn

type value struct {
	Network string
	Target  string
	Action  string
	Time    int64 // .Now().UnixNano()
	Text    string
}

func seenrecord(output chan irc.Message, msg irc.Message) {
	v := value{
		Network: msg.Context["Network"],
		Target:  msg.Params[0],
		Action:  msg.Command,
		Time:    time.Now().UnixNano(),
		Text:    msg.Trailing,
	}

	b, err := json.Marshal(v)
	if err != nil {
		log.Println("Context:", msg.Context, "json marshal of seen record failed:", err)
		return
	}

	err = k.Set(msg.Prefix.Name, b)
	if err != nil {
		log.Println("Context:", msg.Context, "error recording seen record:", err)
	}
}

func seen(output chan irc.Message, msg irc.Message) {
	var v value

	args := strings.Split(msg.Trailing, " ")

	if args[0] != ":seen" {
		return
	}
	if len(args) < 2 {
		return
	}

	b, err := k.GetBytes(args[1])
	if err == kt.ErrNotFound {
		output <- reply(msg, cfg.LookupString(msg.Context, "NotSeenMessage"))
		return
	} else if err != nil {
		output <- reply(msg, fmt.Sprint("error getting record for", args[1], err))
		return
	}

	err = json.Unmarshal(b, &v)
	if err != nil {
		output <- reply(msg, fmt.Sprint("error unmarshaling record for", args[1], err))
		return
	}

	output <- reply(msg, fmt.Sprintf("%+v", v))
}

func init() {
	var err error
	var ktHost = "127.0.0.1"
	var ktPort = 1337

	log.Println("SEEN: connecting to KT")
	k, err = kt.NewConn(ktHost, ktPort, 4, 2*time.Second)
	if err != nil {
		log.Println("error connecting to kyoto tycoon", err)
		return
	}

	log.Println("Registering callbacks")
	addCallback("PRIVMSG", "seen", seen)
	addCallback("PRIVMSG", "seenrecord", seenrecord)
	addCallback("JOIN", "seenrecord", seenrecord)
	addCallback("PART", "seenrecord", seenrecord)
	addCallback("QUIT", "seenrecord", seenrecord)
	addCallback("NOTICE", "seenrecord", seenrecord)
}
<commit_msg>Prefix "seen" messages.<commit_after>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package bot

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

	"github.com/cloudflare/gokabinet/kt"

	cfg "github.com/arachnist/gorepost/config"
	"github.com/arachnist/gorepost/irc"
)

var k *kt.Conn

type value struct {
	Network string
	Target  string
	Action  string
	Time    int64 // .Now().UnixNano()
	Text    string
}

func seenrecord(output chan irc.Message, msg irc.Message) {
	v := value{
		Network: msg.Context["Network"],
		Target:  msg.Params[0],
		Action:  msg.Command,
		Time:    time.Now().UnixNano(),
		Text:    msg.Trailing,
	}

	b, err := json.Marshal(v)
	if err != nil {
		log.Println("Context:", msg.Context, "json marshal of seen record failed:", err)
		return
	}

	err = k.Set("seen/"+msg.Prefix.Name, b)
	if err != nil {
		log.Println("Context:", msg.Context, "error recording seen record:", err)
	}
}

func seen(output chan irc.Message, msg irc.Message) {
	var v value

	args := strings.Split(msg.Trailing, " ")

	if args[0] != ":seen" {
		return
	}
	if len(args) < 2 {
		return
	}

	b, err := k.GetBytes("seen/" + args[1])
	if err == kt.ErrNotFound {
		output <- reply(msg, cfg.LookupString(msg.Context, "NotSeenMessage"))
		return
	} else if err != nil {
		output <- reply(msg, fmt.Sprint("error getting record for", args[1], err))
		return
	}

	err = json.Unmarshal(b, &v)
	if err != nil {
		output <- reply(msg, fmt.Sprint("error unmarshaling record for", args[1], err))
		return
	}

	output <- reply(msg, fmt.Sprintf("%+v", v))
}

func init() {
	var err error
	var ktHost = "127.0.0.1"
	var ktPort = 1337

	log.Println("SEEN: connecting to KT")
	k, err = kt.NewConn(ktHost, ktPort, 4, 2*time.Second)
	if err != nil {
		log.Println("error connecting to kyoto tycoon", err)
		return
	}

	log.Println("Registering callbacks")
	addCallback("PRIVMSG", "seen", seen)
	addCallback("PRIVMSG", "seenrecord", seenrecord)
	addCallback("JOIN", "seenrecord", seenrecord)
	addCallback("PART", "seenrecord", seenrecord)
	addCallback("QUIT", "seenrecord", seenrecord)
	addCallback("NOTICE", "seenrecord", seenrecord)
}
<|endoftext|>


<commit_before>// Package diglet/wms is an HTTP Tile Server that also support JSON-RPC & WebSocket requests. Tile subscriptions
// are also available to support real-time map applications with large feature sets.
package wms

import (
	dig "github.com/buckhx/diglet/burrow"
	"github.com/buckhx/diglet/resources"
)

var hub *IoHub
var tilesets *TilesetIndex

const (
	GetTile         string = "get_tile"
	GetRawTile      string = "get_raw_tile"
	GetTileset      string = "get_tileset"
	ListTilesets    string = "list_tilesets"
	SubscribeTile   string = "subscribe_tile"
	UnsubscribeTile string = "unsubscribe_tile"
)

func MBTServer(mbtPath string, port string) *dig.App {
	tilesets = ReadTilesets(mbtPath)
	info("Serving tiles from %s", mbtPath)
	hub = NewHub(tilesets)
	go hub.listen()
	app := dig.NewApp("Diglet")
	app.Port = port
	app.Prefix = "/tileset"
	app.Methods = []dig.Method{
		{
			Name:  "viewer",
			Route: "/viewer",
			Handler: func(ctx *dig.RequestContext) (t interface{}, err *dig.CodedError) {
				w := ctx.HTTPWriter
				w.Write([]byte(resources.Static_html()))
				return
			},
			Help: "A simple tile viewer app",
		},
		{
			Name: GetTile,
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to read from"},
				"x":       {Validator: assertNumber, Help: "E/W Coordinate"},
				"y":       {Validator: assertNumber, Help: "N/S Cooredinate"},
				"z":       {Validator: assertNumber, Help: "Zoom level Coordinate"},
			},
			Handler: getTileHandler,
			Help:    "Retrieve a tile, the response's data field will be binary of the contents",
		},
		{
			Name:   ListTilesets,
			Route:  "/",
			Params: dig.MethodParams{},
			Handler: func(ctx *dig.RequestContext) (tile interface{}, err *dig.CodedError) {
				dict := make(map[string]map[string]string)
				for name, ts := range tilesets.Tilesets {
					dict[name] = ts.Metadata().Attributes()
				}
				return dict, nil
			},
			Help: "List all of the tilesets available, including their metadata",
		},
		{
			Name:  GetTileset,
			Route: "/{tileset}",
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to query for metadata"},
			},
			Handler: func(ctx *dig.RequestContext) (attrs interface{}, err *dig.CodedError) {
				params := ctx.Params
				slug := params["tileset"].GetString()
				if ts, ok := tilesets.Tilesets[slug]; ok {
					attrs = ts.Metadata().Attributes()
				} else {
					err = dig.Cerrorf(dig.RpcInvalidRequest, "No tileset named %s", slug)
				}
				return
			},
			Help: "Query for the tilesets metadata, all values are string representations",
		},
		{
			Name: SubscribeTile,
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to subscribe to"},
				"x":       {Validator: assertNumber, Help: "E/W Coordinate"},
				"y":       {Validator: assertNumber, Help: "N/S Cooredinate"},
				"z":       {Validator: assertNumber, Help: "Zoom level Coordinate"},
			},
			Handler: func(ctx *dig.RequestContext) (res interface{}, err *dig.CodedError) {
				params := ctx.Params
				x := params["x"].GetInt()
				y := params["y"].GetInt()
				z := params["z"].GetInt()
				slug := params["tileset"].GetString()
				if _, ok := tilesets.Tilesets[slug]; !ok {
					err = dig.Cerrorf(dig.RpcInvalidRequest, "Cannot find tileset %s", slug)
				} else {
					xyz := TileXYZ{Tileset: slug, X: x, Y: y, Z: z}
					if e := hub.bindTile(ctx, xyz); err != nil {
						err = dig.Cerrorf(dig.RpcInvalidRequest, e.Error())
					} else {
						// might need to make this a notifucation instead
						// -> no msg.Id
						res = sprintf("Subscribed to tile %s", xyz)
					}
				}
				return
			},
			Help: "Subscribe to changes on a specific tile, changes will be pushd with the same request id",
		},
		{
			Name: UnsubscribeTile,
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to subscribe to"},
				"x":       {Validator: assertNumber, Help: "E/W Coordinate"},
				"y":       {Validator: assertNumber, Help: "N/S Cooredinate"},
				"z":       {Validator: assertNumber, Help: "Zoom level Coordinate"},
			},
			Handler: func(ctx *dig.RequestContext) (res interface{}, err *dig.CodedError) {
				params := ctx.Params
				x := params["x"].GetInt()
				y := params["y"].GetInt()
				z := params["z"].GetInt()
				slug := params["tileset"].GetString()
				if _, ok := tilesets.Tilesets[slug]; !ok {
					err = dig.Cerrorf(dig.RpcInvalidRequest, "Cannot find tileset %s", slug)
				} else {
					xyz := TileXYZ{Tileset: slug, X: x, Y: y, Z: z}
					hub.unbindTile(ctx, xyz)
					res = sprintf("Unsubscribed from tile %s", xyz)
				}
				return
			},
			Help: "Unsubscribe from a tile",
		},
		{
			Name:  GetRawTile,
			Route: "/{tileset}/{z}/{x}/{y}",
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to subscribe to"},
				"x":       {Validator: assertNumber, Help: "E/W Coordinate"},
				"y":       {Validator: assertNumber, Help: "N/S Cooredinate"},
				"z":       {Validator: assertNumber, Help: "Zoom level Coordinate"},
			},
			Handler: func(ctx *dig.RequestContext) (otile interface{}, err *dig.CodedError) {
				itile, err := getTileHandler(ctx)
				if err != nil {
					return
				}
				r := ctx.HTTPReader
				w := ctx.HTTPWriter
				if tile, terr := castTile(itile); err != nil {
					errorlog(terr)
					terr = dig.Cerrorf(500, "Internal Error casting tile contents")
				} else {
					if dojson := r.URL.Query().Get("json"); toLower(dojson) == "true" {
						otile = tile
						return
					}
					//TODO roll sniff encoding into tile object?
					headers := formatEncoding[tile.SniffFormat()]
					for _, h := range headers {
						w.Header().Set(h.key, h.value)
					}
					w.Header().Set("Content-Length", sprintSizeOf(tile.Data))
					w.Header().Set("Access-Control-Allow-Origin", "*")
					w.Write(tile.Data)
				}
				return
			},
			Help: "Gets a tile and only writes it's raw contents. Used for hosting static tiles.",
		},
	}
	return app
}

// This is pulled out so that get_tile & get_raw_tile endpoitn can use the same code
func getTileHandler(ctx *dig.RequestContext) (tile interface{}, err *dig.CodedError) {
	params := ctx.Params
	x := params["x"].GetInt()
	y := params["y"].GetInt()
	z := params["z"].GetInt()
	slug := params["tileset"].GetString()
	xyz := TileXYZ{Tileset: slug, X: x, Y: y, Z: z}
	tile, tserr := tilesets.read(xyz)
	if tserr != nil {
		err = dig.Cerrorf(500, tserr.Error())
	}
	return
}
<commit_msg>viewer -> gallery<commit_after>// Package diglet/wms is an HTTP Tile Server that also support JSON-RPC & WebSocket requests. Tile subscriptions
// are also available to support real-time map applications with large feature sets.
package wms

import (
	dig "github.com/buckhx/diglet/burrow"
	"github.com/buckhx/diglet/resources"
)

var hub *IoHub
var tilesets *TilesetIndex

const (
	GetTile         string = "get_tile"
	GetRawTile      string = "get_raw_tile"
	GetTileset      string = "get_tileset"
	ListTilesets    string = "list_tilesets"
	SubscribeTile   string = "subscribe_tile"
	UnsubscribeTile string = "unsubscribe_tile"
)

func MBTServer(mbtPath string, port string) *dig.App {
	tilesets = ReadTilesets(mbtPath)
	info("Serving tiles from %s", mbtPath)
	hub = NewHub(tilesets)
	go hub.listen()
	app := dig.NewApp("Diglet")
	app.Port = port
	app.Prefix = "/tileset"
	app.Methods = []dig.Method{
		{
			Name:  "gallery",
			Route: "/gallery",
			Handler: func(ctx *dig.RequestContext) (t interface{}, err *dig.CodedError) {
				w := ctx.HTTPWriter
				w.Write([]byte(resources.Static_html()))
				return
			},
			Help: "A simple tile viewer gallery app",
		},
		{
			Name: GetTile,
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to read from"},
				"x":       {Validator: assertNumber, Help: "E/W Coordinate"},
				"y":       {Validator: assertNumber, Help: "N/S Cooredinate"},
				"z":       {Validator: assertNumber, Help: "Zoom level Coordinate"},
			},
			Handler: getTileHandler,
			Help:    "Retrieve a tile, the response's data field will be binary of the contents",
		},
		{
			Name:   ListTilesets,
			Route:  "/",
			Params: dig.MethodParams{},
			Handler: func(ctx *dig.RequestContext) (tile interface{}, err *dig.CodedError) {
				dict := make(map[string]map[string]string)
				for name, ts := range tilesets.Tilesets {
					dict[name] = ts.Metadata().Attributes()
				}
				return dict, nil
			},
			Help: "List all of the tilesets available, including their metadata",
		},
		{
			Name:  GetTileset,
			Route: "/{tileset}",
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to query for metadata"},
			},
			Handler: func(ctx *dig.RequestContext) (attrs interface{}, err *dig.CodedError) {
				params := ctx.Params
				slug := params["tileset"].GetString()
				if ts, ok := tilesets.Tilesets[slug]; ok {
					attrs = ts.Metadata().Attributes()
				} else {
					err = dig.Cerrorf(dig.RpcInvalidRequest, "No tileset named %s", slug)
				}
				return
			},
			Help: "Query for the tilesets metadata, all values are string representations",
		},
		{
			Name: SubscribeTile,
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to subscribe to"},
				"x":       {Validator: assertNumber, Help: "E/W Coordinate"},
				"y":       {Validator: assertNumber, Help: "N/S Cooredinate"},
				"z":       {Validator: assertNumber, Help: "Zoom level Coordinate"},
			},
			Handler: func(ctx *dig.RequestContext) (res interface{}, err *dig.CodedError) {
				params := ctx.Params
				x := params["x"].GetInt()
				y := params["y"].GetInt()
				z := params["z"].GetInt()
				slug := params["tileset"].GetString()
				if _, ok := tilesets.Tilesets[slug]; !ok {
					err = dig.Cerrorf(dig.RpcInvalidRequest, "Cannot find tileset %s", slug)
				} else {
					xyz := TileXYZ{Tileset: slug, X: x, Y: y, Z: z}
					if e := hub.bindTile(ctx, xyz); err != nil {
						err = dig.Cerrorf(dig.RpcInvalidRequest, e.Error())
					} else {
						// might need to make this a notifucation instead
						// -> no msg.Id
						res = sprintf("Subscribed to tile %s", xyz)
					}
				}
				return
			},
			Help: "Subscribe to changes on a specific tile, changes will be pushd with the same request id",
		},
		{
			Name: UnsubscribeTile,
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to subscribe to"},
				"x":       {Validator: assertNumber, Help: "E/W Coordinate"},
				"y":       {Validator: assertNumber, Help: "N/S Cooredinate"},
				"z":       {Validator: assertNumber, Help: "Zoom level Coordinate"},
			},
			Handler: func(ctx *dig.RequestContext) (res interface{}, err *dig.CodedError) {
				params := ctx.Params
				x := params["x"].GetInt()
				y := params["y"].GetInt()
				z := params["z"].GetInt()
				slug := params["tileset"].GetString()
				if _, ok := tilesets.Tilesets[slug]; !ok {
					err = dig.Cerrorf(dig.RpcInvalidRequest, "Cannot find tileset %s", slug)
				} else {
					xyz := TileXYZ{Tileset: slug, X: x, Y: y, Z: z}
					hub.unbindTile(ctx, xyz)
					res = sprintf("Unsubscribed from tile %s", xyz)
				}
				return
			},
			Help: "Unsubscribe from a tile",
		},
		{
			Name:  GetRawTile,
			Route: "/{tileset}/{z}/{x}/{y}",
			Params: dig.MethodParams{
				"tileset": {Validator: assertString, Help: "Tileset to subscribe to"},
				"x":       {Validator: assertNumber, Help: "E/W Coordinate"},
				"y":       {Validator: assertNumber, Help: "N/S Cooredinate"},
				"z":       {Validator: assertNumber, Help: "Zoom level Coordinate"},
			},
			Handler: func(ctx *dig.RequestContext) (otile interface{}, err *dig.CodedError) {
				itile, err := getTileHandler(ctx)
				if err != nil {
					return
				}
				r := ctx.HTTPReader
				w := ctx.HTTPWriter
				if tile, terr := castTile(itile); err != nil {
					errorlog(terr)
					terr = dig.Cerrorf(500, "Internal Error casting tile contents")
				} else {
					if dojson := r.URL.Query().Get("json"); toLower(dojson) == "true" {
						otile = tile
						return
					}
					//TODO roll sniff encoding into tile object?
					headers := formatEncoding[tile.SniffFormat()]
					for _, h := range headers {
						w.Header().Set(h.key, h.value)
					}
					w.Header().Set("Content-Length", sprintSizeOf(tile.Data))
					w.Header().Set("Access-Control-Allow-Origin", "*")
					w.Write(tile.Data)
				}
				return
			},
			Help: "Gets a tile and only writes it's raw contents. Used for hosting static tiles.",
		},
	}
	return app
}

// This is pulled out so that get_tile & get_raw_tile endpoitn can use the same code
func getTileHandler(ctx *dig.RequestContext) (tile interface{}, err *dig.CodedError) {
	params := ctx.Params
	x := params["x"].GetInt()
	y := params["y"].GetInt()
	z := params["z"].GetInt()
	slug := params["tileset"].GetString()
	xyz := TileXYZ{Tileset: slug, X: x, Y: y, Z: z}
	tile, tserr := tilesets.read(xyz)
	if tserr != nil {
		err = dig.Cerrorf(500, tserr.Error())
	}
	return
}
<|endoftext|>


<commit_before>package otaru

// better than nothing cryptography.
// This code has not gone through any security audit, so don't trust this code / otaru encryption.

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"
)

const (
	BtnFrameMaxPayload = 256 * 1024
)

func RandomBytes(size int) []byte {
	nonce := make([]byte, size)
	var l int
	l, err := rand.Read(nonce)
	if err != nil {
		panic(err)
	}
	if l != size {
		panic("generated rand too short")
	}
	return nonce
}

func gcmFromKey(key []byte) (cipher.AEAD, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("Failed to initialize AES: %v", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		panic(err)
	}

	return gcm, nil
}

func Encrypt(key, plain []byte) ([]byte, error) {
	gcm, err := gcmFromKey(key)
	if err != nil {
		return nil, err
	}

	nonceSize := gcm.NonceSize()
	nonce := RandomBytes(nonceSize)

	envelope := gcm.Seal(nonce, nonce, plain, nil)
	return envelope, nil
}

type frameEncryptor struct {
	gcm       cipher.AEAD
	b         bytes.Buffer
	encrypted []byte
}

func newFrameEncryptor(gcm cipher.AEAD) *frameEncryptor {
	return &frameEncryptor{
		gcm:       gcm,
		encrypted: make([]byte, 0, gcm.NonceSize()+BtnFrameMaxPayload+gcm.Overhead()),
	}
}

func (f *frameEncryptor) Write(p []byte) (int, error) {
	if _, err := f.b.Write(p); err != nil {
		panic(err)
	}

	return len(p), nil
}

func (f *frameEncryptor) Written() int {
	return f.b.Len()
}

func (f *frameEncryptor) CapacityLeft() int {
	return BtnFrameMaxPayload - f.b.Len()
}

func (f *frameEncryptor) Flush() ([]byte, error) {
	if f.Written() > BtnFrameMaxPayload {
		return nil, fmt.Errorf("frame payload size exceeding max len: %d > %d", f.Written(), BtnFrameMaxPayload)
	}

	nonce := RandomBytes(f.gcm.NonceSize())

	f.encrypted = f.encrypted[:len(nonce)]
	copy(f.encrypted, nonce)

	f.encrypted = f.gcm.Seal(f.encrypted, nonce, f.b.Bytes(), nil)
	f.b.Reset()
	return f.encrypted, nil
}

type BtnEncryptWriteCloser struct {
	target   io.Writer
	key      []byte
	lenTotal int
	written  int
	*frameEncryptor
}

func NewBtnEncryptWriteCloser(target io.Writer, key []byte, lenTotal int) (*BtnEncryptWriteCloser, error) {
	gcm, err := gcmFromKey(key)
	if err != nil {
		return nil, err
	}

	bew := &BtnEncryptWriteCloser{
		target:         target,
		key:            key,
		lenTotal:       lenTotal,
		written:        0,
		frameEncryptor: newFrameEncryptor(gcm),
	}
	return bew, nil
}

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

func (bew *BtnEncryptWriteCloser) flushFrame() error {
	frame, err := bew.frameEncryptor.Flush()
	if err != nil {
		return err
	}
	if _, err := bew.target.Write(frame); err != nil {
		return err
	}
	return nil
}

func (bew *BtnEncryptWriteCloser) Write(p []byte) (int, error) {
	if len(p) == 0 {
		return 0, nil
	}

	left := p
	for len(left) > 0 {
		framePayloadLen := intMin(bew.frameEncryptor.CapacityLeft(), len(p))
		framePayload := left[:framePayloadLen]
		if _, err := bew.frameEncryptor.Write(framePayload); err != nil {
			panic(err)
		}
		left = left[framePayloadLen:]

		if bew.frameEncryptor.CapacityLeft() == 0 {
			if err := bew.flushFrame(); err != nil {
				return 0, err
			}
		}
		if bew.frameEncryptor.CapacityLeft() == 0 {
			panic("flushFrame should brought back capacity")
		}
	}

	bew.written += len(p)
	return len(p), nil
}

func (bew *BtnEncryptWriteCloser) Close() error {
	if bew.lenTotal != bew.written {
		return fmt.Errorf("Incomplete data written")
	}

	if err := bew.flushFrame(); err != nil {
		return err
	}

	return nil
}

func Decrypt(key, envelope []byte) ([]byte, error) {
	gcm, err := gcmFromKey(key)
	if err != nil {
		return nil, err
	}

	nonceSize := gcm.NonceSize()
	nonce := envelope[:nonceSize]
	encrypted := envelope[nonceSize:]
	plain, err := gcm.Open(nil, nonce, encrypted, nil)
	if err != nil {
		return nil, err
	}

	return plain, nil
}
<commit_msg>Refine error msgs<commit_after>package otaru

// better than nothing cryptography.
// This code has not gone through any security audit, so don't trust this code / otaru encryption.

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"
)

const (
	BtnFrameMaxPayload = 256 * 1024
)

func RandomBytes(size int) []byte {
	nonce := make([]byte, size)
	var l int
	l, err := rand.Read(nonce)
	if err != nil {
		panic(err)
	}
	if l != size {
		panic("Generated random sequence is too short.")
	}
	return nonce
}

func gcmFromKey(key []byte) (cipher.AEAD, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("Failed to initialize AES: %v", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		panic(err)
	}

	return gcm, nil
}

func Encrypt(key, plain []byte) ([]byte, error) {
	gcm, err := gcmFromKey(key)
	if err != nil {
		return nil, err
	}

	nonceSize := gcm.NonceSize()
	nonce := RandomBytes(nonceSize)

	envelope := gcm.Seal(nonce, nonce, plain, nil)
	return envelope, nil
}

type frameEncryptor struct {
	gcm       cipher.AEAD
	b         bytes.Buffer
	encrypted []byte
}

func newFrameEncryptor(gcm cipher.AEAD) *frameEncryptor {
	return &frameEncryptor{
		gcm:       gcm,
		encrypted: make([]byte, 0, gcm.NonceSize()+BtnFrameMaxPayload+gcm.Overhead()),
	}
}

func (f *frameEncryptor) Write(p []byte) (int, error) {
	if _, err := f.b.Write(p); err != nil {
		panic(err)
	}

	return len(p), nil
}

func (f *frameEncryptor) Written() int {
	return f.b.Len()
}

func (f *frameEncryptor) CapacityLeft() int {
	return BtnFrameMaxPayload - f.b.Len()
}

func (f *frameEncryptor) Flush() ([]byte, error) {
	if f.Written() > BtnFrameMaxPayload {
		return nil, fmt.Errorf("frame payload size exceeding max len: %d > %d", f.Written(), BtnFrameMaxPayload)
	}

	nonce := RandomBytes(f.gcm.NonceSize())

	f.encrypted = f.encrypted[:len(nonce)]
	copy(f.encrypted, nonce)

	f.encrypted = f.gcm.Seal(f.encrypted, nonce, f.b.Bytes(), nil)
	f.b.Reset()
	return f.encrypted, nil
}

type BtnEncryptWriteCloser struct {
	target   io.Writer
	key      []byte
	lenTotal int
	written  int
	*frameEncryptor
}

func NewBtnEncryptWriteCloser(target io.Writer, key []byte, lenTotal int) (*BtnEncryptWriteCloser, error) {
	gcm, err := gcmFromKey(key)
	if err != nil {
		return nil, err
	}

	bew := &BtnEncryptWriteCloser{
		target:         target,
		key:            key,
		lenTotal:       lenTotal,
		written:        0,
		frameEncryptor: newFrameEncryptor(gcm),
	}
	return bew, nil
}

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

func (bew *BtnEncryptWriteCloser) flushFrame() error {
	frame, err := bew.frameEncryptor.Flush()
	if err != nil {
		return err
	}
	if _, err := bew.target.Write(frame); err != nil {
		return err
	}
	return nil
}

func (bew *BtnEncryptWriteCloser) Write(p []byte) (int, error) {
	if len(p) == 0 {
		return 0, nil
	}

	left := p
	for len(left) > 0 {
		framePayloadLen := intMin(bew.frameEncryptor.CapacityLeft(), len(p))
		framePayload := left[:framePayloadLen]
		if _, err := bew.frameEncryptor.Write(framePayload); err != nil {
			panic(err)
		}
		left = left[framePayloadLen:]

		if bew.frameEncryptor.CapacityLeft() == 0 {
			if err := bew.flushFrame(); err != nil {
				return 0, err
			}
		}
		if bew.frameEncryptor.CapacityLeft() == 0 {
			panic("flushFrame should have brought back capacity")
		}
	}

	bew.written += len(p)
	return len(p), nil
}

func (bew *BtnEncryptWriteCloser) Close() error {
	if bew.lenTotal != bew.written {
		return fmt.Errorf("Incomplete data written")
	}

	if err := bew.flushFrame(); err != nil {
		return err
	}

	return nil
}

func Decrypt(key, envelope []byte) ([]byte, error) {
	gcm, err := gcmFromKey(key)
	if err != nil {
		return nil, err
	}

	nonceSize := gcm.NonceSize()
	nonce := envelope[:nonceSize]
	encrypted := envelope[nonceSize:]
	plain, err := gcm.Open(nil, nonce, encrypted, nil)
	if err != nil {
		return nil, err
	}

	return plain, nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"errors"
	"fmt"
	"os"
	"strings"
)

var (
	Global Logger
)

func init() {
	Global = NewDefaultLogger(DEBUG)
}

// Wrapper for (*Logger).LoadConfiguration
func LoadConfiguration(filename string) {
	Global.LoadConfiguration(filename)
}

// Wrapper for (*Logger).AddFilter
func AddFilter(name string, lvl Level, writer LogWriter) {
	Global.AddFilter(name, lvl, writer)
}

// Wrapper for (*Logger).DeleteFilter
func DeleteFilter(name string) {
	Global.DeleteFilter(name)
}

// Disable turns off all log writers
//
// Usually used when you run log4go dependent benchmark tests
func Disable() {
	for filter := range Global {
		Global.DeleteFilter(filter)
	}
}

// Wrapper for (*Logger).Close (closes and removes all logwriters)
func Close() {
	Global.Close()
}

func Crash(args ...interface{}) {
	if len(args) > 0 {
		Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
	}
	panic(args)
}

// Logs the given message and crashes the program
func Crashf(format string, args ...interface{}) {
	Global.intLogf(CRITICAL, format, args...)
	Global.Close() // so that hopefully the messages get logged
	panic(fmt.Sprintf(format, args...))
}

// Compatibility with `log`
func Exit(args ...interface{}) {
	if len(args) > 0 {
		Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
	}
	Global.Close() // so that hopefully the messages get logged
	os.Exit(0)
}

// Compatibility with `log`
func Exitf(format string, args ...interface{}) {
	Global.intLogf(ERROR, format, args...)
	Global.Close() // so that hopefully the messages get logged
	os.Exit(0)
}

// Compatibility with `log`
func Stderr(args ...interface{}) {
	if len(args) > 0 {
		Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
	}
}

// Compatibility with `log`
func Stderrf(format string, args ...interface{}) {
	Global.intLogf(ERROR, format, args...)
}

// Compatibility with `log`
func Stdout(args ...interface{}) {
	if len(args) > 0 {
		Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...)
	}
}

// Compatibility with `log`
func Stdoutf(format string, args ...interface{}) {
	Global.intLogf(INFO, format, args...)
}

// Send a log message manually
// Wrapper for (*Logger).Log
func Log(lvl Level, source, message string) {
	Global.Log(lvl, source, message)
}

// Send a formatted log message easily
// Wrapper for (*Logger).Logf
func Logf(lvl Level, format string, args ...interface{}) {
	Global.intLogf(lvl, format, args...)
}

// Send a closure log message
// Wrapper for (*Logger).Logc
func Logc(lvl Level, closure func() string) {
	Global.intLogc(lvl, closure)
}

// Utility for finest log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Finest
func Finest(arg0 interface{}, args ...interface{}) {
	const (
		lvl = FINEST
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for fine log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Fine
func Fine(arg0 interface{}, args ...interface{}) {
	const (
		lvl = FINE
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for debug log messages
// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged.  The closure runs at most one time.
// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
// Wrapper for (*Logger).Debug
func Debug(arg0 interface{}, args ...interface{}) {
	const (
		lvl = DEBUG
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for trace log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Trace
func Trace(arg0 interface{}, args ...interface{}) {
	const (
		lvl = TRACE
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for info log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Info
func Info(arg0 interface{}, args ...interface{}) {
	const (
		lvl = INFO
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
// These functions will execute a closure exactly once, to build the error message for the return
// Wrapper for (*Logger).Warn
func Warn(arg0 interface{}, args ...interface{}) error {
	const (
		lvl = WARNING
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
		return errors.New(fmt.Sprintf(first, args...))
	case func() string:
		// Log the closure (no other arguments used)
		str := first()
		Global.intLogf(lvl, "%s", str)
		return errors.New(str)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
	}
	return nil
}

// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
// These functions will execute a closure exactly once, to build the error message for the return
// Wrapper for (*Logger).Error
func Error(arg0 interface{}, args ...interface{}) error {
	const (
		lvl = ERROR
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
		return errors.New(fmt.Sprintf(first, args...))
	case func() string:
		// Log the closure (no other arguments used)
		str := first()
		Global.intLogf(lvl, "%s", str)
		return errors.New(str)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
	}
	return nil
}

func Alarm(arg0 interface{}, args ...interface{}) {
	const (
		lvl = ALARM
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		str := first()
		Global.intLogf(lvl, "%s", str)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
// These functions will execute a closure exactly once, to build the error message for the return
// Wrapper for (*Logger).Critical
func Critical(arg0 interface{}, args ...interface{}) error {
	const (
		lvl = CRITICAL
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
		return errors.New(fmt.Sprintf(first, args...))
	case func() string:
		// Log the closure (no other arguments used)
		str := first()
		Global.intLogf(lvl, "%s", str)
		return errors.New(str)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
	}
	return nil
}
<commit_msg>add a handy func: SetLevel<commit_after>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"errors"
	"fmt"
	"os"
	"strings"
)

var (
	Global Logger
)

func init() {
	Global = NewDefaultLogger(DEBUG)
}

// Wrapper for (*Logger).LoadConfiguration
func LoadConfiguration(filename string) {
	Global.LoadConfiguration(filename)
}

// Wrapper for (*Logger).AddFilter
func AddFilter(name string, lvl Level, writer LogWriter) {
	Global.AddFilter(name, lvl, writer)
}

// Wrapper for (*Logger).DeleteFilter
func DeleteFilter(name string) {
	Global.DeleteFilter(name)
}

func SetLevel(lvl Level) {
	for _, filter := range Global {
		filter.Level = lvl
	}
}

// Disable turns off all log writers
//
// Usually used when you run log4go dependent benchmark tests
func Disable() {
	for filter := range Global {
		Global.DeleteFilter(filter)
	}
}

// Wrapper for (*Logger).Close (closes and removes all logwriters)
func Close() {
	Global.Close()
}

func Crash(args ...interface{}) {
	if len(args) > 0 {
		Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
	}
	panic(args)
}

// Logs the given message and crashes the program
func Crashf(format string, args ...interface{}) {
	Global.intLogf(CRITICAL, format, args...)
	Global.Close() // so that hopefully the messages get logged
	panic(fmt.Sprintf(format, args...))
}

// Compatibility with `log`
func Exit(args ...interface{}) {
	if len(args) > 0 {
		Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
	}
	Global.Close() // so that hopefully the messages get logged
	os.Exit(0)
}

// Compatibility with `log`
func Exitf(format string, args ...interface{}) {
	Global.intLogf(ERROR, format, args...)
	Global.Close() // so that hopefully the messages get logged
	os.Exit(0)
}

// Compatibility with `log`
func Stderr(args ...interface{}) {
	if len(args) > 0 {
		Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
	}
}

// Compatibility with `log`
func Stderrf(format string, args ...interface{}) {
	Global.intLogf(ERROR, format, args...)
}

// Compatibility with `log`
func Stdout(args ...interface{}) {
	if len(args) > 0 {
		Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...)
	}
}

// Compatibility with `log`
func Stdoutf(format string, args ...interface{}) {
	Global.intLogf(INFO, format, args...)
}

// Send a log message manually
// Wrapper for (*Logger).Log
func Log(lvl Level, source, message string) {
	Global.Log(lvl, source, message)
}

// Send a formatted log message easily
// Wrapper for (*Logger).Logf
func Logf(lvl Level, format string, args ...interface{}) {
	Global.intLogf(lvl, format, args...)
}

// Send a closure log message
// Wrapper for (*Logger).Logc
func Logc(lvl Level, closure func() string) {
	Global.intLogc(lvl, closure)
}

// Utility for finest log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Finest
func Finest(arg0 interface{}, args ...interface{}) {
	const (
		lvl = FINEST
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for fine log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Fine
func Fine(arg0 interface{}, args ...interface{}) {
	const (
		lvl = FINE
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for debug log messages
// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged.  The closure runs at most one time.
// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
// Wrapper for (*Logger).Debug
func Debug(arg0 interface{}, args ...interface{}) {
	const (
		lvl = DEBUG
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for trace log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Trace
func Trace(arg0 interface{}, args ...interface{}) {
	const (
		lvl = TRACE
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for info log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Info
func Info(arg0 interface{}, args ...interface{}) {
	const (
		lvl = INFO
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		Global.intLogc(lvl, first)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
// These functions will execute a closure exactly once, to build the error message for the return
// Wrapper for (*Logger).Warn
func Warn(arg0 interface{}, args ...interface{}) error {
	const (
		lvl = WARNING
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
		return errors.New(fmt.Sprintf(first, args...))
	case func() string:
		// Log the closure (no other arguments used)
		str := first()
		Global.intLogf(lvl, "%s", str)
		return errors.New(str)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
	}
	return nil
}

// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
// These functions will execute a closure exactly once, to build the error message for the return
// Wrapper for (*Logger).Error
func Error(arg0 interface{}, args ...interface{}) error {
	const (
		lvl = ERROR
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
		return errors.New(fmt.Sprintf(first, args...))
	case func() string:
		// Log the closure (no other arguments used)
		str := first()
		Global.intLogf(lvl, "%s", str)
		return errors.New(str)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
	}
	return nil
}

func Alarm(arg0 interface{}, args ...interface{}) {
	const (
		lvl = ALARM
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
	case func() string:
		// Log the closure (no other arguments used)
		str := first()
		Global.intLogf(lvl, "%s", str)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
	}
}

// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
// These functions will execute a closure exactly once, to build the error message for the return
// Wrapper for (*Logger).Critical
func Critical(arg0 interface{}, args ...interface{}) error {
	const (
		lvl = CRITICAL
	)
	switch first := arg0.(type) {
	case string:
		// Use the string as a format string
		Global.intLogf(lvl, first, args...)
		return errors.New(fmt.Sprintf(first, args...))
	case func() string:
		// Log the closure (no other arguments used)
		str := first()
		Global.intLogf(lvl, "%s", str)
		return errors.New(str)
	default:
		// Build a format string so that it will be similar to Sprint
		Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
		return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
	}
	return nil
}
<|endoftext|>


<commit_before>// Main package wraps sprite_sass tool for use with the command line
// See -h for list of available options
package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"runtime/pprof"
	"strings"
	"sync"
	"time"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	libsass "github.com/wellington/go-libsass"
	"github.com/wellington/wellington/version"

	wt "github.com/wellington/wellington"
	_ "github.com/wellington/wellington/handlers"
)

var (
	proj                          string
	includes                      []string
	font, dir, gen                string
	mainFile, style               string
	comments, watch               bool
	cpuprofile, buildDir          string
	jsDir                         string
	ishttp, showHelp, showVersion bool
	httpPath                      string
	timeB                         bool
	config                        string
	debug                         bool
	cachebust                     string

	// unused
	relativeAssets bool
	cssDir         string
)

/*
   --app APP                    Tell compass what kind of application it is integrating with. E.g. rails
   --fonts-dir FONTS_DIR        The directory where you keep your fonts.
*/
func init() {

	// Interoperability args
}

func flags(set *pflag.FlagSet) {
	// Unused cli args
	set.StringVarP(&buildDir, "build", "b", "",
		"Path to target directory to place generated CSS, relative paths inside project directory are preserved")
	set.BoolVarP(&comments, "comment", "", false, "Turn on source comments")
	set.BoolVar(&debug, "debug", false, "Show detailed debug information")

	var nothingb bool
	set.BoolVar(&debug, "debug-info", false, "")
	set.MarkDeprecated("debug-info", "Use --debug instead")

	set.StringVarP(&dir, "dir", "d", "",
		"Path to locate images for spriting and image functions")
	set.StringVar(&dir, "images-dir", "", "")
	set.MarkDeprecated("images-dir", "Use -d instead")

	set.StringVar(&font, "font", ".", "Path to directory containing fonts")
	set.StringVar(&gen, "generated-images-path", "", "")
	set.MarkDeprecated("generated-images-path", "Use --gen instead")
	set.StringVar(&gen, "gen", ".", "Path to place generated images")

	set.StringVarP(&proj, "proj", "p", "",
		"Path to directory containing Sass stylesheets")
	set.BoolVar(&nothingb, "no-line-comments", false, "UNSUPPORTED: Disable line comments, use comments")
	set.MarkDeprecated("no-line-comments", "Use --comments instead")
	set.BoolVar(&relativeAssets, "relative-assets", false, "UNSUPPORTED: Make compass asset helpers generate relative urls to assets.")

	set.BoolVarP(&showVersion, "version", "v", false, "Show the app version")
	set.StringVar(&cachebust, "cachebust", "", "Defeat cache by appending timestamps to static assets ie. ts, sum, timestamp")
	set.StringVarP(&style, "style", "s", "nested",
		`nested style of output CSS
                        available options: nested, expanded, compact, compressed`)
	set.StringVar(&style, "output-style", "nested", "")
	set.MarkDeprecated("output-style", "Use --style instead")
	set.BoolVar(&timeB, "time", false, "Retrieve timing information")

	var nothing string
	set.StringVar(&nothing, "require", "", "")
	set.MarkDeprecated("require", "Compass backwards compat, Not supported")
	set.MarkDeprecated("require", "Not supported")
	set.StringVar(&nothing, "environment", "", "")
	set.MarkDeprecated("environment", "Not supported")
	set.StringSliceVar(&includes, "includes", nil, "Include Sass from additional directories")
	set.StringSliceVarP(&includes, "", "I", nil, "")
	set.MarkDeprecated("I", "Compass backwards compat, use --includes instead")
	set.StringVar(&buildDir, "css-dir", "",
		"Compass backwards compat. Reference locations relative to Sass project directory")
	set.MarkDeprecated("css-dir", "Use -b instead")
	set.StringVar(&jsDir, "javascripts-dir", "", "")
	set.MarkDeprecated("javascripts-dir", "Compass backwards compat, ignored")
	set.StringSliceVar(&includes, "sass-dir", nil,
		"Compass backwards compat, use --includes instead")
	set.StringVarP(&config, "config", "c", "",
		"Temporarily disabled: Location of the config file")

	set.StringVar(&cpuprofile, "cpuprofile", "", "Go runtime cpu profilling for debugging")
}

var compileCmd = &cobra.Command{
	Use:   "compile",
	Short: "Compile Sass stylesheets to CSS",
	Long: `Fast compilation of Sass stylesheets to CSS. For usage consult
the documentation at https://github.com/wellington/wellington#wellington`,
	Run: Compile,
}

var watchCmd = &cobra.Command{
	Use:   "watch",
	Short: "Watch Sass files for changes and rebuild CSS",
	Long:  ``,
	Run:   Watch,
}

var httpCmd = &cobra.Command{
	Use:   "serve",
	Short: "Starts a http server that will convert Sass to CSS",
	Long:  ``,
	Run:   Serve,
}

func init() {
	hostname := os.Getenv("HOSTNAME")
	if len(hostname) > 0 {
		if !strings.HasPrefix(hostname, "http") {
			hostname = "http://" + hostname
		}
	} else if host, err := os.Hostname(); err == nil {
		hostname = "http://" + host
	}
	httpCmd.Flags().StringVar(&httpPath, "httppath", hostname,
		"Only for HTTP, overrides generated sprite paths to support http")

}

func root() {
	flags(wtCmd.PersistentFlags())
}

// AddCommands attaches the cli subcommands ie. http, compile to the
// main cli entrypoint.
func AddCommands() {
	wtCmd.AddCommand(httpCmd)
	wtCmd.AddCommand(compileCmd)
	wtCmd.AddCommand(watchCmd)
}

var wtCmd = &cobra.Command{
	Use:   "wt",
	Short: "wt is a Sass project tool made to handle large projects. It uses the libSass compiler for efficiency and speed.",
	Run:   Compile,
}

func main() {
	AddCommands()
	root()
	wtCmd.Execute()
}

func argExit() bool {

	if showVersion {
		fmt.Printf("   libsass: %s\n", libsass.Version())
		fmt.Printf("Wellington: %s\n", version.Version)
		return true
	}

	if showHelp {
		fmt.Println("Please specify input filepath.")
		fmt.Println("\nAvailable options:")
		//flag.PrintDefaults()
		return true
	}
	return false

}

func makeabs(wd string, path string) string {
	if filepath.IsAbs(path) {
		return path
	}
	return filepath.Join(wd, path)
}

func parseBuildArgs(paths []string) *wt.BuildArgs {
	style, ok := libsass.Style[style]

	if !ok {
		style = libsass.NESTED_STYLE
	}

	wd, err := os.Getwd()
	if err != nil {
		log.Fatal("could not find working directory", err)
	}

	proj = makeabs(wd, proj)

	incs := make([]string, len(includes))
	for i := range includes {
		incs[i] = makeabs(wd, includes[i])
	}

	dir = makeabs(wd, dir)
	font = makeabs(wd, font)
	if len(buildDir) > 0 {
		buildDir = makeabs(wd, buildDir)
		// If buildDir specified, make relative to that
		gen = makeabs(buildDir, gen)
	} else {
		gen = makeabs(wd, gen)
	}

	gba := &wt.BuildArgs{
		ImageDir:  dir,
		BuildDir:  buildDir,
		Includes:  append([]string{proj}, incs...),
		Font:      font,
		Style:     style,
		Gen:       gen,
		Comments:  comments,
		CacheBust: cachebust,
	}
	gba.WithPaths(paths)
	return gba
}

func globalRun(paths []string) (*wt.SafePartialMap, *wt.BuildArgs) {
	// fmt.Printf("paths: %s args: % #v\n", paths, pflag.Args())
	if argExit() {
		return nil, nil
	}

	// Profiling code
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Starting profiler")
		pprof.StartCPUProfile(f)
		defer func() {
			pprof.StopCPUProfile()
			err := f.Close()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Stopping Profiller")
		}()
	}

	for _, v := range paths {
		if strings.HasPrefix(v, "-") {
			log.Fatalf("Please specify flags before other arguments: %s", v)
		}
	}

	if gen != "" {
		err := os.MkdirAll(gen, 0755)
		if err != nil {
			log.Fatal(err)
		}
	}

	pMap := wt.NewPartialMap()
	gba := parseBuildArgs(paths)
	if debug {
		log.Printf("      Font  Dir: %s\n", gba.Font)
		log.Printf("      Image Dir: %s\n", gba.ImageDir)
		log.Printf("      Build Dir: %s\n", gba.BuildDir)
		log.Printf("Build Image Dir: %s\n", gba.Gen)
		log.Printf(" Include Dir(s): %s\n", gba.Includes)
		log.Println("===================================")
	}
	return pMap, gba

}

// Watch accepts a set of paths starting a recursive file watcher
func Watch(cmd *cobra.Command, paths []string) {
	pMap, gba := globalRun(paths)
	var err error
	bOpts := wt.NewBuild(paths, gba, pMap)
	err = bOpts.Run()
	if err != nil {
		log.Fatal(err)
	}
	w, err := wt.NewWatcher(&wt.WatchOptions{
		Paths:      paths,
		BArgs:      gba,
		PartialMap: pMap,
	})
	if err != nil {
		log.Fatal("failed to start watcher: ", err)
	}
	err = w.Watch()
	if err != nil {
		log.Fatal("filewatcher error: ", err)
	}

	fmt.Println("File watcher started use `ctrl+d` to exit")
	in := bufio.NewReader(os.Stdin)
	for {
		_, err := in.ReadString(' ')
		if err != nil {
			if err == io.EOF {
				os.Exit(0)
			}
			fmt.Println("error", err)
		}
	}
}

// lis is exposed so test suite can shut it down
var lis net.Listener

// Serve starts a web server accepting POST calls and return CSS
func Serve(cmd *cobra.Command, paths []string) {

	_, gba := globalRun(paths)
	if len(gba.Gen) == 0 {
		log.Fatal("Must pass an image build directory to use HTTP")
	}

	http.Handle("/build/", wt.FileHandler(gba.Gen))
	log.Println("Web server started on :12345")

	var err error
	lis, err = net.Listen("tcp", ":12345")
	if err != nil {
		log.Fatal("Error listening on :12345", err)
	}

	http.HandleFunc("/", wt.HTTPHandler(gba, httpPath))
	http.Serve(lis, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
	log.Println("Server closed")
}

// Compile handles compile files and stdin operations.
func Compile(cmd *cobra.Command, paths []string) {
	start := time.Now()
	pMap, gba := globalRun(paths)
	if gba == nil {
		return
	}

	defer func() {
		log.Printf("Compilation took: %s\n", time.Since(start))
	}()

	run(paths, pMap, gba)
}

// Run is the main entrypoint for the cli.
func run(paths []string, pMap *wt.SafePartialMap, gba *wt.BuildArgs) {

	// No paths given, read from stdin and wait
	if len(paths) == 0 {

		log.Println("Reading from stdin, -h for help")
		out := os.Stdout
		in := os.Stdin
		comp, err := wt.FromBuildArgs(out, in, gba)
		if err != nil {
			log.Fatal(err)
		}
		err = comp.Run()
		if err != nil {
			color.Red(err.Error())
		}
		return
	}

	bOpts := wt.NewBuild(paths, gba, pMap)

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

	// FIXME: move this to a Payload.Close() method

	// Before shutting down, check that every sprite has been
	// flushed to disk.
	img := sync.WaitGroup{}
	pMap.RLock()
	// It's not currently possible to wait on Image. This is often
	// to inline images, so it shouldn't be a factor...
	// for _, s := range gba.Payload.Image().M {
	// 	img.Add(1)
	// 	err := s.Wait()
	// 	img.Done()
	// 	if err != nil {
	// 		log.Printf("error writing image: %s\n", err)
	// 	}
	// }
	for _, s := range gba.Payload.Sprite().M {
		img.Add(1)
		err := s.Wait()
		img.Done()
		if err != nil {
			log.Printf("error writing sprite: %s\n", err)
		}
	}
	img.Wait()
	pMap.RUnlock()

}
<commit_msg>always add the passed paths to the include paths<commit_after>// Main package wraps sprite_sass tool for use with the command line
// See -h for list of available options
package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"runtime/pprof"
	"strings"
	"sync"
	"time"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	libsass "github.com/wellington/go-libsass"
	"github.com/wellington/wellington/version"

	wt "github.com/wellington/wellington"
	_ "github.com/wellington/wellington/handlers"
)

var (
	proj                          string
	includes                      []string
	font, dir, gen                string
	mainFile, style               string
	comments, watch               bool
	cpuprofile, buildDir          string
	jsDir                         string
	ishttp, showHelp, showVersion bool
	httpPath                      string
	timeB                         bool
	config                        string
	debug                         bool
	cachebust                     string

	// unused
	relativeAssets bool
	cssDir         string
)

/*
   --app APP                    Tell compass what kind of application it is integrating with. E.g. rails
   --fonts-dir FONTS_DIR        The directory where you keep your fonts.
*/
func init() {

	// Interoperability args
}

func flags(set *pflag.FlagSet) {
	// Unused cli args
	set.StringVarP(&buildDir, "build", "b", "",
		"Path to target directory to place generated CSS, relative paths inside project directory are preserved")
	set.BoolVarP(&comments, "comment", "", false, "Turn on source comments")
	set.BoolVar(&debug, "debug", false, "Show detailed debug information")

	var nothingb bool
	set.BoolVar(&debug, "debug-info", false, "")
	set.MarkDeprecated("debug-info", "Use --debug instead")

	set.StringVarP(&dir, "dir", "d", "",
		"Path to locate images for spriting and image functions")
	set.StringVar(&dir, "images-dir", "", "")
	set.MarkDeprecated("images-dir", "Use -d instead")

	set.StringVar(&font, "font", ".", "Path to directory containing fonts")
	set.StringVar(&gen, "generated-images-path", "", "")
	set.MarkDeprecated("generated-images-path", "Use --gen instead")
	set.StringVar(&gen, "gen", ".", "Path to place generated images")

	set.StringVarP(&proj, "proj", "p", "",
		"Path to directory containing Sass stylesheets")
	set.BoolVar(&nothingb, "no-line-comments", false, "UNSUPPORTED: Disable line comments, use comments")
	set.MarkDeprecated("no-line-comments", "Use --comments instead")
	set.BoolVar(&relativeAssets, "relative-assets", false, "UNSUPPORTED: Make compass asset helpers generate relative urls to assets.")

	set.BoolVarP(&showVersion, "version", "v", false, "Show the app version")
	set.StringVar(&cachebust, "cachebust", "", "Defeat cache by appending timestamps to static assets ie. ts, sum, timestamp")
	set.StringVarP(&style, "style", "s", "nested",
		`nested style of output CSS
                        available options: nested, expanded, compact, compressed`)
	set.StringVar(&style, "output-style", "nested", "")
	set.MarkDeprecated("output-style", "Use --style instead")
	set.BoolVar(&timeB, "time", false, "Retrieve timing information")

	var nothing string
	set.StringVar(&nothing, "require", "", "")
	set.MarkDeprecated("require", "Compass backwards compat, Not supported")
	set.MarkDeprecated("require", "Not supported")
	set.StringVar(&nothing, "environment", "", "")
	set.MarkDeprecated("environment", "Not supported")
	set.StringSliceVar(&includes, "includes", nil, "Include Sass from additional directories")
	set.StringSliceVarP(&includes, "", "I", nil, "")
	set.MarkDeprecated("I", "Compass backwards compat, use --includes instead")
	set.StringVar(&buildDir, "css-dir", "",
		"Compass backwards compat. Reference locations relative to Sass project directory")
	set.MarkDeprecated("css-dir", "Use -b instead")
	set.StringVar(&jsDir, "javascripts-dir", "", "")
	set.MarkDeprecated("javascripts-dir", "Compass backwards compat, ignored")
	set.StringSliceVar(&includes, "sass-dir", nil,
		"Compass backwards compat, use --includes instead")
	set.StringVarP(&config, "config", "c", "",
		"Temporarily disabled: Location of the config file")

	set.StringVar(&cpuprofile, "cpuprofile", "", "Go runtime cpu profilling for debugging")
}

var compileCmd = &cobra.Command{
	Use:   "compile",
	Short: "Compile Sass stylesheets to CSS",
	Long: `Fast compilation of Sass stylesheets to CSS. For usage consult
the documentation at https://github.com/wellington/wellington#wellington`,
	Run: Compile,
}

var watchCmd = &cobra.Command{
	Use:   "watch",
	Short: "Watch Sass files for changes and rebuild CSS",
	Long:  ``,
	Run:   Watch,
}

var httpCmd = &cobra.Command{
	Use:   "serve",
	Short: "Starts a http server that will convert Sass to CSS",
	Long:  ``,
	Run:   Serve,
}

func init() {
	hostname := os.Getenv("HOSTNAME")
	if len(hostname) > 0 {
		if !strings.HasPrefix(hostname, "http") {
			hostname = "http://" + hostname
		}
	} else if host, err := os.Hostname(); err == nil {
		hostname = "http://" + host
	}
	httpCmd.Flags().StringVar(&httpPath, "httppath", hostname,
		"Only for HTTP, overrides generated sprite paths to support http")

}

func root() {
	flags(wtCmd.PersistentFlags())
}

// AddCommands attaches the cli subcommands ie. http, compile to the
// main cli entrypoint.
func AddCommands() {
	wtCmd.AddCommand(httpCmd)
	wtCmd.AddCommand(compileCmd)
	wtCmd.AddCommand(watchCmd)
}

var wtCmd = &cobra.Command{
	Use:   "wt",
	Short: "wt is a Sass project tool made to handle large projects. It uses the libSass compiler for efficiency and speed.",
	Run:   Compile,
}

func main() {
	AddCommands()
	root()
	wtCmd.Execute()
}

func argExit() bool {

	if showVersion {
		fmt.Printf("   libsass: %s\n", libsass.Version())
		fmt.Printf("Wellington: %s\n", version.Version)
		return true
	}

	if showHelp {
		fmt.Println("Please specify input filepath.")
		fmt.Println("\nAvailable options:")
		//flag.PrintDefaults()
		return true
	}
	return false

}

func makeabs(wd string, path string) string {
	if filepath.IsAbs(path) {
		return path
	}
	return filepath.Join(wd, path)
}

func parseBuildArgs(paths []string) *wt.BuildArgs {
	style, ok := libsass.Style[style]

	if !ok {
		style = libsass.NESTED_STYLE
	}

	wd, err := os.Getwd()
	if err != nil {
		log.Fatal("could not find working directory", err)
	}

	proj = makeabs(wd, proj)

	incs := make([]string, len(includes))
	for i := range includes {
		incs[i] = makeabs(wd, includes[i])
	}

	dir = makeabs(wd, dir)
	font = makeabs(wd, font)
	if len(buildDir) > 0 {
		buildDir = makeabs(wd, buildDir)
		// If buildDir specified, make relative to that
		gen = makeabs(buildDir, gen)
	} else {
		gen = makeabs(wd, gen)
	}
	incs = append(incs, paths...)

	gba := &wt.BuildArgs{
		ImageDir:  dir,
		BuildDir:  buildDir,
		Includes:  append([]string{proj}, incs...),
		Font:      font,
		Style:     style,
		Gen:       gen,
		Comments:  comments,
		CacheBust: cachebust,
	}
	gba.WithPaths(paths)
	return gba
}

func globalRun(paths []string) (*wt.SafePartialMap, *wt.BuildArgs) {
	// fmt.Printf("paths: %s args: % #v\n", paths, pflag.Args())
	if argExit() {
		return nil, nil
	}

	// Profiling code
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Starting profiler")
		pprof.StartCPUProfile(f)
		defer func() {
			pprof.StopCPUProfile()
			err := f.Close()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Stopping Profiller")
		}()
	}

	for _, v := range paths {
		if strings.HasPrefix(v, "-") {
			log.Fatalf("Please specify flags before other arguments: %s", v)
		}
	}

	if gen != "" {
		err := os.MkdirAll(gen, 0755)
		if err != nil {
			log.Fatal(err)
		}
	}

	pMap := wt.NewPartialMap()
	gba := parseBuildArgs(paths)
	if debug {
		log.Printf("      Font  Dir: %s\n", gba.Font)
		log.Printf("      Image Dir: %s\n", gba.ImageDir)
		log.Printf("      Build Dir: %s\n", gba.BuildDir)
		log.Printf("Build Image Dir: %s\n", gba.Gen)
		log.Printf(" Include Dir(s): %s\n", gba.Includes)
		log.Println("===================================")
	}
	return pMap, gba

}

// Watch accepts a set of paths starting a recursive file watcher
func Watch(cmd *cobra.Command, paths []string) {
	pMap, gba := globalRun(paths)
	var err error
	bOpts := wt.NewBuild(paths, gba, pMap)
	err = bOpts.Run()
	if err != nil {
		log.Fatal(err)
	}
	w, err := wt.NewWatcher(&wt.WatchOptions{
		Paths:      paths,
		BArgs:      gba,
		PartialMap: pMap,
	})
	if err != nil {
		log.Fatal("failed to start watcher: ", err)
	}
	err = w.Watch()
	if err != nil {
		log.Fatal("filewatcher error: ", err)
	}

	fmt.Println("File watcher started use `ctrl+d` to exit")
	in := bufio.NewReader(os.Stdin)
	for {
		_, err := in.ReadString(' ')
		if err != nil {
			if err == io.EOF {
				os.Exit(0)
			}
			fmt.Println("error", err)
		}
	}
}

// lis is exposed so test suite can shut it down
var lis net.Listener

// Serve starts a web server accepting POST calls and return CSS
func Serve(cmd *cobra.Command, paths []string) {

	_, gba := globalRun(paths)
	if len(gba.Gen) == 0 {
		log.Fatal("Must pass an image build directory to use HTTP")
	}

	http.Handle("/build/", wt.FileHandler(gba.Gen))
	log.Println("Web server started on :12345")

	var err error
	lis, err = net.Listen("tcp", ":12345")
	if err != nil {
		log.Fatal("Error listening on :12345", err)
	}

	http.HandleFunc("/", wt.HTTPHandler(gba, httpPath))
	http.Serve(lis, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
	log.Println("Server closed")
}

// Compile handles compile files and stdin operations.
func Compile(cmd *cobra.Command, paths []string) {
	start := time.Now()
	pMap, gba := globalRun(paths)
	if gba == nil {
		return
	}

	defer func() {
		log.Printf("Compilation took: %s\n", time.Since(start))
	}()

	run(paths, pMap, gba)
}

// Run is the main entrypoint for the cli.
func run(paths []string, pMap *wt.SafePartialMap, gba *wt.BuildArgs) {

	// No paths given, read from stdin and wait
	if len(paths) == 0 {

		log.Println("Reading from stdin, -h for help")
		out := os.Stdout
		in := os.Stdin
		comp, err := wt.FromBuildArgs(out, in, gba)
		if err != nil {
			log.Fatal(err)
		}
		err = comp.Run()
		if err != nil {
			color.Red(err.Error())
		}
		return
	}

	bOpts := wt.NewBuild(paths, gba, pMap)

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

	// FIXME: move this to a Payload.Close() method

	// Before shutting down, check that every sprite has been
	// flushed to disk.
	img := sync.WaitGroup{}
	pMap.RLock()
	// It's not currently possible to wait on Image. This is often
	// to inline images, so it shouldn't be a factor...
	// for _, s := range gba.Payload.Image().M {
	// 	img.Add(1)
	// 	err := s.Wait()
	// 	img.Done()
	// 	if err != nil {
	// 		log.Printf("error writing image: %s\n", err)
	// 	}
	// }
	for _, s := range gba.Payload.Sprite().M {
		img.Add(1)
		err := s.Wait()
		img.Done()
		if err != nil {
			log.Printf("error writing sprite: %s\n", err)
		}
	}
	img.Wait()
	pMap.RUnlock()

}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"github.com/nais/naisd/api/app"
	k8sapps "k8s.io/api/apps/v1"
	v1 "k8s.io/api/core/v1"
	k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"strconv"
)

const (
	defaultRedisPort          = 6379
	defaultRedisExporterPort  = 9121
	defaultRedisExporterImage = "oliver006/redis_exporter:v1.2.0-alpine"
	defaultRedisImage         = "redis:5-alpine"
)

type Redis struct {
	Enabled  bool
	Image    string
	Limits   ResourceList
	Requests ResourceList
}

func updateDefaultRedisValues(redis Redis) Redis {
	if redis.Image == "" {
		redis.Image = defaultRedisImage
	}
	if len(redis.Limits.Cpu) == 0 {
		redis.Limits.Cpu = "100m"
	}
	if len(redis.Limits.Memory) == 0 {
		redis.Limits.Memory = "128Mi"
	}
	if len(redis.Requests.Cpu) == 0 {
		redis.Requests.Cpu = "100m"
	}
	if len(redis.Requests.Memory) == 0 {
		redis.Requests.Memory = "128Mi"
	}
	return redis
}

func createRedisPodSpec(redis Redis) v1.PodSpec {
	return v1.PodSpec{
		Containers: []v1.Container{
			{
				Name:  "redis",
				Image: redis.Image,
				Resources: createResourceLimits(redis.Requests.Cpu, redis.Requests.Memory,
					redis.Limits.Cpu, redis.Limits.Memory),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
			{
				Name:  "exporter",
				Image: defaultRedisExporterImage,
				Resources: createResourceLimits("100m", "100Mi",
					"100m", "100Mi"),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisExporterPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
		},
	}
}

func createRedisDeploymentSpec(redisSpec app.Spec, redis Redis) k8sapps.DeploymentSpec {
	objectMeta := generateObjectMeta(redisSpec)
	objectMeta.Annotations = map[string]string{
		"prometheus.io/scrape": "true",
		"prometheus.io/port":   strconv.Itoa(defaultRedisExporterPort),
		"prometheus.io/path":   "/metrics",
	}

	return k8sapps.DeploymentSpec{
		Replicas: int32p(1),
		Selector: &k8smeta.LabelSelector{
			MatchLabels: createPodSelector(redisSpec),
		},
		Strategy: k8sapps.DeploymentStrategy{
			Type: k8sapps.RecreateDeploymentStrategyType,
		},
		ProgressDeadlineSeconds: int32p(300),
		RevisionHistoryLimit:    int32p(10),
		Template: v1.PodTemplateSpec{
			ObjectMeta: objectMeta,
			Spec:       createRedisPodSpec(redis),
		},
	}
}

func createRedisDeploymentDef(redisSpec app.Spec, redis Redis, existingDeployment *k8sapps.Deployment) *k8sapps.Deployment {
	deploymentSpec := createRedisDeploymentSpec(redisSpec, redis)
	if existingDeployment != nil {
		existingDeployment.ObjectMeta = addLabelsToObjectMeta(existingDeployment.ObjectMeta, redisSpec)
		existingDeployment.Spec = deploymentSpec
		return existingDeployment
	} else {
		return &k8sapps.Deployment{
			TypeMeta: k8smeta.TypeMeta{
				Kind:       "Deployment",
				APIVersion: "apps/v1",
			},
			ObjectMeta: generateObjectMeta(redisSpec),
			Spec:       deploymentSpec,
		}
	}
}

func createOrUpdateRedisInstance(spec app.Spec, redis Redis, k8sClient kubernetes.Interface) (*k8sapps.Deployment, error) {
	redisSpec := app.Spec{
		Application: fmt.Sprintf("%s-redis", spec.ResourceName()),
		Namespace:   spec.Namespace,
		Team:        spec.Team,
	}
	existingDeployment, err := getExistingDeployment(redisSpec.ResourceName(), redisSpec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing deployment: %s", err)
	}

	deploymentDef := createRedisDeploymentDef(redisSpec, redis, existingDeployment)

	return createOrUpdateDeploymentResource(deploymentDef, redisSpec.Namespace, k8sClient)
}

func createRedisServiceDef(redisSpec app.Spec) *v1.Service {
	return &v1.Service{
		TypeMeta: k8smeta.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: generateObjectMeta(redisSpec),
		Spec: v1.ServiceSpec{
			Type: v1.ServiceTypeClusterIP,
			Selector: map[string]string{
				"app": redisSpec.ResourceName(),
			},
			Ports: []v1.ServicePort{
				{
					Name:     DefaultPortName,
					Protocol: v1.ProtocolTCP,
					Port:     6379,
					TargetPort: intstr.IntOrString{
						Type:   intstr.String,
						StrVal: DefaultPortName,
					},
				},
			},
		},
	}
}

func createOrUpdateRedisService(spec app.Spec, k8sClient kubernetes.Interface) (*v1.Service, error) {
	redisSpec := app.Spec{
		Application: fmt.Sprintf("%s-redis", spec.ResourceName()),
		Namespace:   spec.Namespace,
		Team:        spec.Team,
	}
	service, err := getExistingService(redisSpec.ResourceName(), redisSpec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing service: %s", err)
	} else if service == nil {
		service = createRedisServiceDef(redisSpec)
	}

	service.ObjectMeta = addLabelsToObjectMeta(service.ObjectMeta, redisSpec)
	return createOrUpdateServiceResource(service, redisSpec.Namespace, k8sClient)
}
<commit_msg>Nytt redis-exporter image<commit_after>package api

import (
	"fmt"
	"github.com/nais/naisd/api/app"
	k8sapps "k8s.io/api/apps/v1"
	v1 "k8s.io/api/core/v1"
	k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"strconv"
)

const (
	defaultRedisPort          = 6379
	defaultRedisExporterPort  = 9121
	defaultRedisExporterImage = "oliver006/redis_exporter:v1.3.4-alpine"
	defaultRedisImage         = "redis:5-alpine"
)

type Redis struct {
	Enabled  bool
	Image    string
	Limits   ResourceList
	Requests ResourceList
}

func updateDefaultRedisValues(redis Redis) Redis {
	if redis.Image == "" {
		redis.Image = defaultRedisImage
	}
	if len(redis.Limits.Cpu) == 0 {
		redis.Limits.Cpu = "100m"
	}
	if len(redis.Limits.Memory) == 0 {
		redis.Limits.Memory = "128Mi"
	}
	if len(redis.Requests.Cpu) == 0 {
		redis.Requests.Cpu = "100m"
	}
	if len(redis.Requests.Memory) == 0 {
		redis.Requests.Memory = "128Mi"
	}
	return redis
}

func createRedisPodSpec(redis Redis) v1.PodSpec {
	return v1.PodSpec{
		Containers: []v1.Container{
			{
				Name:  "redis",
				Image: redis.Image,
				Resources: createResourceLimits(redis.Requests.Cpu, redis.Requests.Memory,
					redis.Limits.Cpu, redis.Limits.Memory),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
			{
				Name:  "exporter",
				Image: defaultRedisExporterImage,
				Resources: createResourceLimits("100m", "100Mi",
					"100m", "100Mi"),
				ImagePullPolicy: v1.PullIfNotPresent,
				Ports: []v1.ContainerPort{
					{
						ContainerPort: int32(defaultRedisExporterPort),
						Name:          DefaultPortName,
						Protocol:      v1.ProtocolTCP,
					},
				},
			},
		},
	}
}

func createRedisDeploymentSpec(redisSpec app.Spec, redis Redis) k8sapps.DeploymentSpec {
	objectMeta := generateObjectMeta(redisSpec)
	objectMeta.Annotations = map[string]string{
		"prometheus.io/scrape": "true",
		"prometheus.io/port":   strconv.Itoa(defaultRedisExporterPort),
		"prometheus.io/path":   "/metrics",
	}

	return k8sapps.DeploymentSpec{
		Replicas: int32p(1),
		Selector: &k8smeta.LabelSelector{
			MatchLabels: createPodSelector(redisSpec),
		},
		Strategy: k8sapps.DeploymentStrategy{
			Type: k8sapps.RecreateDeploymentStrategyType,
		},
		ProgressDeadlineSeconds: int32p(300),
		RevisionHistoryLimit:    int32p(10),
		Template: v1.PodTemplateSpec{
			ObjectMeta: objectMeta,
			Spec:       createRedisPodSpec(redis),
		},
	}
}

func createRedisDeploymentDef(redisSpec app.Spec, redis Redis, existingDeployment *k8sapps.Deployment) *k8sapps.Deployment {
	deploymentSpec := createRedisDeploymentSpec(redisSpec, redis)
	if existingDeployment != nil {
		existingDeployment.ObjectMeta = addLabelsToObjectMeta(existingDeployment.ObjectMeta, redisSpec)
		existingDeployment.Spec = deploymentSpec
		return existingDeployment
	} else {
		return &k8sapps.Deployment{
			TypeMeta: k8smeta.TypeMeta{
				Kind:       "Deployment",
				APIVersion: "apps/v1",
			},
			ObjectMeta: generateObjectMeta(redisSpec),
			Spec:       deploymentSpec,
		}
	}
}

func createOrUpdateRedisInstance(spec app.Spec, redis Redis, k8sClient kubernetes.Interface) (*k8sapps.Deployment, error) {
	redisSpec := app.Spec{
		Application: fmt.Sprintf("%s-redis", spec.ResourceName()),
		Namespace:   spec.Namespace,
		Team:        spec.Team,
	}
	existingDeployment, err := getExistingDeployment(redisSpec.ResourceName(), redisSpec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing deployment: %s", err)
	}

	deploymentDef := createRedisDeploymentDef(redisSpec, redis, existingDeployment)

	return createOrUpdateDeploymentResource(deploymentDef, redisSpec.Namespace, k8sClient)
}

func createRedisServiceDef(redisSpec app.Spec) *v1.Service {
	return &v1.Service{
		TypeMeta: k8smeta.TypeMeta{
			Kind:       "Service",
			APIVersion: "v1",
		},
		ObjectMeta: generateObjectMeta(redisSpec),
		Spec: v1.ServiceSpec{
			Type: v1.ServiceTypeClusterIP,
			Selector: map[string]string{
				"app": redisSpec.ResourceName(),
			},
			Ports: []v1.ServicePort{
				{
					Name:     DefaultPortName,
					Protocol: v1.ProtocolTCP,
					Port:     6379,
					TargetPort: intstr.IntOrString{
						Type:   intstr.String,
						StrVal: DefaultPortName,
					},
				},
			},
		},
	}
}

func createOrUpdateRedisService(spec app.Spec, k8sClient kubernetes.Interface) (*v1.Service, error) {
	redisSpec := app.Spec{
		Application: fmt.Sprintf("%s-redis", spec.ResourceName()),
		Namespace:   spec.Namespace,
		Team:        spec.Team,
	}
	service, err := getExistingService(redisSpec.ResourceName(), redisSpec.Namespace, k8sClient)

	if err != nil {
		return nil, fmt.Errorf("unable to get existing service: %s", err)
	} else if service == nil {
		service = createRedisServiceDef(redisSpec)
	}

	service.ObjectMeta = addLabelsToObjectMeta(service.ObjectMeta, redisSpec)
	return createOrUpdateServiceResource(service, redisSpec.Namespace, k8sClient)
}
<|endoftext|>


<commit_before>package in_test

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"

	"gopkg.in/yaml.v2"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
	"github.com/pivotal-cf-experimental/pivnet-resource/concourse"
	"github.com/pivotal-cf-experimental/pivnet-resource/in"
	"github.com/pivotal-cf-experimental/pivnet-resource/logger"
	"github.com/pivotal-cf-experimental/pivnet-resource/metadata"
	"github.com/pivotal-cf-experimental/pivnet-resource/pivnet"
	"github.com/pivotal-cf-experimental/pivnet-resource/sanitizer"
	"github.com/pivotal-cf-experimental/pivnet-resource/versions"
)

var _ = Describe("In", func() {
	var (
		server *ghttp.Server

		releaseID int

		file1URLPath         string
		productFiles         []pivnet.ProductFile
		productFilesResponse pivnet.ProductFiles

		downloadDir string

		ginkgoLogger logger.Logger

		productVersion  string
		etag            string
		versionWithETag string

		inRequest              concourse.InRequest
		inCommand              *in.InCommand
		pivnetReleasesResponse *pivnet.ReleasesResponse
	)

	BeforeEach(func() {
		server = ghttp.NewServer()

		productVersion = "C"
		etag = "etag-0"

		var err error
		versionWithETag, err = versions.CombineVersionAndETag(productVersion, etag)
		Expect(err).NotTo(HaveOccurred())

		releaseID = 1234
		file1URLPath = "/file1"
		file1URL := fmt.Sprintf("%s%s", server.URL(), file1URLPath)
		productFiles = []pivnet.ProductFile{
			{
				ID:           1234,
				Name:         "product file 1234",
				Description:  "some product file 1234",
				AWSObjectKey: "some-key 1234",
				FileType:     "some-file-type 1234",
				FileVersion:  "some-file-version 1234",
				MD5:          "some-md5 1234",
				Links: &pivnet.Links{
					Download: map[string]string{
						"href": "foo",
					},
				},
			},
			{
				ID:           3456,
				Name:         "product file 3456",
				Description:  "some product file 3456",
				AWSObjectKey: "some-key 3456",
				FileType:     "some-file-type 3456",
				FileVersion:  "some-file-version 3456",
				MD5:          "some-md5 3456",
				Links: &pivnet.Links{
					Download: map[string]string{
						"href": "bar",
					},
				},
			},
		}
		productFilesResponse = pivnet.ProductFiles{
			ProductFiles: productFiles,
		}

		pivnetReleasesResponse = &pivnet.ReleasesResponse{
			Releases: []pivnet.Release{
				{
					Version: "A",
				},
				{
					Version: productVersion,
					ID:      releaseID,
					Links: &pivnet.Links{
						ProductFiles: map[string]string{
							"href": file1URL,
						},
					},
				},
				{
					Version: "B",
				},
			},
		}

		downloadDir, err = ioutil.TempDir("", "")
		Expect(err).NotTo(HaveOccurred())

		inRequest = concourse.InRequest{
			Source: concourse.Source{
				APIToken:    "some-api-token",
				ProductSlug: productSlug,
				Endpoint:    server.URL(),
			},
			Version: concourse.Version{
				versionWithETag,
			},
		}

	})

	JustBeforeEach(func() {
		server.AppendHandlers(
			ghttp.CombineHandlers(
				ghttp.VerifyRequest(
					"GET",
					fmt.Sprintf("%s/products/%s/releases", apiPrefix, productSlug),
				),
				ghttp.RespondWithJSONEncoded(http.StatusOK, pivnetReleasesResponse),
			),
		)

		server.AppendHandlers(
			ghttp.CombineHandlers(
				ghttp.VerifyRequest(
					"POST",
					fmt.Sprintf(
						"%s/products/%s/releases/%d/eula_acceptance",
						apiPrefix,
						productSlug,
						releaseID,
					),
				),
				ghttp.RespondWith(http.StatusOK, ""),
			),
		)

		server.AppendHandlers(
			ghttp.CombineHandlers(
				ghttp.VerifyRequest(
					"GET",
					file1URLPath,
				),
				ghttp.RespondWithJSONEncoded(http.StatusOK, productFilesResponse),
			),
		)

		for _, p := range productFiles {
			server.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest(
						"GET",
						fmt.Sprintf(
							"%s/products/%s/releases/%d/product_files/%d",
							apiPrefix,
							productSlug,
							releaseID,
							p.ID,
						),
					),
					ghttp.RespondWithJSONEncoded(http.StatusOK, p),
				),
			)
		}

		sanitized := concourse.SanitizedSource(inRequest.Source)
		sanitizer := sanitizer.NewSanitizer(sanitized, GinkgoWriter)

		ginkgoLogger = logger.NewLogger(sanitizer)

		binaryVersion := "v0.1.2-unit-tests"
		inCommand = in.NewInCommand(binaryVersion, ginkgoLogger, downloadDir)
	})

	AfterEach(func() {
		server.Close()

		err := os.RemoveAll(downloadDir)
		Expect(err).NotTo(HaveOccurred())
	})

	It("writes a version file with the downloaded version and etag", func() {
		_, err := inCommand.Run(inRequest)
		Expect(err).NotTo(HaveOccurred())

		versionFilepath := filepath.Join(downloadDir, "version")
		versionContents, err := ioutil.ReadFile(versionFilepath)
		Expect(err).NotTo(HaveOccurred())
		Expect(string(versionContents)).To(Equal(versionWithETag))
	})

	var validateProductFilesMetadata = func(
		writtenMetadata metadata.Metadata,
		productFiles []pivnet.ProductFile,
	) {
		Expect(writtenMetadata.ProductFiles).To(HaveLen(len(productFiles)))
		for i, p := range productFiles {
			Expect(writtenMetadata.ProductFiles[i].File).To(Equal(p.Name))
			Expect(writtenMetadata.ProductFiles[i].Description).To(Equal(p.Description))
			Expect(writtenMetadata.ProductFiles[i].ID).To(Equal(p.ID))
			Expect(writtenMetadata.ProductFiles[i].AWSObjectKey).To(Equal(p.AWSObjectKey))
			Expect(writtenMetadata.ProductFiles[i].FileType).To(Equal(p.FileType))
			Expect(writtenMetadata.ProductFiles[i].FileVersion).To(Equal(p.FileVersion))
			Expect(writtenMetadata.ProductFiles[i].MD5).To(Equal(p.MD5))
			Expect(writtenMetadata.ProductFiles[i].UploadAs).To(BeEmpty())
		}
	}

	It("writes a metadata file in yaml format", func() {
		_, err := inCommand.Run(inRequest)
		Expect(err).NotTo(HaveOccurred())

		versionFilepath := filepath.Join(downloadDir, "metadata.yaml")
		versionContents, err := ioutil.ReadFile(versionFilepath)
		Expect(err).NotTo(HaveOccurred())

		var writtenMetadata metadata.Metadata
		err = yaml.Unmarshal(versionContents, &writtenMetadata)
		Expect(err).NotTo(HaveOccurred())

		Expect(writtenMetadata.Release).NotTo(BeNil())
		Expect(writtenMetadata.Release.Version).To(Equal(productVersion))

		validateProductFilesMetadata(writtenMetadata, productFiles)
	})

	It("writes a metadata file in json format", func() {
		_, err := inCommand.Run(inRequest)
		Expect(err).NotTo(HaveOccurred())

		versionFilepath := filepath.Join(downloadDir, "metadata.json")
		versionContents, err := ioutil.ReadFile(versionFilepath)
		Expect(err).NotTo(HaveOccurred())

		var writtenMetadata metadata.Metadata
		err = json.Unmarshal(versionContents, &writtenMetadata)
		Expect(err).NotTo(HaveOccurred())

		Expect(writtenMetadata.Release).NotTo(BeNil())
		Expect(writtenMetadata.Release.Version).To(Equal(productVersion))

		validateProductFilesMetadata(writtenMetadata, productFiles)
	})

	It("does not download any of the files in the specified release", func() {
		_, err := inCommand.Run(inRequest)
		Expect(err).NotTo(HaveOccurred())

		files, err := ioutil.ReadDir(downloadDir)
		Expect(err).ShouldNot(HaveOccurred())

		// the version and metadata files will always exist
		Expect(len(files)).To(Equal(3))
		Expect(files[0].Name()).To(Equal("metadata.json"))
		Expect(files[1].Name()).To(Equal("metadata.yml"))
		Expect(files[2].Name()).To(Equal("version"))
	})

	Context("when version is provided without etag", func() {
		BeforeEach(func() {
			inRequest.Version = concourse.Version{
				ProductVersion: productVersion,
			}
		})

		It("returns without error", func() {
			_, err := inCommand.Run(inRequest)
			Expect(err).NotTo(HaveOccurred())
		})
	})

	Context("when release has no links", func() {
		BeforeEach(func() {
			pivnetReleasesResponse.Releases[1].Links = nil
		})

		It("returns an error", func() {
			_, err := inCommand.Run(inRequest)
			Expect(err).To(HaveOccurred())

			Expect(err.Error()).To(MatchRegexp("Failed to get Product File"))
		})
	})
})
<commit_msg>One more yml -> yaml.<commit_after>package in_test

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"

	"gopkg.in/yaml.v2"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
	"github.com/pivotal-cf-experimental/pivnet-resource/concourse"
	"github.com/pivotal-cf-experimental/pivnet-resource/in"
	"github.com/pivotal-cf-experimental/pivnet-resource/logger"
	"github.com/pivotal-cf-experimental/pivnet-resource/metadata"
	"github.com/pivotal-cf-experimental/pivnet-resource/pivnet"
	"github.com/pivotal-cf-experimental/pivnet-resource/sanitizer"
	"github.com/pivotal-cf-experimental/pivnet-resource/versions"
)

var _ = Describe("In", func() {
	var (
		server *ghttp.Server

		releaseID int

		file1URLPath         string
		productFiles         []pivnet.ProductFile
		productFilesResponse pivnet.ProductFiles

		downloadDir string

		ginkgoLogger logger.Logger

		productVersion  string
		etag            string
		versionWithETag string

		inRequest              concourse.InRequest
		inCommand              *in.InCommand
		pivnetReleasesResponse *pivnet.ReleasesResponse
	)

	BeforeEach(func() {
		server = ghttp.NewServer()

		productVersion = "C"
		etag = "etag-0"

		var err error
		versionWithETag, err = versions.CombineVersionAndETag(productVersion, etag)
		Expect(err).NotTo(HaveOccurred())

		releaseID = 1234
		file1URLPath = "/file1"
		file1URL := fmt.Sprintf("%s%s", server.URL(), file1URLPath)
		productFiles = []pivnet.ProductFile{
			{
				ID:           1234,
				Name:         "product file 1234",
				Description:  "some product file 1234",
				AWSObjectKey: "some-key 1234",
				FileType:     "some-file-type 1234",
				FileVersion:  "some-file-version 1234",
				MD5:          "some-md5 1234",
				Links: &pivnet.Links{
					Download: map[string]string{
						"href": "foo",
					},
				},
			},
			{
				ID:           3456,
				Name:         "product file 3456",
				Description:  "some product file 3456",
				AWSObjectKey: "some-key 3456",
				FileType:     "some-file-type 3456",
				FileVersion:  "some-file-version 3456",
				MD5:          "some-md5 3456",
				Links: &pivnet.Links{
					Download: map[string]string{
						"href": "bar",
					},
				},
			},
		}
		productFilesResponse = pivnet.ProductFiles{
			ProductFiles: productFiles,
		}

		pivnetReleasesResponse = &pivnet.ReleasesResponse{
			Releases: []pivnet.Release{
				{
					Version: "A",
				},
				{
					Version: productVersion,
					ID:      releaseID,
					Links: &pivnet.Links{
						ProductFiles: map[string]string{
							"href": file1URL,
						},
					},
				},
				{
					Version: "B",
				},
			},
		}

		downloadDir, err = ioutil.TempDir("", "")
		Expect(err).NotTo(HaveOccurred())

		inRequest = concourse.InRequest{
			Source: concourse.Source{
				APIToken:    "some-api-token",
				ProductSlug: productSlug,
				Endpoint:    server.URL(),
			},
			Version: concourse.Version{
				versionWithETag,
			},
		}

	})

	JustBeforeEach(func() {
		server.AppendHandlers(
			ghttp.CombineHandlers(
				ghttp.VerifyRequest(
					"GET",
					fmt.Sprintf("%s/products/%s/releases", apiPrefix, productSlug),
				),
				ghttp.RespondWithJSONEncoded(http.StatusOK, pivnetReleasesResponse),
			),
		)

		server.AppendHandlers(
			ghttp.CombineHandlers(
				ghttp.VerifyRequest(
					"POST",
					fmt.Sprintf(
						"%s/products/%s/releases/%d/eula_acceptance",
						apiPrefix,
						productSlug,
						releaseID,
					),
				),
				ghttp.RespondWith(http.StatusOK, ""),
			),
		)

		server.AppendHandlers(
			ghttp.CombineHandlers(
				ghttp.VerifyRequest(
					"GET",
					file1URLPath,
				),
				ghttp.RespondWithJSONEncoded(http.StatusOK, productFilesResponse),
			),
		)

		for _, p := range productFiles {
			server.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest(
						"GET",
						fmt.Sprintf(
							"%s/products/%s/releases/%d/product_files/%d",
							apiPrefix,
							productSlug,
							releaseID,
							p.ID,
						),
					),
					ghttp.RespondWithJSONEncoded(http.StatusOK, p),
				),
			)
		}

		sanitized := concourse.SanitizedSource(inRequest.Source)
		sanitizer := sanitizer.NewSanitizer(sanitized, GinkgoWriter)

		ginkgoLogger = logger.NewLogger(sanitizer)

		binaryVersion := "v0.1.2-unit-tests"
		inCommand = in.NewInCommand(binaryVersion, ginkgoLogger, downloadDir)
	})

	AfterEach(func() {
		server.Close()

		err := os.RemoveAll(downloadDir)
		Expect(err).NotTo(HaveOccurred())
	})

	It("writes a version file with the downloaded version and etag", func() {
		_, err := inCommand.Run(inRequest)
		Expect(err).NotTo(HaveOccurred())

		versionFilepath := filepath.Join(downloadDir, "version")
		versionContents, err := ioutil.ReadFile(versionFilepath)
		Expect(err).NotTo(HaveOccurred())
		Expect(string(versionContents)).To(Equal(versionWithETag))
	})

	var validateProductFilesMetadata = func(
		writtenMetadata metadata.Metadata,
		productFiles []pivnet.ProductFile,
	) {
		Expect(writtenMetadata.ProductFiles).To(HaveLen(len(productFiles)))
		for i, p := range productFiles {
			Expect(writtenMetadata.ProductFiles[i].File).To(Equal(p.Name))
			Expect(writtenMetadata.ProductFiles[i].Description).To(Equal(p.Description))
			Expect(writtenMetadata.ProductFiles[i].ID).To(Equal(p.ID))
			Expect(writtenMetadata.ProductFiles[i].AWSObjectKey).To(Equal(p.AWSObjectKey))
			Expect(writtenMetadata.ProductFiles[i].FileType).To(Equal(p.FileType))
			Expect(writtenMetadata.ProductFiles[i].FileVersion).To(Equal(p.FileVersion))
			Expect(writtenMetadata.ProductFiles[i].MD5).To(Equal(p.MD5))
			Expect(writtenMetadata.ProductFiles[i].UploadAs).To(BeEmpty())
		}
	}

	It("writes a metadata file in yaml format", func() {
		_, err := inCommand.Run(inRequest)
		Expect(err).NotTo(HaveOccurred())

		versionFilepath := filepath.Join(downloadDir, "metadata.yaml")
		versionContents, err := ioutil.ReadFile(versionFilepath)
		Expect(err).NotTo(HaveOccurred())

		var writtenMetadata metadata.Metadata
		err = yaml.Unmarshal(versionContents, &writtenMetadata)
		Expect(err).NotTo(HaveOccurred())

		Expect(writtenMetadata.Release).NotTo(BeNil())
		Expect(writtenMetadata.Release.Version).To(Equal(productVersion))

		validateProductFilesMetadata(writtenMetadata, productFiles)
	})

	It("writes a metadata file in json format", func() {
		_, err := inCommand.Run(inRequest)
		Expect(err).NotTo(HaveOccurred())

		versionFilepath := filepath.Join(downloadDir, "metadata.json")
		versionContents, err := ioutil.ReadFile(versionFilepath)
		Expect(err).NotTo(HaveOccurred())

		var writtenMetadata metadata.Metadata
		err = json.Unmarshal(versionContents, &writtenMetadata)
		Expect(err).NotTo(HaveOccurred())

		Expect(writtenMetadata.Release).NotTo(BeNil())
		Expect(writtenMetadata.Release.Version).To(Equal(productVersion))

		validateProductFilesMetadata(writtenMetadata, productFiles)
	})

	It("does not download any of the files in the specified release", func() {
		_, err := inCommand.Run(inRequest)
		Expect(err).NotTo(HaveOccurred())

		files, err := ioutil.ReadDir(downloadDir)
		Expect(err).ShouldNot(HaveOccurred())

		// the version and metadata files will always exist
		Expect(len(files)).To(Equal(3))
		Expect(files[0].Name()).To(Equal("metadata.json"))
		Expect(files[1].Name()).To(Equal("metadata.yaml"))
		Expect(files[2].Name()).To(Equal("version"))
	})

	Context("when version is provided without etag", func() {
		BeforeEach(func() {
			inRequest.Version = concourse.Version{
				ProductVersion: productVersion,
			}
		})

		It("returns without error", func() {
			_, err := inCommand.Run(inRequest)
			Expect(err).NotTo(HaveOccurred())
		})
	})

	Context("when release has no links", func() {
		BeforeEach(func() {
			pivnetReleasesResponse.Releases[1].Links = nil
		})

		It("returns an error", func() {
			_, err := inCommand.Run(inRequest)
			Expect(err).To(HaveOccurred())

			Expect(err.Error()).To(MatchRegexp("Failed to get Product File"))
		})
	})
})
<|endoftext|>


<commit_before>package statistics

import (
	"models"
	"fmt"
	"net/http"
	"encoding/json"
	"strings"
)

const (
	MEAN   = "mean"
	MEDIAN = "median"
)

var INVALID_URL = map[string]string{
	"Error": "Invalid URL",
}

func GenerateReportDisplay(date string) {

	reportValues, err := GenerateReport(date)

	if err != nil {
		panic(err)
	}

	fmt.Printf("================================================================ \n")
	fmt.Printf("            STATISTICAL ANALYSIS For %s  \n", date)
	fmt.Printf("================================================================ \n")
	fmt.Printf("        Air Temperature    Barometric Pressuare   Wind Speed     \n")
    fmt.Printf("  MEAN     %.2f                %.2f                %.2f          \n",
		              reportValues[models.DATASOURCE_TYPES[0]][MEAN],
		              reportValues[models.DATASOURCE_TYPES[1]][MEAN],
		              reportValues[models.DATASOURCE_TYPES[2]][MEAN]                   )
	fmt.Printf(" MEDIAN    %.2f                %.2f                %.2f           \n",
					  reportValues[models.DATASOURCE_TYPES[0]][MEDIAN],
					  reportValues[models.DATASOURCE_TYPES[1]][MEDIAN],
					  reportValues[models.DATASOURCE_TYPES[2]][MEDIAN]   )
	fmt.Printf("================================================================ \n")

}

func GenerateReport(date string) (map[string]map[string]float64, error) {

	reportValues := map[string]map[string]float64{}

	for _, dataSourceType := range models.DATASOURCE_TYPES {

		var lakeDatas models.DataRecs

		//fmt.Printf("Checking records for %s %s \n", date, dataSourceType)
		recordsExist, err := models.CheckDBRecordsFor(date, dataSourceType)

		if err != nil {
			fmt.Printf("CheckDBRecordsFor - Problems checking Records for %s %s \n", date, dataSourceType)
			return nil, err
		}

		if !recordsExist {
			models.FetchData(date, dataSourceType)
		}

		lakeDatas, err = models.GetDBRecordsFor(date, dataSourceType)
		if err != nil {
			fmt.Printf("GetDBRecordsFor - Problems checking Records for %s %s \n", date, dataSourceType)
			return nil, err
		}

		meanValue := lakeDatas.Mean()
		medianValue := lakeDatas.Median()

		reportValues[dataSourceType] = map[string]float64{
			MEAN   : meanValue,
			MEDIAN : medianValue,
		}
	}

	return reportValues, nil

}

func reportHandler(w http.ResponseWriter, r *http.Request) {

	path := r.URL.Path
	pathParts := strings.Split(path, "/")

	if len(pathParts) != 3 {
		generateJson(w, INVALID_URL)
		return
	}

	date := pathParts[2]

	p, err := GenerateReport(date)
	if err != nil {
		fmt.Printf("Error parsing the %s\n", err)
		http.Error(w, "File not found", http.StatusInternalServerError)
	} else {
		generateJson(w, p)
	}
}

func generateJson(w http.ResponseWriter, data interface{}) {
	jsonEnc := json.NewEncoder(w)
	jsonEnc.Encode(data)
}

func CreateServer() {
	http.HandleFunc("/reports/", reportHandler)
	http.ListenAndServe(":8888", nil)
}

<commit_msg>report changes<commit_after>package statistics

import (
	"models"
	"fmt"
	"net/http"
	"encoding/json"
	"strings"
)

const (
	MEAN   = "mean"
	MEDIAN = "median"
	DATE_FIELD = "date"
)

var INVALID_URL = map[string]string{
	"Error": "Invalid URL",
}

type ReportData struct {
	Mean float64
	Median float64
}

type ReportOutput struct {
	Date string
	ReportData map[string]ReportData
}

func GenerateReportDisplay(date string) {

	reportOutput, err := GenerateReport(date)

	if err != nil {
		panic(err)
	}
	reportValues := reportOutput.ReportData
	fmt.Printf("================================================================ \n")
	fmt.Printf("            STATISTICAL ANALYSIS For %s  \n", reportOutput.Date     )
	fmt.Printf("================================================================ \n")
	fmt.Printf("        Air Temperature    Barometric Pressuare   Wind Speed     \n")
    fmt.Printf("  MEAN     %.2f                %.2f                %.2f          \n",
		              reportValues[models.DATASOURCE_TYPES[0]].Mean,
		              reportValues[models.DATASOURCE_TYPES[1]].Mean,
		              reportValues[models.DATASOURCE_TYPES[2]].Mean                   )
	fmt.Printf(" MEDIAN    %.2f                %.2f                %.2f           \n",
					  reportValues[models.DATASOURCE_TYPES[0]].Median,
					  reportValues[models.DATASOURCE_TYPES[1]].Median,
					  reportValues[models.DATASOURCE_TYPES[2]].Median   )
	fmt.Printf("================================================================ \n")

}

func GenerateReport(date string) (ReportOutput, error) {

	reportValues := map[string]ReportData{}

	for _, dataSourceType := range models.DATASOURCE_TYPES {

		var lakeDatas models.DataRecs

		//fmt.Printf("Checking records for %s %s \n", date, dataSourceType)
		recordsExist, err := models.CheckDBRecordsFor(date, dataSourceType)

		if err != nil {
			fmt.Printf("CheckDBRecordsFor - Problems checking Records for %s %s \n", date, dataSourceType)
			return ReportOutput{}, err
		}

		if !recordsExist {
			models.FetchData(date, dataSourceType)
		}

		lakeDatas, err = models.GetDBRecordsFor(date, dataSourceType)
		if err != nil {
			fmt.Printf("GetDBRecordsFor - Problems checking Records for %s %s \n", date, dataSourceType)
			return ReportOutput{}, err
		}

		meanValue := lakeDatas.Mean()
		medianValue := lakeDatas.Median()

		reportValues[dataSourceType] = ReportData{
			Mean   : meanValue,
			Median : medianValue,
		}
	}

	reportOutput := ReportOutput{
		Date: date,
		ReportData:reportValues,
	}

	return reportOutput, nil

}

func reportHandler(w http.ResponseWriter, r *http.Request) {

	path := r.URL.Path
	pathParts := strings.Split(path, "/")

	if len(pathParts) != 3 {
		generateJson(w, INVALID_URL)
		return
	}

	date := pathParts[2]

	p, err := GenerateReport(date)
	if err != nil {
		fmt.Printf("Error parsing the %s\n", err)
		http.Error(w, "File not found", http.StatusInternalServerError)
	} else {
		generateJson(w, p)
	}
}

func generateJson(w http.ResponseWriter, data interface{}) {
	jsonEnc := json.NewEncoder(w)
	jsonEnc.Encode(data)
}

func CreateServer() {
	http.HandleFunc("/reports/", reportHandler)
	http.ListenAndServe(":8888", nil)
}

<|endoftext|>


<commit_before>package zzk

import (
	"sync"
)

type Listener interface {
	Listen(shutdown <-chan interface{})
}

// Start starts a group of listeners that are governed by a master listener.
// When the master exits, it shuts down all of the child listeners and waits
// for all of the subprocesses to exit
func Start(shutdown <-chan interface{}, master Listener, listeners ...Listener) {
	var wg sync.WaitGroup
	_shutdown := make(chan interface{})
	for _, listener := range listeners {
		wg.Add(1)
		go func() {
			defer wg.Done()
			listener.Listen(_shutdown)
		}()
	}
	master.Listen(shutdown)
	close(_shutdown)
	wg.Wait()
}<commit_msg>added logging<commit_after>package zzk

import (
	"sync"

	"github.com/zenoss/glog"
)

type Listener interface {
	Listen(shutdown <-chan interface{})
}

// Start starts a group of listeners that are governed by a master listener.
// When the master exits, it shuts down all of the child listeners and waits
// for all of the subprocesses to exit
func Start(shutdown <-chan interface{}, master Listener, listeners ...Listener) {
	var wg sync.WaitGroup
	_shutdown := make(chan interface{})
	for _, listener := range listeners {
		wg.Add(1)
		go func() {
			defer wg.Done()
			listener.Listen(_shutdown)
		}()
	}
	master.Listen(shutdown)
	glog.Infof("shutdown finished for %#v", master)
	close(_shutdown)
	wg.Wait()
	glog.Info("all listeners stopped")
}
<|endoftext|>