<commit_before>/*

	filesystem is a storage layer that stores information about games as JSON
	files within a given folder, one per game. It's extremely inefficient and
	doesn't even persist extended game information to disk. It's most useful
	for cases where having an easy-to-read, diffable representation for games
	makes sense, for example to create golden tester games for use in testing.

	filesystem stores files according to their gametype in the given base
	folder, for example 'checkers/a22ffcdef.json'. If the sub-folders don't
	exist, they will be created. Folders may be soft-linked from within the
	base folder; often when using the filesystem storage layer to help
	generate test cases you set up soft-links from a central location to a
	folder for test files in each game's sub-directory, so the test files can
	be in the same place.

*/
package filesystem

import (
	"bytes"
	"errors"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/server/api/extendedgame"
	"github.com/jkomoros/boardgame/server/api/listing"
	"github.com/jkomoros/boardgame/storage/filesystem/record"
	"github.com/jkomoros/boardgame/storage/internal/helpers"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"strings"
)

type StorageManager struct {
	//Fall back on those methods
	*helpers.ExtendedMemoryStorageManager
	basePath         string
	goldenFolderName string
	managers         []*boardgame.GameManager
}

//Store seen ids and remember where the path was
var idToPath map[string]string

func init() {
	idToPath = make(map[string]string)
}

//NewStorageManager returns a new filesystem storage manager. basePath is the
//folder, relative to this executable, to have as the root of the storage
//pool. If goldenFolderName is not "", then we will use reflection to find the
//package path for each delegate, ensure a folder exists within it with that
//name, create a soft-link from basePath to that folder, and create a
//`golden_test.go` file that automatically tests all of those golden files.
//The result is that the underlying files will be stored in folders adjacent
//to the games they are relative to, which is convenient if you're adding new
//golden games to the test set.
func NewStorageManager(basePath string, goldenFolderName string) *StorageManager {

	result := &StorageManager{
		basePath:         basePath,
		goldenFolderName: goldenFolderName,
	}

	result.ExtendedMemoryStorageManager = helpers.NewExtendedMemoryStorageManager(result)

	return result
}

func (s *StorageManager) Name() string {
	return "filesystem"
}

func (s *StorageManager) Connect(config string) error {

	if _, err := os.Stat(s.basePath); os.IsNotExist(err) {
		if err := os.Mkdir(s.basePath, 0700); err != nil {
			return errors.New("Base path didn't exist and couldn't create it: " + err.Error())
		}
	}

	if s.goldenFolderName != "" {

		log.Println("Creating and linking golden folders")
		for _, manager := range s.managers {
			pkgPath := reflect.ValueOf(manager.Delegate()).Elem().Type().PkgPath()
			if err := s.LinkGoldenFolder(manager.Delegate().Name(), pkgPath); err != nil {
				return errors.New("Couldn't link golden folder for " + manager.Delegate().Name() + ": " + err.Error())
			}
		}
	}

	return nil
}

func (s *StorageManager) LinkGoldenFolder(gameType, pkgPath string) error {
	//TODO: does this handle vendoring correctly?

	if s.goldenFolderName == "" {
		return nil
	}

	goPath := os.Getenv("GOPATH")

	if goPath == "" {
		return errors.New("Gopath wasn't set")
	}

	fullPkgPath := filepath.Join(goPath, "src", pkgPath)

	fullPath := filepath.Join(fullPkgPath, s.goldenFolderName)

	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
		log.Println("Creating " + fullPath)
		if err := os.Mkdir(fullPath, 0700); err != nil {
			return errors.New("Could not make golden path: " + err.Error())
		}
	}

	gamePath := filepath.Join(s.basePath, gameType)

	if _, err := os.Stat(gamePath); os.IsNotExist(err) {
		log.Println("Linking " + gamePath + " to " + fullPath)
		//Soft link from basePath.
		if err := os.Symlink(fullPath, gamePath); err != nil {
			return errors.New("Couldn't create symlink: " + err.Error())
		}
	}

	if err := s.SaveGoldenTest(gameType, fullPkgPath); err != nil {
		return errors.New("Couldn't store golden test: " + err.Error())
	}

	return nil

}

func (s *StorageManager) SaveGoldenTest(gameType, fullPkgPath string) error {

	buf := new(bytes.Buffer)

	//TODO: this presumes that the gametype is also the name of the package.
	//Either document that, or derive it automatically from source.
	err := goldenTestTemplate.Execute(buf, map[string]string{
		"gametype": gameType,
		"folder":   s.goldenFolderName,
	})

	if err != nil {
		return errors.New("Couldn't generate blob from template: " + err.Error())
	}

	return ioutil.WriteFile(filepath.Join(fullPkgPath, "golden_test.go"), buf.Bytes(), 0644)

}

func (s *StorageManager) WithManagers(managers []*boardgame.GameManager) {
	s.managers = managers
}

func (s *StorageManager) CleanUp() {
	os.RemoveAll(s.basePath)
}

//pathForId will look through each sub-folder and look for a file named
//gameId.json, returning its relative path if it is found, "" otherwise.
func pathForId(basePath, gameId string) string {

	if path, ok := idToPath[gameId]; ok {
		return path
	}

	items, err := ioutil.ReadDir(basePath)
	if err != nil {
		return ""
	}
	for _, item := range items {
		if item.IsDir() {
			if recursiveResult := pathForId(filepath.Join(basePath, item.Name()), gameId); recursiveResult != "" {
				return recursiveResult
			}
			continue
		}

		if item.Name() == gameId+".json" {
			result := filepath.Join(basePath, item.Name())
			idToPath[gameId] = result
			return result
		}
	}
	return ""
}

func (s *StorageManager) recordForId(gameId string) (*record.Record, error) {
	if s.basePath == "" {
		return nil, errors.New("No base path provided")
	}

	gameId = strings.ToLower(gameId)

	path := pathForId(s.basePath, gameId)

	if path == "" {
		return nil, errors.New("Couldn't find file matching: " + gameId)
	}

	return record.New(path)
}

func (s *StorageManager) saveRecordForId(gameId string, rec *record.Record) error {
	if s.basePath == "" {
		return errors.New("Invalid base path")
	}

	if rec.Game() == nil {
		return errors.New("Game record in rec was nil")
	}

	gameId = strings.ToLower(gameId)

	path := filepath.Join(s.basePath, rec.Game().Name, gameId+".json")

	dir, _ := filepath.Split(path)

	if err := os.MkdirAll(dir, 0700); err != nil {
		return errors.New("Couldn't create all necessary sub-paths: " + err.Error())
	}

	if err := rec.Save(path); err != nil {
		return err
	}

	idToPath[gameId] = path

	return nil
}

func (s *StorageManager) State(gameId string, version int) (boardgame.StateStorageRecord, error) {
	rec, err := s.recordForId(gameId)

	if err != nil {
		return nil, err
	}

	result, err := rec.State(version)

	if err != nil {
		return nil, err
	}

	return boardgame.StateStorageRecord(result), nil

}

func (s *StorageManager) Move(gameId string, version int) (*boardgame.MoveStorageRecord, error) {
	rec, err := s.recordForId(gameId)

	if err != nil {
		return nil, err
	}

	return rec.Move(version)
}

func (s *StorageManager) Moves(gameId string, fromVersion, toVersion int) ([]*boardgame.MoveStorageRecord, error) {
	return helpers.MovesHelper(s, gameId, fromVersion, toVersion)
}

func (s *StorageManager) Game(id string) (*boardgame.GameStorageRecord, error) {

	rec, err := s.recordForId(id)

	if err != nil {
		return nil, err
	}

	return rec.Game(), nil
}

func (s *StorageManager) SaveGameAndCurrentState(game *boardgame.GameStorageRecord, state boardgame.StateStorageRecord, move *boardgame.MoveStorageRecord) error {
	rec, err := s.recordForId(game.Id)

	if err != nil {
		//Must be the first save.
		rec = &record.Record{}
	}

	if err := rec.AddGameAndCurrentState(game, state, move); err != nil {
		return errors.New("Couldn't add state: " + err.Error())
	}

	return s.saveRecordForId(game.Id, rec)

}

func (s *StorageManager) CombinedGame(id string) (*extendedgame.CombinedStorageRecord, error) {
	rec, err := s.recordForId(id)

	if err != nil {
		return nil, err
	}

	eGame, err := s.ExtendedGame(id)

	if err != nil {
		return nil, err
	}

	return &extendedgame.CombinedStorageRecord{
		*rec.Game(),
		*eGame,
	}, nil
}

func idFromPath(path string) string {
	_, filename := filepath.Split(path)
	return strings.TrimSuffix(filename, ".json")
}

func (s *StorageManager) recursiveAllGames(basePath string) []*boardgame.GameStorageRecord {

	files, err := ioutil.ReadDir(basePath)

	if err != nil {
		return nil
	}

	var result []*boardgame.GameStorageRecord

	for _, file := range files {

		if file.IsDir() {
			result = append(result, s.recursiveAllGames(filepath.Join(basePath, file.Name()))...)
			continue
		}
		ext := filepath.Ext(file.Name())
		if ext != ".json" {
			continue
		}
		rec, err := s.recordForId(idFromPath(file.Name()))
		if err != nil {
			return nil
		}
		result = append(result, rec.Game())
	}
	return result
}

func (s *StorageManager) AllGames() []*boardgame.GameStorageRecord {
	return s.recursiveAllGames(s.basePath)
}

func (s *StorageManager) ListGames(max int, list listing.Type, userId string, gameType string) []*extendedgame.CombinedStorageRecord {
	return helpers.ListGamesHelper(s, max, list, userId, gameType)
}
<commit_msg>Updated some TODOs and documentation. Part of #648.<commit_after>/*

	filesystem is a storage layer that stores information about games as JSON
	files within a given folder, one per game. It's extremely inefficient and
	doesn't even persist extended game information to disk. It's most useful
	for cases where having an easy-to-read, diffable representation for games
	makes sense, for example to create golden tester games for use in testing.

	filesystem stores files according to their gametype in the given base
	folder, for example 'checkers/a22ffcdef.json'. If the sub-folders don't
	exist, they will be created. Folders may be soft-linked from within the
	base folder; often when using the filesystem storage layer to help
	generate test cases you set up soft-links from a central location to a
	folder for test files in each game's sub-directory, so the test files can
	be in the same place.

*/
package filesystem

import (
	"bytes"
	"errors"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/server/api/extendedgame"
	"github.com/jkomoros/boardgame/server/api/listing"
	"github.com/jkomoros/boardgame/storage/filesystem/record"
	"github.com/jkomoros/boardgame/storage/internal/helpers"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"strings"
)

type StorageManager struct {
	//Fall back on those methods
	*helpers.ExtendedMemoryStorageManager
	basePath         string
	goldenFolderName string
	managers         []*boardgame.GameManager
}

//Store seen ids and remember where the path was
var idToPath map[string]string

func init() {
	idToPath = make(map[string]string)
}

//NewStorageManager returns a new filesystem storage manager. basePath is the
//folder, relative to this executable, to have as the root of the storage
//pool. If goldenFolderName is not "", then we will use reflection to find the
//package path for each delegate, ensure a folder exists within it with that
//name, create a soft-link from basePath to that folder, and create a
//`golden_test.go` file that automatically tests all of those golden files.
//The result is that the underlying files will be stored in folders adjacent
//to the games they are relative to, which is convenient if you're adding new
//golden games to the test set.
func NewStorageManager(basePath string, goldenFolderName string) *StorageManager {

	result := &StorageManager{
		basePath:         basePath,
		goldenFolderName: goldenFolderName,
	}

	result.ExtendedMemoryStorageManager = helpers.NewExtendedMemoryStorageManager(result)

	return result
}

func (s *StorageManager) Name() string {
	return "filesystem"
}

func (s *StorageManager) Connect(config string) error {

	if _, err := os.Stat(s.basePath); os.IsNotExist(err) {
		if err := os.Mkdir(s.basePath, 0700); err != nil {
			return errors.New("Base path didn't exist and couldn't create it: " + err.Error())
		}
	}

	if s.goldenFolderName != "" {

		log.Println("Creating and linking golden folders")
		for _, manager := range s.managers {
			pkgPath := reflect.ValueOf(manager.Delegate()).Elem().Type().PkgPath()
			if err := s.LinkGoldenFolder(manager.Delegate().Name(), pkgPath); err != nil {
				return errors.New("Couldn't link golden folder for " + manager.Delegate().Name() + ": " + err.Error())
			}
		}
	}

	return nil
}

func (s *StorageManager) LinkGoldenFolder(gameType, pkgPath string) error {
	//TODO: does this handle vendoring correctly?

	if s.goldenFolderName == "" {
		return nil
	}

	goPath := os.Getenv("GOPATH")

	if goPath == "" {
		return errors.New("Gopath wasn't set")
	}

	fullPkgPath := filepath.Join(goPath, "src", pkgPath)

	fullPath := filepath.Join(fullPkgPath, s.goldenFolderName)

	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
		log.Println("Creating " + fullPath)
		if err := os.Mkdir(fullPath, 0700); err != nil {
			return errors.New("Could not make golden path: " + err.Error())
		}
	}

	gamePath := filepath.Join(s.basePath, gameType)

	if _, err := os.Stat(gamePath); os.IsNotExist(err) {
		log.Println("Linking " + gamePath + " to " + fullPath)
		//Soft link from basePath.
		if err := os.Symlink(fullPath, gamePath); err != nil {
			return errors.New("Couldn't create symlink: " + err.Error())
		}
	}

	//TODO: ok, this is getting weird that the filesystem storage layer is now
	//a full-fledged util.

	//TODO: allow this to be skipped as an otpion.
	if err := s.SaveGoldenTest(gameType, fullPkgPath); err != nil {
		return errors.New("Couldn't store golden test: " + err.Error())
	}

	return nil

}

func (s *StorageManager) SaveGoldenTest(gameType, fullPkgPath string) error {

	buf := new(bytes.Buffer)

	//TODO: this presumes that the gametype is also the name of the package.
	//Either document that, or derive it automatically from source.
	err := goldenTestTemplate.Execute(buf, map[string]string{
		"gametype": gameType,
		"folder":   s.goldenFolderName,
	})

	if err != nil {
		return errors.New("Couldn't generate blob from template: " + err.Error())
	}

	return ioutil.WriteFile(filepath.Join(fullPkgPath, "golden_test.go"), buf.Bytes(), 0644)

}

func (s *StorageManager) WithManagers(managers []*boardgame.GameManager) {
	s.managers = managers
}

func (s *StorageManager) CleanUp() {
	os.RemoveAll(s.basePath)
}

//pathForId will look through each sub-folder and look for a file named
//gameId.json, returning its relative path if it is found, "" otherwise.
func pathForId(basePath, gameId string) string {

	if path, ok := idToPath[gameId]; ok {
		return path
	}

	items, err := ioutil.ReadDir(basePath)
	if err != nil {
		return ""
	}
	for _, item := range items {
		if item.IsDir() {
			if recursiveResult := pathForId(filepath.Join(basePath, item.Name()), gameId); recursiveResult != "" {
				return recursiveResult
			}
			continue
		}

		if item.Name() == gameId+".json" {
			result := filepath.Join(basePath, item.Name())
			idToPath[gameId] = result
			return result
		}
	}
	return ""
}

func (s *StorageManager) recordForId(gameId string) (*record.Record, error) {
	if s.basePath == "" {
		return nil, errors.New("No base path provided")
	}

	gameId = strings.ToLower(gameId)

	path := pathForId(s.basePath, gameId)

	if path == "" {
		return nil, errors.New("Couldn't find file matching: " + gameId)
	}

	return record.New(path)
}

func (s *StorageManager) saveRecordForId(gameId string, rec *record.Record) error {
	if s.basePath == "" {
		return errors.New("Invalid base path")
	}

	if rec.Game() == nil {
		return errors.New("Game record in rec was nil")
	}

	gameId = strings.ToLower(gameId)

	path := filepath.Join(s.basePath, rec.Game().Name, gameId+".json")

	dir, _ := filepath.Split(path)

	if err := os.MkdirAll(dir, 0700); err != nil {
		return errors.New("Couldn't create all necessary sub-paths: " + err.Error())
	}

	if err := rec.Save(path); err != nil {
		return err
	}

	idToPath[gameId] = path

	return nil
}

func (s *StorageManager) State(gameId string, version int) (boardgame.StateStorageRecord, error) {
	rec, err := s.recordForId(gameId)

	if err != nil {
		return nil, err
	}

	result, err := rec.State(version)

	if err != nil {
		return nil, err
	}

	return boardgame.StateStorageRecord(result), nil

}

func (s *StorageManager) Move(gameId string, version int) (*boardgame.MoveStorageRecord, error) {
	rec, err := s.recordForId(gameId)

	if err != nil {
		return nil, err
	}

	return rec.Move(version)
}

func (s *StorageManager) Moves(gameId string, fromVersion, toVersion int) ([]*boardgame.MoveStorageRecord, error) {
	return helpers.MovesHelper(s, gameId, fromVersion, toVersion)
}

func (s *StorageManager) Game(id string) (*boardgame.GameStorageRecord, error) {

	rec, err := s.recordForId(id)

	if err != nil {
		return nil, err
	}

	return rec.Game(), nil
}

func (s *StorageManager) SaveGameAndCurrentState(game *boardgame.GameStorageRecord, state boardgame.StateStorageRecord, move *boardgame.MoveStorageRecord) error {
	rec, err := s.recordForId(game.Id)

	if err != nil {
		//Must be the first save.
		rec = &record.Record{}
	}

	if err := rec.AddGameAndCurrentState(game, state, move); err != nil {
		return errors.New("Couldn't add state: " + err.Error())
	}

	return s.saveRecordForId(game.Id, rec)

}

func (s *StorageManager) CombinedGame(id string) (*extendedgame.CombinedStorageRecord, error) {
	rec, err := s.recordForId(id)

	if err != nil {
		return nil, err
	}

	eGame, err := s.ExtendedGame(id)

	if err != nil {
		return nil, err
	}

	return &extendedgame.CombinedStorageRecord{
		*rec.Game(),
		*eGame,
	}, nil
}

func idFromPath(path string) string {
	_, filename := filepath.Split(path)
	return strings.TrimSuffix(filename, ".json")
}

func (s *StorageManager) recursiveAllGames(basePath string) []*boardgame.GameStorageRecord {

	files, err := ioutil.ReadDir(basePath)

	if err != nil {
		return nil
	}

	var result []*boardgame.GameStorageRecord

	for _, file := range files {

		if file.IsDir() {
			result = append(result, s.recursiveAllGames(filepath.Join(basePath, file.Name()))...)
			continue
		}
		ext := filepath.Ext(file.Name())
		if ext != ".json" {
			continue
		}
		rec, err := s.recordForId(idFromPath(file.Name()))
		if err != nil {
			return nil
		}
		result = append(result, rec.Game())
	}
	return result
}

func (s *StorageManager) AllGames() []*boardgame.GameStorageRecord {
	return s.recursiveAllGames(s.basePath)
}

func (s *StorageManager) ListGames(max int, list listing.Type, userId string, gameType string) []*extendedgame.CombinedStorageRecord {
	return helpers.ListGamesHelper(s, max, list, userId, gameType)
}
<|endoftext|>


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

// Handler for Docker containers.
package docker

import (
	"fmt"
	"io/ioutil"
	"math"
	"path"
	"strings"
	"time"

	"github.com/google/cadvisor/container"
	containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
	"github.com/google/cadvisor/fs"
	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/utils"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/opencontainers/runc/libcontainer/cgroups"
	cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
	libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
)

const (
	// The read write layers exist here.
	aufsRWLayer = "diff"
	// Path to the directory where docker stores log files if the json logging driver is enabled.
	pathToContainersDir = "containers"
)

type dockerContainerHandler struct {
	client             *docker.Client
	name               string
	id                 string
	aliases            []string
	machineInfoFactory info.MachineInfoFactory

	// Absolute path to the cgroup hierarchies of this container.
	// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
	cgroupPaths map[string]string

	// Manager of this container's cgroups.
	cgroupManager cgroups.Manager

	storageDriver    storageDriver
	fsInfo           fs.FsInfo
	rootfsStorageDir string

	// Time at which this container was created.
	creationTime time.Time

	// Metadata associated with the container.
	labels map[string]string
	envs   map[string]string

	// The container PID used to switch namespaces as required
	pid int

	// Image name used for this container.
	image string

	// The host root FS to read
	rootFs string

	// The network mode of the container
	networkMode string

	// Filesystem handler.
	fsHandler fsHandler

	ignoreMetrics container.MetricSet
}

func getRwLayerID(containerID, storageDir string, sd storageDriver, dockerVersion []int) (string, error) {
	const (
		// Docker version >=1.10.0 have a randomized ID for the root fs of a container.
		randomizedRWLayerMinorVersion = 10
		rwLayerIDFile                 = "mount-id"
	)
	if (dockerVersion[0] <= 1) && (dockerVersion[1] < randomizedRWLayerMinorVersion) {
		return containerID, nil
	}

	bytes, err := ioutil.ReadFile(path.Join(storageDir, "image", string(sd), "layerdb", "mounts", containerID, rwLayerIDFile))
	if err != nil {
		return "", fmt.Errorf("failed to identify the read-write layer ID for container %q. - %v", containerID, err)
	}
	return string(bytes), err
}

func newDockerContainerHandler(
	client *docker.Client,
	name string,
	machineInfoFactory info.MachineInfoFactory,
	fsInfo fs.FsInfo,
	storageDriver storageDriver,
	storageDir string,
	cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
	inHostNamespace bool,
	metadataEnvs []string,
	dockerVersion []int,
	ignoreMetrics container.MetricSet,
) (container.ContainerHandler, error) {
	// Create the cgroup paths.
	cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
	for key, val := range cgroupSubsystems.MountPoints {
		cgroupPaths[key] = path.Join(val, name)
	}

	// Generate the equivalent cgroup manager for this container.
	cgroupManager := &cgroupfs.Manager{
		Cgroups: &libcontainerconfigs.Cgroup{
			Name: name,
		},
		Paths: cgroupPaths,
	}

	rootFs := "/"
	if !inHostNamespace {
		rootFs = "/rootfs"
		storageDir = path.Join(rootFs, storageDir)
	}

	id := ContainerNameToDockerId(name)

	// Add the Containers dir where the log files are stored.
	// FIXME: Give `otherStorageDir` a more descriptive name.
	otherStorageDir := path.Join(storageDir, pathToContainersDir, id)

	rwLayerID, err := getRwLayerID(id, storageDir, storageDriver, dockerVersion)
	if err != nil {
		return nil, err
	}
	var rootfsStorageDir string
	switch storageDriver {
	case aufsStorageDriver:
		rootfsStorageDir = path.Join(storageDir, string(aufsStorageDriver), aufsRWLayer, rwLayerID)
	case overlayStorageDriver:
		rootfsStorageDir = path.Join(storageDir, string(overlayStorageDriver), rwLayerID)
	}

	handler := &dockerContainerHandler{
		id:                 id,
		client:             client,
		name:               name,
		machineInfoFactory: machineInfoFactory,
		cgroupPaths:        cgroupPaths,
		cgroupManager:      cgroupManager,
		storageDriver:      storageDriver,
		fsInfo:             fsInfo,
		rootFs:             rootFs,
		rootfsStorageDir:   rootfsStorageDir,
		envs:               make(map[string]string),
		ignoreMetrics:      ignoreMetrics,
	}

	if !ignoreMetrics.Has(container.DiskUsageMetrics) {
		handler.fsHandler = newFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo)
	}

	// We assume that if Inspect fails then the container is not known to docker.
	ctnr, err := client.InspectContainer(id)
	if err != nil {
		return nil, fmt.Errorf("failed to inspect container %q: %v", id, err)
	}
	handler.creationTime = ctnr.Created
	handler.pid = ctnr.State.Pid

	// Add the name and bare ID as aliases of the container.
	handler.aliases = append(handler.aliases, strings.TrimPrefix(ctnr.Name, "/"), id)
	handler.labels = ctnr.Config.Labels
	handler.image = ctnr.Config.Image
	handler.networkMode = ctnr.HostConfig.NetworkMode

	// split env vars to get metadata map.
	for _, exposedEnv := range metadataEnvs {
		for _, envVar := range ctnr.Config.Env {
			splits := strings.SplitN(envVar, "=", 2)
			if splits[0] == exposedEnv {
				handler.envs[strings.ToLower(exposedEnv)] = splits[1]
			}
		}
	}

	return handler, nil
}

func (self *dockerContainerHandler) Start() {
	// Start the filesystem handler.
	self.fsHandler.start()
}

func (self *dockerContainerHandler) Cleanup() {
	self.fsHandler.stop()
}

func (self *dockerContainerHandler) ContainerReference() (info.ContainerReference, error) {
	return info.ContainerReference{
		Id:        self.id,
		Name:      self.name,
		Aliases:   self.aliases,
		Namespace: DockerNamespace,
		Labels:    self.labels,
	}, nil
}

func (self *dockerContainerHandler) readLibcontainerConfig() (*libcontainerconfigs.Config, error) {
	config, err := containerlibcontainer.ReadConfig(*dockerRootDir, *dockerRunDir, self.id)
	if err != nil {
		return nil, fmt.Errorf("failed to read libcontainer config: %v", err)
	}

	// Replace cgroup parent and name with our own since we may be running in a different context.
	if config.Cgroups == nil {
		config.Cgroups = new(libcontainerconfigs.Cgroup)
	}
	config.Cgroups.Name = self.name
	config.Cgroups.Parent = "/"

	return config, nil
}

func libcontainerConfigToContainerSpec(config *libcontainerconfigs.Config, mi *info.MachineInfo) info.ContainerSpec {
	var spec info.ContainerSpec
	spec.HasMemory = true
	spec.Memory.Limit = math.MaxUint64
	spec.Memory.SwapLimit = math.MaxUint64

	if config.Cgroups.Resources != nil {
		if config.Cgroups.Resources.Memory > 0 {
			spec.Memory.Limit = uint64(config.Cgroups.Resources.Memory)
		}
		if config.Cgroups.Resources.MemorySwap > 0 {
			spec.Memory.SwapLimit = uint64(config.Cgroups.Resources.MemorySwap)
		}

		// Get CPU info
		spec.HasCpu = true
		spec.Cpu.Limit = 1024
		if config.Cgroups.Resources.CpuShares != 0 {
			spec.Cpu.Limit = uint64(config.Cgroups.Resources.CpuShares)
		}
		spec.Cpu.Mask = utils.FixCpuMask(config.Cgroups.Resources.CpusetCpus, mi.NumCores)
	}

	spec.HasDiskIo = true

	return spec
}

func (self *dockerContainerHandler) needNet() bool {
	if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) {
		return !strings.HasPrefix(self.networkMode, "container:")
	}
	return false
}

func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) {
	mi, err := self.machineInfoFactory.GetMachineInfo()
	if err != nil {
		return info.ContainerSpec{}, err
	}
	libcontainerConfig, err := self.readLibcontainerConfig()
	if err != nil {
		return info.ContainerSpec{}, err
	}

	spec := libcontainerConfigToContainerSpec(libcontainerConfig, mi)
	spec.CreationTime = self.creationTime

	if !self.ignoreMetrics.Has(container.DiskUsageMetrics) {
		switch self.storageDriver {
		case aufsStorageDriver, overlayStorageDriver, zfsStorageDriver:
			spec.HasFilesystem = true
		}
	}

	spec.Labels = self.labels
	spec.Envs = self.envs
	spec.Image = self.image
	spec.HasNetwork = self.needNet()

	return spec, err
}

func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error {
	if self.ignoreMetrics.Has(container.DiskUsageMetrics) {
		return nil
	}
	switch self.storageDriver {
	case aufsStorageDriver, overlayStorageDriver, zfsStorageDriver:
	default:
		return nil
	}

	deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir)
	if err != nil {
		return err
	}

	mi, err := self.machineInfoFactory.GetMachineInfo()
	if err != nil {
		return err
	}
	var (
		limit  uint64
		fsType string
	)

	// Docker does not impose any filesystem limits for containers. So use capacity as limit.
	for _, fs := range mi.Filesystems {
		if fs.Device == deviceInfo.Device {
			limit = fs.Capacity
			fsType = fs.Type
			break
		}
	}

	fsStat := info.FsStats{Device: deviceInfo.Device, Type: fsType, Limit: limit}

	fsStat.BaseUsage, fsStat.Usage = self.fsHandler.usage()
	stats.Filesystem = append(stats.Filesystem, fsStat)

	return nil
}

// TODO(vmarmol): Get from libcontainer API instead of cgroup manager when we don't have to support older Dockers.
func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) {
	stats, err := containerlibcontainer.GetStats(self.cgroupManager, self.rootFs, self.pid, self.ignoreMetrics)
	if err != nil {
		return stats, err
	}
	// Clean up stats for containers that don't have their own network - this
	// includes containers running in Kubernetes pods that use the network of the
	// infrastructure container. This stops metrics being reported multiple times
	// for each container in a pod.
	if !self.needNet() {
		stats.Network = info.NetworkStats{}
	}

	// Get filesystem stats.
	err = self.getFsStats(stats)
	if err != nil {
		return stats, err
	}

	return stats, nil
}

func (self *dockerContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
	// No-op for Docker driver.
	return []info.ContainerReference{}, nil
}

func (self *dockerContainerHandler) GetCgroupPath(resource string) (string, error) {
	path, ok := self.cgroupPaths[resource]
	if !ok {
		return "", fmt.Errorf("could not find path for resource %q for container %q\n", resource, self.name)
	}
	return path, nil
}

func (self *dockerContainerHandler) ListThreads(listType container.ListType) ([]int, error) {
	// TODO(vmarmol): Implement.
	return nil, nil
}

func (self *dockerContainerHandler) GetContainerLabels() map[string]string {
	return self.labels
}

func (self *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
	return containerlibcontainer.GetProcesses(self.cgroupManager)
}

func (self *dockerContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error {
	return fmt.Errorf("watch is unimplemented in the Docker container driver")
}

func (self *dockerContainerHandler) StopWatchingSubcontainers() error {
	// No-op for Docker driver.
	return nil
}

func (self *dockerContainerHandler) Exists() bool {
	return containerlibcontainer.Exists(*dockerRootDir, *dockerRunDir, self.id)
}

func DockerInfo() (map[string]string, error) {
	client, err := Client()
	if err != nil {
		return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
	}
	info, err := client.Info()
	if err != nil {
		return nil, err
	}
	return info.Map(), nil
}

func DockerImages() ([]docker.APIImages, error) {
	client, err := Client()
	if err != nil {
		return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
	}
	images, err := client.ListImages(docker.ListImagesOptions{All: false})
	if err != nil {
		return nil, err
	}
	return images, nil
}
<commit_msg>nil pointer check<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Handler for Docker containers.
package docker

import (
	"fmt"
	"io/ioutil"
	"math"
	"path"
	"strings"
	"time"

	"github.com/google/cadvisor/container"
	containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
	"github.com/google/cadvisor/fs"
	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/utils"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/opencontainers/runc/libcontainer/cgroups"
	cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
	libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
)

const (
	// The read write layers exist here.
	aufsRWLayer = "diff"
	// Path to the directory where docker stores log files if the json logging driver is enabled.
	pathToContainersDir = "containers"
)

type dockerContainerHandler struct {
	client             *docker.Client
	name               string
	id                 string
	aliases            []string
	machineInfoFactory info.MachineInfoFactory

	// Absolute path to the cgroup hierarchies of this container.
	// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
	cgroupPaths map[string]string

	// Manager of this container's cgroups.
	cgroupManager cgroups.Manager

	storageDriver    storageDriver
	fsInfo           fs.FsInfo
	rootfsStorageDir string

	// Time at which this container was created.
	creationTime time.Time

	// Metadata associated with the container.
	labels map[string]string
	envs   map[string]string

	// The container PID used to switch namespaces as required
	pid int

	// Image name used for this container.
	image string

	// The host root FS to read
	rootFs string

	// The network mode of the container
	networkMode string

	// Filesystem handler.
	fsHandler fsHandler

	ignoreMetrics container.MetricSet
}

func getRwLayerID(containerID, storageDir string, sd storageDriver, dockerVersion []int) (string, error) {
	const (
		// Docker version >=1.10.0 have a randomized ID for the root fs of a container.
		randomizedRWLayerMinorVersion = 10
		rwLayerIDFile                 = "mount-id"
	)
	if (dockerVersion[0] <= 1) && (dockerVersion[1] < randomizedRWLayerMinorVersion) {
		return containerID, nil
	}

	bytes, err := ioutil.ReadFile(path.Join(storageDir, "image", string(sd), "layerdb", "mounts", containerID, rwLayerIDFile))
	if err != nil {
		return "", fmt.Errorf("failed to identify the read-write layer ID for container %q. - %v", containerID, err)
	}
	return string(bytes), err
}

func newDockerContainerHandler(
	client *docker.Client,
	name string,
	machineInfoFactory info.MachineInfoFactory,
	fsInfo fs.FsInfo,
	storageDriver storageDriver,
	storageDir string,
	cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
	inHostNamespace bool,
	metadataEnvs []string,
	dockerVersion []int,
	ignoreMetrics container.MetricSet,
) (container.ContainerHandler, error) {
	// Create the cgroup paths.
	cgroupPaths := make(map[string]string, len(cgroupSubsystems.MountPoints))
	for key, val := range cgroupSubsystems.MountPoints {
		cgroupPaths[key] = path.Join(val, name)
	}

	// Generate the equivalent cgroup manager for this container.
	cgroupManager := &cgroupfs.Manager{
		Cgroups: &libcontainerconfigs.Cgroup{
			Name: name,
		},
		Paths: cgroupPaths,
	}

	rootFs := "/"
	if !inHostNamespace {
		rootFs = "/rootfs"
		storageDir = path.Join(rootFs, storageDir)
	}

	id := ContainerNameToDockerId(name)

	// Add the Containers dir where the log files are stored.
	// FIXME: Give `otherStorageDir` a more descriptive name.
	otherStorageDir := path.Join(storageDir, pathToContainersDir, id)

	rwLayerID, err := getRwLayerID(id, storageDir, storageDriver, dockerVersion)
	if err != nil {
		return nil, err
	}
	var rootfsStorageDir string
	switch storageDriver {
	case aufsStorageDriver:
		rootfsStorageDir = path.Join(storageDir, string(aufsStorageDriver), aufsRWLayer, rwLayerID)
	case overlayStorageDriver:
		rootfsStorageDir = path.Join(storageDir, string(overlayStorageDriver), rwLayerID)
	}

	handler := &dockerContainerHandler{
		id:                 id,
		client:             client,
		name:               name,
		machineInfoFactory: machineInfoFactory,
		cgroupPaths:        cgroupPaths,
		cgroupManager:      cgroupManager,
		storageDriver:      storageDriver,
		fsInfo:             fsInfo,
		rootFs:             rootFs,
		rootfsStorageDir:   rootfsStorageDir,
		envs:               make(map[string]string),
		ignoreMetrics:      ignoreMetrics,
	}

	if !ignoreMetrics.Has(container.DiskUsageMetrics) {
		handler.fsHandler = newFsHandler(time.Minute, rootfsStorageDir, otherStorageDir, fsInfo)
	}

	// We assume that if Inspect fails then the container is not known to docker.
	ctnr, err := client.InspectContainer(id)
	if err != nil {
		return nil, fmt.Errorf("failed to inspect container %q: %v", id, err)
	}
	handler.creationTime = ctnr.Created
	handler.pid = ctnr.State.Pid

	// Add the name and bare ID as aliases of the container.
	handler.aliases = append(handler.aliases, strings.TrimPrefix(ctnr.Name, "/"), id)
	handler.labels = ctnr.Config.Labels
	handler.image = ctnr.Config.Image
	handler.networkMode = ctnr.HostConfig.NetworkMode

	// split env vars to get metadata map.
	for _, exposedEnv := range metadataEnvs {
		for _, envVar := range ctnr.Config.Env {
			splits := strings.SplitN(envVar, "=", 2)
			if splits[0] == exposedEnv {
				handler.envs[strings.ToLower(exposedEnv)] = splits[1]
			}
		}
	}

	return handler, nil
}

func (self *dockerContainerHandler) Start() {
	// Start the filesystem handler.
	if self.fsHandler != nil {
		self.fsHandler.start()
	}
}

func (self *dockerContainerHandler) Cleanup() {
	if self.fsHandler != nil {
		self.fsHandler.stop()
	}
}

func (self *dockerContainerHandler) ContainerReference() (info.ContainerReference, error) {
	return info.ContainerReference{
		Id:        self.id,
		Name:      self.name,
		Aliases:   self.aliases,
		Namespace: DockerNamespace,
		Labels:    self.labels,
	}, nil
}

func (self *dockerContainerHandler) readLibcontainerConfig() (*libcontainerconfigs.Config, error) {
	config, err := containerlibcontainer.ReadConfig(*dockerRootDir, *dockerRunDir, self.id)
	if err != nil {
		return nil, fmt.Errorf("failed to read libcontainer config: %v", err)
	}

	// Replace cgroup parent and name with our own since we may be running in a different context.
	if config.Cgroups == nil {
		config.Cgroups = new(libcontainerconfigs.Cgroup)
	}
	config.Cgroups.Name = self.name
	config.Cgroups.Parent = "/"

	return config, nil
}

func libcontainerConfigToContainerSpec(config *libcontainerconfigs.Config, mi *info.MachineInfo) info.ContainerSpec {
	var spec info.ContainerSpec
	spec.HasMemory = true
	spec.Memory.Limit = math.MaxUint64
	spec.Memory.SwapLimit = math.MaxUint64

	if config.Cgroups.Resources != nil {
		if config.Cgroups.Resources.Memory > 0 {
			spec.Memory.Limit = uint64(config.Cgroups.Resources.Memory)
		}
		if config.Cgroups.Resources.MemorySwap > 0 {
			spec.Memory.SwapLimit = uint64(config.Cgroups.Resources.MemorySwap)
		}

		// Get CPU info
		spec.HasCpu = true
		spec.Cpu.Limit = 1024
		if config.Cgroups.Resources.CpuShares != 0 {
			spec.Cpu.Limit = uint64(config.Cgroups.Resources.CpuShares)
		}
		spec.Cpu.Mask = utils.FixCpuMask(config.Cgroups.Resources.CpusetCpus, mi.NumCores)
	}

	spec.HasDiskIo = true

	return spec
}

func (self *dockerContainerHandler) needNet() bool {
	if !self.ignoreMetrics.Has(container.NetworkUsageMetrics) {
		return !strings.HasPrefix(self.networkMode, "container:")
	}
	return false
}

func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) {
	mi, err := self.machineInfoFactory.GetMachineInfo()
	if err != nil {
		return info.ContainerSpec{}, err
	}
	libcontainerConfig, err := self.readLibcontainerConfig()
	if err != nil {
		return info.ContainerSpec{}, err
	}

	spec := libcontainerConfigToContainerSpec(libcontainerConfig, mi)
	spec.CreationTime = self.creationTime

	if !self.ignoreMetrics.Has(container.DiskUsageMetrics) {
		switch self.storageDriver {
		case aufsStorageDriver, overlayStorageDriver, zfsStorageDriver:
			spec.HasFilesystem = true
		}
	}

	spec.Labels = self.labels
	spec.Envs = self.envs
	spec.Image = self.image
	spec.HasNetwork = self.needNet()

	return spec, err
}

func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error {
	if self.ignoreMetrics.Has(container.DiskUsageMetrics) {
		return nil
	}
	switch self.storageDriver {
	case aufsStorageDriver, overlayStorageDriver, zfsStorageDriver:
	default:
		return nil
	}

	deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir)
	if err != nil {
		return err
	}

	mi, err := self.machineInfoFactory.GetMachineInfo()
	if err != nil {
		return err
	}
	var (
		limit  uint64
		fsType string
	)

	// Docker does not impose any filesystem limits for containers. So use capacity as limit.
	for _, fs := range mi.Filesystems {
		if fs.Device == deviceInfo.Device {
			limit = fs.Capacity
			fsType = fs.Type
			break
		}
	}

	fsStat := info.FsStats{Device: deviceInfo.Device, Type: fsType, Limit: limit}

	fsStat.BaseUsage, fsStat.Usage = self.fsHandler.usage()
	stats.Filesystem = append(stats.Filesystem, fsStat)

	return nil
}

// TODO(vmarmol): Get from libcontainer API instead of cgroup manager when we don't have to support older Dockers.
func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) {
	stats, err := containerlibcontainer.GetStats(self.cgroupManager, self.rootFs, self.pid, self.ignoreMetrics)
	if err != nil {
		return stats, err
	}
	// Clean up stats for containers that don't have their own network - this
	// includes containers running in Kubernetes pods that use the network of the
	// infrastructure container. This stops metrics being reported multiple times
	// for each container in a pod.
	if !self.needNet() {
		stats.Network = info.NetworkStats{}
	}

	// Get filesystem stats.
	err = self.getFsStats(stats)
	if err != nil {
		return stats, err
	}

	return stats, nil
}

func (self *dockerContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
	// No-op for Docker driver.
	return []info.ContainerReference{}, nil
}

func (self *dockerContainerHandler) GetCgroupPath(resource string) (string, error) {
	path, ok := self.cgroupPaths[resource]
	if !ok {
		return "", fmt.Errorf("could not find path for resource %q for container %q\n", resource, self.name)
	}
	return path, nil
}

func (self *dockerContainerHandler) ListThreads(listType container.ListType) ([]int, error) {
	// TODO(vmarmol): Implement.
	return nil, nil
}

func (self *dockerContainerHandler) GetContainerLabels() map[string]string {
	return self.labels
}

func (self *dockerContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
	return containerlibcontainer.GetProcesses(self.cgroupManager)
}

func (self *dockerContainerHandler) WatchSubcontainers(events chan container.SubcontainerEvent) error {
	return fmt.Errorf("watch is unimplemented in the Docker container driver")
}

func (self *dockerContainerHandler) StopWatchingSubcontainers() error {
	// No-op for Docker driver.
	return nil
}

func (self *dockerContainerHandler) Exists() bool {
	return containerlibcontainer.Exists(*dockerRootDir, *dockerRunDir, self.id)
}

func DockerInfo() (map[string]string, error) {
	client, err := Client()
	if err != nil {
		return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
	}
	info, err := client.Info()
	if err != nil {
		return nil, err
	}
	return info.Map(), nil
}

func DockerImages() ([]docker.APIImages, error) {
	client, err := Client()
	if err != nil {
		return nil, fmt.Errorf("unable to communicate with docker daemon: %v", err)
	}
	images, err := client.ListImages(docker.ListImagesOptions{All: false})
	if err != nil {
		return nil, err
	}
	return images, nil
}
<|endoftext|>


<commit_before>package commands

import (
	"path"

	log "github.com/Sirupsen/logrus"

	"github.com/AlexanderThaller/lablog/src/helper"

	"github.com/AlexanderThaller/cobra"
	"github.com/juju/errgo"
	"github.com/mitchellh/go-homedir"
)

var flagDataDir string

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

	homepath, err := homedir.Dir()
	helper.ErrExit(errgo.Notef(err, "can not get homepath"))

	datadir := path.Join(homepath, ".lablog")
	cmdMain.PersistentFlags().StringVarP(&flagDataDir, "datadir", "d",
		datadir, "The path to the datadir for retreiving and storing the data.")
}

var cmdMain = &cobra.Command{
	Use:   "lablog [command]",
	Short: "lablog makes taking notes and todos easy.",
	Long:  `lablog orders notes and todos into projects and subprojects without dictating a specific format.`,
}

func Run() {
	cmdMain.AddCommand(cmdVersion)

	// show
	cmdMain.AddCommand(cmdShow)
	cmdShow.AddCommand(cmdShowProjects)
	cmdShow.AddCommand(cmdShowNotes)
	cmdShow.AddCommand(cmdShowTodos)

	// add
	cmdMain.AddCommand(cmdAdd)
	cmdAdd.AddCommand(cmdAddNote)
	cmdAdd.AddCommand(cmdAddTodo)

	// todo
	cmdAddTodo.AddCommand(cmdAddTodoActive)
	cmdAddTodo.AddCommand(cmdAddTodoInActive)

	cmdMain.Execute()
}
<commit_msg>Added loglevel flag.<commit_after>package commands

import (
	"path"

	log "github.com/Sirupsen/logrus"

	"github.com/AlexanderThaller/lablog/src/helper"

	"github.com/AlexanderThaller/cobra"
	"github.com/juju/errgo"
	"github.com/mitchellh/go-homedir"
)

var flagDataDir string
var flagLogLevel string

func init() {
	homepath, err := homedir.Dir()
	helper.ErrExit(errgo.Notef(err, "can not get homepath"))

	datadir := path.Join(homepath, ".lablog")
	cmdMain.PersistentFlags().StringVarP(&flagDataDir, "datadir", "d",
		datadir, "The path to the datadir for retreiving and storing the data.")
	cmdMain.PersistentFlags().StringVarP(&flagLogLevel, "loglevel", "l",
		"warn", "The loglevel for which to run in. Default is warn. There are panic, fatal, error, warn info and debug as levels.")
}

var cmdMain = &cobra.Command{
	Use:              "lablog [command]",
	Short:            "lablog makes taking notes and todos easy.",
	Long:             `lablog orders notes and todos into projects and subprojects without dictating a specific format.`,
	PersistentPreRun: setLogLevel,
}

func Run() {
	cmdMain.AddCommand(cmdVersion)

	// show
	cmdMain.AddCommand(cmdShow)
	cmdShow.AddCommand(cmdShowProjects)
	cmdShow.AddCommand(cmdShowNotes)
	cmdShow.AddCommand(cmdShowTodos)

	// add
	cmdMain.AddCommand(cmdAdd)
	cmdAdd.AddCommand(cmdAddNote)
	cmdAdd.AddCommand(cmdAddTodo)

	// todo
	cmdAddTodo.AddCommand(cmdAddTodoActive)
	cmdAddTodo.AddCommand(cmdAddTodoInActive)

	cmdMain.Execute()
}

func setLogLevel(cmd *cobra.Command, args []string) {
	level, err := log.ParseLevel(flagLogLevel)
	if err != nil {
		log.Fatal(errgo.Notef(err, "can not parse loglevel from flag"))
	}

	log.SetLevel(level)
}
<|endoftext|>


<commit_before>package db

import (
  . "seeme/models"

  "net/http"
  "errors"

	"database/sql"
  _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func InitMySQLConnection() {
  db, _ = sql.Open("mysql", "root:#54nFr4nc15c0@/seeme_db")
}

func VerifyMySQLConnection(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  if err := db.Ping(); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  next(w, r)
}

func GetDatabaseInstance() (*sql.DB) {
	return db
}

func PostDeleteQuery(query string, params ...interface{}) (int64, error) {
  db := GetDatabaseInstance()
  qry, err := db.Prepare(query)
  if err != nil {
    return 0, errors.New("Prepare Update Error!")
  }

  res, err := qry.Exec(params...)
  if err != nil {
    return 0, errors.New("Update Query Error!")
  }

  affect, err := res.RowsAffected()
  if err != nil {
    return affect, errors.New("Internal Update Error!")
  }

  return affect, nil
}

func GetQueryUserList(query string, params ...interface{}) ([]User, error) {
  db := GetDatabaseInstance()
  var userList []User
  var user User

  rows, err := db.Query(query, params...)
  if err != nil {
    return []User{}, err
  }
  defer rows.Close()
  for rows.Next() {
    if err = rows.Scan(&user.FirstName, &user.LastName, &user.Role, &user.Username,
                        &user.Secret, &user.Discoverable, &user.Network); err != nil {
      return []User{}, err
    } else {
      userList = append(userList, user)
    }
  }
  if err = rows.Err(); err != nil {
    return []User{}, err
  }

  return userList, nil
}

func GetQueryResultsMap(query string, params ...interface{}) (map[string]bool, error) {
	db := GetDatabaseInstance()
	var data string
  dataMap := make(map[string]bool)

	rows, err := db.Query(query, params...)
  if err != nil {
    return dataMap, err
  }
  defer rows.Close()
  for rows.Next() {
    if err = rows.Scan(&data); err != nil {
      return dataMap, err
    } else {
      dataMap[data] = true
    }
  }
  if err = rows.Err(); err != nil {
    return dataMap, err
  }

  return dataMap, nil
}
<commit_msg>added sql.NullString fields for role and network to db_instance.GetQueryUserList<commit_after>package db

import (
  . "seeme/models"

  "net/http"
  "errors"

	"database/sql"
  _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func InitMySQLConnection() {
  db, _ = sql.Open("mysql", "root:#54nFr4nc15c0@/seeme_db")
}

func VerifyMySQLConnection(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  if err := db.Ping(); err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  next(w, r)
}

func GetDatabaseInstance() (*sql.DB) {
	return db
}

func PostDeleteQuery(query string, params ...interface{}) (int64, error) {
  db := GetDatabaseInstance()
  qry, err := db.Prepare(query)
  if err != nil {
    return 0, errors.New("Prepare Update Error!")
  }

  res, err := qry.Exec(params...)
  if err != nil {
    return 0, errors.New("Update Query Error!")
  }

  affect, err := res.RowsAffected()
  if err != nil {
    return affect, errors.New("Internal Update Error!")
  }

  return affect, nil
}

func GetQueryUserList(query string, params ...interface{}) ([]User, error) {
  db := GetDatabaseInstance()
  var userList []User
  var user User
  var network sql.NullString
  var role sql.NullString

  rows, err := db.Query(query, params...)
  if err != nil {
    return []User{}, err
  }
  defer rows.Close()
  for rows.Next() {
    if err = rows.Scan(&user.FirstName, &user.LastName, &role, &user.Username,
                        &user.Secret, &user.Discoverable, &network); err != nil {
      return []User{}, err
    } else {
      if role.Valid {
        if val, err := role.Value(); err == nil {
          user.Role = val.(string)
        }
      }
      if network.Valid {
        if val, err := network.Value(); err == nil {
          user.Network = val.(string)
        }
      }
      userList = append(userList, user)
    }
  }
  if err = rows.Err(); err != nil {
    return []User{}, err
  }

  return userList, nil
}

func GetQueryResultsMap(query string, params ...interface{}) (map[string]bool, error) {
	db := GetDatabaseInstance()
	var data string
  dataMap := make(map[string]bool)

	rows, err := db.Query(query, params...)
  if err != nil {
    return dataMap, err
  }
  defer rows.Close()
  for rows.Next() {
    if err = rows.Scan(&data); err != nil {
      return dataMap, err
    } else {
      dataMap[data] = true
    }
  }
  if err = rows.Err(); err != nil {
    return dataMap, err
  }

  return dataMap, nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"goji.io"
	"goji.io/pat"
	"io"
	"io/ioutil"
	"k8s.io/client-go/kubernetes"
	"net/http"
)

type Api struct {
	Clientset              kubernetes.Interface
	FasitUrl               string
	ClusterSubdomain       string
	ClusterName            string
	DeploymentStatusViewer DeploymentStatusViewer
}

type NaisDeploymentRequest struct {
	Application  string `json:"application"`
	Version      string `json:"version"`
	Environment  string `json:"environment"`
	Zone         string `json:"zone"`
	AppConfigUrl string `json:"appconfigurl,omitempty"`
	NoAppConfig  bool   `json:"-"`
	Username     string `json:"username"`
	Password     string `json:"password"`
	Namespace    string `json:"namespace"`
}

type AppError interface {
	error
	Code() int
}

type appError struct {
	OriginalError error
	Message       string
	StatusCode    int
}

func (e appError) Code() int {
	return e.StatusCode
}
func (e appError) Error() string {
	if e.OriginalError != nil {
		return fmt.Sprintf("%s: %s, (%d)", e.Message, e.OriginalError.Error(), e.StatusCode)
	}
	return fmt.Sprintf("%s: (%d)", e.Message, e.StatusCode)

}

type appHandler func(w http.ResponseWriter, r *http.Request) *appError

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if e := fn(w, r); e != nil { // e is *appError, not os.Error.
		glog.Errorf(e.Error())
		http.Error(w, e.Error(), e.StatusCode)
	}
}

var (
	requests = prometheus.NewCounterVec(
		prometheus.CounterOpts{Name: "requests", Help: "requests pr path"}, []string{"path"},
	)
	deploys = prometheus.NewCounterVec(
		prometheus.CounterOpts{Name: "deployments", Help: "deployments done by NaisD"}, []string{"nais_app"},
	)
)

func init() {
	prometheus.MustRegister(requests)
	prometheus.MustRegister(deploys)
}

func (api Api) Handler() http.Handler {
	mux := goji.NewMux()

	mux.Handle(pat.Get("/isalive"), appHandler(api.isAlive))
	mux.Handle(pat.Post("/deploy"), appHandler(api.deploy))
	mux.Handle(pat.Get("/metrics"), promhttp.Handler())
	mux.Handle(pat.Get("/deploystatus/:namespace/:deployName"), appHandler(api.deploymentStatusHandler))
	return mux
}

func NewApi(clientset kubernetes.Interface, fasitUrl, clusterDomain, clusterName string, d DeploymentStatusViewer) Api {
	return Api{
		Clientset:              clientset,
		FasitUrl:               fasitUrl,
		ClusterSubdomain:       clusterDomain,
		ClusterName:            clusterName,
		DeploymentStatusViewer: d,
	}
}

func (api Api) deploymentStatusHandler(w http.ResponseWriter, r *http.Request) *appError {
	namespace := pat.Param(r, "namespace")
	deployName := pat.Param(r, "deployName")

	status, view, err := api.DeploymentStatusViewer.DeploymentStatusView(namespace, deployName)

	if err != nil {
		return &appError{err, "Deployment not found ", http.StatusNotFound}
	}

	switch status {
	case InProgress:
		w.WriteHeader(http.StatusAccepted)
	case Failed:
		w.WriteHeader(http.StatusInternalServerError)
	case Success:
		w.WriteHeader(http.StatusOK)
	}

	if b, err := json.Marshal(view); err == nil {
		w.Write(b)
	} else {
		glog.Errorf("Unable to marshal deploy status view: %+v", view)
	}

	return nil
}

func (api Api) isAlive(w http.ResponseWriter, _ *http.Request) *appError {
	requests.With(prometheus.Labels{"path": "isAlive"}).Inc()
	fmt.Fprint(w, "")
	return nil
}

func validateFasitRequirements(fasit FasitClientAdapter, application, fasitEnvironment string) error {
	if _, err := fasit.GetFasitEnvironment(fasitEnvironment); err != nil {
		glog.Errorf("Environment '%s' does not exist in Fasit", fasitEnvironment)
		return err
	}
	if err := fasit.GetFasitApplication(application); err != nil {
		glog.Errorf("Application '%s' does not exist in Fasit", application)
		return err
	}

	return nil
}

func hasResources(appConfig NaisAppConfig) bool {
	if len(appConfig.FasitResources.Used) == 0 && len(appConfig.FasitResources.Exposed) == 0 {
		return false
	}
	return true
}
func (api Api) deploy(w http.ResponseWriter, r *http.Request) *appError {
	requests.With(prometheus.Labels{"path": "deploy"}).Inc()

	deploymentRequest, err := unmarshalDeploymentRequest(r.Body)
	if err != nil {
		return &appError{err, fmt.Sprintf("Unable to unmarshal deployment request: %s", err.Error()), http.StatusBadRequest}
	}

	fasit := FasitClient{api.FasitUrl, deploymentRequest.Username, deploymentRequest.Password}

	glog.Infof("Starting deployment. Deploying %s:%s to %s\n", deploymentRequest.Application, deploymentRequest.Version, deploymentRequest.Environment)

	appConfig, err := GenerateAppConfig(deploymentRequest)
	if err != nil {
		return &appError{err, fmt.Sprintf("Unable to fetch manifest: %s", err.Error()), http.StatusInternalServerError}
	}

	fasitEnvironment := fasit.environmentNameFromNamespaceBuilder(deploymentRequest.Namespace, api.ClusterName)
	var fasitEnvironmentClass string

	if hasResources(appConfig) {
		if err := validateFasitRequirements(fasit, deploymentRequest.Application, fasitEnvironment); err != nil {
			return &appError{err, "Validating requirements for deployment failed", http.StatusInternalServerError}
		}
		fasitEnvironmentClass, err = fasit.GetFasitEnvironment(fasitEnvironment)
	}

	glog.Infof("Starting deployment. Deploying %s:%s to %s\n", deploymentRequest.Application, deploymentRequest.Version, deploymentRequest.Environment)

	naisResources, err := fetchFasitResources(fasit, deploymentRequest, appConfig)
	if err != nil {
		return &appError{err, "Unable to fetch fasit resources", http.StatusBadRequest}
	}

	deploymentResult, err := createOrUpdateK8sResources(deploymentRequest, appConfig, naisResources, api.ClusterSubdomain, api.Clientset)
	if err != nil {
		return &appError{err, fmt.Sprintf("Failed while creating or updating k8s-resources: %s", err.Error()), http.StatusInternalServerError}
	}

	deploys.With(prometheus.Labels{"nais_app": deploymentRequest.Application}).Inc()

	ingressHostnames := deploymentResult.Ingress.Spec.Rules
	var ingressHostname string
	if len(ingressHostnames) > 0 {
		ingressHostname = ingressHostnames[len(ingressHostnames)-1].Host
	}

	if hasResources(appConfig) {
		if err := updateFasit(fasit, deploymentRequest, naisResources, appConfig, ingressHostname, fasitEnvironmentClass, api.ClusterName, api.ClusterSubdomain); err != nil {
			return &appError{err, "Failed while updating Fasit", http.StatusInternalServerError}
		}
	}

	w.WriteHeader(200)
	w.Write(createResponse(deploymentResult))
	return nil
}

func createResponse(deploymentResult DeploymentResult) []byte {

	response := "result: \n"

	if deploymentResult.Deployment != nil {
		response += "- created deployment\n"
	}
	if deploymentResult.Secret != nil {
		response += "- created secret\n"
	}
	if deploymentResult.Service != nil {
		response += "- created service\n"
	}
	if deploymentResult.Ingress != nil {
		response += "- created ingress\n"
	}
	if deploymentResult.Autoscaler != nil {
		response += "- created autoscaler\n"
	}

	return []byte(response)
}

func (r NaisDeploymentRequest) Validate() []error {
	required := map[string]*string{
		"Application": &r.Application,
		"Version":     &r.Version,
		"Environment": &r.Environment,
		"Zone":        &r.Zone,
		"Username":    &r.Username,
		"Password":    &r.Password,
		"Namespace":   &r.Namespace,
	}

	var errs []error
	for key, pointer := range required {
		if len(*pointer) == 0 {
			errs = append(errs, fmt.Errorf("%s is required and is empty", key))
		}
	}

	if r.Zone != "fss" && r.Zone != "sbs" && r.Zone != "iapp" {
		errs = append(errs, errors.New("Zone can only be fss, sbs or iapp"))
	}

	return errs
}

func unmarshalDeploymentRequest(body io.ReadCloser) (NaisDeploymentRequest, error) {
	requestBody, err := ioutil.ReadAll(body)
	if err != nil {
		return NaisDeploymentRequest{}, fmt.Errorf("Could not read deployment request body %s", err)
	}

	var deploymentRequest NaisDeploymentRequest
	if err = json.Unmarshal(requestBody, &deploymentRequest); err != nil {
		return NaisDeploymentRequest{}, fmt.Errorf("Could not unmarshal body %s", err)
	}

	return deploymentRequest, nil
}
<commit_msg>Sending correct fasit environment, rather than namespace to updateFasit<commit_after>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"goji.io"
	"goji.io/pat"
	"io"
	"io/ioutil"
	"k8s.io/client-go/kubernetes"
	"net/http"
)

type Api struct {
	Clientset              kubernetes.Interface
	FasitUrl               string
	ClusterSubdomain       string
	ClusterName            string
	DeploymentStatusViewer DeploymentStatusViewer
}

type NaisDeploymentRequest struct {
	Application  string `json:"application"`
	Version      string `json:"version"`
	Environment  string `json:"environment"`
	Zone         string `json:"zone"`
	AppConfigUrl string `json:"appconfigurl,omitempty"`
	NoAppConfig  bool   `json:"-"`
	Username     string `json:"username"`
	Password     string `json:"password"`
	Namespace    string `json:"namespace"`
}

type AppError interface {
	error
	Code() int
}

type appError struct {
	OriginalError error
	Message       string
	StatusCode    int
}

func (e appError) Code() int {
	return e.StatusCode
}
func (e appError) Error() string {
	if e.OriginalError != nil {
		return fmt.Sprintf("%s: %s, (%d)", e.Message, e.OriginalError.Error(), e.StatusCode)
	}
	return fmt.Sprintf("%s: (%d)", e.Message, e.StatusCode)

}

type appHandler func(w http.ResponseWriter, r *http.Request) *appError

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if e := fn(w, r); e != nil { // e is *appError, not os.Error.
		glog.Errorf(e.Error())
		http.Error(w, e.Error(), e.StatusCode)
	}
}

var (
	requests = prometheus.NewCounterVec(
		prometheus.CounterOpts{Name: "requests", Help: "requests pr path"}, []string{"path"},
	)
	deploys = prometheus.NewCounterVec(
		prometheus.CounterOpts{Name: "deployments", Help: "deployments done by NaisD"}, []string{"nais_app"},
	)
)

func init() {
	prometheus.MustRegister(requests)
	prometheus.MustRegister(deploys)
}

func (api Api) Handler() http.Handler {
	mux := goji.NewMux()

	mux.Handle(pat.Get("/isalive"), appHandler(api.isAlive))
	mux.Handle(pat.Post("/deploy"), appHandler(api.deploy))
	mux.Handle(pat.Get("/metrics"), promhttp.Handler())
	mux.Handle(pat.Get("/deploystatus/:namespace/:deployName"), appHandler(api.deploymentStatusHandler))
	return mux
}

func NewApi(clientset kubernetes.Interface, fasitUrl, clusterDomain, clusterName string, d DeploymentStatusViewer) Api {
	return Api{
		Clientset:              clientset,
		FasitUrl:               fasitUrl,
		ClusterSubdomain:       clusterDomain,
		ClusterName:            clusterName,
		DeploymentStatusViewer: d,
	}
}

func (api Api) deploymentStatusHandler(w http.ResponseWriter, r *http.Request) *appError {
	namespace := pat.Param(r, "namespace")
	deployName := pat.Param(r, "deployName")

	status, view, err := api.DeploymentStatusViewer.DeploymentStatusView(namespace, deployName)

	if err != nil {
		return &appError{err, "Deployment not found ", http.StatusNotFound}
	}

	switch status {
	case InProgress:
		w.WriteHeader(http.StatusAccepted)
	case Failed:
		w.WriteHeader(http.StatusInternalServerError)
	case Success:
		w.WriteHeader(http.StatusOK)
	}

	if b, err := json.Marshal(view); err == nil {
		w.Write(b)
	} else {
		glog.Errorf("Unable to marshal deploy status view: %+v", view)
	}

	return nil
}

func (api Api) isAlive(w http.ResponseWriter, _ *http.Request) *appError {
	requests.With(prometheus.Labels{"path": "isAlive"}).Inc()
	fmt.Fprint(w, "")
	return nil
}

func validateFasitRequirements(fasit FasitClientAdapter, application, fasitEnvironment string) error {
	if _, err := fasit.GetFasitEnvironment(fasitEnvironment); err != nil {
		glog.Errorf("Environment '%s' does not exist in Fasit", fasitEnvironment)
		return err
	}
	if err := fasit.GetFasitApplication(application); err != nil {
		glog.Errorf("Application '%s' does not exist in Fasit", application)
		return err
	}

	return nil
}

func hasResources(appConfig NaisAppConfig) bool {
	if len(appConfig.FasitResources.Used) == 0 && len(appConfig.FasitResources.Exposed) == 0 {
		return false
	}
	return true
}
func (api Api) deploy(w http.ResponseWriter, r *http.Request) *appError {
	requests.With(prometheus.Labels{"path": "deploy"}).Inc()

	deploymentRequest, err := unmarshalDeploymentRequest(r.Body)
	if err != nil {
		return &appError{err, fmt.Sprintf("Unable to unmarshal deployment request: %s", err.Error()), http.StatusBadRequest}
	}

	fasit := FasitClient{api.FasitUrl, deploymentRequest.Username, deploymentRequest.Password}

	glog.Infof("Starting deployment. Deploying %s:%s to %s\n", deploymentRequest.Application, deploymentRequest.Version, deploymentRequest.Environment)

	appConfig, err := GenerateAppConfig(deploymentRequest)
	if err != nil {
		return &appError{err, fmt.Sprintf("Unable to fetch manifest: %s", err.Error()), http.StatusInternalServerError}
	}

	fasitEnvironment := fasit.environmentNameFromNamespaceBuilder(deploymentRequest.Namespace, api.ClusterName)
	var fasitEnvironmentClass string

	if hasResources(appConfig) {
		if err := validateFasitRequirements(fasit, deploymentRequest.Application, fasitEnvironment); err != nil {
			return &appError{err, "Validating requirements for deployment failed", http.StatusInternalServerError}
		}
		fasitEnvironmentClass, err = fasit.GetFasitEnvironment(fasitEnvironment)
	}

	glog.Infof("Starting deployment. Deploying %s:%s to %s\n", deploymentRequest.Application, deploymentRequest.Version, deploymentRequest.Environment)

	naisResources, err := fetchFasitResources(fasit, deploymentRequest, appConfig)
	if err != nil {
		return &appError{err, "Unable to fetch fasit resources", http.StatusBadRequest}
	}

	deploymentResult, err := createOrUpdateK8sResources(deploymentRequest, appConfig, naisResources, api.ClusterSubdomain, api.Clientset)
	if err != nil {
		return &appError{err, fmt.Sprintf("Failed while creating or updating k8s-resources: %s", err.Error()), http.StatusInternalServerError}
	}

	deploys.With(prometheus.Labels{"nais_app": deploymentRequest.Application}).Inc()

	ingressHostnames := deploymentResult.Ingress.Spec.Rules
	var ingressHostname string
	if len(ingressHostnames) > 0 {
		ingressHostname = ingressHostnames[len(ingressHostnames)-1].Host
	}

	if hasResources(appConfig) {
		if err := updateFasit(fasit, deploymentRequest, naisResources, appConfig, ingressHostname, fasitEnvironmentClass, fasitEnvironment, api.ClusterSubdomain); err != nil {
			return &appError{err, "Failed while updating Fasit", http.StatusInternalServerError}
		}
	}

	w.WriteHeader(200)
	w.Write(createResponse(deploymentResult))
	return nil
}

func createResponse(deploymentResult DeploymentResult) []byte {

	response := "result: \n"

	if deploymentResult.Deployment != nil {
		response += "- created deployment\n"
	}
	if deploymentResult.Secret != nil {
		response += "- created secret\n"
	}
	if deploymentResult.Service != nil {
		response += "- created service\n"
	}
	if deploymentResult.Ingress != nil {
		response += "- created ingress\n"
	}
	if deploymentResult.Autoscaler != nil {
		response += "- created autoscaler\n"
	}

	return []byte(response)
}

func (r NaisDeploymentRequest) Validate() []error {
	required := map[string]*string{
		"Application": &r.Application,
		"Version":     &r.Version,
		"Environment": &r.Environment,
		"Zone":        &r.Zone,
		"Username":    &r.Username,
		"Password":    &r.Password,
		"Namespace":   &r.Namespace,
	}

	var errs []error
	for key, pointer := range required {
		if len(*pointer) == 0 {
			errs = append(errs, fmt.Errorf("%s is required and is empty", key))
		}
	}

	if r.Zone != "fss" && r.Zone != "sbs" && r.Zone != "iapp" {
		errs = append(errs, errors.New("Zone can only be fss, sbs or iapp"))
	}

	return errs
}

func unmarshalDeploymentRequest(body io.ReadCloser) (NaisDeploymentRequest, error) {
	requestBody, err := ioutil.ReadAll(body)
	if err != nil {
		return NaisDeploymentRequest{}, fmt.Errorf("Could not read deployment request body %s", err)
	}

	var deploymentRequest NaisDeploymentRequest
	if err = json.Unmarshal(requestBody, &deploymentRequest); err != nil {
		return NaisDeploymentRequest{}, fmt.Errorf("Could not unmarshal body %s", err)
	}

	return deploymentRequest, nil
}
<|endoftext|>


<commit_before>package model

import (
	"time"

	"gopkg.in/mgo.v2/bson"
)

const OfferCollectionName = "offers"

type (
	CommonOfferFields struct {
		ID          bson.ObjectId   `json:"_id,omitempty"        bson:"_id,omitempty"`
		Restaurant  OfferRestaurant `json:"restaurant"           bson:"restaurant"`
		Title       string          `json:"title"                bson:"title"`
		FromTime    time.Time       `json:"from_time"            bson:"from_time"`
		ToTime      time.Time       `json:"to_time"              bson:"to_time"`
		Ingredients []string        `json:"ingredients"          bson:"ingredients"`
		Price       float64         `json:"price"                bson:"price"`
		Tags        []string        `json:"tags"                 bson:"tags"`
		FBPostID    string          `json:"fb_post_id,omitempty" bson:"fb_post_id,omitempty"`
	}

	// Offer provides the mapping to the offers as represented in the DB
	Offer struct {
		CommonOfferFields `bson:",inline"`
		ImageChecksum     string `bson:"image_checksum,omitempty"`
	}

	// OfferJSON is the view of an offer that gets sent to the users
	OfferJSON struct {
		CommonOfferFields
		Image *OfferImagePaths `json:"image,omitempty"`
	}

	// OfferJSON is the view of an offer that gets sent to the users
	OfferPOST struct {
		CommonOfferFields
		ImageData string `json:"image_data,omitempty"`
	}

	// OfferImagePaths holds paths to the various sizes of the offer's image
	OfferImagePaths struct {
		Large     string `json:"large"     bson:"large"`
		Thumbnail string `json:"thumbnail" bson:"thumbnail"`
	}

	// OfferRestaurant holds the information about the restaurant that gets included
	// in every offer
	OfferRestaurant struct {
		Name     string   `json:"name"     bson:"name"`
		Region   string   `json:"region"   bson:"region"`
		Address  string   `json:"address"  bson:"address"`
		Location Location `json:"location" bson:"location"`
		Phone    string   `json:"phone"    bson:"phone"`
	}

	// OfferRestaurantWithDistance wraps an OfferRestaurant and adds a distance field.
	// This struct can be used to respond to queries about nearby offers.
	OfferRestaurantWithDistance struct {
		OfferRestaurant
		Distance float64 `json:"distance" bson:"distance"`
	}

	// OfferWithDistance wraps an offer and adds a distance field to the included
	// restaurant struct. This struct can be used to respond to queries about nearby offers.
	OfferWithDistance struct {
		Offer
		Restaurant OfferRestaurantWithDistance `json:"restaurant" bson:"restaurant"`
	}

	// OfferWithDistance wraps an offer's JSON representation and adds a distance field to the included
	// restaurant struct. This struct can be used to respond to queries about nearby offers.
	OfferWithDistanceJSON struct {
		OfferJSON
		Restaurant OfferRestaurantWithDistance `json:"restaurant" bson:"restaurant"`
	}
)

func MapOfferToJSON(offer *Offer, imageToPathMapper func(string) (*OfferImagePaths, error)) (*OfferJSON, error) {
	image, err := imageToPathMapper(offer.ImageChecksum)
	if err != nil {
		return nil, err
	}
	return &OfferJSON{
		CommonOfferFields: offer.CommonOfferFields,
		Image:             image,
	}, nil
}

func MapOfferPOSTToOffer(offer *OfferPOST, imageDataToChecksumMapper func(string) (string, error)) (*Offer, error) {
	imageChecksum, err := imageDataToChecksumMapper(offer.ImageData)
	if err != nil {
		return nil, err
	}
	return &Offer{
		CommonOfferFields: offer.CommonOfferFields,
		ImageChecksum:     imageChecksum,
	}, nil
}

func MapOfferWithDistanceToJSON(offer *OfferWithDistance, imageToPathMapper func(string) (*OfferImagePaths, error)) (*OfferWithDistanceJSON, error) {
	offerJSON, err := MapOfferToJSON(&offer.Offer, imageToPathMapper)
	if err != nil {
		return nil, err
	}
	return &OfferWithDistanceJSON{
		OfferJSON:  *offerJSON,
		Restaurant: offer.Restaurant,
	}, nil
}
<commit_msg>Remove unused struct tags<commit_after>package model

import (
	"time"

	"gopkg.in/mgo.v2/bson"
)

const OfferCollectionName = "offers"

type (
	CommonOfferFields struct {
		ID          bson.ObjectId   `json:"_id,omitempty"        bson:"_id,omitempty"`
		Restaurant  OfferRestaurant `json:"restaurant"           bson:"restaurant"`
		Title       string          `json:"title"                bson:"title"`
		FromTime    time.Time       `json:"from_time"            bson:"from_time"`
		ToTime      time.Time       `json:"to_time"              bson:"to_time"`
		Ingredients []string        `json:"ingredients"          bson:"ingredients"`
		Price       float64         `json:"price"                bson:"price"`
		Tags        []string        `json:"tags"                 bson:"tags"`
		FBPostID    string          `json:"fb_post_id,omitempty" bson:"fb_post_id,omitempty"`
	}

	// Offer provides the mapping to the offers as represented in the DB
	Offer struct {
		CommonOfferFields `bson:",inline"`
		ImageChecksum     string `bson:"image_checksum,omitempty"`
	}

	// OfferJSON is the view of an offer that gets sent to the users
	OfferJSON struct {
		CommonOfferFields
		Image *OfferImagePaths `json:"image,omitempty"`
	}

	// OfferJSON is the view of an offer that gets sent to the users
	OfferPOST struct {
		CommonOfferFields
		ImageData string `json:"image_data,omitempty"`
	}

	// OfferImagePaths holds paths to the various sizes of the offer's image
	OfferImagePaths struct {
		Large     string `json:"large"`
		Thumbnail string `json:"thumbnail"`
	}

	// OfferRestaurant holds the information about the restaurant that gets included
	// in every offer
	OfferRestaurant struct {
		Name     string   `json:"name"     bson:"name"`
		Region   string   `json:"region"   bson:"region"`
		Address  string   `json:"address"  bson:"address"`
		Location Location `json:"location" bson:"location"`
		Phone    string   `json:"phone"    bson:"phone"`
	}

	// OfferRestaurantWithDistance wraps an OfferRestaurant and adds a distance field.
	// This struct can be used to respond to queries about nearby offers.
	OfferRestaurantWithDistance struct {
		OfferRestaurant
		Distance float64 `json:"distance"`
	}

	// OfferWithDistance wraps an offer and adds a distance field to the included
	// restaurant struct. This struct can be used to respond to queries about nearby offers.
	OfferWithDistance struct {
		Offer
		Restaurant OfferRestaurantWithDistance `json:"restaurant"`
	}

	// OfferWithDistance wraps an offer's JSON representation and adds a distance field to the included
	// restaurant struct. This struct can be used to respond to queries about nearby offers.
	OfferWithDistanceJSON struct {
		OfferJSON
		Restaurant OfferRestaurantWithDistance `json:"restaurant"`
	}
)

func MapOfferToJSON(offer *Offer, imageToPathMapper func(string) (*OfferImagePaths, error)) (*OfferJSON, error) {
	image, err := imageToPathMapper(offer.ImageChecksum)
	if err != nil {
		return nil, err
	}
	return &OfferJSON{
		CommonOfferFields: offer.CommonOfferFields,
		Image:             image,
	}, nil
}

func MapOfferPOSTToOffer(offer *OfferPOST, imageDataToChecksumMapper func(string) (string, error)) (*Offer, error) {
	imageChecksum, err := imageDataToChecksumMapper(offer.ImageData)
	if err != nil {
		return nil, err
	}
	return &Offer{
		CommonOfferFields: offer.CommonOfferFields,
		ImageChecksum:     imageChecksum,
	}, nil
}

func MapOfferWithDistanceToJSON(offer *OfferWithDistance, imageToPathMapper func(string) (*OfferImagePaths, error)) (*OfferWithDistanceJSON, error) {
	offerJSON, err := MapOfferToJSON(&offer.Offer, imageToPathMapper)
	if err != nil {
		return nil, err
	}
	return &OfferWithDistanceJSON{
		OfferJSON:  *offerJSON,
		Restaurant: offer.Restaurant,
	}, nil
}
<|endoftext|>


<commit_before>package gogl

import (
	. "github.com/sdboyer/gocheck"
	"gopkg.in/fatih/set.v0"
)

// Tests for collection functors
type CollectionFunctorsSuite struct{}

var _ = Suite(&CollectionFunctorsSuite{})

func (s *CollectionFunctorsSuite) TestCollectVertices(c *C) {
	slice := CollectVertices(graphFixtures["3e5v1i"].(Graph))

	c.Assert(len(slice), Equals, 5)

	set := set.NewNonTS()
	for _, v := range slice {
		set.Add(v)
	}

	c.Assert(set.Has("foo"), Equals, true)
	c.Assert(set.Has("bar"), Equals, true)
	c.Assert(set.Has("baz"), Equals, true)
	c.Assert(set.Has("qux"), Equals, true)
	c.Assert(set.Has("isolate"), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectAdjacentVertices(c *C) {
	slice := CollectVerticesAdjacentTo("foo", graphFixtures["3e5v1i"].(Graph))

	c.Assert(len(slice), Equals, 2)

	set := set.NewNonTS()
	for _, v := range slice {
		set.Add(v)
	}

	c.Assert(set.Has("bar"), Equals, true)
	c.Assert(set.Has("qux"), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectEdges(c *C) {
	slice := CollectEdges(graphFixtures["3e5v1i"].(Graph))

	c.Assert(len(slice), Equals, 3)

	set := set.NewNonTS()
	for _, e := range slice {
		set.Add(e)
	}

	c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true)
	c.Assert(set.Has(NewEdge("bar", "baz")), Equals, true)
	c.Assert(set.Has(NewEdge("foo", "qux")), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectEdgesIncidentTo(c *C) {
	slice := CollectEdgesIncidentTo("foo", graphFixtures["3e5v1i"].(Graph))

	c.Assert(len(slice), Equals, 2)

	set := set.NewNonTS()
	for _, e := range slice {
		set.Add(e)
	}

	c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true)
	c.Assert(set.Has(NewEdge("foo", "qux")), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectArcsFrom(c *C) {
	digraph := G().Directed().Using(graphFixtures["arctest"]).Create(AdjacencyList).(Digraph)
	slice := CollectArcsFrom("foo", digraph)

	c.Assert(len(slice), Equals, 2)

	set := set.NewNonTS()
	for _, e := range slice {
		set.Add(e)
	}

	c.Assert(set.Has(NewEdge("foo", "qux")), Equals, true)
	c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectArcsTo(c *C) {
	digraph := G().Directed().Using(graphFixtures["arctest"]).Create(AdjacencyList).(Digraph)
	slice := CollectArcsTo("bar", digraph)

	c.Assert(len(slice), Equals, 2)

	set := set.NewNonTS()
	for _, e := range slice {
		set.Add(e)
	}

	c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true)
	c.Assert(set.Has(NewEdge("qux", "bar")), Equals, true)
}
<commit_msg>Add direct tests for Size() and Order().<commit_after>package gogl

import (
	. "github.com/sdboyer/gocheck"
	"gopkg.in/fatih/set.v0"
)

// Tests for collection functors
type CollectionFunctorsSuite struct{}

var _ = Suite(&CollectionFunctorsSuite{})

func (s *CollectionFunctorsSuite) TestCollectVertices(c *C) {
	slice := CollectVertices(graphFixtures["3e5v1i"].(Graph))

	c.Assert(len(slice), Equals, 5)

	set := set.NewNonTS()
	for _, v := range slice {
		set.Add(v)
	}

	c.Assert(set.Has("foo"), Equals, true)
	c.Assert(set.Has("bar"), Equals, true)
	c.Assert(set.Has("baz"), Equals, true)
	c.Assert(set.Has("qux"), Equals, true)
	c.Assert(set.Has("isolate"), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectAdjacentVertices(c *C) {
	slice := CollectVerticesAdjacentTo("foo", graphFixtures["3e5v1i"].(Graph))

	c.Assert(len(slice), Equals, 2)

	set := set.NewNonTS()
	for _, v := range slice {
		set.Add(v)
	}

	c.Assert(set.Has("bar"), Equals, true)
	c.Assert(set.Has("qux"), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectEdges(c *C) {
	slice := CollectEdges(graphFixtures["3e5v1i"].(Graph))

	c.Assert(len(slice), Equals, 3)

	set := set.NewNonTS()
	for _, e := range slice {
		set.Add(e)
	}

	c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true)
	c.Assert(set.Has(NewEdge("bar", "baz")), Equals, true)
	c.Assert(set.Has(NewEdge("foo", "qux")), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectEdgesIncidentTo(c *C) {
	slice := CollectEdgesIncidentTo("foo", graphFixtures["3e5v1i"].(Graph))

	c.Assert(len(slice), Equals, 2)

	set := set.NewNonTS()
	for _, e := range slice {
		set.Add(e)
	}

	c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true)
	c.Assert(set.Has(NewEdge("foo", "qux")), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectArcsFrom(c *C) {
	digraph := G().Directed().Using(graphFixtures["arctest"]).Create(AdjacencyList).(Digraph)
	slice := CollectArcsFrom("foo", digraph)

	c.Assert(len(slice), Equals, 2)

	set := set.NewNonTS()
	for _, e := range slice {
		set.Add(e)
	}

	c.Assert(set.Has(NewEdge("foo", "qux")), Equals, true)
	c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true)
}

func (s *CollectionFunctorsSuite) TestCollectArcsTo(c *C) {
	digraph := G().Directed().Using(graphFixtures["arctest"]).Create(AdjacencyList).(Digraph)
	slice := CollectArcsTo("bar", digraph)

	c.Assert(len(slice), Equals, 2)

	set := set.NewNonTS()
	for _, e := range slice {
		set.Add(e)
	}

	c.Assert(set.Has(NewEdge("foo", "bar")), Equals, true)
	c.Assert(set.Has(NewEdge("qux", "bar")), Equals, true)
}

type CountingFunctorsSuite struct{}

var _ = Suite(&CountingFunctorsSuite{})

func (s *CountingFunctorsSuite) TestOrder(c *C) {
	// TODO need a way to test both paths here
	order := Order(graphFixtures["3e5v1i"].(Graph))

	c.Assert(order, Equals, 5)
}

func (s *CountingFunctorsSuite) TestSize(c *C) {
	// TODO need a way to test both paths here
	size := Size(graphFixtures["3e5v1i"].(Graph))

	c.Assert(size, Equals, 3)
}
<|endoftext|>


<commit_before>package lang

import "fmt"

func Run(btc Bytecode) {
	env := makeEnvironment(nil)
	runBlob(env, btc)
}

type environment struct {
	parent *environment
	stack  []Object
	state  map[string]Object
}

func (e *environment) pushToStack(obj Object) {
	e.stack = append(e.stack, obj)
}

func (e *environment) popFromStack() Object {
	obj := e.stack[len(e.stack)-1]
	e.stack = e.stack[:len(e.stack)-1]
	return obj
}

func (e *environment) alloc(name string) {
	e.state[name] = ObjectNone{}
}

func (e *environment) store(name string, obj Object) {
	if _, ok := e.state[name]; ok {
		e.state[name] = obj
	} else {
		e.parent.store(name, obj)
	}
}

func (e *environment) load(name string) Object {
	if obj, ok := e.state[name]; ok {
		return obj
	} else {
		return e.parent.load(name)
	}
}

func makeEnvironment(parent *environment) *environment {
	return &environment{
		parent: parent,
		state:  make(map[string]Object),
	}
}

func runBlob(env *environment, blob Bytecode) Object {
	fmt.Println("---")
	var ip uint32 = 0
	instr := blob.Instructions[ip]
	for {
		fmt.Printf("%s %s\n", Address(ip), instr)
		if _, ok := instr.(InstrHalt); ok {
			fmt.Println("---")
			return nil
		} else if _, ok := instr.(InstrReturn); ok {
			fmt.Println("---")
			return env.popFromStack()
		}

		ip = runInstr(ip, env, instr)
		instr = blob.Instructions[ip]
	}
}

func runInstr(ip uint32, env *environment, instr Instr) uint32 {
	switch instr := instr.(type) {
	case InstrHalt:
		return ip
	case InstrNOP:
		// do nothing
	case InstrPush:
		env.pushToStack(instr.Val)
	case InstrPop:
		env.popFromStack()
	case InstrCopy:
		a := env.popFromStack()
		env.pushToStack(a)
		env.pushToStack(a)
	case InstrReserve:
		env.alloc(instr.Name)
	case InstrStore:
		a := env.popFromStack()
		env.store(instr.Name, a)
	case InstrLoadAttr:
		a := env.popFromStack()
		env.pushToStack(a.(ObjectStruct).Member(instr.Name))
	case InstrLoad:
		a := env.load(instr.Name)
		env.pushToStack(a)
	case InstrDispatch:
		obj := env.popFromStack()
		switch fn := obj.(type) {
		case ObjectFunction:
			child := makeEnvironment(env)
			for _, sym := range fn.params {
				child.alloc(sym)
				obj := env.popFromStack()
				child.store(sym, obj)
			}
			ret := runBlob(child, fn.bytecode)
			env.pushToStack(ret)
		case ObjectBuiltin:
			var args []Object
			for i := 0; i < instr.args; i++ {
				args = append(args, env.popFromStack())
			}
			if ret, err := fn.val(args); err != nil {
				panic(err)
			} else {
				env.pushToStack(ret)
			}
		default:
			panic(fmt.Sprintf("cannot call %T", obj))
		}
	case InstrAdd:
		b := env.popFromStack().(ObjectInt)
		a := env.popFromStack().(ObjectInt)
		sum := a.val + b.val
		env.pushToStack(&ObjectInt{sum})
	case InstrSub:
		b := env.popFromStack().(ObjectInt)
		a := env.popFromStack().(ObjectInt)
		sum := a.val - b.val
		env.pushToStack(&ObjectInt{sum})
	case InstrPrint:
		a := env.popFromStack()
		fmt.Println(a.String())
	default:
		panic(fmt.Sprintf("cannot interpret %T instructions", instr))
	}

	return ip + 1
}
<commit_msg>use pointers to objects<commit_after>package lang

import "fmt"

func Run(btc Bytecode) {
	env := makeEnvironment(nil)
	runBlob(env, btc)
}

type environment struct {
	parent *environment
	stack  []Object
	state  map[string]Object
}

func (e *environment) pushToStack(obj Object) {
	e.stack = append(e.stack, obj)
}

func (e *environment) popFromStack() Object {
	obj := e.stack[len(e.stack)-1]
	e.stack = e.stack[:len(e.stack)-1]
	return obj
}

func (e *environment) alloc(name string) {
	e.state[name] = ObjectNone{}
}

func (e *environment) store(name string, obj Object) {
	if _, ok := e.state[name]; ok {
		e.state[name] = obj
	} else {
		e.parent.store(name, obj)
	}
}

func (e *environment) load(name string) Object {
	if obj, ok := e.state[name]; ok {
		return obj
	} else {
		return e.parent.load(name)
	}
}

func makeEnvironment(parent *environment) *environment {
	return &environment{
		parent: parent,
		state:  make(map[string]Object),
	}
}

func runBlob(env *environment, blob Bytecode) Object {
	fmt.Println("---")
	var ip uint32 = 0
	instr := blob.Instructions[ip]
	for {
		fmt.Printf("%s %s\n", Address(ip), instr)
		if _, ok := instr.(InstrHalt); ok {
			fmt.Println("---")
			return nil
		} else if _, ok := instr.(InstrReturn); ok {
			fmt.Println("---")
			return env.popFromStack()
		}

		ip = runInstr(ip, env, instr)
		instr = blob.Instructions[ip]
	}
}

func runInstr(ip uint32, env *environment, instr Instr) uint32 {
	switch instr := instr.(type) {
	case InstrHalt:
		return ip
	case InstrNOP:
		// do nothing
	case InstrPush:
		env.pushToStack(instr.Val)
	case InstrPop:
		env.popFromStack()
	case InstrCopy:
		a := env.popFromStack()
		env.pushToStack(a)
		env.pushToStack(a)
	case InstrReserve:
		env.alloc(instr.Name)
	case InstrStore:
		a := env.popFromStack()
		env.store(instr.Name, a)
	case InstrLoadAttr:
		a := env.popFromStack()
		env.pushToStack(a.(ObjectStruct).Member(instr.Name))
	case InstrLoad:
		a := env.load(instr.Name)
		env.pushToStack(a)
	case InstrDispatch:
		obj := env.popFromStack()
		switch fn := obj.(type) {
		case ObjectFunction:
			child := makeEnvironment(env)
			for _, sym := range fn.params {
				child.alloc(sym)
				obj := env.popFromStack()
				child.store(sym, obj)
			}
			ret := runBlob(child, fn.bytecode)
			env.pushToStack(ret)
		case ObjectBuiltin:
			var args []Object
			for i := 0; i < instr.args; i++ {
				args = append(args, env.popFromStack())
			}
			if ret, err := fn.val(args); err != nil {
				panic(err)
			} else {
				env.pushToStack(ret)
			}
		default:
			panic(fmt.Sprintf("cannot call %T", obj))
		}
	case InstrAdd:
		b := env.popFromStack().(*ObjectInt)
		a := env.popFromStack().(*ObjectInt)
		sum := a.val + b.val
		env.pushToStack(&ObjectInt{sum})
	case InstrSub:
		b := env.popFromStack().(*ObjectInt)
		a := env.popFromStack().(*ObjectInt)
		sum := a.val - b.val
		env.pushToStack(&ObjectInt{sum})
	case InstrPrint:
		a := env.popFromStack()
		fmt.Println(a.String())
	default:
		panic(fmt.Sprintf("cannot interpret %T instructions", instr))
	}

	return ip + 1
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"

	"bytes"

	"github.com/davidlu1997/fast-shortener/config"
	"github.com/davidlu1997/fast-shortener/model"
	"github.com/davidlu1997/fast-shortener/shortener"
	"github.com/valyala/fasthttp"
)

type API struct {
	shortener shortener.Shortener
	config    *config.Configuration
}

func InitAPI(config *config.Configuration) *API {
	return &API{
		shortener: shortener.InitCacheShortener(config),
		config:    config,
	}
}

func (a *API) putLinkHandler(ctx *fasthttp.RequestCtx) {
	var link model.Link
	if err := json.Unmarshal(ctx.PostBody(), &link); err != nil {
		ctx.Error(err.Error(), fasthttp.StatusBadRequest)
	}

	if !link.IsValid(a.config) {
		ctx.Error("link not valid", fasthttp.StatusBadRequest)
		return
	}

	if err := a.shortener.Put(&link); err != nil {
		ctx.Error(err.Error(), fasthttp.StatusBadRequest)
		return
	}

	ctx.SetStatusCode(fasthttp.StatusOK)
}

func (a *API) getLinkHandler(ctx *fasthttp.RequestCtx) {
	path := ctx.Path()
	key := path[bytes.LastIndexByte(path, '/')+1:]
	link := a.shortener.Get(string(key))
	if link == nil {
		ctx.Error("", fasthttp.StatusNotFound)
		return
	}

	json, err := json.Marshal(link)
	if err != nil {
		ctx.Error(err.Error(), fasthttp.StatusBadGateway)
		return
	}

	ctx.SetBody(json)
	ctx.SetStatusCode(fasthttp.StatusOK)
}

func (a *API) okHandler(ctx *fasthttp.RequestCtx) {
	ctx.SetStatusCode(fasthttp.StatusOK)
}

func (a *API) RequestHandler(ctx *fasthttp.RequestCtx) {
	switch string(ctx.Path()) {
	case "/put":
		a.putLinkHandler(ctx)
	case "/ok":
		a.okHandler(ctx)
	default:
		a.getLinkHandler(ctx)
	}
}
<commit_msg>Check valid outside<commit_after>package api

import (
	"encoding/json"

	"bytes"

	"github.com/davidlu1997/fast-shortener/config"
	"github.com/davidlu1997/fast-shortener/model"
	"github.com/davidlu1997/fast-shortener/shortener"
	"github.com/valyala/fasthttp"
)

type API struct {
	shortener shortener.Shortener
	config    *config.Configuration
}

func InitAPI(config *config.Configuration) *API {
	return &API{
		shortener: shortener.InitCacheShortener(config),
		config:    config,
	}
}

func (a *API) putLinkHandler(ctx *fasthttp.RequestCtx) {
	var link model.Link
	if err := json.Unmarshal(ctx.PostBody(), &link); err != nil {
		ctx.Error(err.Error(), fasthttp.StatusBadRequest)
	}

	if err := a.shortener.Put(&link); err != nil {
		ctx.Error(err.Error(), fasthttp.StatusBadRequest)
		return
	}

	ctx.SetStatusCode(fasthttp.StatusOK)
}

func (a *API) getLinkHandler(ctx *fasthttp.RequestCtx) {
	path := ctx.Path()
	key := path[bytes.LastIndexByte(path, '/')+1:]
	link := a.shortener.Get(string(key))
	if link == nil {
		ctx.Error("", fasthttp.StatusNotFound)
		return
	}

	json, err := json.Marshal(link)
	if err != nil {
		ctx.Error(err.Error(), fasthttp.StatusBadGateway)
		return
	}

	ctx.SetBody(json)
	ctx.SetStatusCode(fasthttp.StatusOK)
}

func (a *API) okHandler(ctx *fasthttp.RequestCtx) {
	ctx.SetStatusCode(fasthttp.StatusOK)
}

func (a *API) RequestHandler(ctx *fasthttp.RequestCtx) {
	switch string(ctx.Path()) {
	case "/put":
		a.putLinkHandler(ctx)
	case "/ok":
		a.okHandler(ctx)
	default:
		a.getLinkHandler(ctx)
	}
}
<|endoftext|>


<commit_before>package logger

import (
	"os"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/pkgerrors"
)

// NewLogger sets up the zerolog.Logger
func NewLogger() zerolog.Logger {

	// write logs using Unix timestamps
	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix

	// set ErrorStackMarshaler to pkgerrors.MarshalStack
	// to enable error stack traces
	zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack

	// start a new logger with Stdout as the target
	lgr := zerolog.New(os.Stdout).With().Timestamp().Logger()

	// Add Severity Hook. Zerolog by default outputs structured logs
	// with "level":"error" as its leveling. Google Cloud as an
	// example expects "severity","ERROR" for its leveling. This
	// hook will add severity to each message
	lgr = lgr.Hook(GCPSeverityHook{})

	return lgr
}

// The GCPSeverityHook struct satisfies the zerolog.Hook interface
// as it has the Run method defined with the appropriate parameters
type GCPSeverityHook struct{}

// Run method satisfies zerolog.Hook interface and adds a severity
// level to all logs, given zerolog.Level passed in. Zerolog levels
// are mapped to log levels recognized by GCP
func (h GCPSeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {

	const lvlKey string = "severity"

	switch level {
	case zerolog.PanicLevel:
		e.Str(lvlKey, "EMERGENCY")
	case zerolog.FatalLevel:
		e.Str(lvlKey, "EMERGENCY")
	case zerolog.ErrorLevel:
		e.Str(lvlKey, "ERROR")
	case zerolog.WarnLevel:
		e.Str(lvlKey, "WARNING")
	case zerolog.InfoLevel:
		e.Str(lvlKey, "INFO")
	case zerolog.DebugLevel:
		e.Str(lvlKey, "DEBUG")
	case zerolog.TraceLevel:
		e.Str(lvlKey, "DEBUG")
	default:
		e.Str(lvlKey, "DEFAULT")
	}
}
<commit_msg>pull out globals from initializer<commit_after>package logger

import (
	"io"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/pkgerrors"
)

// NewLogger is a convenience function to initialize a zerolog.Logger
func NewLogger(w io.Writer, withTimestamp bool) zerolog.Logger {
	lgr := zerolog.New(w)
	if withTimestamp {
		lgr = lgr.With().Timestamp().Logger()
	}

	// Add Severity Hook. Zerolog by default outputs structured logs
	// with "level":"error" as its leveling. Google Cloud as an
	// example expects "severity","ERROR" for its leveling. This
	// hook will add severity to each message
	lgr = lgr.Hook(GCPSeverityHook{})

	return lgr
}

// The GCPSeverityHook struct satisfies the zerolog.Hook interface
// as it has the Run method defined with the appropriate parameters
type GCPSeverityHook struct{}

// Run method satisfies zerolog.Hook interface and adds a severity
// level to all logs, given zerolog.Level passed in. Zerolog levels
// are mapped to log levels recognized by GCP
func (h GCPSeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {

	const lvlKey string = "severity"

	switch level {
	case zerolog.PanicLevel:
		e.Str(lvlKey, "EMERGENCY")
	case zerolog.FatalLevel:
		e.Str(lvlKey, "EMERGENCY")
	case zerolog.ErrorLevel:
		e.Str(lvlKey, "ERROR")
	case zerolog.WarnLevel:
		e.Str(lvlKey, "WARNING")
	case zerolog.InfoLevel:
		e.Str(lvlKey, "INFO")
	case zerolog.DebugLevel:
		e.Str(lvlKey, "DEBUG")
	case zerolog.TraceLevel:
		e.Str(lvlKey, "DEBUG")
	default:
		e.Str(lvlKey, "DEFAULT")
	}
}

// WriteErrorStackGlobal is a convenience wrapper to set the zerolog
// Global variable ErrorStackMarshaler to write Error stacks for logs
func WriteErrorStackGlobal(i bool) {
	if !i {
		zerolog.ErrorStackMarshaler = nil
		return
	}
	// set ErrorStackMarshaler to pkgerrors.MarshalStack
	// to enable error stack traces
	zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
}
<|endoftext|>


<commit_before>package config

import (
	"os"

	"github.com/BurntSushi/toml"
	"github.com/njpatel/loggo"

	"utils"
)

//go:generate bash bake_default_config.sh
const defaultTomlConfig = `
# This is where top level info is stored for the counter manager we
# could also use a boltDB in the DataDir but this would make it harder
# to sync over replicas since not all replicas will hold the all the
# counters.
info_dir = "~/.skizze"

# This is where the data is stored either as json or .count (pure bytes)
data_dir = "~/.skizze/data"

# The host interface for the server
host = "localhost"

# The port number for the server
port = 3596

# Treshold for saving a sketch to disk
save_threshold_seconds = 1
`

var logger = loggo.GetLogger("config")

// Config stores all configuration parameters for Go
type Config struct {
	InfoDir              string `toml:"info_dir"`
	DataDir              string `toml:"data_dir"`
	Host                 string `toml:"host"`
	Port                 int    `toml:"port"`
	SaveThresholdSeconds uint   `toml:"save_threshold_seconds"`
}

var config *Config
var InfoDir              string
var DataDir              string
var Host                 string
var Port                 int
var SaveThresholdSeconds uint

// MaxKeySize ...
const MaxKeySize int = 32768 // max key size BoltDB in bytes

func parseConfigTOML() *Config {
	config = &Config{}
	if _, err := toml.Decode(defaultTomlConfig, &config); err != nil {
		utils.PanicOnError(err)
	}

	configPath := os.Getenv("SKIZZE_CONFIG")
	if configPath != "" {
		_, err := os.Open(configPath)
		if err != nil {
			logger.Warningf("Unable to find config file, using defaults")
			return config
		}
		if _, err := toml.DecodeFile(configPath, &config); err != nil {
			logger.Warningf("Error parsing config file, using defaults")
		}
	}

	return config
}

// GetConfig returns a singleton Configuration
func GetConfig() *Config {
	if config == nil {
		config = &parseConfigTOML()

		if err := os.MkdirAll(config.DataDir, os.ModePerm); err != nil {
			panic(err)
		}

		InfoDir = config.InfoDir
		DataDir = config.DataDir
		Host = config.Host
		Port = config.Port
		SaveThresholdSeconds = config.SaveThresholdSeconds
	}
	return config
}

// init initializes a singleton Configuration
func init() {
	GetConfig()
}

// Reset ...
func Reset() {
	GetConfig()
}
<commit_msg>don't write to global during parsing<commit_after>package config

import (
	"os"

	"github.com/BurntSushi/toml"
	"github.com/njpatel/loggo"

	"utils"
)

//go:generate bash bake_default_config.sh
const defaultTomlConfig = `
# This is where top level info is stored for the counter manager we
# could also use a boltDB in the DataDir but this would make it harder
# to sync over replicas since not all replicas will hold the all the
# counters.
info_dir = "~/.skizze"

# This is where the data is stored either as json or .count (pure bytes)
data_dir = "~/.skizze/data"

# The host interface for the server
host = "localhost"

# The port number for the server
port = 3596

# Treshold for saving a sketch to disk
save_threshold_seconds = 1
`

var logger = loggo.GetLogger("config")

// Config stores all configuration parameters for Go
type Config struct {
	InfoDir              string `toml:"info_dir"`
	DataDir              string `toml:"data_dir"`
	Host                 string `toml:"host"`
	Port                 int    `toml:"port"`
	SaveThresholdSeconds uint   `toml:"save_threshold_seconds"`
}

var config *Config
var InfoDir              string
var DataDir              string
var Host                 string
var Port                 int
var SaveThresholdSeconds uint

// MaxKeySize ...
const MaxKeySize int = 32768 // max key size BoltDB in bytes

func parseConfigTOML() *Config {
	cfg := &Config{}
	if _, err := toml.Decode(defaultTomlConfig, &cfg); err != nil {
		utils.PanicOnError(err)
	}

	configPath := os.Getenv("SKIZZE_CONFIG")
	if configPath != "" {
		_, err := os.Open(configPath)
		if err != nil {
			logger.Warningf("Unable to find config file, using defaults")
			return cfg
		}
		if _, err := toml.DecodeFile(configPath, &cfg); err != nil {
			logger.Warningf("Error parsing config file, using defaults")
		}
	}

	return cfg
}

// GetConfig returns a singleton Configuration
func GetConfig() *Config {
	if config == nil {
		config = parseConfigTOML()

		if err := os.MkdirAll(config.DataDir, os.ModePerm); err != nil {
			panic(err)
		}

		InfoDir = config.InfoDir
		DataDir = config.DataDir
		Host = config.Host
		Port = config.Port
		SaveThresholdSeconds = config.SaveThresholdSeconds
	}
	return config
}

// init initializes a singleton Configuration
func init() {
	GetConfig()
}

// Reset ...
func Reset() {
	GetConfig()
}
<|endoftext|>