<commit_before>package middleware

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/api"
	"github.com/Aptomi/aptomi/pkg/api/codec"
	"github.com/Aptomi/aptomi/pkg/runtime"
	log "github.com/Sirupsen/logrus"
	"net/http"
	"runtime/debug"
)

type panicHandler struct {
	handler     http.Handler
	contentType *codec.ContentTypeHandler
}

// NewPanicHandler returns HTTP handler for Panics processing
func NewPanicHandler(handler http.Handler) http.Handler {
	contentTypeHandler := codec.NewContentTypeHandler(runtime.NewRegistry().Append(api.Objects...))
	return &panicHandler{handler, contentTypeHandler}
}

func (h *panicHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			log.WithField("request", request).Errorf("Error while serving request: %s", err)

			if log.GetLevel() >= log.DebugLevel {
				log.Debug(string(debug.Stack()))
			}

			serverErr := api.NewServerError(fmt.Sprintf("%s", err))

			h.contentType.WriteOneWithStatus(writer, request, serverErr, http.StatusInternalServerError)
		}
	}()

	h.handler.ServeHTTP(writer, request)
}
<commit_msg>Use only needed objects registry in panic handler<commit_after>package middleware

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/api"
	"github.com/Aptomi/aptomi/pkg/api/codec"
	"github.com/Aptomi/aptomi/pkg/runtime"
	log "github.com/Sirupsen/logrus"
	"net/http"
	"runtime/debug"
)

type panicHandler struct {
	handler     http.Handler
	contentType *codec.ContentTypeHandler
}

// NewPanicHandler returns HTTP handler for Panics processing
func NewPanicHandler(handler http.Handler) http.Handler {
	contentTypeHandler := codec.NewContentTypeHandler(runtime.NewRegistry().Append(api.ServerErrorObject))
	return &panicHandler{handler, contentTypeHandler}
}

func (h *panicHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			log.WithField("request", request).Errorf("Error while serving request: %s", err)

			if log.GetLevel() >= log.DebugLevel {
				log.Debug(string(debug.Stack()))
			}

			serverErr := api.NewServerError(fmt.Sprintf("%s", err))

			h.contentType.WriteOneWithStatus(writer, request, serverErr, http.StatusInternalServerError)
		}
	}()

	h.handler.ServeHTTP(writer, request)
}
<|endoftext|>


<commit_before><commit_msg>Adjust for change in library.<commit_after><|endoftext|>


<commit_before>package filestorage

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/relab/raft"
	pb "github.com/relab/raft/raftpb"
)

// How events are persisted to stable storage.
const (
	STOREFILE     = "%d.storage"
	STORETERM     = "TERM,%d\n"
	STOREVOTEDFOR = "VOTED,%d\n"
	STORECOMMAND  = "%d,%d,%d,%s\n"
)

// ErrCorruptedFile is returned if data cannot be retrieved from a file.
var ErrCorruptedFile = errors.New("File is corrupted.")

// FileStorage implements Storage using a file.
type FileStorage struct {
	file       *os.File
	persistent *raft.Persistent
}

// New returns a empty FileStorage.
func New(filename string) (*FileStorage, error) {
	file, err := os.Create(filename)

	if err != nil {
		return nil, err
	}

	return &FileStorage{
		file: file,
		persistent: &raft.Persistent{
			Commands: make(map[raft.UniqueCommand]*pb.ClientCommandRequest, raft.BufferSize),
		},
	}, nil
}

// FromFile return a FileStorage initialized from a file.
func FromFile(filename string) (*FileStorage, error) {
	_, err := os.Stat(filename)

	if os.IsNotExist(err) {
		return nil, err
	}

	file, err := os.Open(filename)

	if err != nil {
		return nil, err
	}

	// Load data
	p, err := loadFromFile(file)

	// Open append only.
	file, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)

	return &FileStorage{
		file:       file,
		persistent: p,
	}, nil
}

// Load implements the Storage interface. Can currently only be called once.
func (fs *FileStorage) Load() raft.Persistent {
	p := *fs.persistent
	fs.persistent = nil

	return p
}

// SaveState implements the Storage interface.
func (fs *FileStorage) SaveState(term uint64, votedFor uint64) error {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf(STORETERM, term))
	buffer.WriteString(fmt.Sprintf(STOREVOTEDFOR, votedFor))

	return fs.write(buffer.Bytes())
}

// SaveEntries implements the SaveEntries interface.
func (fs *FileStorage) SaveEntries(entries []*pb.Entry) error {
	var buffer bytes.Buffer

	for _, entry := range entries {
		buffer.WriteString(fmt.Sprintf(STORECOMMAND, entry.Term, entry.Data.ClientID, entry.Data.SequenceNumber, entry.Data.Command))
	}

	return fs.write(buffer.Bytes())
}

func (fs *FileStorage) write(buffer []byte) error {
	n, err := fs.file.Write(buffer)

	if err != nil {
		return err
	}

	if n < len(buffer) {
		return io.ErrShortWrite
	}

	return fs.file.Sync()
}

func loadFromFile(file *os.File) (*raft.Persistent, error) {
	p := &raft.Persistent{
		Commands: make(map[raft.UniqueCommand]*pb.ClientCommandRequest, raft.BufferSize),
	}

	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		text := scanner.Text()
		split := strings.Split(text, ",")

		switch split[0] {
		case "TERM":
			term, err := strconv.Atoi(split[1])

			if err != nil {
				return nil, err
			}

			p.CurrentTerm = uint64(term)
		case "VOTED":
			votedFor, err := strconv.Atoi(split[1])

			if err != nil {
				return nil, err
			}

			p.VotedFor = uint64(votedFor)
		default:
			var entry pb.Entry

			if len(split) != 4 {
				return nil, ErrCorruptedFile
			}

			term, err := strconv.Atoi(split[0])

			if err != nil {
				return nil, err
			}

			clientID, err := strconv.Atoi(split[1])

			if err != nil {
				return nil, err
			}

			sequenceNumber, err := strconv.Atoi(split[2])

			if err != nil {
				return nil, err
			}

			command := split[3]

			entry.Term = uint64(term)
			entry.Data = &pb.ClientCommandRequest{ClientID: uint32(clientID), SequenceNumber: uint64(sequenceNumber), Command: command}

			p.Log = append(p.Log, &entry)
			p.Commands[raft.UniqueCommand{entry.Data.ClientID, entry.Data.SequenceNumber}] = entry.Data
		}
	}

	return p, scanner.Err()
}
<commit_msg>filestorage.go: Add field names<commit_after>package filestorage

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/relab/raft"
	pb "github.com/relab/raft/raftpb"
)

// How events are persisted to stable storage.
const (
	STOREFILE     = "%d.storage"
	STORETERM     = "TERM,%d\n"
	STOREVOTEDFOR = "VOTED,%d\n"
	STORECOMMAND  = "%d,%d,%d,%s\n"
)

// ErrCorruptedFile is returned if data cannot be retrieved from a file.
var ErrCorruptedFile = errors.New("File is corrupted.")

// FileStorage implements Storage using a file.
type FileStorage struct {
	file       *os.File
	persistent *raft.Persistent
}

// New returns a empty FileStorage.
func New(filename string) (*FileStorage, error) {
	file, err := os.Create(filename)

	if err != nil {
		return nil, err
	}

	return &FileStorage{
		file: file,
		persistent: &raft.Persistent{
			Commands: make(map[raft.UniqueCommand]*pb.ClientCommandRequest, raft.BufferSize),
		},
	}, nil
}

// FromFile return a FileStorage initialized from a file.
func FromFile(filename string) (*FileStorage, error) {
	_, err := os.Stat(filename)

	if os.IsNotExist(err) {
		return nil, err
	}

	file, err := os.Open(filename)

	if err != nil {
		return nil, err
	}

	// Load data
	p, err := loadFromFile(file)

	// Open append only.
	file, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)

	return &FileStorage{
		file:       file,
		persistent: p,
	}, nil
}

// Load implements the Storage interface. Can currently only be called once.
func (fs *FileStorage) Load() raft.Persistent {
	p := *fs.persistent
	fs.persistent = nil

	return p
}

// SaveState implements the Storage interface.
func (fs *FileStorage) SaveState(term uint64, votedFor uint64) error {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf(STORETERM, term))
	buffer.WriteString(fmt.Sprintf(STOREVOTEDFOR, votedFor))

	return fs.write(buffer.Bytes())
}

// SaveEntries implements the SaveEntries interface.
func (fs *FileStorage) SaveEntries(entries []*pb.Entry) error {
	var buffer bytes.Buffer

	for _, entry := range entries {
		buffer.WriteString(fmt.Sprintf(STORECOMMAND, entry.Term, entry.Data.ClientID, entry.Data.SequenceNumber, entry.Data.Command))
	}

	return fs.write(buffer.Bytes())
}

func (fs *FileStorage) write(buffer []byte) error {
	n, err := fs.file.Write(buffer)

	if err != nil {
		return err
	}

	if n < len(buffer) {
		return io.ErrShortWrite
	}

	return fs.file.Sync()
}

func loadFromFile(file *os.File) (*raft.Persistent, error) {
	p := &raft.Persistent{
		Commands: make(map[raft.UniqueCommand]*pb.ClientCommandRequest, raft.BufferSize),
	}

	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		text := scanner.Text()
		split := strings.Split(text, ",")

		switch split[0] {
		case "TERM":
			term, err := strconv.Atoi(split[1])

			if err != nil {
				return nil, err
			}

			p.CurrentTerm = uint64(term)
		case "VOTED":
			votedFor, err := strconv.Atoi(split[1])

			if err != nil {
				return nil, err
			}

			p.VotedFor = uint64(votedFor)
		default:
			var entry pb.Entry

			if len(split) != 4 {
				return nil, ErrCorruptedFile
			}

			term, err := strconv.Atoi(split[0])

			if err != nil {
				return nil, err
			}

			clientID, err := strconv.Atoi(split[1])

			if err != nil {
				return nil, err
			}

			sequenceNumber, err := strconv.Atoi(split[2])

			if err != nil {
				return nil, err
			}

			command := split[3]

			entry.Term = uint64(term)
			entry.Data = &pb.ClientCommandRequest{ClientID: uint32(clientID), SequenceNumber: uint64(sequenceNumber), Command: command}

			p.Log = append(p.Log, &entry)
			p.Commands[raft.UniqueCommand{
				ClientID:       entry.Data.ClientID,
				SequenceNumber: entry.Data.SequenceNumber,
			}] = entry.Data
		}
	}

	return p, scanner.Err()
}
<|endoftext|>


<commit_before>package docker

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"strings"

	yaml "gopkg.in/yaml.v2"

	"github.com/ViBiOh/dashboard/auth"
	"github.com/ViBiOh/httputils"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/network"
)

const minMemory = 16777216
const maxMemory = 805306368
const defaultTag = `:latest`
const deploySuffix = `_deploy`
const networkMode = `traefik`
const linkSeparator = `:`

var imageTag = regexp.MustCompile(`^\S*?:\S+$`)

type dockerComposeService struct {
	Image       string
	Command     []string
	Environment map[string]string
	Labels      map[string]string
	Links       []string
	Ports       []string
	ReadOnly    bool  `yaml:"read_only"`
	CPUShares   int64 `yaml:"cpu_shares"`
	MemoryLimit int64 `yaml:"mem_limit"`
}

type dockerCompose struct {
	Version  string
	Services map[string]dockerComposeService
}

type deployedService struct {
	ID   string
	Name string
}

func getConfig(service *dockerComposeService, user *auth.User, appName string) *container.Config {
	environments := make([]string, 0, len(service.Environment))
	for key, value := range service.Environment {
		environments = append(environments, key+`=`+value)
	}

	if service.Labels == nil {
		service.Labels = make(map[string]string)
	}

	service.Labels[ownerLabel] = user.Username
	service.Labels[appLabel] = appName

	config := container.Config{
		Image:  service.Image,
		Labels: service.Labels,
		Env:    environments,
	}

	if len(service.Command) != 0 {
		config.Cmd = service.Command
	}

	return &config
}

func getHostConfig(service *dockerComposeService) *container.HostConfig {
	hostConfig := container.HostConfig{
		LogConfig: container.LogConfig{Type: `json-file`, Config: map[string]string{
			`max-size`: `50m`,
		}},
		NetworkMode:   networkMode,
		RestartPolicy: container.RestartPolicy{Name: `on-failure`, MaximumRetryCount: 5},
		Resources: container.Resources{
			CPUShares: 128,
			Memory:    minMemory,
		},
		SecurityOpt: []string{`no-new-privileges`},
	}

	if service.ReadOnly {
		hostConfig.ReadonlyRootfs = true
	}

	if service.CPUShares != 0 {
		hostConfig.Resources.CPUShares = service.CPUShares
	}

	if service.MemoryLimit != 0 {
		if service.MemoryLimit <= maxMemory {
			hostConfig.Resources.Memory = service.MemoryLimit
		} else {
			hostConfig.Resources.Memory = maxMemory
		}
	}

	return &hostConfig
}

func getNetworkConfig(service *dockerComposeService, deployedServices map[string]*deployedService) *network.NetworkingConfig {
	traefikConfig := network.EndpointSettings{}

	for _, link := range service.Links {
		linkParts := strings.Split(link, linkSeparator)

		target := linkParts[0]
		if linkedService, ok := deployedServices[target]; ok {
			target = getFinalName(linkedService.Name)
		}

		alias := linkParts[0]
		if len(linkParts) > 1 {
			alias = linkParts[1]
		}

		traefikConfig.Links = append(traefikConfig.Links, target+linkSeparator+alias)
	}

	return &network.NetworkingConfig{
		EndpointsConfig: map[string]*network.EndpointSettings{
			networkMode: &traefikConfig,
		},
	}
}

func pullImage(image string, user *auth.User) error {
	if !imageTag.MatchString(image) {
		image = image + defaultTag
	}

	pull, err := docker.ImagePull(context.Background(), image, types.ImagePullOptions{})
	if err != nil {
		return fmt.Errorf(`Error while pulling image: %v`, err)
	}

	httputils.ReadBody(pull)
	return nil
}

func cleanContainers(containers []types.Container, user *auth.User) {
	for _, container := range containers {
		log.Printf(`[%s] Stopping containers %s`, user.Username, strings.Join(container.Names, `, `))
		stopContainer(container.ID)
	}

	for _, container := range containers {
		log.Printf(`[%s] Deleting containers %s`, user.Username, strings.Join(container.Names, `, `))
		rmContainer(container.ID)
	}
}

func renameDeployedContainers(containers map[string]*deployedService, user *auth.User) error {
	for service, container := range containers {
		if err := docker.ContainerRename(context.Background(), container.ID, getFinalName(container.Name)); err != nil {
			return fmt.Errorf(`Error while renaming container %s: %v`, service, err)
		}
	}

	return nil
}

func getServiceFullName(app string, service string) string {
	return app + `_` + service + deploySuffix
}

func getFinalName(serviceFullName string) string {
	return strings.TrimSuffix(serviceFullName, deploySuffix)
}

func deleteServices(appName []byte, services map[string]*deployedService, user *auth.User) {
	log.Printf(`[%s] Deleting services for %s`, user.Username, appName)
	for service, container := range services {
		if infos, err := inspectContainer(container.ID); err != nil {
			log.Printf(`[%s] Error while inspecting service %s for %s: %v`, user.Username, service, appName, err)
		} else if infos.State.Health != nil {
			logs := make([]string, 0)
			for _, log := range infos.State.Health.Log {
				logs = append(logs, log.Output)
			}

			log.Printf(`[%s] Healthcheck output for %s: %s`, user.Username, service, logs)
		}

		if err := stopContainer(container.ID); err != nil {
			log.Printf(`[%s] Error while stopping service %s for %s: %v`, user.Username, service, appName, err)
		}

		if err := rmContainer(container.ID); err != nil {
			log.Printf(`[%s] Error while deleting service %s for %s: %v`, user.Username, service, appName, err)
		}
	}
}

func startServices(appName []byte, services map[string]*deployedService, user *auth.User) error {
	for service, container := range services {
		if err := startContainer(container.ID); err != nil {
			return fmt.Errorf(`Error while starting service %s for %s: %v`, service, appName, err)
		}
	}

	return nil
}

func inspectServices(services map[string]*deployedService, user *auth.User) []*types.ContainerJSON {
	containers := make([]*types.ContainerJSON, 0, len(services))

	for service, container := range services {
		infos, err := inspectContainer(container.ID)
		if err != nil {
			log.Printf(`[%s] Error while inspecting container %s: %v`, user.Username, service, err)
		}

		containers = append(containers, &infos)
	}

	return containers
}

func areContainersHealthy(ctx context.Context, user *auth.User, appName []byte, containers []*types.ContainerJSON) bool {
	containersIdsWithHealthcheck := make([]string, 0, len(containers))
	for _, container := range containers {
		if container.Config.Healthcheck != nil && len(container.Config.Healthcheck.Test) != 0 {
			containersIdsWithHealthcheck = append(containersIdsWithHealthcheck, container.ID)
		}
	}

	if len(containersIdsWithHealthcheck) == 0 {
		return true
	}

	filtersArgs := filters.NewArgs()
	healthyStatusFilters(&filtersArgs, containersIdsWithHealthcheck)

	timeoutCtx, cancel := context.WithTimeout(ctx, DeployTimeout)
	defer cancel()

	messages, errors := docker.Events(timeoutCtx, types.EventsOptions{Filters: filtersArgs})
	healthyContainers := make(map[string]bool, len(containersIdsWithHealthcheck))

	for {
		select {
		case <-ctx.Done():
			return false
		case message := <-messages:
			healthyContainers[message.ID] = true
			log.Printf(`[%s] Container %s for %s is healthy`, user.Username, appName, message.From)

			if len(healthyContainers) == len(containersIdsWithHealthcheck) {
				return true
			}
		case err := <-errors:
			log.Printf(`[%s] Error while reading healthy events: %v`, user.Username, err)
			return false
		}
	}
}

func finishDeploy(ctx context.Context, cancel context.CancelFunc, user *auth.User, appName []byte, services map[string]*deployedService, oldContainers []types.Container) {
	defer cancel()
	defer func() {
		backgroundMutex.Lock()
		defer backgroundMutex.Unlock()

		delete(backgroundTasks, string(appName))
	}()

	if areContainersHealthy(ctx, user, appName, inspectServices(services, user)) {
		cleanContainers(oldContainers, user)

		if err := renameDeployedContainers(services, user); err != nil {
			log.Print(err)
		}
		log.Printf(`[%s] Succeeded to deploy %s`, user.Username, appName)
	} else {
		deleteServices(appName, services, user)
		log.Printf(`[%s] Failed to deploy %s: %v`, user.Username, appName, fmt.Errorf(`[Health check failed`))
	}
}

func createContainer(user *auth.User, appName []byte, serviceName string, services map[string]*deployedService, service *dockerComposeService) (*deployedService, error) {
	if err := pullImage(service.Image, user); err != nil {
		return nil, err
	}

	serviceFullName := getServiceFullName(string(appName), serviceName)

	createdContainer, err := docker.ContainerCreate(context.Background(), getConfig(service, user, string(appName)), getHostConfig(service), getNetworkConfig(service, services), serviceFullName)
	if err != nil {
		return nil, fmt.Errorf(`Error while creating service %s for %s: %v`, serviceName, appName, err)
	}

	return &deployedService{ID: createdContainer.ID, Name: serviceFullName}, nil
}

func composeFailed(w http.ResponseWriter, user *auth.User, appName []byte, err error) {
	httputils.InternalServer(w, fmt.Errorf(`[%s] Failed to deploy %s: %v`, user.Username, appName, err))
}

func composeHandler(w http.ResponseWriter, user *auth.User, appName []byte, composeFile []byte) {
	if user == nil {
		httputils.BadRequest(w, fmt.Errorf(`A user is required`))
		return
	}

	if len(appName) == 0 || len(composeFile) == 0 {
		httputils.BadRequest(w, fmt.Errorf(`[%s] An application name and a compose file are required`, user.Username))
		return
	}

	compose := dockerCompose{}
	if err := yaml.Unmarshal(composeFile, &compose); err != nil {
		httputils.BadRequest(w, fmt.Errorf(`[%s] Error while unmarshalling compose file: %v`, user.Username, err))
		return
	}

	appNameStr := string(appName)

	backgroundMutex.Lock()
	if _, ok := backgroundTasks[appNameStr]; ok {
		backgroundMutex.Unlock()
		composeFailed(w, user, appName, fmt.Errorf(`Application already in deployment`))
		return
	}

	backgroundTasks[appNameStr] = true
	backgroundMutex.Unlock()

	oldContainers, err := listContainers(user, appNameStr)
	if err != nil {
		composeFailed(w, user, appName, err)
		return
	}

	if len(oldContainers) > 0 && oldContainers[0].Labels[ownerLabel] != user.Username {
		composeFailed(w, user, appName, fmt.Errorf(`Application not owned`))
		httputils.Forbidden(w)
	}

	newServices := make(map[string]*deployedService)
	for serviceName, service := range compose.Services {
		if deployedService, err := createContainer(user, appName, serviceName, newServices, &service); err != nil {
			break
		} else {
			newServices[serviceName] = deployedService
		}
	}

	ctx, cancel := context.WithCancel(context.Background())
	go finishDeploy(ctx, cancel, user, appName, newServices, oldContainers)

	if err == nil {
		err = startServices(appName, newServices, user)
	}

	if err != nil {
		cancel()
		composeFailed(w, user, appName, err)
	} else {
		httputils.ResponseArrayJSON(w, newServices)
	}
}
<commit_msg>Adding carriage return<commit_after>package docker

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"strings"

	yaml "gopkg.in/yaml.v2"

	"github.com/ViBiOh/dashboard/auth"
	"github.com/ViBiOh/httputils"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/network"
)

const minMemory = 16777216
const maxMemory = 805306368
const defaultTag = `:latest`
const deploySuffix = `_deploy`
const networkMode = `traefik`
const linkSeparator = `:`

var imageTag = regexp.MustCompile(`^\S*?:\S+$`)

type dockerComposeService struct {
	Image       string
	Command     []string
	Environment map[string]string
	Labels      map[string]string
	Links       []string
	Ports       []string
	ReadOnly    bool  `yaml:"read_only"`
	CPUShares   int64 `yaml:"cpu_shares"`
	MemoryLimit int64 `yaml:"mem_limit"`
}

type dockerCompose struct {
	Version  string
	Services map[string]dockerComposeService
}

type deployedService struct {
	ID   string
	Name string
}

func getConfig(service *dockerComposeService, user *auth.User, appName string) *container.Config {
	environments := make([]string, 0, len(service.Environment))
	for key, value := range service.Environment {
		environments = append(environments, key+`=`+value)
	}

	if service.Labels == nil {
		service.Labels = make(map[string]string)
	}

	service.Labels[ownerLabel] = user.Username
	service.Labels[appLabel] = appName

	config := container.Config{
		Image:  service.Image,
		Labels: service.Labels,
		Env:    environments,
	}

	if len(service.Command) != 0 {
		config.Cmd = service.Command
	}

	return &config
}

func getHostConfig(service *dockerComposeService) *container.HostConfig {
	hostConfig := container.HostConfig{
		LogConfig: container.LogConfig{Type: `json-file`, Config: map[string]string{
			`max-size`: `50m`,
		}},
		NetworkMode:   networkMode,
		RestartPolicy: container.RestartPolicy{Name: `on-failure`, MaximumRetryCount: 5},
		Resources: container.Resources{
			CPUShares: 128,
			Memory:    minMemory,
		},
		SecurityOpt: []string{`no-new-privileges`},
	}

	if service.ReadOnly {
		hostConfig.ReadonlyRootfs = true
	}

	if service.CPUShares != 0 {
		hostConfig.Resources.CPUShares = service.CPUShares
	}

	if service.MemoryLimit != 0 {
		if service.MemoryLimit <= maxMemory {
			hostConfig.Resources.Memory = service.MemoryLimit
		} else {
			hostConfig.Resources.Memory = maxMemory
		}
	}

	return &hostConfig
}

func getNetworkConfig(service *dockerComposeService, deployedServices map[string]*deployedService) *network.NetworkingConfig {
	traefikConfig := network.EndpointSettings{}

	for _, link := range service.Links {
		linkParts := strings.Split(link, linkSeparator)

		target := linkParts[0]
		if linkedService, ok := deployedServices[target]; ok {
			target = getFinalName(linkedService.Name)
		}

		alias := linkParts[0]
		if len(linkParts) > 1 {
			alias = linkParts[1]
		}

		traefikConfig.Links = append(traefikConfig.Links, target+linkSeparator+alias)
	}

	return &network.NetworkingConfig{
		EndpointsConfig: map[string]*network.EndpointSettings{
			networkMode: &traefikConfig,
		},
	}
}

func pullImage(image string, user *auth.User) error {
	if !imageTag.MatchString(image) {
		image = image + defaultTag
	}

	pull, err := docker.ImagePull(context.Background(), image, types.ImagePullOptions{})
	if err != nil {
		return fmt.Errorf(`Error while pulling image: %v`, err)
	}

	httputils.ReadBody(pull)
	return nil
}

func cleanContainers(containers []types.Container, user *auth.User) {
	for _, container := range containers {
		log.Printf(`[%s] Stopping containers %s`, user.Username, strings.Join(container.Names, `, `))
		stopContainer(container.ID)
	}

	for _, container := range containers {
		log.Printf(`[%s] Deleting containers %s`, user.Username, strings.Join(container.Names, `, `))
		rmContainer(container.ID)
	}
}

func renameDeployedContainers(containers map[string]*deployedService, user *auth.User) error {
	for service, container := range containers {
		if err := docker.ContainerRename(context.Background(), container.ID, getFinalName(container.Name)); err != nil {
			return fmt.Errorf(`Error while renaming container %s: %v`, service, err)
		}
	}

	return nil
}

func getServiceFullName(app string, service string) string {
	return app + `_` + service + deploySuffix
}

func getFinalName(serviceFullName string) string {
	return strings.TrimSuffix(serviceFullName, deploySuffix)
}

func deleteServices(appName []byte, services map[string]*deployedService, user *auth.User) {
	log.Printf(`[%s] Deleting services for %s`, user.Username, appName)
	for service, container := range services {
		if infos, err := inspectContainer(container.ID); err != nil {
			log.Printf(`[%s] Error while inspecting service %s for %s: %v`, user.Username, service, appName, err)
		} else if infos.State.Health != nil {
			logs := make([]string, 0)

			logs = append(logs, "\n")
			for _, log := range infos.State.Health.Log {
				logs = append(logs, log.Output)
			}

			log.Printf(`[%s] Healthcheck output for %s: %s`, user.Username, service, logs)
		}

		if err := stopContainer(container.ID); err != nil {
			log.Printf(`[%s] Error while stopping service %s for %s: %v`, user.Username, service, appName, err)
		}

		if err := rmContainer(container.ID); err != nil {
			log.Printf(`[%s] Error while deleting service %s for %s: %v`, user.Username, service, appName, err)
		}
	}
}

func startServices(appName []byte, services map[string]*deployedService, user *auth.User) error {
	for service, container := range services {
		if err := startContainer(container.ID); err != nil {
			return fmt.Errorf(`Error while starting service %s for %s: %v`, service, appName, err)
		}
	}

	return nil
}

func inspectServices(services map[string]*deployedService, user *auth.User) []*types.ContainerJSON {
	containers := make([]*types.ContainerJSON, 0, len(services))

	for service, container := range services {
		infos, err := inspectContainer(container.ID)
		if err != nil {
			log.Printf(`[%s] Error while inspecting container %s: %v`, user.Username, service, err)
		}

		containers = append(containers, &infos)
	}

	return containers
}

func areContainersHealthy(ctx context.Context, user *auth.User, appName []byte, containers []*types.ContainerJSON) bool {
	containersIdsWithHealthcheck := make([]string, 0, len(containers))
	for _, container := range containers {
		if container.Config.Healthcheck != nil && len(container.Config.Healthcheck.Test) != 0 {
			containersIdsWithHealthcheck = append(containersIdsWithHealthcheck, container.ID)
		}
	}

	if len(containersIdsWithHealthcheck) == 0 {
		return true
	}

	filtersArgs := filters.NewArgs()
	healthyStatusFilters(&filtersArgs, containersIdsWithHealthcheck)

	timeoutCtx, cancel := context.WithTimeout(ctx, DeployTimeout)
	defer cancel()

	messages, errors := docker.Events(timeoutCtx, types.EventsOptions{Filters: filtersArgs})
	healthyContainers := make(map[string]bool, len(containersIdsWithHealthcheck))

	for {
		select {
		case <-ctx.Done():
			return false
		case message := <-messages:
			healthyContainers[message.ID] = true
			log.Printf(`[%s] Container %s for %s is healthy`, user.Username, appName, message.From)

			if len(healthyContainers) == len(containersIdsWithHealthcheck) {
				return true
			}
		case err := <-errors:
			log.Printf(`[%s] Error while reading healthy events: %v`, user.Username, err)
			return false
		}
	}
}

func finishDeploy(ctx context.Context, cancel context.CancelFunc, user *auth.User, appName []byte, services map[string]*deployedService, oldContainers []types.Container) {
	defer cancel()
	defer func() {
		backgroundMutex.Lock()
		defer backgroundMutex.Unlock()

		delete(backgroundTasks, string(appName))
	}()

	if areContainersHealthy(ctx, user, appName, inspectServices(services, user)) {
		cleanContainers(oldContainers, user)

		if err := renameDeployedContainers(services, user); err != nil {
			log.Print(err)
		}
		log.Printf(`[%s] Succeeded to deploy %s`, user.Username, appName)
	} else {
		deleteServices(appName, services, user)
		log.Printf(`[%s] Failed to deploy %s: %v`, user.Username, appName, fmt.Errorf(`[Health check failed`))
	}
}

func createContainer(user *auth.User, appName []byte, serviceName string, services map[string]*deployedService, service *dockerComposeService) (*deployedService, error) {
	if err := pullImage(service.Image, user); err != nil {
		return nil, err
	}

	serviceFullName := getServiceFullName(string(appName), serviceName)

	createdContainer, err := docker.ContainerCreate(context.Background(), getConfig(service, user, string(appName)), getHostConfig(service), getNetworkConfig(service, services), serviceFullName)
	if err != nil {
		return nil, fmt.Errorf(`Error while creating service %s for %s: %v`, serviceName, appName, err)
	}

	return &deployedService{ID: createdContainer.ID, Name: serviceFullName}, nil
}

func composeFailed(w http.ResponseWriter, user *auth.User, appName []byte, err error) {
	httputils.InternalServer(w, fmt.Errorf(`[%s] Failed to deploy %s: %v`, user.Username, appName, err))
}

func composeHandler(w http.ResponseWriter, user *auth.User, appName []byte, composeFile []byte) {
	if user == nil {
		httputils.BadRequest(w, fmt.Errorf(`A user is required`))
		return
	}

	if len(appName) == 0 || len(composeFile) == 0 {
		httputils.BadRequest(w, fmt.Errorf(`[%s] An application name and a compose file are required`, user.Username))
		return
	}

	compose := dockerCompose{}
	if err := yaml.Unmarshal(composeFile, &compose); err != nil {
		httputils.BadRequest(w, fmt.Errorf(`[%s] Error while unmarshalling compose file: %v`, user.Username, err))
		return
	}

	appNameStr := string(appName)

	backgroundMutex.Lock()
	if _, ok := backgroundTasks[appNameStr]; ok {
		backgroundMutex.Unlock()
		composeFailed(w, user, appName, fmt.Errorf(`Application already in deployment`))
		return
	}

	backgroundTasks[appNameStr] = true
	backgroundMutex.Unlock()

	oldContainers, err := listContainers(user, appNameStr)
	if err != nil {
		composeFailed(w, user, appName, err)
		return
	}

	if len(oldContainers) > 0 && oldContainers[0].Labels[ownerLabel] != user.Username {
		composeFailed(w, user, appName, fmt.Errorf(`Application not owned`))
		httputils.Forbidden(w)
	}

	newServices := make(map[string]*deployedService)
	for serviceName, service := range compose.Services {
		if deployedService, err := createContainer(user, appName, serviceName, newServices, &service); err != nil {
			break
		} else {
			newServices[serviceName] = deployedService
		}
	}

	ctx, cancel := context.WithCancel(context.Background())
	go finishDeploy(ctx, cancel, user, appName, newServices, oldContainers)

	if err == nil {
		err = startServices(appName, newServices, user)
	}

	if err != nil {
		cancel()
		composeFailed(w, user, appName, err)
	} else {
		httputils.ResponseArrayJSON(w, newServices)
	}
}
<|endoftext|>


<commit_before>package koding

import (
	"fmt"
	"koding/kites/kloud/api/amazon"
	"koding/kites/kloud/machinestate"

	"labix.org/v2/mgo"

	"github.com/mitchellh/goamz/ec2"
	"golang.org/x/net/context"
)

// Destroy implements the Destroyer interface. It uses destroyMachine(ctx)
// function but updates/deletes the MongoDB document once finished.
func (m *Machine) Destroy(ctx context.Context) (err error) {
	if err := m.UpdateState("Machine is termating", machinestate.Terminating); err != nil {
		return err
	}

	// update the state to intiial state if something goes wrong, we are going
	// to change latestate to a more safe state if we passed a certain step
	// below
	latestState := m.State()
	defer func() {
		if err != nil {
			m.UpdateState("Machine is marked as "+latestState.String(), latestState)
		}
	}()

	// try to destroy the instance, however if the instance is not available
	// anymore just continue.
	if m.Meta.InstanceId != "" {
		err := m.Session.AWSClient.Destroy(ctx, 10, 50)
		if err != amazon.ErrNoInstances {
			// if it's something else return it
			return err
		}
	}

	m.push("Deleting base domain", 85, machinestate.Terminating)
	if err := m.Session.DNSClient.Delete(m.Domain); err != nil {
		// if it's already deleted, for example because of a STOP, than we just
		// log it here instead of returning the error
		m.Log.Error("deleting domain during destroying err: %s", err.Error())
	}

	domains, err := m.Session.DNSStorage.GetByMachine(m.Id.Hex())
	if err != nil {
		m.Log.Error("fetching domains for unsetting err: %s", err.Error())
	}

	m.push("Deleting custom domain", 90, machinestate.Terminating)
	for _, domain := range domains {
		if err := m.Session.DNSClient.Delete(domain.Name); err != nil {
			m.Log.Error("couldn't delete domain: %s", err.Error())
		}

		err := m.Session.DNSStorage.UpdateMachine(domain.Name, "")
		if err != nil {
			m.Log.Error("couldn't unset machine domain: %s", err.Error())
		}
	}

	// try to release/delete a public elastic IP, if there is an error we don't
	// care (the instance might not have an elastic IP, aka a free user.
	if resp, err := m.Session.AWSClient.Client.Addresses(
		[]string{m.IpAddress},
		nil,
		ec2.NewFilter(),
	); err == nil {
		if len(resp.Addresses) != 0 {
			address := resp.Addresses[0]
			m.Log.Debug("Got an elastic IP %+v. Going to relaease it", address)

			m.Session.AWSClient.Client.ReleaseAddress(address.AllocationId)
		}
	}

	// clean up these details, the instance doesn't exist anymore
	m.Meta.InstanceId = ""
	m.Meta.InstanceName = ""
	m.IpAddress = ""
	m.QueryString = ""

	return m.DeleteDocument()
}

// DeleteDocument deletes the associated MongoDB document.
func (m *Machine) DeleteDocument() error {
	m.Log.Debug("Deleting machine document")
	err := m.Session.DB.Run("jMachines", func(c *mgo.Collection) error {
		return c.RemoveId(m.Id)
	})

	if err != nil {
		return fmt.Errorf("Couldn't delete document with id: %s err: %s", m.Id.Hex(), err)
	}

	return nil
}
<commit_msg>kloud/destroy: do only return if the err is non nil<commit_after>package koding

import (
	"fmt"
	"koding/kites/kloud/api/amazon"
	"koding/kites/kloud/machinestate"

	"labix.org/v2/mgo"

	"github.com/mitchellh/goamz/ec2"
	"golang.org/x/net/context"
)

// Destroy implements the Destroyer interface. It uses destroyMachine(ctx)
// function but updates/deletes the MongoDB document once finished.
func (m *Machine) Destroy(ctx context.Context) (err error) {
	if err := m.UpdateState("Machine is termating", machinestate.Terminating); err != nil {
		return err
	}

	// update the state to intiial state if something goes wrong, we are going
	// to change latestate to a more safe state if we passed a certain step
	// below
	latestState := m.State()
	defer func() {
		if err != nil {
			m.UpdateState("Machine is marked as "+latestState.String(), latestState)
		}
	}()

	// try to destroy the instance, however if the instance is not available
	// anymore just continue.
	if m.Meta.InstanceId != "" {
		m.Log.Debug("Destroying machine")
		err := m.Session.AWSClient.Destroy(ctx, 10, 50)
		if err != nil && err != amazon.ErrNoInstances {
			// if it's something else return it
			return err
		}
	}

	m.Log.Debug("Removing attached domains")
	m.push("Deleting base domain", 85, machinestate.Terminating)
	if err := m.Session.DNSClient.Delete(m.Domain); err != nil {
		// if it's already deleted, for example because of a STOP, than we just
		// log it here instead of returning the error
		m.Log.Error("deleting domain during destroying err: %s", err.Error())
	}

	domains, err := m.Session.DNSStorage.GetByMachine(m.Id.Hex())
	if err != nil {
		m.Log.Error("fetching domains for unsetting err: %s", err.Error())
	}

	m.push("Deleting custom domain", 90, machinestate.Terminating)
	for _, domain := range domains {
		if err := m.Session.DNSClient.Delete(domain.Name); err != nil {
			m.Log.Error("couldn't delete domain: %s", err.Error())
		}

		err := m.Session.DNSStorage.UpdateMachine(domain.Name, "")
		if err != nil {
			m.Log.Error("couldn't unset machine domain: %s", err.Error())
		}
	}

	// try to release/delete a public elastic IP, if there is an error we don't
	// care (the instance might not have an elastic IP, aka a free user.
	m.Log.Debug("Releasing attached public IP's")
	if resp, err := m.Session.AWSClient.Client.Addresses(
		[]string{m.IpAddress},
		nil,
		ec2.NewFilter(),
	); err == nil {
		if len(resp.Addresses) != 0 {
			address := resp.Addresses[0]
			m.Log.Debug("Got an elastic IP %+v. Going to relaease it", address)

			m.Session.AWSClient.Client.ReleaseAddress(address.AllocationId)
		}
	}

	// clean up these details, the instance doesn't exist anymore
	m.Meta.InstanceId = ""
	m.Meta.InstanceName = ""
	m.IpAddress = ""
	m.QueryString = ""

	return m.DeleteDocument()
}

// DeleteDocument deletes the associated MongoDB document.
func (m *Machine) DeleteDocument() error {
	m.Log.Debug("Deleting machine document")
	err := m.Session.DB.Run("jMachines", func(c *mgo.Collection) error {
		return c.RemoveId(m.Id)
	})

	if err != nil {
		return fmt.Errorf("Couldn't delete document with id: %s err: %s", m.Id.Hex(), err)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import "github.com/hashicorp/terraform/helper/schema"

func resourceKubernetesService() *schema.Resource {
	return &schema.Resource{
		Create: resourceKubernetesServiceCreate,
		Read:   resourceKubernetesServiceRead,
		Update: resourceKubernetesServiceUpdate,
		Delete: resourceKubernetesServiceDelete,
		Schema: map[string]*schema.Schema{
			"spec": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     resourceUnitServiceSpec(),
			},
			"metadata": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     resourceUnitMetadata(),
			},
		},
	}
}

func resourceUnitServiceSpec() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"ports": &schema.Schema{
				Type:     schema.TypeList,
				Required: true,
				Elem:     resourceUnitServicePort(),
			},
			"selector": &schema.Schema{
				Type:     schema.TypeMap,
				Optional: true,
			},
			"cluster_ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"session_affinity": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func resourceUnitServicePort() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"protocol": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"node_port": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
		},
	}
}
<commit_msg>adding in required states<commit_after>package main

import "github.com/hashicorp/terraform/helper/schema"

func resourceKubernetesService() *schema.Resource {
	return &schema.Resource{
		Create: resourceKubernetesServiceCreate,
		Read:   resourceKubernetesServiceRead,
		Update: resourceKubernetesServiceUpdate,
		Delete: resourceKubernetesServiceDelete,
		Schema: map[string]*schema.Schema{
			"spec": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     resourceUnitServiceSpec(),
			},
			"metadata": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     resourceUnitMetadata(),
			},
		},
	}
}

func resourceUnitServiceSpec() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"ports": &schema.Schema{
				Type:     	schema.TypeList,
				Required: 	true,
				Elem:     	resourceUnitServicePort(),
			},
			"selector": &schema.Schema{
				Type:     	schema.TypeMap,
				Optional: 	true,
			},
			"cluster_ip": &schema.Schema{
				Type:     	schema.TypeString,
				Optional: 	true,
			},
			"type": &schema.Schema{
				Type:     	schema.TypeString,
				Optional: 	true,
			},
			"session_affinity": &schema.Schema{
				Type:     	schema.TypeString,
				Required: 	true,
				Default:	"None",
			},
		},
	}
}

func resourceUnitServicePort() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"protocol": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},
			"node_port": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
		},
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/signal"
	"path/filepath"

	"github.com/OpenBazaar/openbazaar-go/cmd"
	"github.com/OpenBazaar/openbazaar-go/core"
	"github.com/ipfs/go-ipfs/repo/fsrepo"
	"github.com/jessevdk/go-flags"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("main")

type Opts struct {
	Version bool `short:"v" long:"version" description:"Print the version number and exit"`
}

type Stop struct{}

type Restart struct{}

var stopServer Stop
var restartServer Restart

var opts Opts

var parser = flags.NewParser(&opts, flags.Default)

func main() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for sig := range c {
			log.Noticef("Received %s\n", sig)
			log.Info("OpenBazaar Server shutting down...")
			if core.Node != nil {
				if core.Node.MessageRetriever != nil {
					core.Node.RecordAgingNotifier.Stop()
					close(core.Node.MessageRetriever.DoneChan)
					core.Node.MessageRetriever.Wait()
				}
				core.OfflineMessageWaitGroup.Wait()
				core.PublishLock.Lock()
				core.Node.Datastore.Close()
				repoLockFile := filepath.Join(core.Node.RepoPath, fsrepo.LockFile)
				os.Remove(repoLockFile)
				core.Node.Multiwallet.Close()
				core.Node.IpfsNode.Close()
			}
			os.Exit(1)
		}
	}()

	parser.AddCommand("gencerts",
		"Generate certificates",
		"Generate self-singned certificates",
		&cmd.GenerateCertificates{})
	parser.AddCommand("init",
		"initialize a new repo and exit",
		"Initializes a new repo without starting the server",
		&cmd.Init{})
	parser.AddCommand("status",
		"get the repo status",
		"Returns the status of the repo ― Uninitialized, Encrypted, Decrypted. Also returns whether Tor is available.",
		&cmd.Status{})
	parser.AddCommand("setapicreds",
		"set API credentials",
		"The API password field in the config file takes a SHA256 hash of the password. This command will generate the hash for you and save it to the config file.",
		&cmd.SetAPICreds{})
	parser.AddCommand("start",
		"start the OpenBazaar-Server",
		"The start command starts the OpenBazaar-Server",
		&cmd.Start{})
	parser.AddCommand("stop",
		"shutdown the server and disconnect",
		"The stop command disconnects from peers and shuts down OpenBazaar-Server",
		&stopServer)
	parser.AddCommand("restart",
		"restart the server",
		"The restart command shuts down the server and restarts",
		&restartServer)
	parser.AddCommand("encryptdatabase",
		"encrypt your database",
		"This command encrypts the database containing your bitcoin private keys, identity key, and contracts",
		&cmd.EncryptDatabase{})
	parser.AddCommand("decryptdatabase",
		"decrypt your database",
		"This command decrypts the database containing your bitcoin private keys, identity key, and contracts.\n [Warning] doing so may put your bitcoins at risk.",
		&cmd.DecryptDatabase{})
	parser.AddCommand("restore",
		"restore user data",
		"This command will attempt to restore user data (profile, listings, ratings, etc) by downloading them from the network. This will only work if the IPNS mapping is still available in the DHT. Optionally it will take a mnemonic seed to restore from.",
		&cmd.Restore{})
	parser.AddCommand("convert",
		"convert this node to a different coin type",
		"This command will convert the node to use a different cryptocurrency",
		&cmd.Convert{})
	if len(os.Args) > 1 && (os.Args[1] == "--version" || os.Args[1] == "-v") {
		fmt.Println(core.VERSION)
		return
	}
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}
}
<commit_msg>Remove stubbed subcommands Stop and Restart<commit_after>package main

import (
	"fmt"
	"os"
	"os/signal"
	"path/filepath"

	"github.com/OpenBazaar/openbazaar-go/cmd"
	"github.com/OpenBazaar/openbazaar-go/core"
	"github.com/ipfs/go-ipfs/repo/fsrepo"
	"github.com/jessevdk/go-flags"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("main")

type Opts struct {
	Version bool `short:"v" long:"version" description:"Print the version number and exit"`
}

var opts Opts

var parser = flags.NewParser(&opts, flags.Default)

func main() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for sig := range c {
			log.Noticef("Received %s\n", sig)
			log.Info("OpenBazaar Server shutting down...")
			if core.Node != nil {
				if core.Node.MessageRetriever != nil {
					core.Node.RecordAgingNotifier.Stop()
					close(core.Node.MessageRetriever.DoneChan)
					core.Node.MessageRetriever.Wait()
				}
				core.OfflineMessageWaitGroup.Wait()
				core.PublishLock.Lock()
				core.Node.Datastore.Close()
				repoLockFile := filepath.Join(core.Node.RepoPath, fsrepo.LockFile)
				os.Remove(repoLockFile)
				core.Node.Multiwallet.Close()
				core.Node.IpfsNode.Close()
			}
			os.Exit(1)
		}
	}()

	parser.AddCommand("gencerts",
		"Generate certificates",
		"Generate self-singned certificates",
		&cmd.GenerateCertificates{})
	parser.AddCommand("init",
		"initialize a new repo and exit",
		"Initializes a new repo without starting the server",
		&cmd.Init{})
	parser.AddCommand("status",
		"get the repo status",
		"Returns the status of the repo ― Uninitialized, Encrypted, Decrypted. Also returns whether Tor is available.",
		&cmd.Status{})
	parser.AddCommand("setapicreds",
		"set API credentials",
		"The API password field in the config file takes a SHA256 hash of the password. This command will generate the hash for you and save it to the config file.",
		&cmd.SetAPICreds{})
	parser.AddCommand("start",
		"start the OpenBazaar-Server",
		"The start command starts the OpenBazaar-Server",
		&cmd.Start{})
	parser.AddCommand("encryptdatabase",
		"encrypt your database",
		"This command encrypts the database containing your bitcoin private keys, identity key, and contracts",
		&cmd.EncryptDatabase{})
	parser.AddCommand("decryptdatabase",
		"decrypt your database",
		"This command decrypts the database containing your bitcoin private keys, identity key, and contracts.\n [Warning] doing so may put your bitcoins at risk.",
		&cmd.DecryptDatabase{})
	parser.AddCommand("restore",
		"restore user data",
		"This command will attempt to restore user data (profile, listings, ratings, etc) by downloading them from the network. This will only work if the IPNS mapping is still available in the DHT. Optionally it will take a mnemonic seed to restore from.",
		&cmd.Restore{})
	parser.AddCommand("convert",
		"convert this node to a different coin type",
		"This command will convert the node to use a different cryptocurrency",
		&cmd.Convert{})
	if len(os.Args) > 1 && (os.Args[1] == "--version" || os.Args[1] == "-v") {
		fmt.Println(core.VERSION)
		return
	}
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Package imgur implements a Service which adds !commands for imgur image search
package imgur

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/matrix-org/go-neb/types"
	"github.com/matrix-org/gomatrix"
)

// ServiceType of the Imgur service
const ServiceType = "imgur"

var httpClient = &http.Client{}

type imgurGalleryImage struct {
	ID          string `json:"id"`          // The ID for the image
	Title       string `json:"title"`       // The title of the image.
	Description string `json:"description"` // Description of the image.
	DateTime    int64  `json:"datetime"`    // Time inserted into the gallery, epoch time
	Type        string `json:"type"`        // Image MIME type.
	Animated    bool   `json:"animated"`    // Is the image animated
	Width       int    `json:"width"`       // The width of the image in pixels
	Height      int    `json:"height"`      // The height of the image in pixels
	Size        int64  `json:"size"`        // The size of the image in bytes
	Views       int64  `json:"views"`       // The number of image views
	Link        string `json:"link"`        // The direct link to the the image. (Note: if fetching an animated GIF that was over 20MB in original size, a .gif thumbnail will be returned)
	Gifv        string `json:"gifv"`        // OPTIONAL, The .gifv link. Only available if the image is animated and type is 'image/gif'.
	MP4         string `json:"mp4"`         // OPTIONAL, The direct link to the .mp4. Only available if the image is animated and type is 'image/gif'.
	MP4Size     int64  `json:"mp4_size"`    // OPTIONAL, The Content-Length of the .mp4. Only available if the image is animated and type is 'image/gif'. Note that a zero value (0) is possible if the video has not yet been generated
	Looping     bool   `json:"looping"`     // OPTIONAL, Whether the image has a looping animation. Only available if the image is animated and type is 'image/gif'.
	NSFW        bool   `json:"nsfw"`        // Indicates if the image has been marked as nsfw or not. Defaults to null if information is not available.
	Topic       string `json:"topic"`       // Topic of the gallery image.
	Section     string `json:"section"`     // If the image has been categorized by our backend then this will contain the section the image belongs in. (funny, cats, adviceanimals, wtf, etc)
	IsAlbum     bool   `json:"is_album"`    // If it's an album or not
	// ** Unimplemented fields **
	// bandwidth	integer	Bandwidth consumed by the image in bytes
	// deletehash	string	OPTIONAL, the deletehash, if you're logged in as the image owner
	// comment_count	int	Number of comments on the gallery image.
	// topic_id	integer	Topic ID of the gallery image.
	// vote	string	The current user's vote on the album. null if not signed in or if the user hasn't voted on it.
	// favorite	boolean	Indicates if the current user favorited the image. Defaults to false if not signed in.
	// account_url	string	The username of the account that uploaded it, or null.
	// account_id	integer	The account ID of the account that uploaded it, or null.
	// ups	integer	Upvotes for the image
	// downs	integer	Number of downvotes for the image
	// points	integer	Upvotes minus downvotes
	// score	integer	Imgur popularity score
}

type imgurGalleryAlbum struct {
	ID          string              `json:"id"`           // The ID for the album
	Title       string              `json:"title"`        // The title of the album.
	Description string              `json:"description"`  // Description of the album.
	DateTime    int64               `json:"datetime"`     // Time inserted into the gallery, epoch time
	Views       int64               `json:"views"`        // The number of album views
	Link        string              `json:"link"`         // The URL link to the album
	NSFW        bool                `json:"nsfw"`         // Indicates if the album has been marked as nsfw or not. Defaults to null if information is not available.
	Topic       string              `json:"topic"`        // Topic of the gallery album.
	IsAlbum     bool                `json:"is_album"`     // If it's an album or not
	Cover       string              `json:"cover"`        // The ID of the album cover image
	CoverWidth  int                 `json:"cover_width"`  // The width, in pixels, of the album cover image
	CoverHeight int                 `json:"cover_height"` // The height, in pixels, of the album cover image
	ImagesCount int                 `json:"images_count"` // The total number of images in the album
	Images      []imgurGalleryImage `json:"images"`       // An array of all the images in the album (only available when requesting the direct album)

	// ** Unimplemented fields **
	// account_url	string	The account username or null if it's anonymous.
	// account_id	integer	The account ID of the account that uploaded it, or null.
	// privacy	string	The privacy level of the album, you can only view public if not logged in as album owner
	// layout	string	The view layout of the album.
	// views	integer	The number of image views
	// ups	integer	Upvotes for the image
	// downs	integer	Number of downvotes for the image
	// points	integer	Upvotes minus downvotes
	// score	integer	Imgur popularity score
	// vote	string	The current user's vote on the album. null if not signed in or if the user hasn't voted on it.
	// favorite	boolean	Indicates if the current user favorited the album. Defaults to false if not signed in.
	// comment_count	int	Number of comments on the gallery album.
	// topic_id	integer	Topic ID of the gallery album.
}

type imgurSearchResponse struct {
	Data    []json.RawMessage `json:"data"`
	Success bool              `json:"success"` // Request completed successfully
	Status  int               `json:"status"`  // HTTP response code
}

// Service contains the Config fields for the Imgur service.
//
// Example request:
//   {
//			"client_id": "AIzaSyA4FD39..."
//			"client_secret": "ASdsaijwdfASD..."
//   }
type Service struct {
	types.DefaultService
	// The Imgur client ID
	ClientID string `json:"client_id"`
	// The API key to use when making HTTP requests to Imgur.
	ClientSecret string `json:"client_secret"`
}

// Commands supported:
//    !imgur some_search_query_without_quotes
// Responds with a suitable image into the same room as the command.
func (s *Service) Commands(client *gomatrix.Client) []types.Command {
	return []types.Command{
		types.Command{
			Path: []string{"imgur", "help"},
			Command: func(roomID, userID string, args []string) (interface{}, error) {
				return usageMessage(), nil
			},
		},
		types.Command{
			Path: []string{"imgur"},
			Command: func(roomID, userID string, args []string) (interface{}, error) {
				return s.cmdImgSearch(client, roomID, userID, args)
			},
		},
	}
}

// usageMessage returns a matrix TextMessage representation of the service usage
func usageMessage() *gomatrix.TextMessage {
	return &gomatrix.TextMessage{"m.notice",
		`Usage: !imgur image_search_text`}
}

func (s *Service) cmdImgSearch(client *gomatrix.Client, roomID, userID string, args []string) (interface{}, error) {

	if len(args) < 1 {
		return usageMessage(), nil
	}

	// Get the query text to search for.
	querySentence := strings.Join(args, " ")

	searchResultImage, searchResultAlbum, err := s.text2img(querySentence)

	if err != nil {
		return nil, err
	}

	// Image returned
	if searchResultImage != nil {
		var imgURL = searchResultImage.Link
		if imgURL == "" {
			return gomatrix.TextMessage{
				MsgType: "m.notice",
				Body:    "No image found!",
			}, nil
		}

		resUpload, err := client.UploadLink(imgURL)
		if err != nil {
			return nil, fmt.Errorf("Failed to upload Imgur image (%s) to matrix: %s", imgURL, err.Error())
		}

		return gomatrix.ImageMessage{
			MsgType: "m.image",
			Body:    querySentence,
			URL:     resUpload.ContentURI,
			Info: gomatrix.ImageInfo{
				Height:   uint(searchResultImage.Height),
				Width:    uint(searchResultImage.Width),
				Mimetype: searchResultImage.Type,
			},
		}, nil
	} else if searchResultAlbum != nil {
		return gomatrix.TextMessage{
			MsgType: "m.notice",
			Body:    "Search returned an album - Not currently supported",
		}, nil
	} else {
		return gomatrix.TextMessage{
			MsgType: "m.notice",
			Body:    "No image found!",
		}, nil
	}
}

// text2img returns info about an image or an album
func (s *Service) text2img(query string) (*imgurGalleryImage, *imgurGalleryAlbum, error) {
	log.Info("Searching Imgur for an image of a ", query)

	query = url.QueryEscape(query)

	var sort = "time"  // time | viral | top
	var window = "all" // day | week | month | year | all
	var page = 1
	var urlString = fmt.Sprintf("%s/%s/%s/%d?q=%s", "https://api.imgur.com/3/gallery/search", sort, window, page, query)
	// var urlString = fmt.Sprintf("%s?q=%s", base, query)

	u, err := url.Parse(urlString)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Add("Authorization", "Client-ID "+s.ClientID)
	res, err := httpClient.Do(req)
	if res != nil {
		defer res.Body.Close()
	}
	if err != nil {
		return nil, nil, err
	}
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return nil, nil, fmt.Errorf("Request error: %d, %s", res.StatusCode, response2String(res))
	}

	var searchResults imgurSearchResponse
	if err := json.NewDecoder(res.Body).Decode(&searchResults); err != nil {
		return nil, nil, fmt.Errorf("No images found - %s", err.Error())
	} else if len(searchResults.Data) < 1 {
		return nil, nil, fmt.Errorf("No images found")
	}

	log.Printf("%d results were returned from Imgur", len(searchResults.Data))
	// Return a random image result
	var images []imgurGalleryImage
	for i := 0; i < len(searchResults.Data); i++ {
		var image imgurGalleryImage
		if err := json.Unmarshal(searchResults.Data[i], &image); err == nil && !image.IsAlbum {
			images = append(images, image)
		}
	}
	if len(images) > 0 {
		var r = 0
		if len(images) > 1 {
			r = rand.Intn(len(images) - 1)
		}
		return &images[r], nil, nil
	}

	return nil, nil, fmt.Errorf("No images found")
}

// response2String returns a string representation of an HTTP response body
func response2String(res *http.Response) string {
	bs, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "Failed to decode response body"
	}
	str := string(bs)
	return str
}

// Initialise the service
func init() {
	types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service {
		return &Service{
			DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
		}
	})
}
<commit_msg>Refactor to reduce cyclomatic complexity<commit_after>// Package imgur implements a Service which adds !commands for imgur image search
package imgur

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/matrix-org/go-neb/types"
	"github.com/matrix-org/gomatrix"
)

// ServiceType of the Imgur service
const ServiceType = "imgur"

var httpClient = &http.Client{}

type imgurGalleryImage struct {
	ID          string `json:"id"`          // The ID for the image
	Title       string `json:"title"`       // The title of the image.
	Description string `json:"description"` // Description of the image.
	DateTime    int64  `json:"datetime"`    // Time inserted into the gallery, epoch time
	Type        string `json:"type"`        // Image MIME type.
	Animated    bool   `json:"animated"`    // Is the image animated
	Width       int    `json:"width"`       // The width of the image in pixels
	Height      int    `json:"height"`      // The height of the image in pixels
	Size        int64  `json:"size"`        // The size of the image in bytes
	Views       int64  `json:"views"`       // The number of image views
	Link        string `json:"link"`        // The direct link to the the image. (Note: if fetching an animated GIF that was over 20MB in original size, a .gif thumbnail will be returned)
	Gifv        string `json:"gifv"`        // OPTIONAL, The .gifv link. Only available if the image is animated and type is 'image/gif'.
	MP4         string `json:"mp4"`         // OPTIONAL, The direct link to the .mp4. Only available if the image is animated and type is 'image/gif'.
	MP4Size     int64  `json:"mp4_size"`    // OPTIONAL, The Content-Length of the .mp4. Only available if the image is animated and type is 'image/gif'. Note that a zero value (0) is possible if the video has not yet been generated
	Looping     bool   `json:"looping"`     // OPTIONAL, Whether the image has a looping animation. Only available if the image is animated and type is 'image/gif'.
	NSFW        bool   `json:"nsfw"`        // Indicates if the image has been marked as nsfw or not. Defaults to null if information is not available.
	Topic       string `json:"topic"`       // Topic of the gallery image.
	Section     string `json:"section"`     // If the image has been categorized by our backend then this will contain the section the image belongs in. (funny, cats, adviceanimals, wtf, etc)
	IsAlbum     bool   `json:"is_album"`    // If it's an album or not
	// ** Unimplemented fields **
	// bandwidth	integer	Bandwidth consumed by the image in bytes
	// deletehash	string	OPTIONAL, the deletehash, if you're logged in as the image owner
	// comment_count	int	Number of comments on the gallery image.
	// topic_id	integer	Topic ID of the gallery image.
	// vote	string	The current user's vote on the album. null if not signed in or if the user hasn't voted on it.
	// favorite	boolean	Indicates if the current user favorited the image. Defaults to false if not signed in.
	// account_url	string	The username of the account that uploaded it, or null.
	// account_id	integer	The account ID of the account that uploaded it, or null.
	// ups	integer	Upvotes for the image
	// downs	integer	Number of downvotes for the image
	// points	integer	Upvotes minus downvotes
	// score	integer	Imgur popularity score
}

type imgurGalleryAlbum struct {
	ID          string              `json:"id"`           // The ID for the album
	Title       string              `json:"title"`        // The title of the album.
	Description string              `json:"description"`  // Description of the album.
	DateTime    int64               `json:"datetime"`     // Time inserted into the gallery, epoch time
	Views       int64               `json:"views"`        // The number of album views
	Link        string              `json:"link"`         // The URL link to the album
	NSFW        bool                `json:"nsfw"`         // Indicates if the album has been marked as nsfw or not. Defaults to null if information is not available.
	Topic       string              `json:"topic"`        // Topic of the gallery album.
	IsAlbum     bool                `json:"is_album"`     // If it's an album or not
	Cover       string              `json:"cover"`        // The ID of the album cover image
	CoverWidth  int                 `json:"cover_width"`  // The width, in pixels, of the album cover image
	CoverHeight int                 `json:"cover_height"` // The height, in pixels, of the album cover image
	ImagesCount int                 `json:"images_count"` // The total number of images in the album
	Images      []imgurGalleryImage `json:"images"`       // An array of all the images in the album (only available when requesting the direct album)

	// ** Unimplemented fields **
	// account_url	string	The account username or null if it's anonymous.
	// account_id	integer	The account ID of the account that uploaded it, or null.
	// privacy	string	The privacy level of the album, you can only view public if not logged in as album owner
	// layout	string	The view layout of the album.
	// views	integer	The number of image views
	// ups	integer	Upvotes for the image
	// downs	integer	Number of downvotes for the image
	// points	integer	Upvotes minus downvotes
	// score	integer	Imgur popularity score
	// vote	string	The current user's vote on the album. null if not signed in or if the user hasn't voted on it.
	// favorite	boolean	Indicates if the current user favorited the album. Defaults to false if not signed in.
	// comment_count	int	Number of comments on the gallery album.
	// topic_id	integer	Topic ID of the gallery album.
}

type imgurSearchResponse struct {
	Data    []json.RawMessage `json:"data"`
	Success bool              `json:"success"` // Request completed successfully
	Status  int               `json:"status"`  // HTTP response code
}

// Service contains the Config fields for the Imgur service.
//
// Example request:
//   {
//			"client_id": "AIzaSyA4FD39..."
//			"client_secret": "ASdsaijwdfASD..."
//   }
type Service struct {
	types.DefaultService
	// The Imgur client ID
	ClientID string `json:"client_id"`
	// The API key to use when making HTTP requests to Imgur.
	ClientSecret string `json:"client_secret"`
}

// Commands supported:
//    !imgur some_search_query_without_quotes
// Responds with a suitable image into the same room as the command.
func (s *Service) Commands(client *gomatrix.Client) []types.Command {
	return []types.Command{
		types.Command{
			Path: []string{"imgur", "help"},
			Command: func(roomID, userID string, args []string) (interface{}, error) {
				return usageMessage(), nil
			},
		},
		types.Command{
			Path: []string{"imgur"},
			Command: func(roomID, userID string, args []string) (interface{}, error) {
				return s.cmdImgSearch(client, roomID, userID, args)
			},
		},
	}
}

// usageMessage returns a matrix TextMessage representation of the service usage
func usageMessage() *gomatrix.TextMessage {
	return &gomatrix.TextMessage{"m.notice",
		`Usage: !imgur image_search_text`}
}

func (s *Service) cmdImgSearch(client *gomatrix.Client, roomID, userID string, args []string) (interface{}, error) {

	if len(args) < 1 {
		return usageMessage(), nil
	}

	// Get the query text to search for.
	querySentence := strings.Join(args, " ")

	searchResultImage, searchResultAlbum, err := s.text2img(querySentence)

	if err != nil {
		return nil, err
	}

	// Image returned
	if searchResultImage != nil {
		var imgURL = searchResultImage.Link
		if imgURL == "" {
			return gomatrix.TextMessage{
				MsgType: "m.notice",
				Body:    "No image found!",
			}, nil
		}

		resUpload, err := client.UploadLink(imgURL)
		if err != nil {
			return nil, fmt.Errorf("Failed to upload Imgur image (%s) to matrix: %s", imgURL, err.Error())
		}

		return gomatrix.ImageMessage{
			MsgType: "m.image",
			Body:    querySentence,
			URL:     resUpload.ContentURI,
			Info: gomatrix.ImageInfo{
				Height:   uint(searchResultImage.Height),
				Width:    uint(searchResultImage.Width),
				Mimetype: searchResultImage.Type,
			},
		}, nil
	} else if searchResultAlbum != nil {
		return gomatrix.TextMessage{
			MsgType: "m.notice",
			Body:    "Search returned an album - Not currently supported",
		}, nil
	} else {
		return gomatrix.TextMessage{
			MsgType: "m.notice",
			Body:    "No image found!",
		}, nil
	}
}

// text2img returns info about an image or an album
func (s *Service) text2img(query string) (*imgurGalleryImage, *imgurGalleryAlbum, error) {
	log.Info("Searching Imgur for an image of a ", query)
	bytes, err := queryImgur(query, s)
	if err != nil {
		return nil, nil, err
	}

	var searchResults imgurSearchResponse
	// if err := json.NewDecoder(res.Body).Decode(&searchResults); err != nil {
	if err := json.Unmarshal(bytes, &searchResults); err != nil {
		return nil, nil, fmt.Errorf("No images found - %s", err.Error())
	} else if len(searchResults.Data) < 1 {
		return nil, nil, fmt.Errorf("No images found")
	}

	log.Printf("%d results were returned from Imgur", len(searchResults.Data))
	// Return a random image result
	var images []imgurGalleryImage
	for i := 0; i < len(searchResults.Data); i++ {
		var image imgurGalleryImage
		if err := json.Unmarshal(searchResults.Data[i], &image); err == nil && !image.IsAlbum {
			images = append(images, image)
		}
	}
	if len(images) > 0 {
		var r = 0
		if len(images) > 1 {
			r = rand.Intn(len(images) - 1)
		}
		return &images[r], nil, nil
	}

	return nil, nil, fmt.Errorf("No images found")
}

// Query imgur and return HTTP response or error
func queryImgur(query string, s *Service) ([]byte, error) {
	query = url.QueryEscape(query)

	var sort = "time"  // time | viral | top
	var window = "all" // day | week | month | year | all
	var page = 1
	var urlString = fmt.Sprintf("%s/%s/%s/%d?q=%s", "https://api.imgur.com/3/gallery/search", sort, window, page, query)
	// var urlString = fmt.Sprintf("%s?q=%s", base, query)

	u, err := url.Parse(urlString)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Authorization", "Client-ID "+s.ClientID)
	res, err := httpClient.Do(req)
	if res != nil {
		defer res.Body.Close()
	}
	if err != nil {
		return nil, err
	}
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return nil, fmt.Errorf("Request error: %d, %s", res.StatusCode, response2String(res))
	}

	bytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}
	return bytes, nil
}

// response2String returns a string representation of an HTTP response body
func response2String(res *http.Response) string {
	bs, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "Failed to decode response body"
	}
	str := string(bs)
	return str
}

// Initialise the service
func init() {
	types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service {
		return &Service{
			DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
		}
	})
}
<|endoftext|>


<commit_before>package net4g

import (
	"github.com/carsonsx/log4g"
	"reflect"
	"runtime/debug"
	"sync"
)

func Dispatch(dispatchers []*dispatcher, req NetReq, res NetRes) {
	for _, p := range dispatchers {
		if p.running {
			p.dispatchChan <- &dispatchData{req: req, res: res}
		}
	}
}

func AddHandler(dispatcher *dispatcher, v interface{}, h func(req NetReq, res NetRes)) {
	dispatcher.AddHandler(h, reflect.TypeOf(v))
}

func NewDispatcher(name string) *dispatcher {
	p := new(dispatcher)
	p.Name = name
	p.dispatchChan = make(chan *dispatchData, 1000)
	p.sessionClosedChan = make(chan NetSession, 100)
	p.destroyChan = make(chan bool, 1)
	p.typeHandlers = make(map[reflect.Type]func(req NetReq, res NetRes))
	p.run()
	log4g.Info("new a %s dispatcher", name)
	return p
}

type dispatchData struct {
	req NetReq
	res NetRes
}

type dispatcher struct {
	Name                    string
	serializer              Serializer
	mgr                     *NetManager
	globalHandlers          []func(req NetReq, res NetRes)
	typeHandlers            map[reflect.Type]func(req NetReq, res NetRes)
	before_interceptors     []func(req NetReq, res NetRes)
	after_interceptors      []func(req NetReq, res NetRes)
	dispatchChan            chan *dispatchData
	sessionClosedChan       chan NetSession
	connectionClosedHandler func(session NetSession)
	destroyChan             chan bool
	destroyHandler          func()
	running                 bool
	wg                      sync.WaitGroup
}

func (p *dispatcher) AddHandler(h func(req NetReq, res NetRes), t ...reflect.Type) {
	if len(t) > 0 {
		p.typeHandlers[t[0]] = h
		log4g.Info("added a handler for %v", t[0])
	} else {
		p.globalHandlers = append(p.globalHandlers, h)
		log4g.Info("added global handler")
	}
}

func (p *dispatcher) OnConnectionClosed(h func(session NetSession)) {
	p.connectionClosedHandler = h
}

func (p *dispatcher) OnDestroy(h func()) {
	p.destroyHandler = h
}

func (p *dispatcher) dispatch(msg *dispatchData) {

	// safe the user handler to avoid the whole server down
	defer func() {
		if r := recover(); r != nil {
			log4g.Error("********************* Handler Panic *********************")
			log4g.Error(r)
			debug.PrintStack()
			msg.res.Close()
			log4g.Error("********************* Handler Panic *********************")
		}
	}()

	for _, i := range p.before_interceptors {
		i(msg.req, msg.res)
	}

	for _, h := range p.globalHandlers {
		h(msg.req, msg.res)
	}

	t := reflect.TypeOf(msg.req.Msg())
	if h, ok := p.typeHandlers[t]; ok {
		log4g.Trace("dispatcher[%s] is dispatching %v to handler ", p.Name, t)
		h(msg.req, msg.res)
	}

	for _, i := range p.after_interceptors {
		i(msg.req, msg.res)
	}
}

func (p *dispatcher) run() {
	p.wg.Add(1)
	// one dispatcher, one goroutine
	go func() {
		defer p.wg.Done()
	outer:
		for {
			select {
			case data := <-p.dispatchChan:
				p.dispatch(data)
			case session := <-p.sessionClosedChan:
				if p.connectionClosedHandler != nil {
					p.connectionClosedHandler(session)
				}
				session.Get("wg").(*sync.WaitGroup).Done()
			case <-p.destroyChan:
				if p.destroyHandler != nil {
					p.destroyHandler()
				}
				break outer
			}
		}
	}()

	p.running = true
}

func (p *dispatcher) Broadcast(v interface{}, filter func(session NetSession) bool) error {
	b, err := p.serializer.Serialize(v)
	if err != nil {
		log4g.Error(err)
		return err
	}
	p.mgr.Broadcast(b, filter)
	return nil
}

func (p *dispatcher) BroadcastAll(v interface{}) error {
	b, err := p.serializer.Serialize(v)
	if err != nil {
		log4g.Error(err)
		return err
	}
	p.mgr.BroadcastAll(b)
	return nil
}

func (p *dispatcher) BroadcastOthers(mySession NetSession, v interface{}) error {
	b, err := p.serializer.Serialize(v)
	if err != nil {
		log4g.Error(err)
		return err
	}
	p.mgr.BroadcastOthers(mySession, b)
	return nil
}

func (p *dispatcher) Someone(v interface{}, filter func(session NetSession) bool) error {
	b, err := p.serializer.Serialize(v)
	if err != nil {
		log4g.Error(err)
		return err
	}
	p.mgr.Someone(b, filter)
	return nil
}

func (p *dispatcher) handleConnectionClosed(session NetSession) {
	wg := new(sync.WaitGroup)
	wg.Add(1)
	session.Set("wg", wg)
	p.sessionClosedChan <- session
	wg.Wait()
	session.Remove("wg")
}

func (p *dispatcher) Destroy() {
	p.running = false
	p.destroyChan <- true
	p.wg.Wait()
	//how to close gracefully
	//close(p.dispatchChan)
	//close(p.sessionClosedChan)
	close(p.destroyChan)
}
<commit_msg>update log<commit_after>package net4g

import (
	"github.com/carsonsx/log4g"
	"reflect"
	"runtime/debug"
	"sync"
)

func Dispatch(dispatchers []*dispatcher, req NetReq, res NetRes) {
	for _, p := range dispatchers {
		if p.running {
			p.dispatchChan <- &dispatchData{req: req, res: res}
		}
	}
}

func AddHandler(dispatcher *dispatcher, v interface{}, h func(req NetReq, res NetRes)) {
	dispatcher.AddHandler(h, reflect.TypeOf(v))
}

func NewDispatcher(name string) *dispatcher {
	p := new(dispatcher)
	p.Name = name
	p.dispatchChan = make(chan *dispatchData, 1000)
	p.sessionClosedChan = make(chan NetSession, 100)
	p.destroyChan = make(chan bool, 1)
	p.typeHandlers = make(map[reflect.Type]func(req NetReq, res NetRes))
	p.run()
	log4g.Info("new a %s dispatcher", name)
	return p
}

type dispatchData struct {
	req NetReq
	res NetRes
}

type dispatcher struct {
	Name                    string
	serializer              Serializer
	mgr                     *NetManager
	globalHandlers          []func(req NetReq, res NetRes)
	typeHandlers            map[reflect.Type]func(req NetReq, res NetRes)
	before_interceptors     []func(req NetReq, res NetRes)
	after_interceptors      []func(req NetReq, res NetRes)
	dispatchChan            chan *dispatchData
	sessionClosedChan       chan NetSession
	connectionClosedHandler func(session NetSession)
	destroyChan             chan bool
	destroyHandler          func()
	running                 bool
	wg                      sync.WaitGroup
}

func (p *dispatcher) AddHandler(h func(req NetReq, res NetRes), t ...reflect.Type) {
	if len(t) > 0 {
		p.typeHandlers[t[0]] = h
		log4g.Info("added a handler for %v", t[0])
	} else {
		p.globalHandlers = append(p.globalHandlers, h)
		log4g.Info("added global handler")
	}
}

func (p *dispatcher) OnConnectionClosed(h func(session NetSession)) {
	p.connectionClosedHandler = h
}

func (p *dispatcher) OnDestroy(h func()) {
	p.destroyHandler = h
}

func (p *dispatcher) dispatch(msg *dispatchData) {

	// safe the user handler to avoid the whole server down
	defer func() {
		if r := recover(); r != nil {
			log4g.Error("********************* Handler Panic *********************")
			log4g.Error(r)
			log4g.Error(string(debug.Stack()))
			msg.res.Close()
			log4g.Error("********************* Handler Panic *********************")
		}
	}()

	for _, i := range p.before_interceptors {
		i(msg.req, msg.res)
	}

	for _, h := range p.globalHandlers {
		h(msg.req, msg.res)
	}

	t := reflect.TypeOf(msg.req.Msg())
	if h, ok := p.typeHandlers[t]; ok {
		log4g.Trace("dispatcher[%s] is dispatching %v to handler ", p.Name, t)
		h(msg.req, msg.res)
	}

	for _, i := range p.after_interceptors {
		i(msg.req, msg.res)
	}
}

func (p *dispatcher) run() {
	p.wg.Add(1)
	// one dispatcher, one goroutine
	go func() {
		defer p.wg.Done()
	outer:
		for {
			select {
			case data := <-p.dispatchChan:
				p.dispatch(data)
			case session := <-p.sessionClosedChan:
				if p.connectionClosedHandler != nil {
					p.connectionClosedHandler(session)
				}
				session.Get("wg").(*sync.WaitGroup).Done()
			case <-p.destroyChan:
				if p.destroyHandler != nil {
					p.destroyHandler()
				}
				break outer
			}
		}
	}()

	p.running = true
}

func (p *dispatcher) Broadcast(v interface{}, filter func(session NetSession) bool) error {
	b, err := p.serializer.Serialize(v)
	if err != nil {
		log4g.Error(err)
		return err
	}
	p.mgr.Broadcast(b, filter)
	return nil
}

func (p *dispatcher) BroadcastAll(v interface{}) error {
	b, err := p.serializer.Serialize(v)
	if err != nil {
		log4g.Error(err)
		return err
	}
	p.mgr.BroadcastAll(b)
	return nil
}

func (p *dispatcher) BroadcastOthers(mySession NetSession, v interface{}) error {
	b, err := p.serializer.Serialize(v)
	if err != nil {
		log4g.Error(err)
		return err
	}
	p.mgr.BroadcastOthers(mySession, b)
	return nil
}

func (p *dispatcher) Someone(v interface{}, filter func(session NetSession) bool) error {
	b, err := p.serializer.Serialize(v)
	if err != nil {
		log4g.Error(err)
		return err
	}
	p.mgr.Someone(b, filter)
	return nil
}

func (p *dispatcher) handleConnectionClosed(session NetSession) {
	wg := new(sync.WaitGroup)
	wg.Add(1)
	session.Set("wg", wg)
	p.sessionClosedChan <- session
	wg.Wait()
	session.Remove("wg")
}

func (p *dispatcher) Destroy() {
	p.running = false
	p.destroyChan <- true
	p.wg.Wait()
	//how to close gracefully
	//close(p.dispatchChan)
	//close(p.sessionClosedChan)
	close(p.destroyChan)
}
<|endoftext|>


<commit_before><commit_msg>:arrow_up: Adds personID to the user model to enable a foreign key link to a person<commit_after><|endoftext|>


<commit_before>package asset

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

type Broker struct {
	Name           string  `json:"name"`
	BasicPrice     float64 `json:"basic_price"`
	CommissionRate float64 `json:"commission_rate"`
	MinRate        float64 `json:"min_rate"`
	MaxRate        float64 `json:"max_rate"`
}

type Brokers map[string]Broker

type Order struct {
	brokerAlias          string
	volume               uint32
	target, actual, stop float64
}

func BrokerRegister() (brokers Brokers, err error) {
	file, err := ioutil.ReadFile("./broker.json")
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		os.Exit(1)
	}

	if err = json.Unmarshal(file, &brokers); err != nil {
		return brokers, err
	}

	return brokers, nil
}

func IsBroker(brokerAlias string) (bool, error) {
	register, err := BrokerRegister()
	if err != nil {
		return false, err
	}

	for b := range register {
		if b == brokerAlias {
			return true, nil
		}
	}

	return false, nil
}

func FindBroker(brokerAlias string) (Broker, error) {
	valid, err := IsBroker(brokerAlias)
	if err != nil {
		return Broker{}, err
	}

	if valid {
		register, err := BrokerRegister()
		if err != nil {
			return Broker{}, err
		}

		for b := range register {
			if b == brokerAlias {
				return register[b], nil
			}
		}
	}

	return Broker{}, nil
}

func StopLoss(actual, stop float64) (float64, error) {
	if stop >= actual {
		return actual, &higherLowerError{stop, actual}
	}

	return stop, nil
}

func RiskRewardRatio(o Order) (rrr float64) {
	chance := o.target - o.actual
	risk := o.actual - o.stop
	rrr = RoundDown(float64(chance/risk), 1)

	return
}

func TotalCommission(o Order, brokerAlias string) (commission float64, err error) {
	commission = 0.0

	broker, err := FindBroker(brokerAlias)
	if err != nil {
		return commission, err
	}

	volumeRateBuy := float64(Amount(o)) * o.actual * broker.CommissionRate
	volumeRateSell := float64(Amount(o)) * o.target * broker.CommissionRate

	buySell := []float64{
		volumeRateBuy,
		volumeRateSell,
	}

	for _, bs := range buySell {
		if (bs + broker.BasicPrice) > broker.MinRate {
			if (bs + broker.BasicPrice) > broker.MaxRate {
				commission += broker.MaxRate
			} else {
				commission += (broker.BasicPrice + bs)
			}
		} else {
			commission += broker.MinRate
		}
	}
	commission = RoundUp(float64(commission), 2)

	return
}

func Amount(o Order) (amount uint32) {
	amountFloat := float64(o.volume) / o.actual
	amount = uint32(RoundDown(float64(amountFloat), 0))

	return
}

func Gain(o Order, broker string) (gain float64, err error) {
	amount := Amount(o)
	commission, err := TotalCommission(o, broker)
	if err != nil {
		return 0.0, err
	}

	gain = (float64(amount) * o.target) - float64(o.volume) - commission
	gain = RoundUp(float64(gain), 2)

	return gain, nil
}

func Loss(o Order, broker string) (loss float64, err error) {
	amount := Amount(o)
	commission, err := TotalCommission(o, broker)
	if err != nil {
		return 0.0, err
	}

	loss = float64(o.volume) - (float64(amount) * o.stop) + commission
	loss = RoundUp(float64(loss), 2)

	return loss, nil
}

func Even(o Order, broker string) (even float64, err error) {
	amount := Amount(o)
	commission, err := TotalCommission(o, broker)
	if err != nil {
		return 0.0, err
	}

	even = (float64(o.volume) + commission) / float64(amount)
	even = RoundUp(float64(even), 2)

	return even, nil
}
<commit_msg>add documentation<commit_after>// package asset provides an api to calculate asset price projections.
package asset

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

type Broker struct {
	Name           string  `json:"name"`
	BasicPrice     float64 `json:"basic_price"`
	CommissionRate float64 `json:"commission_rate"`
	MinRate        float64 `json:"min_rate"`
	MaxRate        float64 `json:"max_rate"`
}

type Brokers map[string]Broker

type Order struct {
	brokerAlias          string
	volume               uint32
	target, actual, stop float64
}

// returns a map of type Brokers which contains all static broker data
// defined in brokers.json
func BrokerRegister() (brokers Brokers, err error) {
	// please note that the static data can be outdated and does not contain all
	// brokers.
	// if your broker and it's rates are missing please add it, and don't hesitate
	// to send a pull request to https://github.com/MaximilianMeister/asset
	file, err := ioutil.ReadFile("./broker.json")
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		os.Exit(1)
	}

	if err = json.Unmarshal(file, &brokers); err != nil {
		return brokers, err
	}

	return brokers, nil
}

// returns bool if broker is available
func IsBroker(brokerAlias string) (bool, error) {
	register, err := BrokerRegister()
	if err != nil {
		return false, err
	}

	for b := range register {
		if b == brokerAlias {
			return true, nil
		}
	}

	return false, nil
}

// returns a Broker type with all static data about a single broker
func FindBroker(brokerAlias string) (Broker, error) {
	valid, err := IsBroker(brokerAlias)
	if err != nil {
		return Broker{}, err
	}

	if valid {
		register, err := BrokerRegister()
		if err != nil {
			return Broker{}, err
		}

		for b := range register {
			if b == brokerAlias {
				return register[b], nil
			}
		}
	}

	return Broker{}, nil
}

// returns a stop loss value for an order
func StopLoss(actual, stop float64) (float64, error) {
	if stop >= actual {
		return actual, &higherLowerError{stop, actual}
	}

	return stop, nil
}

// returns a risk reward ratio value for an order
func RiskRewardRatio(o Order) (rrr float64) {
	chance := o.target - o.actual
	risk := o.actual - o.stop
	rrr = RoundDown(float64(chance/risk), 1)

	return
}

// returns the total broker commission fee for an order
func TotalCommission(o Order, brokerAlias string) (commission float64, err error) {
	commission = 0.0

	broker, err := FindBroker(brokerAlias)
	if err != nil {
		return commission, err
	}

	volumeRateBuy := float64(Amount(o)) * o.actual * broker.CommissionRate
	volumeRateSell := float64(Amount(o)) * o.target * broker.CommissionRate

	buySell := []float64{
		volumeRateBuy,
		volumeRateSell,
	}

	for _, bs := range buySell {
		if (bs + broker.BasicPrice) > broker.MinRate {
			if (bs + broker.BasicPrice) > broker.MaxRate {
				commission += broker.MaxRate
			} else {
				commission += (broker.BasicPrice + bs)
			}
		} else {
			commission += broker.MinRate
		}
	}
	commission = RoundUp(float64(commission), 2)

	return
}

// returns the actual amount of stocks that can be bought for an order
func Amount(o Order) (amount uint32) {
	amountFloat := float64(o.volume) / o.actual
	amount = uint32(RoundDown(float64(amountFloat), 0))

	return
}

// returns the highest possible gain for an order
func Gain(o Order, broker string) (gain float64, err error) {
	amount := Amount(o)
	commission, err := TotalCommission(o, broker)
	if err != nil {
		return 0.0, err
	}

	gain = (float64(amount) * o.target) - float64(o.volume) - commission
	gain = RoundUp(float64(gain), 2)

	return gain, nil
}

// returns the highest possible loss for an order
func Loss(o Order, broker string) (loss float64, err error) {
	amount := Amount(o)
	commission, err := TotalCommission(o, broker)
	if err != nil {
		return 0.0, err
	}

	loss = float64(o.volume) - (float64(amount) * o.stop) + commission
	loss = RoundUp(float64(loss), 2)

	return loss, nil
}

// returns the exact break even point for an order
func Even(o Order, broker string) (even float64, err error) {
	amount := Amount(o)
	commission, err := TotalCommission(o, broker)
	if err != nil {
		return 0.0, err
	}

	even = (float64(o.volume) + commission) / float64(amount)
	even = RoundUp(float64(even), 2)

	return even, nil
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pkcs11

import (
	"crypto/ecdsa"
	"crypto/x509"
	"errors"
	"fmt"

	"github.com/miekg/pkcs11"

	"github.com/hyperledger/fabric/bccsp"
)

type ecdsaPrivateKey struct {
	ski    []byte
	pub    ecdsaPublicKey
	handle pkcs11.ObjectHandle
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ecdsaPrivateKey) Bytes() ([]byte, error) {
	return nil, errors.New("Not supported.")
}

// SKI returns the subject key identifier of this key.
func (k *ecdsaPrivateKey) SKI() []byte {
	return k.ski
}

// Symmetric returns true if this key is a symmetric key,
// false if this key is asymmetric
func (k *ecdsaPrivateKey) Symmetric() bool {
	return false
}

// Private returns true if this key is a private key,
// false otherwise.
func (k *ecdsaPrivateKey) Private() bool {
	return true
}

// PublicKey returns the corresponding public key part of an asymmetric public/private key pair.
// This method returns an error in symmetric key schemes.
func (k *ecdsaPrivateKey) PublicKey() (bccsp.Key, error) {
	return &k.pub, nil
}

type ecdsaPublicKey struct {
	ski    []byte
	pub    *ecdsa.PublicKey
	handle pkcs11.ObjectHandle
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ecdsaPublicKey) Bytes() (raw []byte, err error) {
	raw, err = x509.MarshalPKIXPublicKey(k.pub)
	if err != nil {
		return nil, fmt.Errorf("Failed marshalling key [%s]", err)
	}
	return
}

// SKI returns the subject key identifier of this key.
func (k *ecdsaPublicKey) SKI() []byte {
	return k.ski
}

// Symmetric returns true if this key is a symmetric key,
// false if this key is asymmetric
func (k *ecdsaPublicKey) Symmetric() bool {
	return false
}

// Private returns true if this key is a private key,
// false otherwise.
func (k *ecdsaPublicKey) Private() bool {
	return false
}

// PublicKey returns the corresponding public key part of an asymmetric public/private key pair.
// This method returns an error in symmetric key schemes.
func (k *ecdsaPublicKey) PublicKey() (bccsp.Key, error) {
	return k, nil
}
<commit_msg>Organize Imports in ecdsakey.go<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pkcs11

import (
	"crypto/ecdsa"
	"crypto/x509"
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/bccsp"
	"github.com/miekg/pkcs11"
)

type ecdsaPrivateKey struct {
	ski    []byte
	pub    ecdsaPublicKey
	handle pkcs11.ObjectHandle
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ecdsaPrivateKey) Bytes() ([]byte, error) {
	return nil, errors.New("Not supported.")
}

// SKI returns the subject key identifier of this key.
func (k *ecdsaPrivateKey) SKI() []byte {
	return k.ski
}

// Symmetric returns true if this key is a symmetric key,
// false if this key is asymmetric
func (k *ecdsaPrivateKey) Symmetric() bool {
	return false
}

// Private returns true if this key is a private key,
// false otherwise.
func (k *ecdsaPrivateKey) Private() bool {
	return true
}

// PublicKey returns the corresponding public key part of an asymmetric public/private key pair.
// This method returns an error in symmetric key schemes.
func (k *ecdsaPrivateKey) PublicKey() (bccsp.Key, error) {
	return &k.pub, nil
}

type ecdsaPublicKey struct {
	ski    []byte
	pub    *ecdsa.PublicKey
	handle pkcs11.ObjectHandle
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ecdsaPublicKey) Bytes() (raw []byte, err error) {
	raw, err = x509.MarshalPKIXPublicKey(k.pub)
	if err != nil {
		return nil, fmt.Errorf("Failed marshalling key [%s]", err)
	}
	return
}

// SKI returns the subject key identifier of this key.
func (k *ecdsaPublicKey) SKI() []byte {
	return k.ski
}

// Symmetric returns true if this key is a symmetric key,
// false if this key is asymmetric
func (k *ecdsaPublicKey) Symmetric() bool {
	return false
}

// Private returns true if this key is a private key,
// false otherwise.
func (k *ecdsaPublicKey) Private() bool {
	return false
}

// PublicKey returns the corresponding public key part of an asymmetric public/private key pair.
// This method returns an error in symmetric key schemes.
func (k *ecdsaPublicKey) PublicKey() (bccsp.Key, error) {
	return k, nil
}
<|endoftext|>