<commit_before>package main

import (
	"iiif"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"version"

	"github.com/BurntSushi/toml"
	"github.com/hashicorp/golang-lru"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

var tilePath string
var infoCache *lru.Cache
var tileCache *lru.TwoQueueCache

const defaultAddress = ":12415"
const defaultInfoCacheLen = 10000

func main() {
	// Defaults
	viper.SetDefault("Address", defaultAddress)
	viper.SetDefault("InfoCacheLen", defaultInfoCacheLen)

	// Allow all configuration to be in environment variables
	viper.SetEnvPrefix("RAIS")
	viper.AutomaticEnv()

	// Config file options
	viper.SetConfigName("rais")
	viper.AddConfigPath("/etc")
	viper.AddConfigPath(".")
	viper.ReadInConfig()

	// CLI flags
	pflag.String("iiif-url", "", `Base URL for serving IIIF requests, e.g., "http://example.com:8888/images/iiif"`)
	viper.BindPFlag("IIIFURL", pflag.CommandLine.Lookup("iiif-url"))
	pflag.String("address", defaultAddress, "http service address")
	viper.BindPFlag("Address", pflag.CommandLine.Lookup("address"))
	pflag.String("tile-path", "", "Base path for images")
	viper.BindPFlag("TilePath", pflag.CommandLine.Lookup("tile-path"))
	pflag.Int("iiif-info-cache-size", defaultInfoCacheLen, "Maximum cached image info entries (IIIF only)")
	viper.BindPFlag("InfoCacheLen", pflag.CommandLine.Lookup("iiif-info-cache-size"))
	pflag.String("capabilities-file", "", "TOML file describing capabilities, rather than everything RAIS supports")
	viper.BindPFlag("CapabilitiesFile", pflag.CommandLine.Lookup("capabilities-file"))

	pflag.Parse()

	// Make sure required values exist
	if !viper.IsSet("TilePath") {
		log.Println("ERROR: --tile-path is required")
		pflag.Usage()
		os.Exit(1)
	}

	// Pull all values we need for all cases
	tilePath = viper.GetString("TilePath")
	address := viper.GetString("Address")

	// Handle IIIF data only if we have a IIIF URL
	ih := NewImageHandler(tilePath)
	if viper.IsSet("IIIFURL") {
		iiifURL := viper.GetString("IIIFURL")
		iiifBase, err := url.Parse(iiifURL)
		if err == nil && iiifBase.Scheme == "" {
			err = fmt.Errorf("empty scheme")
		}
		if err == nil && iiifBase.Host == "" {
			err = fmt.Errorf("empty host")
		}
		if err == nil && iiifBase.Path == "" {
			err = fmt.Errorf("empty path")
		}
		if err != nil {
			log.Fatalf("Invalid IIIF URL (%s) specified: %s", iiifURL, err)
		}

		icl := viper.GetInt("InfoCacheLen")
		if icl > 0 {
			infoCache, err = lru.New(icl)
			if err != nil {
				log.Fatalf("Unable to start info cache: %s", err)
			}
		}

		tcl := viper.GetInt("TileCacheLen")
		if tcl > 0 {
			log.Printf("Creating a tile cache to hold up to %d tiles", tcl)
			tileCache, err = lru.New2Q(tcl)
			if err != nil {
				log.Fatalf("Unable to start info cache: %s", err)
			}
		}

		log.Printf("IIIF enabled at %s\n", iiifBase.String())
		ih.EnableIIIF(iiifBase)

		if viper.IsSet("CapabilitiesFile") {
			filename := viper.GetString("CapabilitiesFile")
			ih.FeatureSet = &iiif.FeatureSet{}
			_, err := toml.DecodeFile(filename, &ih.FeatureSet)
			if err != nil {
				log.Fatalf("Invalid file or formatting in capabilities file '%s'", filename)
			}
			log.Printf("Setting IIIF capabilities from file '%s'", filename)
		}

		http.HandleFunc(ih.IIIFBase.Path+"/", ih.IIIFRoute)
	}

	http.HandleFunc("/images/tiles/", TileHandler)
	http.HandleFunc("/images/resize/", ResizeHandler)
	http.HandleFunc("/images/dzi/", ih.DZIRoute)
	http.HandleFunc("/version", VersionHandler)
	if err := http.ListenAndServe(address, nil); err != nil {
		log.Fatalf("Error starting listener: %s", err)
	}
}

// VersionHandler spits out the raw version string to the browser
func VersionHandler(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte(version.Version))
}
<commit_msg>Don't listen for DZI unless IIIF is enabled<commit_after>package main

import (
	"iiif"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"version"

	"github.com/BurntSushi/toml"
	"github.com/hashicorp/golang-lru"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

var tilePath string
var infoCache *lru.Cache
var tileCache *lru.TwoQueueCache

const defaultAddress = ":12415"
const defaultInfoCacheLen = 10000

func main() {
	// Defaults
	viper.SetDefault("Address", defaultAddress)
	viper.SetDefault("InfoCacheLen", defaultInfoCacheLen)

	// Allow all configuration to be in environment variables
	viper.SetEnvPrefix("RAIS")
	viper.AutomaticEnv()

	// Config file options
	viper.SetConfigName("rais")
	viper.AddConfigPath("/etc")
	viper.AddConfigPath(".")
	viper.ReadInConfig()

	// CLI flags
	pflag.String("iiif-url", "", `Base URL for serving IIIF requests, e.g., "http://example.com:8888/images/iiif"`)
	viper.BindPFlag("IIIFURL", pflag.CommandLine.Lookup("iiif-url"))
	pflag.String("address", defaultAddress, "http service address")
	viper.BindPFlag("Address", pflag.CommandLine.Lookup("address"))
	pflag.String("tile-path", "", "Base path for images")
	viper.BindPFlag("TilePath", pflag.CommandLine.Lookup("tile-path"))
	pflag.Int("iiif-info-cache-size", defaultInfoCacheLen, "Maximum cached image info entries (IIIF only)")
	viper.BindPFlag("InfoCacheLen", pflag.CommandLine.Lookup("iiif-info-cache-size"))
	pflag.String("capabilities-file", "", "TOML file describing capabilities, rather than everything RAIS supports")
	viper.BindPFlag("CapabilitiesFile", pflag.CommandLine.Lookup("capabilities-file"))

	pflag.Parse()

	// Make sure required values exist
	if !viper.IsSet("TilePath") {
		log.Println("ERROR: --tile-path is required")
		pflag.Usage()
		os.Exit(1)
	}

	// Pull all values we need for all cases
	tilePath = viper.GetString("TilePath")
	address := viper.GetString("Address")

	// Handle IIIF data only if we have a IIIF URL
	ih := NewImageHandler(tilePath)
	if viper.IsSet("IIIFURL") {
		iiifURL := viper.GetString("IIIFURL")
		iiifBase, err := url.Parse(iiifURL)
		if err == nil && iiifBase.Scheme == "" {
			err = fmt.Errorf("empty scheme")
		}
		if err == nil && iiifBase.Host == "" {
			err = fmt.Errorf("empty host")
		}
		if err == nil && iiifBase.Path == "" {
			err = fmt.Errorf("empty path")
		}
		if err != nil {
			log.Fatalf("Invalid IIIF URL (%s) specified: %s", iiifURL, err)
		}

		icl := viper.GetInt("InfoCacheLen")
		if icl > 0 {
			infoCache, err = lru.New(icl)
			if err != nil {
				log.Fatalf("Unable to start info cache: %s", err)
			}
		}

		tcl := viper.GetInt("TileCacheLen")
		if tcl > 0 {
			log.Printf("Creating a tile cache to hold up to %d tiles", tcl)
			tileCache, err = lru.New2Q(tcl)
			if err != nil {
				log.Fatalf("Unable to start info cache: %s", err)
			}
		}

		log.Printf("IIIF enabled at %s\n", iiifBase.String())
		ih.EnableIIIF(iiifBase)

		if viper.IsSet("CapabilitiesFile") {
			filename := viper.GetString("CapabilitiesFile")
			ih.FeatureSet = &iiif.FeatureSet{}
			_, err := toml.DecodeFile(filename, &ih.FeatureSet)
			if err != nil {
				log.Fatalf("Invalid file or formatting in capabilities file '%s'", filename)
			}
			log.Printf("Setting IIIF capabilities from file '%s'", filename)
		}

		http.HandleFunc(ih.IIIFBase.Path+"/", ih.IIIFRoute)
		http.HandleFunc("/images/dzi/", ih.DZIRoute)
	}

	http.HandleFunc("/images/tiles/", TileHandler)
	http.HandleFunc("/images/resize/", ResizeHandler)
	http.HandleFunc("/version", VersionHandler)
	if err := http.ListenAndServe(address, nil); err != nil {
		log.Fatalf("Error starting listener: %s", err)
	}
}

// VersionHandler spits out the raw version string to the browser
func VersionHandler(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte(version.Version))
}
<|endoftext|>


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

package server

import (
	"bytes"
	"log"
	"os"
	"path/filepath"
	"strings"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	lumberjack "gopkg.in/natefinch/lumberjack.v2"
)

type LoggingFormat int8

const (
	JSONFormat LoggingFormat = iota - 1
	StackdriverFormat
)

func SetupLogging(tmpLogger *zap.Logger, config Config) (*zap.Logger, *zap.Logger) {
	zapLevel := zapcore.InfoLevel
	switch strings.ToLower(config.GetLogger().Level) {
	case "debug":
		zapLevel = zapcore.DebugLevel
	case "info":
		zapLevel = zapcore.InfoLevel
	case "warn":
		zapLevel = zapcore.WarnLevel
	case "error":
		zapLevel = zapcore.ErrorLevel
	default:
		tmpLogger.Fatal("Logger level invalid, must be one of: DEBUG, INFO, WARN, or ERROR")
	}

	format := JSONFormat
	switch strings.ToLower(config.GetLogger().Format) {
	case "":
		fallthrough
	case "json":
		format = JSONFormat
	case "stackdriver":
		format = StackdriverFormat
	default:
		tmpLogger.Fatal("Logger mode invalid, must be one of: '', 'json', or 'stackdriver")
	}

	consoleLogger := NewJSONLogger(os.Stdout, zapLevel, format)
	var fileLogger *zap.Logger
	if config.GetLogger().Rotation {
		fileLogger = NewRotatingJSONFileLogger(consoleLogger, config, zapLevel, format)
	} else {
		fileLogger = NewJSONFileLogger(consoleLogger, config.GetLogger().File, zapLevel, format)
	}

	if fileLogger != nil {
		multiLogger := NewMultiLogger(consoleLogger, fileLogger)

		if config.GetLogger().Stdout {
			RedirectStdLog(multiLogger)
			return multiLogger, multiLogger
		}
		RedirectStdLog(fileLogger)
		return fileLogger, multiLogger
	}

	RedirectStdLog(consoleLogger)
	return consoleLogger, consoleLogger
}

func NewJSONFileLogger(consoleLogger *zap.Logger, fileName string, level zapcore.Level, format LoggingFormat) *zap.Logger {
	if len(fileName) == 0 {
		return nil
	}

	output, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		consoleLogger.Fatal("Could not create log file", zap.Error(err))
		return nil
	}

	return NewJSONLogger(output, level, format)
}

func NewRotatingJSONFileLogger(consoleLogger *zap.Logger, config Config, level zapcore.Level, format LoggingFormat) *zap.Logger {
	fileName := config.GetLogger().File
	if len(fileName) == 0 {
		consoleLogger.Fatal("Rotating log file is enabled but log file name is empty")
		return nil
	}

	logDir := filepath.Dir(fileName)
	if _, err := os.Stat(logDir); os.IsNotExist(err) {
		if err := os.MkdirAll(logDir, 0755); err != nil {
			consoleLogger.Fatal("Could not create log directory", zap.Error(err))
			return nil
		}
	}

	jsonEncoder := newJSONEncoder(format)

	// lumberjack.Logger is already safe for concurrent use, so we don't need to lock it.
	writeSyncer := zapcore.AddSync(&lumberjack.Logger{
		Filename:   fileName,
		MaxSize:    config.GetLogger().MaxSize,
		MaxAge:     config.GetLogger().MaxAge,
		MaxBackups: config.GetLogger().MaxBackups,
		LocalTime:  config.GetLogger().LocalTime,
		Compress:   config.GetLogger().Compress,
	})
	core := zapcore.NewCore(jsonEncoder, writeSyncer, level)
	options := []zap.Option{zap.AddCaller()}
	return zap.New(core, options...)
}

func NewMultiLogger(loggers ...*zap.Logger) *zap.Logger {
	cores := make([]zapcore.Core, 0, len(loggers))
	for _, logger := range loggers {
		cores = append(cores, logger.Core())
	}

	teeCore := zapcore.NewTee(cores...)
	options := []zap.Option{zap.AddCaller()}
	return zap.New(teeCore, options...)
}

func NewJSONLogger(output *os.File, level zapcore.Level, format LoggingFormat) *zap.Logger {
	jsonEncoder := newJSONEncoder(format)

	core := zapcore.NewCore(jsonEncoder, zapcore.Lock(output), level)
	options := []zap.Option{zap.AddCaller()}
	return zap.New(core, options...)
}

// Create a new JSON log encoder with the correct settings.
func newJSONEncoder(format LoggingFormat) zapcore.Encoder {
	if format == StackdriverFormat {
		return zapcore.NewJSONEncoder(zapcore.EncoderConfig{
			TimeKey:        "timestamp",
			LevelKey:       "severity",
			NameKey:        "logger",
			CallerKey:      "caller",
			MessageKey:     "message",
			StacktraceKey:  "stacktrace",
			EncodeLevel:    StackdriverLevelEncoder,
			EncodeTime:     zapcore.RFC3339NanoTimeEncoder,
			EncodeDuration: zapcore.StringDurationEncoder,
			EncodeCaller:   zapcore.ShortCallerEncoder,
		})
	}

	return zapcore.NewJSONEncoder(zapcore.EncoderConfig{
		TimeKey:        "ts",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	})
}

func StackdriverLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
	switch l {
	case zapcore.DebugLevel:
		enc.AppendString("DEBUG")
	case zapcore.InfoLevel:
		enc.AppendString("INFO")
	case zapcore.WarnLevel:
		enc.AppendString("WARNING")
	case zapcore.ErrorLevel:
		enc.AppendString("ERROR")
	case zapcore.DPanicLevel:
		enc.AppendString("CRITICAL")
	case zapcore.PanicLevel:
		enc.AppendString("CRITICAL")
	case zapcore.FatalLevel:
		enc.AppendString("CRITICAL")
	default:
		enc.AppendString("DEFAULT")
	}
}

type RedirectStdLogWriter struct {
	logger *zap.Logger
}

func (r *RedirectStdLogWriter) Write(p []byte) (int, error) {
	s := string(bytes.TrimSpace(p))
	if strings.HasPrefix(s, "http: panic serving") {
		r.logger.Error(s)
	} else {
		r.logger.Info(s)
	}
	return len(s), nil
}

func RedirectStdLog(logger *zap.Logger) {
	log.SetFlags(0)
	log.SetPrefix("")
	skipLogger := logger.WithOptions(zap.AddCallerSkip(3))
	log.SetOutput(&RedirectStdLogWriter{skipLogger})
}
<commit_msg>Rename Stackdriver log field 'timestamp' to 'time'. (#764)<commit_after>// Copyright 2018 The Nakama Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"bytes"
	"log"
	"os"
	"path/filepath"
	"strings"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	lumberjack "gopkg.in/natefinch/lumberjack.v2"
)

type LoggingFormat int8

const (
	JSONFormat LoggingFormat = iota - 1
	StackdriverFormat
)

func SetupLogging(tmpLogger *zap.Logger, config Config) (*zap.Logger, *zap.Logger) {
	zapLevel := zapcore.InfoLevel
	switch strings.ToLower(config.GetLogger().Level) {
	case "debug":
		zapLevel = zapcore.DebugLevel
	case "info":
		zapLevel = zapcore.InfoLevel
	case "warn":
		zapLevel = zapcore.WarnLevel
	case "error":
		zapLevel = zapcore.ErrorLevel
	default:
		tmpLogger.Fatal("Logger level invalid, must be one of: DEBUG, INFO, WARN, or ERROR")
	}

	format := JSONFormat
	switch strings.ToLower(config.GetLogger().Format) {
	case "":
		fallthrough
	case "json":
		format = JSONFormat
	case "stackdriver":
		format = StackdriverFormat
	default:
		tmpLogger.Fatal("Logger mode invalid, must be one of: '', 'json', or 'stackdriver")
	}

	consoleLogger := NewJSONLogger(os.Stdout, zapLevel, format)
	var fileLogger *zap.Logger
	if config.GetLogger().Rotation {
		fileLogger = NewRotatingJSONFileLogger(consoleLogger, config, zapLevel, format)
	} else {
		fileLogger = NewJSONFileLogger(consoleLogger, config.GetLogger().File, zapLevel, format)
	}

	if fileLogger != nil {
		multiLogger := NewMultiLogger(consoleLogger, fileLogger)

		if config.GetLogger().Stdout {
			RedirectStdLog(multiLogger)
			return multiLogger, multiLogger
		}
		RedirectStdLog(fileLogger)
		return fileLogger, multiLogger
	}

	RedirectStdLog(consoleLogger)
	return consoleLogger, consoleLogger
}

func NewJSONFileLogger(consoleLogger *zap.Logger, fileName string, level zapcore.Level, format LoggingFormat) *zap.Logger {
	if len(fileName) == 0 {
		return nil
	}

	output, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		consoleLogger.Fatal("Could not create log file", zap.Error(err))
		return nil
	}

	return NewJSONLogger(output, level, format)
}

func NewRotatingJSONFileLogger(consoleLogger *zap.Logger, config Config, level zapcore.Level, format LoggingFormat) *zap.Logger {
	fileName := config.GetLogger().File
	if len(fileName) == 0 {
		consoleLogger.Fatal("Rotating log file is enabled but log file name is empty")
		return nil
	}

	logDir := filepath.Dir(fileName)
	if _, err := os.Stat(logDir); os.IsNotExist(err) {
		if err := os.MkdirAll(logDir, 0755); err != nil {
			consoleLogger.Fatal("Could not create log directory", zap.Error(err))
			return nil
		}
	}

	jsonEncoder := newJSONEncoder(format)

	// lumberjack.Logger is already safe for concurrent use, so we don't need to lock it.
	writeSyncer := zapcore.AddSync(&lumberjack.Logger{
		Filename:   fileName,
		MaxSize:    config.GetLogger().MaxSize,
		MaxAge:     config.GetLogger().MaxAge,
		MaxBackups: config.GetLogger().MaxBackups,
		LocalTime:  config.GetLogger().LocalTime,
		Compress:   config.GetLogger().Compress,
	})
	core := zapcore.NewCore(jsonEncoder, writeSyncer, level)
	options := []zap.Option{zap.AddCaller()}
	return zap.New(core, options...)
}

func NewMultiLogger(loggers ...*zap.Logger) *zap.Logger {
	cores := make([]zapcore.Core, 0, len(loggers))
	for _, logger := range loggers {
		cores = append(cores, logger.Core())
	}

	teeCore := zapcore.NewTee(cores...)
	options := []zap.Option{zap.AddCaller()}
	return zap.New(teeCore, options...)
}

func NewJSONLogger(output *os.File, level zapcore.Level, format LoggingFormat) *zap.Logger {
	jsonEncoder := newJSONEncoder(format)

	core := zapcore.NewCore(jsonEncoder, zapcore.Lock(output), level)
	options := []zap.Option{zap.AddCaller()}
	return zap.New(core, options...)
}

// Create a new JSON log encoder with the correct settings.
func newJSONEncoder(format LoggingFormat) zapcore.Encoder {
	if format == StackdriverFormat {
		return zapcore.NewJSONEncoder(zapcore.EncoderConfig{
			TimeKey:        "time",
			LevelKey:       "severity",
			NameKey:        "logger",
			CallerKey:      "caller",
			MessageKey:     "message",
			StacktraceKey:  "stacktrace",
			EncodeLevel:    StackdriverLevelEncoder,
			EncodeTime:     zapcore.RFC3339NanoTimeEncoder,
			EncodeDuration: zapcore.StringDurationEncoder,
			EncodeCaller:   zapcore.ShortCallerEncoder,
		})
	}

	return zapcore.NewJSONEncoder(zapcore.EncoderConfig{
		TimeKey:        "ts",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	})
}

func StackdriverLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
	switch l {
	case zapcore.DebugLevel:
		enc.AppendString("DEBUG")
	case zapcore.InfoLevel:
		enc.AppendString("INFO")
	case zapcore.WarnLevel:
		enc.AppendString("WARNING")
	case zapcore.ErrorLevel:
		enc.AppendString("ERROR")
	case zapcore.DPanicLevel:
		enc.AppendString("CRITICAL")
	case zapcore.PanicLevel:
		enc.AppendString("CRITICAL")
	case zapcore.FatalLevel:
		enc.AppendString("CRITICAL")
	default:
		enc.AppendString("DEFAULT")
	}
}

type RedirectStdLogWriter struct {
	logger *zap.Logger
}

func (r *RedirectStdLogWriter) Write(p []byte) (int, error) {
	s := string(bytes.TrimSpace(p))
	if strings.HasPrefix(s, "http: panic serving") {
		r.logger.Error(s)
	} else {
		r.logger.Info(s)
	}
	return len(s), nil
}

func RedirectStdLog(logger *zap.Logger) {
	log.SetFlags(0)
	log.SetPrefix("")
	skipLogger := logger.WithOptions(zap.AddCallerSkip(3))
	log.SetOutput(&RedirectStdLogWriter{skipLogger})
}
<|endoftext|>


<commit_before>package cgroup

import (
	"path/filepath"
	"os/exec"
)

const (
	cgCPUPathPrefix    = "/sys/fs/cgroup/cpu/"
	cgMemoryPathPrefix = "/sys/fs/cgroup/memory/"
)

func InitCGroup(pid, containerID, memory string) error {
	if err := cpuCGroup(pid, containerID); err != nil {
		return err
	}

	if err := memoryCGroup(pid, containerID, memory); err != nil {
		return err
	}

	return nil
}

func cpuCGroup(pid, containerID string) error {
	cgCPUPath := filepath.Join(cgCPUPathPrefix, containerID)

	// add sub cgroup system
	mkdirCmd := exec.Command("/usr/bin/mkdir", cgCPUPath)
	if err := mkdirCmd.Run(); err != nil {
		return err
	}

	// add current pid to cgroup cpu
	taskCmd := exec.Command("/usr/bin/echo", pid, ">", filepath.Join(cgCPUPath, "/tasks"))
	if err := taskCmd.Run(); err != nil {
		return err
	}

	// cpu usage max up to 2%
	quotaCmd := exec.Command("/usr/bin/echo", "2000", ">", filepath.Join(cgCPUPath, "/cpu.cfs_quota_us"))
	if err := quotaCmd.Run(); err != nil {
		return err
	}

	return nil
}

func memoryCGroup(pid, containerID, memory string) error {
	cgMemoryPath := filepath.Join(cgMemoryPathPrefix, containerID)

	// add sub cgroup system
	mkdirCmd := exec.Command("/usr/bin/mkdir", cgMemoryPath)
	if err := mkdirCmd.Run(); err != nil {
		return err
	}

	// add current pid to cgroup memory
	taskCmd := exec.Command("/usr/bin/echo", pid, ">", filepath.Join(cgMemoryPath, "/tasks"))
	if err := taskCmd.Run(); err != nil {
		return err
	}

	// set memory usage limitation
	quotaMemoryCmd := exec.Command("/usr/bin/echo", memory+"m", ">", filepath.Join(cgMemoryPath, "/memory.limit_in_bytes"))
	if err := quotaMemoryCmd.Run(); err != nil {
		return err
	}

	quotaMemorySwapCmd := exec.Command("/usr/bin/echo", memory+"m", ">", filepath.Join(cgMemoryPath, "/memory.memsw.limit_in_bytes"))
	if err := quotaMemorySwapCmd.Run(); err != nil {
		return err
	}

	return nil
}

func Cleanup(containerID string) error {
	cleanCPUCommand := exec.Command("rmdir", cgCPUPathPrefix + containerID)
	if cpuErr := cleanCPUCommand.Run(); cpuErr != nil {
		return cpuErr
	}

	cleanMemoryCommand := exec.Command("rmdir", cgMemoryPathPrefix + containerID)
	if cpuErr := cleanMemoryCommand.Run(); cpuErr != nil {
		return cpuErr
	}

	return nil
}
<commit_msg>add pid cgroup limitation<commit_after>package cgroup

import (
	"path/filepath"
	"os/exec"
)

const (
	cgCPUPathPrefix    = "/sys/fs/cgroup/cpu/"
	cgPidPathPrefix    = "/sys/fs/cgroup/pids/"
	cgMemoryPathPrefix = "/sys/fs/cgroup/memory/"
)

func InitCGroup(pid, containerID, memory string) error {
	if err := cpuCGroup(pid, containerID); err != nil {
		return err
	}

	if err := pidCGroup(pid, containerID); err != nil {
		return err
	}

	if err := memoryCGroup(pid, containerID, memory); err != nil {
		return err
	}

	return nil
}

func cpuCGroup(pid, containerID string) error {
	cgCPUPath := filepath.Join(cgCPUPathPrefix, containerID)

	// add sub cgroup system
	mkdirCmd := exec.Command("/usr/bin/mkdir", cgCPUPath)
	if err := mkdirCmd.Run(); err != nil {
		return err
	}

	// add current pid to cgroup cpu
	taskCmd := exec.Command("/usr/bin/echo", pid, ">", filepath.Join(cgCPUPath, "/tasks"))
	if err := taskCmd.Run(); err != nil {
		return err
	}

	// cpu usage max up to 2%
	quotaCmd := exec.Command("/usr/bin/echo", "2000", ">", filepath.Join(cgCPUPath, "/cpu.cfs_quota_us"))
	if err := quotaCmd.Run(); err != nil {
		return err
	}

	return nil
}

func pidCGroup(pid, containerID string) error {
	cgPidPath := filepath.Join(cgPidPathPrefix, containerID)

	// add sub cgroup system
	mkdirCmd := exec.Command("/usr/bin/mkdir", cgPidPath)
	if err := mkdirCmd.Run(); err != nil {
		return err
	}

	// add current pid to cgroup pids
	taskCmd := exec.Command("/usr/bin/echo", pid, ">", filepath.Join(cgPidPath, "/cgroup.procs"))
	if err := taskCmd.Run(); err != nil {
		return err
	}

	// max limitation on fork() and clone()
	// https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt
	quotaCmd := exec.Command("/usr/bin/echo", "4", ">", filepath.Join(cgPidPath, "/pids.max"))
	if err := quotaCmd.Run(); err != nil {
		return err
	}

	return nil
}

func memoryCGroup(pid, containerID, memory string) error {
	cgMemoryPath := filepath.Join(cgMemoryPathPrefix, containerID)

	// add sub cgroup system
	mkdirCmd := exec.Command("/usr/bin/mkdir", cgMemoryPath)
	if err := mkdirCmd.Run(); err != nil {
		return err
	}

	// add current pid to cgroup memory
	taskCmd := exec.Command("/usr/bin/echo", pid, ">", filepath.Join(cgMemoryPath, "/tasks"))
	if err := taskCmd.Run(); err != nil {
		return err
	}

	// set memory usage limitation
	quotaMemoryCmd := exec.Command("/usr/bin/echo", memory+"m", ">", filepath.Join(cgMemoryPath, "/memory.limit_in_bytes"))
	if err := quotaMemoryCmd.Run(); err != nil {
		return err
	}

	quotaMemorySwapCmd := exec.Command("/usr/bin/echo", memory+"m", ">", filepath.Join(cgMemoryPath, "/memory.memsw.limit_in_bytes"))
	if err := quotaMemorySwapCmd.Run(); err != nil {
		return err
	}

	return nil
}

func Cleanup(containerID string) error {
	cleanCPUCommand := exec.Command("rmdir", filepath.Join(cgCPUPathPrefix, containerID))
	if err := cleanCPUCommand.Run(); err != nil {
		return err
	}

	cleanPidCommand := exec.Command("rmdir", filepath.Join(cgPidPathPrefix, containerID))
	if err := cleanPidCommand.Run(); err != nil {
		return err
	}

	cleanMemoryCommand := exec.Command("rmdir", filepath.Join(cgMemoryPathPrefix, containerID))
	if err := cleanMemoryCommand.Run(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"github.com/andreaskoch/docs/path"
	"github.com/andreaskoch/docs/renderer"
	"github.com/andreaskoch/docs/repository"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

var routes map[string]repository.Pather

func Serve(repositoryPaths []string) {

	// An array of all indices for
	// the given repositories.
	indices := renderer.RenderRepositories(repositoryPaths)

	// Initialize the routing table
	initializeRoutes(indices)

	var error404Handler = func(w http.ResponseWriter, r *http.Request) {
		requestedPath := r.URL.Path
		fmt.Fprintf(w, "Not found: %v", requestedPath)
	}

	var itemHandler = func(w http.ResponseWriter, r *http.Request) {
		requestedPath := r.URL.Path

		fmt.Println(requestedPath)

		item, ok := routes[requestedPath]
		if !ok {

			// check for fallbacks before returning a 404
			item = getFallbackItem(requestedPath)
			if item == nil {
				error404Handler(w, r)
				return
			}
		}

		data, err := ioutil.ReadFile(item.Path())
		if err != nil {
			error404Handler(w, r)
			return
		}

		fmt.Fprintf(w, "%s", data)
	}

	var indexDebugger = func(w http.ResponseWriter, r *http.Request) {
		for route, _ := range routes {
			fmt.Fprintln(w, route)
		}
	}

	// register handlers
	http.HandleFunc("/", itemHandler)
	http.HandleFunc("/debug/index", indexDebugger)

	// start http server
	http.ListenAndServe(":8080", nil)
}

func getFallbackItem(requestedPath string) repository.Pather {

	if strings.HasSuffix(requestedPath, "/index.html") {
		return nil
	}

	newRoute := strings.TrimRight(requestedPath, "/") + "/" + "index.html"
	if item, ok := routes[newRoute]; ok {
		return item
	}

	return nil
}

func initializeRoutes(indices []*repository.ItemIndex) {

	routes = make(map[string]repository.Pather)

	for _, index := range indices {

		pathProvider := path.NewProvider(index.Path())

		updateRouteTable := func(item *repository.Item) {

			// get the item route and
			// add it to the routing table
			registerRoute(pathProvider, item)

			// get the file routes and
			// add them to the routing table
			for _, file := range item.Files.Items() {
				registerRoute(pathProvider, file)
			}
		}

		index.Walk(func(item *repository.Item) {

			// add the current item to the route table
			updateRouteTable(item)

			// update route table again if item changes
			item.RegisterOnChangeCallback("UpdateRouteTableOnChange", func(i *repository.Item) {
				updateRouteTable(i)
			})
		})

	}
}

func registerRoute(pathProvider *path.Provider, pather repository.Pather) {

	if pather == nil {
		log.Printf("Cannot add a route for an uninitialized item %q.\n", pather.Path())
		return
	}

	route := pathProvider.GetWebRoute(pather)

	if strings.TrimSpace(route) == "" {
		log.Printf("Cannot add an empty route to the routing table. Item %q\n", pather.Path())
		return
	}

	routes[route] = pather
}
<commit_msg>Server feature: Redirect to the fallback route instead of rendering the found fallback directly.<commit_after>package server

import (
	"fmt"
	"github.com/andreaskoch/docs/path"
	"github.com/andreaskoch/docs/renderer"
	"github.com/andreaskoch/docs/repository"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

var routes map[string]repository.Pather

func Serve(repositoryPaths []string) {

	// An array of all indices for
	// the given repositories.
	indices := renderer.RenderRepositories(repositoryPaths)

	// Initialize the routing table
	initializeRoutes(indices)

	var error404Handler = func(w http.ResponseWriter, r *http.Request) {
		requestedPath := r.URL.Path
		fmt.Fprintf(w, "Not found: %v", requestedPath)
	}

	var itemHandler = func(w http.ResponseWriter, r *http.Request) {
		requestedPath := r.URL.Path

		fmt.Println(requestedPath)

		item, ok := routes[requestedPath]
		if !ok {

			// check for fallbacks before returning a 404
			if fallbackRoute, fallbackRouteFound := getFallbackRoute(requestedPath); fallbackRouteFound {
				redirect(w, r, fallbackRoute)
				return
			}

			error404Handler(w, r)
			return
		}

		data, err := ioutil.ReadFile(item.Path())
		if err != nil {
			error404Handler(w, r)
			return
		}

		fmt.Fprintf(w, "%s", data)
	}

	var indexDebugger = func(w http.ResponseWriter, r *http.Request) {
		for route, _ := range routes {
			fmt.Fprintln(w, route)
		}
	}

	// register handlers
	http.HandleFunc("/", itemHandler)
	http.HandleFunc("/debug/index", indexDebugger)

	// start http server
	http.ListenAndServe(":8080", nil)
}

func redirect(w http.ResponseWriter, r *http.Request, route string) {
	http.Redirect(w, r, route, http.StatusMovedPermanently)
}

func getFallbackRoute(requestedPath string) (fallbackRoute string, found bool) {

	if strings.HasSuffix(requestedPath, "/index.html") {
		return "", false
	}

	route := strings.TrimRight(requestedPath, "/") + "/" + "index.html"
	if _, ok := routes[route]; ok {
		return route, true
	}

	return "", false
}

func initializeRoutes(indices []*repository.ItemIndex) {

	routes = make(map[string]repository.Pather)

	for _, index := range indices {

		pathProvider := path.NewProvider(index.Path())

		updateRouteTable := func(item *repository.Item) {

			// get the item route and
			// add it to the routing table
			registerRoute(pathProvider, item)

			// get the file routes and
			// add them to the routing table
			for _, file := range item.Files.Items() {
				registerRoute(pathProvider, file)
			}
		}

		index.Walk(func(item *repository.Item) {

			// add the current item to the route table
			updateRouteTable(item)

			// update route table again if item changes
			item.RegisterOnChangeCallback("UpdateRouteTableOnChange", func(i *repository.Item) {
				updateRouteTable(i)
			})
		})

	}
}

func registerRoute(pathProvider *path.Provider, pather repository.Pather) {

	if pather == nil {
		log.Printf("Cannot add a route for an uninitialized item %q.\n", pather.Path())
		return
	}

	route := pathProvider.GetWebRoute(pather)

	if strings.TrimSpace(route) == "" {
		log.Printf("Cannot add an empty route to the routing table. Item %q\n", pather.Path())
		return
	}

	routes[route] = pather
}
<|endoftext|>


<commit_before>package server

import (
    "log"
    "net/http"

    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"

    "github.com/orangesys/orangeapi/controller"
    "github.com/orangesys/orangeapi/common"
)

func accessible(c echo.Context) error {
    return c.String(http.StatusOK, "Accessible")
}

func create(c echo.Context) error {
	rp   := c.QueryParam("rp")
	uuid := c.QueryParam("uuid")

        pvc := map[string]string{
             "10d": "10Gi",
             "40d": "50Gi",
             "400d": "100Gi",
        }
	wp := "mypassword"
//	uuid := "iGzNX6QzfudVlwKtR8CQCj0itIU2"

	if _, ok := pvc[rp]; ok {
		if err := controller.CheckConsumer(uuid); err != nil {
			return c.String(http.StatusNotFound, "Not Found UUID in Firebase")
		}

		name := common.ReleaseName()
		if err := controller.CreateConsumer(name, wp, uuid); err != nil {
			return c.String(http.StatusNotFound, "Some Wrong with UUI or RP")
		}
		return c.String(http.StatusOK, "Processing")
	}

	return c.String(http.StatusNotFound, "Not Found Retention Plan with RP")
}

func Run() {
    log.Println("Starting orangeapi...")
    e := echo.New()

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
	AllowOrigins: []string{"*"},
	AllowMethods: []string{
		echo.GET,
		echo.POST,
	},
    }))

    // Login route
    e.POST("/create", create)

    // Unauthenticated route
    e.GET("/", accessible)

    e.Logger.Fatal(e.Start(":1323"))
}
<commit_msg>fix server<commit_after>package server

import (
    "log"
    "net/http"

    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"

    "github.com/orangesys/orangeapi/controller"
    "github.com/orangesys/orangeapi/common"
)

func accessible(c echo.Context) error {
    return c.String(http.StatusOK, "Accessible")
}

func create(c echo.Context) error {
  	retention   := c.QueryParam("rp")
  	uuid := c.QueryParam("uuid")
    pvc := map[string]string{
         "10d": "10Gi",
         "40d": "50Gi",
         "400d": "100Gi",
    }

//	uuid := "iGzNX6QzfudVlwKtR8CQCj0itIU2"
  	if pvcsize, ok := pvc[retention]; ok {
    		if err := controller.CheckConsumer(uuid); err != nil {
            log.Println(err)
      			return c.String(http.StatusNotFound, "Not Found UUID in Firebase")
    		}

    		name := common.ReleaseName()
    		if err := controller.CreateConsumer(name, retention, pvcsize, uuid); err != nil {
            log.Println(err)
      			return c.String(http.StatusNotFound, "Some Wrong with UUI or RP")
    		}
    		return c.String(http.StatusOK, "Processing")
    	  }
  	return c.String(http.StatusNotFound, "Not Found Retention Plan with RP")
}

func Run() {
    log.Println("Starting orangeapi...")
    e := echo.New()

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
      	AllowOrigins: []string{"*"},
      	AllowMethods: []string{
      		echo.GET,
      		echo.POST,
      	},
    }))

    // Login route
    e.POST("/create", create)

    // Unauthenticated route
    e.GET("/", accessible)

    e.Logger.Fatal(e.Start(":1323"))
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"code.google.com/p/go.crypto/ssh"
)

type CertificateCollection map[string][]*ssh.Certificate

type CertificateParameters struct {
	CertType   string
	User       string
	Permission map[string][]string
	PrivateKey string
	Key        string //public key (base 64 encoded bytes converted to string)
}

type RevokeInfo struct {
	User string
	Key  string
}

var Certificates CertificateCollection

func init() {
	Certificates = make(CertificateCollection)
}

func (c CertificateCollection) New(params CertificateParameters) error {
	privateKeyBytes, err := ioutil.ReadFile(params.PrivateKey)
	if err != nil {
		return err
	}

	authority, err := ssh.ParsePrivateKey(privateKeyBytes) // the private key used to sign the certificate.
	if err != nil {
		return err
	}

	keyToSignBytes := []byte(params.Key)
	keyToSign, _, _, _, err := ssh.ParseAuthorizedKey(keyToSignBytes)
	if err != nil {
		return err
	}

	if keyToSign == nil {
		panic("public key is nil")
	}

	cert := &ssh.Certificate{
		Nonce:       []byte{},
		Key:         keyToSign,
		CertType:    ssh.UserCert,
		KeyId:       "user_" + params.User,
		ValidBefore: ssh.CertTimeInfinity, // this will change in later versions
		Permissions: ssh.Permissions{
			CriticalOptions: map[string]string{},
			Extensions:      map[string]string{},
		},
		ValidPrincipals: []string{params.User},
	}

	for _, perm := range params.Permission["extensions"] {
		cert.Permissions.Extensions[perm] = ""
	}

	for _, criticalOpts := range params.Permission["criticalOptions"] {
		cert.Permissions.CriticalOptions[criticalOpts] = ""
	}

	err = cert.SignCert(rand.Reader, authority)
	if err != nil {
		return err
	}

	// add newly created cert to the global map + write to local disk (for now).
	certs, ok := c[params.User]
	if !ok {
		c[params.User] = []*ssh.Certificate{cert}
	} else {
		c[params.User] = append(certs, cert)
	}

	err = ioutil.WriteFile(os.ExpandEnv("$HOME/.ssh/id_rsa-cert.pub"), ssh.MarshalAuthorizedKey(cert), 0600)
	if err != nil {
		return err
	}

	return nil
}

func (c CertificateCollection) Revoke(revokeInfo RevokeInfo) error {
	key := revokeInfo.Key

	if _, ok := c[key]; !ok {
		return fmt.Errorf("user %s does not exist", revokeInfo.User)
	}

	delete(c, key)
	return nil
}

// SignHandler creates a new certificate from the parameters specified in the request.
func SignHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var params CertificateParameters

	err := decoder.Decode(&params)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err.Error())
		return
	}

	err = Certificates.New(params)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err.Error())
	}
}

// TODO: abstract the decode code into a common function, will have to use type inference for that to work.
// TODO: verify correct http error code being used.
func RevokeHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var revokeInfo RevokeInfo

	err := decoder.Decode(&revokeInfo)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err.Error())
		return
	}

	err = Certificates.Revoke(revokeInfo)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err.Error())
	}
}

func main() {
	http.HandleFunc("/v1/sign", SignHandler)
	http.HandleFunc("/v1/revoke", RevokeHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>fingerprint incorporated, as well as revoke mechanism<commit_after>package main

import (
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"code.google.com/p/go.crypto/ssh"
)

type CertificateCollection map[[16]byte][]*ssh.Certificate

type CertificateParameters struct {
	CertType   string
	User       string
	Permission map[string][]string
	PrivateKey string
	Key        string //public key (base 64 encoded bytes converted to string)
}

type RevokeInfo struct {
	User string
	Key  string
}

var Certificates CertificateCollection

func init() {
	Certificates = make(CertificateCollection)
}

func (c CertificateCollection) New(params CertificateParameters) error {
	privateKeyBytes, err := ioutil.ReadFile(params.PrivateKey)
	if err != nil {
		return err
	}

	authority, err := ssh.ParsePrivateKey(privateKeyBytes) // the private key used to sign the certificate.
	if err != nil {
		return err
	}

	keyToSignBytes := []byte(params.Key)
	keyToSign, _, _, _, err := ssh.ParseAuthorizedKey(keyToSignBytes)
	if err != nil {
		return err
	}

	if keyToSign == nil {
		panic("public key is nil")
	}

	// encoded := strings.Split(params.Key, " ")[1]
	// data, err := base64.StdEncoding.DecodeString(encoded)
	// if err != nil {
	// 	fmt.Println("error:", err)
	// 	return err
	// }
	// fmt.Printf("fingerPrint: % x", md5.Sum(data))

	cert := &ssh.Certificate{
		Nonce:       []byte{},
		Key:         keyToSign,
		CertType:    ssh.UserCert,
		KeyId:       "user_" + params.User,
		ValidBefore: ssh.CertTimeInfinity, // this will change in later versions
		Permissions: ssh.Permissions{
			CriticalOptions: map[string]string{},
			Extensions:      map[string]string{},
		},
		ValidPrincipals: []string{params.User},
	}

	for _, perm := range params.Permission["extensions"] {
		cert.Permissions.Extensions[perm] = ""
	}

	for _, criticalOpts := range params.Permission["criticalOptions"] {
		cert.Permissions.CriticalOptions[criticalOpts] = ""
	}

	err = cert.SignCert(rand.Reader, authority)
	if err != nil {
		return err
	}

	// add newly created cert to the global map (with fingerprint as key) and then write to local disk (for now).
	fingerprint, err := getFingerPrint(params.Key)
	if err != nil {
		return err
	}
	fmt.Printf("Final check: % x", fingerprint)

	certs, ok := c[fingerprint]
	if !ok {
		c[fingerprint] = []*ssh.Certificate{cert}
	} else {
		c[fingerprint] = append(certs, cert)
	}

	err = ioutil.WriteFile(os.ExpandEnv("$HOME/.ssh/id_rsa-cert.pub"), ssh.MarshalAuthorizedKey(cert), 0600)
	if err != nil {
		return err
	}

	return nil
}

// Revoke uses the public key provided in the request to delete the corresponding certificate
// from the map. However, if a username is provided then a certificate is deleted only if the fingerprint is found in the map
// and the user is listed as a valid principal in the certificate. Reports an error otherwise.
func (c CertificateCollection) Revoke(revokeInfo RevokeInfo) error {
	fingerprint, err := getFingerPrint(revokeInfo.Key)
	if err != nil {
		return err
	}

	certs, ok := c[fingerprint]
	if !ok {
		return errors.New("certificate not found, check if you have specified the correct public key")
	}

	user := revokeInfo.User
	checkPrincipal := func(certs []*ssh.Certificate, principal string) bool {
		for _, cert := range certs {
			if cert.ValidPrincipals[0] == principal {
				return true
			}
		}
		return false
	}
	if user != "" && checkPrincipal(certs, user) {
		return errors.New("certificates valid principal differs from username provided")
	}

	delete(c, fingerprint)
	return nil
}

// SignHandler creates a new certificate from the parameters specified in the request.
func SignHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var params CertificateParameters

	err := decoder.Decode(&params)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err.Error())
		return
	}

	err = Certificates.New(params)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err.Error())
	}
}

// TODO: abstract the decode code into a common function, will have to use type inference for that to work.
// TODO: verify correct http error code being used.
func RevokeHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var revokeInfo RevokeInfo

	err := decoder.Decode(&revokeInfo)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err.Error())
		return
	}

	err = Certificates.Revoke(revokeInfo)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprintf(w, "%s", err.Error())
	}
}

// Fingerprint for a publick key is the md5 sum of the base64 encoded key.
func getFingerPrint(publicKey string) (fp [16]byte, err error) {
	data, err := base64.StdEncoding.DecodeString(strings.Split(publicKey, " ")[1])
	if err != nil {
		return fp, err
	}
	fp = md5.Sum(data)
	return fp, nil
}

func main() {
	http.HandleFunc("/v1/sign", SignHandler)
	http.HandleFunc("/v1/revoke", RevokeHandler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>// Copyright 2012 Miguel Pignatelli. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"net/http"
	"log"
	"os"
	"os/exec"
	"encoding/json"
	"bytes"
	"flag"
	"go/build"
	"path"
	"errors"
)

const (
	projectDirName = "github.com/emepyc/guiHive"
)

var (
	port string
)

func init () {
	flag.StringVar(&port, "port", "12345", "Port to listen (defaults to 12345)")
	flag.Parse()
}

func checkError (s string, err error, ss ...string) {
	if err != nil {
		log.Printf("%s: %s [%s]\n",s, err, ss)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	checkError("Can't parse Form", err)

	debug("METHOD: %s", r.Method)
	debug("URL: %s", r.URL)
	debug("FRAGMENT: %s", r.URL.Fragment)
	debug("PATH: %s", r.URL.Path)
	debug("BODY: %s", r.Body)
	debug("URL2: %s", r.Form)

	var outMsg bytes.Buffer
	var errMsg bytes.Buffer

	fname := ".." + r.URL.Path
	args, err := json.Marshal(r.Form)
	checkError("Can't Marshal JSON:", err)
	debug("ARGS in Go side: %s", args)

	debug("EXECUTING SCRIPT: %s", fname)
	cmd := exec.Command(fname, string(args))
	cmd.Stdout = &outMsg
	cmd.Stderr = &errMsg

	if err := cmd.Start(); err != nil {
		log.Println("Error Starting Command: ", err)
	}
	if err := cmd.Wait(); err != nil {
		log.Println("Error Executing Command: ", err)
	}

	debug("OUTMSG: %s", outMsg.Bytes())
	debug("ERRMSG: %s", errMsg.Bytes())
	fmt.Fprintf(w, string(outMsg.Bytes()))

}

func pathExists(name string) bool {
	_, err := os.Stat(name)
	return err == nil
}

func guessProjectDir() (string, error) {
	// First, we try to find the project dir in the working directory
	workingDirectory, err := os.Getwd()
	if err != nil {
		return "", err
	}
	pathToIndex := workingDirectory + "/../static/index.html"
	if pathExists(pathToIndex) {
		return path.Clean(workingDirectory + "/.."), nil
	}
	for _, srcdir := range build.Default.SrcDirs() {
		dirName := path.Join(srcdir, projectDirName)
		if pathExists(dirName) {
			return dirName, nil
		}
	}
	return "", errors.New("Project directory not found")
}

func setEnvVar() error {
	projectDirectory, err := guessProjectDir()
	if err != nil {
		return err
	}
	debug("PROJECT_DIRECTORY: %s\n", projectDirectory)

	// PER5LIB
	perl5lib := os.Getenv("PERL5LIB")
	pathToLibs := path.Clean(projectDirectory + "/scripts/lib")
	if perl5lib == "" {
		perl5lib = pathToLibs
	} else {
		perl5lib = perl5lib + ":" + pathToLibs
	}
	err = os.Setenv("PERL5LIB", perl5lib)
	if err != nil {
		return err
	}
	debug("PERL5LIB: %s\n", os.Getenv("PERL5LIB"))

	//GUIHIVE_BASEDIR
	if err := os.Setenv("GUIHIVE_BASEDIR", projectDirectory); err != nil {
		return err
	}
	debug("GUIHIVE_BASEDIR: %s", os.Getenv("GUIHIVE_BASEDIR"))

	return nil
}

func main() {

	//  Fix environmental variables
	errV := setEnvVar()
	checkError("Problem setting environmental variables: ", errV);

	relPath := ".."
	http.HandleFunc("/",         handler)
	http.Handle("/static/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/styles/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/javascript/",  http.FileServer(http.Dir(relPath)))
	http.Handle("/images/",      http.FileServer(http.Dir(relPath)))
	http.HandleFunc("/scripts/", scriptHandler)
	debug("Listening to port: %s", port)
	err := http.ListenAndServe(":"+port, nil)
	checkError("ListenAndServe ", err)
}
<commit_msg>Removed rel paths and check if err IsNotExist<commit_after>// Copyright 2012 Miguel Pignatelli. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"net/http"
	"log"
	"os"
	"os/exec"
	"encoding/json"
	"bytes"
	"flag"
	"go/build"
	"path"
	"errors"
)

const (
	projectDirName = "github.com/emepyc/guiHive"
)

var (
	port string
)

func init () {
	flag.StringVar(&port, "port", "12345", "Port to listen (defaults to 12345)")
	flag.Parse()
}

func checkError (s string, err error, ss ...string) {
	if err != nil {
		log.Printf("%s: %s [%s]\n",s, err, ss)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	checkError("Can't parse Form", err)

	debug("METHOD: %s"  , r.Method)
	debug("URL: %s"     , r.URL)
	debug("FRAGMENT: %s", r.URL.Fragment)
	debug("PATH: %s"    , r.URL.Path)
	debug("BODY: %s"    , r.Body)
	debug("URL2: %s"    , r.Form)

	var outMsg bytes.Buffer
	var errMsg bytes.Buffer

	fname := os.Getenv("GUIHIVE_BASEDIR") + r.URL.Path
	args, err := json.Marshal(r.Form)
	checkError("Can't Marshal JSON:", err)
	debug("ARGS in Go side: %s", args)

	debug("EXECUTING SCRIPT: %s", fname)
	cmd := exec.Command(fname, string(args))
	cmd.Stdout = &outMsg
	cmd.Stderr = &errMsg

	if err := cmd.Start(); err != nil {
		log.Println("Error Starting Command: ", err)
	}
	if err := cmd.Wait(); err != nil {
		log.Println("Error Executing Command: ", err)
	}

	debug("OUTMSG: %s", outMsg.Bytes())
	debug("ERRMSG: %s", errMsg.Bytes())
	fmt.Fprintf(w, string(outMsg.Bytes()))

}

func pathExists(name string) bool {
	_, err := os.Stat(name)
	if os.IsNotExist(err) {
		return false
	}
	return err == nil
}

func guessProjectDir() (string, error) {
	// First, we try to find the project dir in the working directory
	workingDirectory, err := os.Getwd()
	if err != nil {
		return "", err
	}
	pathToIndex := workingDirectory + "/../static/index.html"
	if pathExists(pathToIndex) {
		return path.Clean(workingDirectory + "/.."), nil
	}
	for _, srcdir := range build.Default.SrcDirs() {
		dirName := path.Join(srcdir, projectDirName)
		if pathExists(dirName) {
			return dirName, nil
		}
	}
	return "", errors.New("Project directory not found")
}

func setEnvVar() error {
	projectDirectory, err := guessProjectDir()
	if err != nil {
		return err
	}
	debug("PROJECT_DIRECTORY: %s\n", projectDirectory)

	// PER5LIB
	perl5lib := os.Getenv("PERL5LIB")
	pathToLibs := path.Clean(projectDirectory + "/scripts/lib")
	if perl5lib == "" {
		perl5lib = pathToLibs
	} else {
		perl5lib = perl5lib + ":" + pathToLibs
	}
	err = os.Setenv("PERL5LIB", perl5lib)
	if err != nil {
		return err
	}
	debug("PERL5LIB: %s\n", os.Getenv("PERL5LIB"))

	//GUIHIVE_BASEDIR
	if err := os.Setenv("GUIHIVE_BASEDIR", projectDirectory); err != nil {
		return err
	}
	debug("GUIHIVE_BASEDIR: %s", os.Getenv("GUIHIVE_BASEDIR"))

	return nil
}

func main() {

	//  Fix environmental variables
	errV := setEnvVar()
	checkError("Problem setting environmental variables: ", errV);

	relPath := os.Getenv("GUIHIVE_BASEDIR")
	http.HandleFunc("/",         handler)
	http.Handle("/static/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/styles/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/javascript/",  http.FileServer(http.Dir(relPath)))
	http.Handle("/images/",      http.FileServer(http.Dir(relPath)))
	http.HandleFunc("/scripts/", scriptHandler)
	debug("Listening to port: %s", port)
	err := http.ListenAndServe(":"+port, nil)
	checkError("ListenAndServe ", err)
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"sync"
)

// ErrNotRunning is an error returned by (*Server).Shutdown() if the server was not started yet.
var ErrNotStarted = errors.New("server not running")

// Server is a type representing an HTTP server.
type Server struct {
	Addr string

	ln net.Listener
	mu sync.Mutex
}

// New returns an unstarted *Server instance that serves connections on provided host:port.
func New(host string, port int) *Server {
	return &Server{Addr: fmt.Sprintf("%s:%d", host, port)}
}

// Run starts the server and spawns a goroutine that accepts incoming connections and handles them using http.Handler.
func (srv *Server) Run(h http.Handler) error {
	srv.mu.Lock()
	defer srv.mu.Unlock()

	ln, err := net.Listen("tcp", srv.Addr)
	if err != nil {
		return fmt.Errorf("failed to listen on %s: %s", srv.Addr, err)
	}
	srv.ln = ln

	go func(srv *http.Server, ln net.Listener) {
		if err := srv.Serve(ln); err != nil {
			log.Fatal(err)
		}
	}(&http.Server{Handler: h}, srv.ln)

	return nil
}

// Shutdown terminates running server.
func (srv *Server) Shutdown() error {
	if srv.ln == nil {
		return ErrNotStarted
	}

	srv.mu.Lock()
	defer srv.mu.Unlock()

	if err := srv.ln.Close(); err != nil {
		return err
	}

	srv.ln = nil

	return nil
}
<commit_msg>Update comment for server.ErrNotStarted<commit_after>package server

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"sync"
)

// ErrNotStarted is an error returned by (*Server).Shutdown() if the server was not started yet.
var ErrNotStarted = errors.New("server not running")

// Server is a type representing an HTTP server.
type Server struct {
	Addr string

	ln net.Listener
	mu sync.Mutex
}

// New returns an unstarted *Server instance that serves connections on provided host:port.
func New(host string, port int) *Server {
	return &Server{Addr: fmt.Sprintf("%s:%d", host, port)}
}

// Run starts the server and spawns a goroutine that accepts incoming connections and handles them using http.Handler.
func (srv *Server) Run(h http.Handler) error {
	srv.mu.Lock()
	defer srv.mu.Unlock()

	ln, err := net.Listen("tcp", srv.Addr)
	if err != nil {
		return fmt.Errorf("failed to listen on %s: %s", srv.Addr, err)
	}
	srv.ln = ln

	go func(srv *http.Server, ln net.Listener) {
		if err := srv.Serve(ln); err != nil {
			log.Fatal(err)
		}
	}(&http.Server{Handler: h}, srv.ln)

	return nil
}

// Shutdown terminates running server.
func (srv *Server) Shutdown() error {
	if srv.ln == nil {
		return ErrNotStarted
	}

	srv.mu.Lock()
	defer srv.mu.Unlock()

	if err := srv.ln.Close(); err != nil {
		return err
	}

	srv.ln = nil

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"sync"
)

// Todo is a representation of each item that
// we are going to maintain in our service
type Todo struct {
	ID        uint64
	Task      string
	Completed bool
}

var todos []Todo
var lock sync.RWMutex

func main() {
	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		// Authenticate and return JWT
	})

	http.HandleFunc("/todos", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodGet {
			// Return all the Todo items
		} else if r.Method == http.MethodPost {
			// Create a new Todo item
		} else {
			http.NotFound(w, r)
		}
	})

	http.HandleFunc("/todo", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPut {
			// Update the particular Todo item
		} else if r.Method == http.MethodDelete {
			// Delete the particular Todo item
		} else {
			http.NotFound(w, r)
		}
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>Middleware for JWT auth check<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"strings"
	"sync"

	"github.com/dgrijalva/jwt-go"
)

// Todo is a representation of each item that
// we are going to maintain in our service
type Todo struct {
	ID        uint64
	Task      string
	Completed bool
}

var todos []Todo
var lock sync.RWMutex

var hmacSampleSecret = []byte("Top secret signing key")

// A middleware for validating JWT tokens
func authCheck(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tokenString := r.Header.Get("Authorization")
		if tokenString == "" {
			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
			return
		}

		tokenString = strings.TrimPrefix(tokenString, "Bearer ")
		token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			// Don't forget to validate the alg is what you expect:
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
			}
			return hmacSampleSecret, nil
		})

		if err != nil {
			http.Error(w, err.Error(), http.StatusUnauthorized)
			return
		}

		if claims, ok := token.Claims.(jwt.StandardClaims); ok && token.Valid {
			log.Println(claims)
		} else {
			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
			return
		}
		next.ServeHTTP(w, r)
	})
}

func main() {
	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		// Authenticate and return JWT
	})

	http.Handle("/todos", authCheck(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodGet {
			// Return all the Todo items
		} else if r.Method == http.MethodPost {
			// Create a new Todo item
		} else {
			http.NotFound(w, r)
		}
	})))

	http.Handle("/todo", authCheck(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPut {
			// Update the particular Todo item
		} else if r.Method == http.MethodDelete {
			// Delete the particular Todo item
		} else {
			http.NotFound(w, r)
		}
	})))

	log.Fatal(http.ListenAndServe(":8080", nil))
}
<|endoftext|>


<commit_before>package main

import (
	// General
	"github.com/golang/glog"
	"flag"
	"github.com/iambc/xerrors"

	//API
	"net/http"
	"encoding/json"

	//DB
	"database/sql"
	_ "github.com/lib/pq"
)

/*
TODO:
1) Add api_key, api_authentication
2) Add different input/output formats for the API
3) Start adding settings to the boards, etc
*/

type image_board_clusters struct {
    Id int
    Descr string
    LongDescr string
    BoardLimitCount int
}

type boards struct {
    Id int `json:"id"`
    Name string `json:"name"`
    Descr string
    ImageBoardCluster_id string
    BoardLimitCount int
}

type threads struct{
    Id int
    Name string
    Descr string
    Board_id int
    MaxPostsPerThread int
    AreAttachmentsAllowed bool
    LimitsReachedActionId int
}

type thread_posts struct{
    Id int
    Body string
    ThreadId int
    AttachmentUrl int
}

type thread_limits_reached_actions struct{
    Id	    int
    Name    string
    Descr   string
}

type api_request struct{
    Status  string
    Msg	    *string
    Payload interface{}
}

func getBoards(res http.ResponseWriter, req *http.Request)  error {
    if req == nil {
	return xerrors.NewSysErr()
    }

    dbh, err := sql.Open("postgres", "user=abc_api password=123 dbname=abc_dev_cluster sslmode=disable")
    if err != nil {
	return xerrors.NewUiErr(err.Error(), err.Error())
    }
    rows, err := dbh.Query("select id, name from boards;")
    if err != nil {
	return xerrors.NewUiErr(err.Error(), err.Error())
    }
    defer rows.Close()

    var curr_boards []boards
    for rows.Next() {
	var board boards
	err = rows.Scan(&board.Id, &board.Name)
	if err != nil {
	    return xerrors.NewUiErr(err.Error(), err.Error())
	}
	curr_boards = append(curr_boards, board)
    }
    bytes, err1 := json.Marshal(api_request{"ok", nil, &curr_boards})
    if err1 != nil {
	return xerrors.NewUiErr(err1.Error(), err1.Error())
    }
    res.Write(bytes)
    return nil
}

func getActiveThreadsForBoard(res http.ResponseWriter, req *http.Request)  error {
    if req == nil {
        return xerrors.NewSysErr()
    }
    values := req.URL.Query()
    board_id, is_passed := values[`board_id`]
    if !is_passed {
	res.Write([]byte(`Invalid params: No board_id given!`))
	return xerrors.NewUiErr(`Invalid params: No board_id given!`, `Invalid params: No board_id given!`)
    }

    dbh, err := sql.Open("postgres", "user=abc_api password=123 dbname=abc_dev_cluster sslmode=disable")
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }
    rows, err := dbh.Query("select id, name from threads where board_id = $1;", board_id[0])
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }
    defer rows.Close()

    var active_threads []threads
    for rows.Next() {
	glog.Info("Popped new thread")
        var thread threads
        err = rows.Scan(&thread.Id, &thread.Name)
        if err != nil {
            return xerrors.NewUiErr(err.Error(), err.Error())
        }
        active_threads = append(active_threads, thread)
    }
    var bytes []byte
    var err1 error
    if(len(active_threads) == 0){
        errMsg := "No objects returned."
        bytes, err1 = json.Marshal(api_request{"error", &errMsg, &active_threads})
    }else {
        bytes, err1 = json.Marshal(api_request{"ok", nil, &active_threads})
    }

    if err1 != nil {
        return xerrors.NewUiErr(err1.Error(), err1.Error())
    }
    res.Write(bytes)

    return nil
}

func getPostsForThread(res http.ResponseWriter, req *http.Request)  error {
    if req == nil {
        return xerrors.NewSysErr()
    }
    values := req.URL.Query()
    thread_id, is_passed := values[`thread_id`]
    if !is_passed {
        res.Write([]byte(`Invalid params: No thread_id given!`))
        return xerrors.NewUiErr(`Invalid params: No thread_id given!`, `Invalid params: No thread_id given!`)
    }

    dbh, err := sql.Open("postgres", "user=abc_api password=123 dbname=abc_dev_cluster sslmode=disable")
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }

    rows, err := dbh.Query("select id, body from thread_posts where thread_id = $1;", thread_id[0])
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }
    defer rows.Close()

    var curr_posts []thread_posts
    for rows.Next() {
        glog.Info("Popped new thread")
        var curr_post thread_posts
        err = rows.Scan(&curr_post.Id, &curr_post.Body)
        if err != nil {
            return xerrors.NewUiErr(err.Error(), err.Error())
        }
        curr_posts = append(curr_posts, curr_post)
    }

    var bytes []byte
    var err1 error
    if(len(curr_posts) == 0){
	errMsg := "No objects returned."
	bytes, err1 = json.Marshal(api_request{"error", &errMsg, &curr_posts})
    }else {
	bytes, err1 = json.Marshal(api_request{"ok", nil, &curr_posts})
    }

    if err1 != nil {
        return xerrors.NewUiErr(err1.Error(), err1.Error())
    }
    res.Write(bytes)

    return nil
}

func addPostToThread(res http.ResponseWriter, req *http.Request) error {
    if req == nil {
        return xerrors.NewSysErr()
    }
    values := req.URL.Query()
    thread_id, is_passed := values[`thread_id`]
    if !is_passed {
        res.Write([]byte(`{"Status":"error","Msg":"Invalid params: No thread_id given!","Payload":null}`))
        return xerrors.NewUiErr(`Invalid params: No thread_id given!`, `Invalid params: No thread_id given!`)
    }

    thread_body_post, is_passed := values[`thread_post_body`]
    if !is_passed {
        res.Write([]byte(`{"Status":"error","Msg":"Invalid params: No thread_post_body given!","Payload":null}`))
        return xerrors.NewUiErr(`Invalid params: No thread_post_body given!`, `Invalid params: No thread_post_body given!`)
    }

    attachment_urls, is_passed := values[`attachment_url`]
    var attachment_url *string
    if !is_passed{
	attachment_url = nil
    }else{
	attachment_url = &attachment_urls[0]
    }

    dbh, err := sql.Open("postgres", "user=abc_api password=123 dbname=abc_dev_cluster sslmode=disable")
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }

    _, err = dbh.Query("INSERT INTO thread_posts(body, thread_id, attachment_url) VALUES($1, $2, $3)", thread_body_post[0], thread_id[0], attachment_url)

    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }

    bytes, err1 := json.Marshal(api_request{"ok", nil, nil})
    if err1 != nil {
        return xerrors.NewUiErr(err1.Error(), err1.Error())
    }
    res.Write(bytes)

    return nil
}

// sample usage
func main() {
    flag.Parse()

    commands := map[string]func(http.ResponseWriter, *http.Request) error{
				"getBoards": getBoards,
				"getActiveThreadsForBoard": getActiveThreadsForBoard,
				"getPostsForThread": getPostsForThread,
				"addPostToThread": addPostToThread,
			       }

    http.HandleFunc("/api", func(res http.ResponseWriter, req *http.Request) {
					values := req.URL.Query()
					command, is_passed := values[`command`]
					if !is_passed {
					    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'command' is undefined.","Payload":null}`))
					    return
					}
					_, is_passed = commands[command[0]]
					if !is_passed{
					    res.Write([]byte(`{"Status":"error","Msg":"No such command exists.","Payload":null}`))
					    return
					}

					err := commands[command[0]](res, req)
					if err != nil{
					    glog.Error(err)
					}
    })

    http.ListenAndServe(`:8089`, nil)
}

<commit_msg>feat: draft for api_key isolation<commit_after>package main

import (
	// General
	"github.com/golang/glog"
	"flag"
	"github.com/iambc/xerrors"

	//API
	"net/http"
	"encoding/json"

	//DB
	"database/sql"
	_ "github.com/lib/pq"
)

/*
TODO:
1) Add api_authentication
2) Add different input/output formats for the API
3) Start adding settings to the boards, etc
*/

type image_board_clusters struct {
    Id int
    Descr string
    LongDescr string
    BoardLimitCount int
}

type boards struct {
    Id int `json:"id"`
    Name string `json:"name"`
    Descr string
    ImageBoardCluster_id string
    BoardLimitCount int
}

type threads struct{
    Id int
    Name string
    Descr string
    Board_id int
    MaxPostsPerThread int
    AreAttachmentsAllowed bool
    LimitsReachedActionId int
}

type thread_posts struct{
    Id int
    Body string
    ThreadId int
    AttachmentUrl int
}

type thread_limits_reached_actions struct{
    Id	    int
    Name    string
    Descr   string
}

type api_request struct{
    Status  string
    Msg	    *string
    Payload interface{}
}

func getBoards(res http.ResponseWriter, req *http.Request)  error {
    if req == nil {
	return xerrors.NewSysErr()
    }

    dbh, err := sql.Open("postgres", "user=abc_api password=123 dbname=abc_dev_cluster sslmode=disable")
    if err != nil {
	return xerrors.NewUiErr(err.Error(), err.Error())
    }

    values := req.URL.Query()
    api_key := values[`api_key`][0]
    rows, err := dbh.Query("select b.id, b.name from boards b join image_board_clusters ibc on ibc.id = b.id where api_key = $1;", api_key)
    if err != nil {
	return xerrors.NewUiErr(err.Error(), err.Error())
    }
    defer rows.Close()

    var curr_boards []boards
    for rows.Next() {
	var board boards
	err = rows.Scan(&board.Id, &board.Name)
	if err != nil {
	    return xerrors.NewUiErr(err.Error(), err.Error())
	}
	curr_boards = append(curr_boards, board)
    }
    bytes, err1 := json.Marshal(api_request{"ok", nil, &curr_boards})
    if err1 != nil {
	return xerrors.NewUiErr(err1.Error(), err1.Error())
    }
    res.Write(bytes)
    return nil
}

func getActiveThreadsForBoard(res http.ResponseWriter, req *http.Request)  error {
    if req == nil {
        return xerrors.NewSysErr()
    }
    values := req.URL.Query()
    board_id, is_passed := values[`board_id`]
    if !is_passed {
	res.Write([]byte(`Invalid params: No board_id given!`))
	return xerrors.NewUiErr(`Invalid params: No board_id given!`, `Invalid params: No board_id given!`)
    }

    dbh, err := sql.Open("postgres", "user=abc_api password=123 dbname=abc_dev_cluster sslmode=disable")
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }

    api_key := values[`api_key`][0]
    rows, err := dbh.Query("select t.id, t.name from threads t join boards b on b.id = t.board_id join image_board_clusters ibc on ibc.id = b.id where t.board_id = $1 and ibc.api_key = $2;", board_id[0], api_key)
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }
    defer rows.Close()

    var active_threads []threads
    for rows.Next() {
	glog.Info("Popped new thread")
        var thread threads
        err = rows.Scan(&thread.Id, &thread.Name)
        if err != nil {
            return xerrors.NewUiErr(err.Error(), err.Error())
        }
        active_threads = append(active_threads, thread)
    }
    var bytes []byte
    var err1 error
    if(len(active_threads) == 0){
        errMsg := "No objects returned."
        bytes, err1 = json.Marshal(api_request{"error", &errMsg, &active_threads})
    }else {
        bytes, err1 = json.Marshal(api_request{"ok", nil, &active_threads})
    }

    if err1 != nil {
        return xerrors.NewUiErr(err1.Error(), err1.Error())
    }
    res.Write(bytes)

    return nil
}

func getPostsForThread(res http.ResponseWriter, req *http.Request)  error {
    if req == nil {
        return xerrors.NewSysErr()
    }
    values := req.URL.Query()
    thread_id, is_passed := values[`thread_id`]
    if !is_passed {
        res.Write([]byte(`Invalid params: No thread_id given!`))
        return xerrors.NewUiErr(`Invalid params: No thread_id given!`, `Invalid params: No thread_id given!`)
    }

    dbh, err := sql.Open("postgres", "user=abc_api password=123 dbname=abc_dev_cluster sslmode=disable")
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }

    api_key := values[`api_key`][0]
    rows, err := dbh.Query("select tp.id, tp.body from thread_posts tp join threads t on t.id = tp.thread_id join boards b on b.id = t.board_id join image_board_clusters ibc on ibc.id = b.id where tp.thread_id = $1 and ibc.api_key = $2;", thread_id[0], api_key)
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }
    defer rows.Close()

    var curr_posts []thread_posts
    for rows.Next() {
        var curr_post thread_posts
        err = rows.Scan(&curr_post.Id, &curr_post.Body)
        if err != nil {
            return xerrors.NewUiErr(err.Error(), err.Error())
        }
        curr_posts = append(curr_posts, curr_post)
    }

    var bytes []byte
    var err1 error
    if(len(curr_posts) == 0){
	errMsg := "No objects returned."
	bytes, err1 = json.Marshal(api_request{"error", &errMsg, &curr_posts})
    }else {
	bytes, err1 = json.Marshal(api_request{"ok", nil, &curr_posts})
    }

    if err1 != nil {
        return xerrors.NewUiErr(err1.Error(), err1.Error())
    }
    res.Write(bytes)

    return nil
}

func addPostToThread(res http.ResponseWriter, req *http.Request) error {
    if req == nil {
        return xerrors.NewSysErr()
    }
    values := req.URL.Query()
    thread_id, is_passed := values[`thread_id`]
    if !is_passed {
        res.Write([]byte(`{"Status":"error","Msg":"Invalid params: No thread_id given!","Payload":null}`))
        return xerrors.NewUiErr(`Invalid params: No thread_id given!`, `Invalid params: No thread_id given!`)
    }

    thread_body_post, is_passed := values[`thread_post_body`]
    if !is_passed {
        res.Write([]byte(`{"Status":"error","Msg":"Invalid params: No thread_post_body given!","Payload":null}`))
        return xerrors.NewUiErr(`Invalid params: No thread_post_body given!`, `Invalid params: No thread_post_body given!`)
    }

    attachment_urls, is_passed := values[`attachment_url`]
    var attachment_url *string
    if !is_passed{
	attachment_url = nil
    }else{
	attachment_url = &attachment_urls[0]
    }

    dbh, err := sql.Open("postgres", "user=abc_api password=123 dbname=abc_dev_cluster sslmode=disable")
    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }

    _, err = dbh.Query("INSERT INTO thread_posts(body, thread_id, attachment_url) VALUES($1, $2, $3)", thread_body_post[0], thread_id[0], attachment_url)

    if err != nil {
        return xerrors.NewUiErr(err.Error(), err.Error())
    }

    bytes, err1 := json.Marshal(api_request{"ok", nil, nil})
    if err1 != nil {
        return xerrors.NewUiErr(err1.Error(), err1.Error())
    }
    res.Write(bytes)

    return nil
}

// sample usage
func main() {
    flag.Parse()

    commands := map[string]func(http.ResponseWriter, *http.Request) error{
				"getBoards": getBoards,
				"getActiveThreadsForBoard": getActiveThreadsForBoard,
				"getPostsForThread": getPostsForThread,
				"addPostToThread": addPostToThread,
			       }

    http.HandleFunc("/api", func(res http.ResponseWriter, req *http.Request) {
					values := req.URL.Query()
					command, is_passed := values[`command`]
					if !is_passed {
					    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'command' is undefined.","Payload":null}`))
					    return
					}

					_, is_passed = values[`api_key`]
					if !is_passed {
					    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
					    return
					}

					_, is_passed = commands[command[0]]
					if !is_passed{
					    res.Write([]byte(`{"Status":"error","Msg":"No such command exists.","Payload":null}`))
					    return
					}

					err := commands[command[0]](res, req)

					if err != nil{
					    glog.Error(err)
					}
    })

    http.ListenAndServe(`:8089`, nil)
}

<|endoftext|>


<commit_before>// Package server implements a HTTP(S) server for kites.
package server

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/koding/kite"
)

type Server struct {
	*kite.Kite
	listener  net.Listener
	TLSConfig *tls.Config
	readyC    chan bool // To signal when kite is ready to accept connections
	closeC    chan bool // To signal when kite is closed with Close()
}

func New(k *kite.Kite) *Server {
	return &Server{
		Kite:   k,
		readyC: make(chan bool),
		closeC: make(chan bool),
	}
}

func (s *Server) CloseNotify() chan bool {
	return s.closeC
}

func (s *Server) ReadyNotify() chan bool {
	return s.readyC
}

// Start is like Run(), but does not wait for it to complete. It's nonblocking.
func (s *Server) Start() {
	go s.Run()
	<-s.readyC // wait until we are ready
}

// Run is a blocking method. It runs the kite server and then accepts requests
// asynchronously.
func (s *Server) Run() {
	if os.Getenv("KITE_VERSION") != "" {
		fmt.Println(s.Kite.Kite().Version)
		os.Exit(0)
	}

	// An error string equivalent to net.errClosing for using with http.Serve()
	// during a graceful exit. Needed to declare here again because it is not
	// exported by "net" package.
	const errClosing = "use of closed network connection"

	err := s.listenAndServe()
	if err != nil {
		if strings.Contains(err.Error(), errClosing) {
			// The server is closed by Close() method
			s.Kite.Log.Notice("Kite server is closed.")
			return
		}
		s.Kite.Log.Fatal(err.Error())
	}
}

// Close stops the server.
func (s *Server) Close() {
	s.Kite.Log.Notice("Closing server...")
	s.listener.Close()
}

func (s *Server) Addr() string {
	return net.JoinHostPort(s.Kite.Config.IP, strconv.Itoa(s.Kite.Config.Port))
}

// listenAndServe listens on the TCP network address k.URL.Host and then
// calls Serve to handle requests on incoming connections.
func (s *Server) listenAndServe() error {
	var err error
	s.listener, err = net.Listen("tcp4", s.Addr())
	if err != nil {
		return err
	}

	s.Kite.Log.Notice("Listening: %s", s.listener.Addr().String())

	if s.TLSConfig != nil {
		if s.TLSConfig.NextProtos == nil {
			s.TLSConfig.NextProtos = []string{"http/1.1"}
		}
		s.listener = tls.NewListener(s.listener, s.TLSConfig)
	}

	close(s.readyC) // listener is ready, notify waiters.
	s.Kite.Log.Info("Serving...")
	defer close(s.closeC) // serving is finished, notify waiters.
	return http.Serve(s.listener, s.Kite)
}

func (k *Server) UseTLS(certPEM, keyPEM string) {
	config := &tls.Config{}
	if k.TLSConfig != nil {
		k.TLSConfig = config
	}

	cert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM))
	if err != nil {
		panic(err)
	}

	config.Certificates = append(config.Certificates, cert)
}

func (k *Server) UseTLSFile(certFile, keyFile string) {
	certData, err := ioutil.ReadFile(certFile)
	if err != nil {
		k.Log.Fatal("Cannot read certificate file: %s", err.Error())
	}
	keyData, err := ioutil.ReadFile(certFile)
	if err != nil {
		k.Log.Fatal("Cannot read certificate file: %s", err.Error())
	}
	k.UseTLS(string(certData), string(keyData))
}
<commit_msg>fix server tls file bug<commit_after>// Package server implements a HTTP(S) server for kites.
package server

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/koding/kite"
)

type Server struct {
	*kite.Kite
	listener  net.Listener
	TLSConfig *tls.Config
	readyC    chan bool // To signal when kite is ready to accept connections
	closeC    chan bool // To signal when kite is closed with Close()
}

func New(k *kite.Kite) *Server {
	return &Server{
		Kite:   k,
		readyC: make(chan bool),
		closeC: make(chan bool),
	}
}

func (s *Server) CloseNotify() chan bool {
	return s.closeC
}

func (s *Server) ReadyNotify() chan bool {
	return s.readyC
}

// Start is like Run(), but does not wait for it to complete. It's nonblocking.
func (s *Server) Start() {
	go s.Run()
	<-s.readyC // wait until we are ready
}

// Run is a blocking method. It runs the kite server and then accepts requests
// asynchronously.
func (s *Server) Run() {
	if os.Getenv("KITE_VERSION") != "" {
		fmt.Println(s.Kite.Kite().Version)
		os.Exit(0)
	}

	// An error string equivalent to net.errClosing for using with http.Serve()
	// during a graceful exit. Needed to declare here again because it is not
	// exported by "net" package.
	const errClosing = "use of closed network connection"

	err := s.listenAndServe()
	if err != nil {
		if strings.Contains(err.Error(), errClosing) {
			// The server is closed by Close() method
			s.Kite.Log.Notice("Kite server is closed.")
			return
		}
		s.Kite.Log.Fatal(err.Error())
	}
}

// Close stops the server.
func (s *Server) Close() {
	s.Kite.Log.Notice("Closing server...")
	s.listener.Close()
}

func (s *Server) Addr() string {
	return net.JoinHostPort(s.Kite.Config.IP, strconv.Itoa(s.Kite.Config.Port))
}

// listenAndServe listens on the TCP network address k.URL.Host and then
// calls Serve to handle requests on incoming connections.
func (s *Server) listenAndServe() error {
	var err error
	s.listener, err = net.Listen("tcp4", s.Addr())
	if err != nil {
		return err
	}

	s.Kite.Log.Notice("Listening: %s", s.listener.Addr().String())

	if s.TLSConfig != nil {
		if s.TLSConfig.NextProtos == nil {
			s.TLSConfig.NextProtos = []string{"http/1.1"}
		}
		s.listener = tls.NewListener(s.listener, s.TLSConfig)
	}

	close(s.readyC) // listener is ready, notify waiters.
	s.Kite.Log.Info("Serving...")
	defer close(s.closeC) // serving is finished, notify waiters.
	return http.Serve(s.listener, s.Kite)
}

func (k *Server) UseTLS(certPEM, keyPEM string) {
	config := &tls.Config{}
	if k.TLSConfig != nil {
		k.TLSConfig = config
	}

	cert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM))
	if err != nil {
		panic(err)
	}

	config.Certificates = append(config.Certificates, cert)
}

func (k *Server) UseTLSFile(certFile, keyFile string) {
	certData, err := ioutil.ReadFile(certFile)
	if err != nil {
		k.Log.Fatal("Cannot read certificate file: %s", err.Error())
	}
	keyData, err := ioutil.ReadFile(keyFile)
	if err != nil {
		k.Log.Fatal("Cannot read certificate file: %s", err.Error())
	}
	k.UseTLS(string(certData), string(keyData))
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"sync"
	"syscall"

	"github.com/tsileo/blobstash/api"
	"github.com/tsileo/blobstash/backend"
	"github.com/tsileo/blobstash/config"
	"github.com/tsileo/blobstash/config/pathutil"
	"github.com/tsileo/blobstash/embed"
	"github.com/tsileo/blobstash/meta"
	"github.com/tsileo/blobstash/router"
	"github.com/tsileo/blobstash/vkv"
)

var Version = "0.0.0"

var DefaultConf = map[string]interface{}{
	"backends": map[string]interface{}{
		"blobs": map[string]interface{}{
			"backend-type": "blobsfile",
			"backend-args": map[string]interface{}{
				"path": "blobs",
			},
		},
	},
	"router": []interface{}{[]interface{}{"default", "blobs"}},
}

type Server struct {
	Router      *router.Router
	Backends    map[string]backend.BlobHandler
	DB          *vkv.DB
	metaHandler *meta.MetaHandler

	KvUpdate chan *vkv.KeyValue
	blobs    chan *router.Blob

	shutdown chan struct{}
	stop     chan struct{}
	wg       sync.WaitGroup
}

func New(conf map[string]interface{}) *Server {
	if conf == nil {
		conf = DefaultConf
	}
	vardir := pathutil.VarDir()
	os.MkdirAll(vardir, 0700)
	db, err := vkv.New(filepath.Join(vardir, "vkv.db"))
	if err != nil {
		panic(err)
	}
	server := &Server{
		Router:   router.New(conf["router"].([]interface{})),
		Backends: map[string]backend.BlobHandler{},
		DB:       db,
		KvUpdate: make(chan *vkv.KeyValue),
		shutdown: make(chan struct{}, 1),
		stop:     make(chan struct{}),
		blobs:    make(chan *router.Blob),
	}
	backends := conf["backends"].(map[string]interface{})
	for _, b := range server.Router.ResolveBackends() {
		server.Backends[b] = config.NewFromConfig(backends[b].(map[string]interface{}))
		server.Router.Backends[b] = server.Backends[b]
	}
	server.metaHandler = meta.New(server.Router, server.DB)
	return server
}

func (s *Server) processBlobs() {
	s.wg.Add(1)
	defer s.wg.Done()
	for {
		select {
		case blob := <-s.blobs:
			log.Printf("processBlobs: %+v", blob)
			backend := s.Router.Route(blob.Req)
			exists, err := backend.Exists(blob.Hash)
			if err != nil {
				panic(fmt.Errorf("processBlobs error: %v", err))
			}
			if !exists {
				if err := backend.Put(blob.Hash, blob.Blob); err != nil {
					panic(fmt.Errorf("processBlobs error: %v", err))
				}
			}
		case <-s.stop:
			return
		}
	}
}

func (s *Server) Embed() {
	// Start meta handler: watch for kv update and create meta blob
	go s.metaHandler.WatchKvUpdate(s.wg, s.blobs, s.KvUpdate)
	// Scan existing meta blob
	go func() {
		s.wg.Add(1)
		defer s.wg.Done()
		if err := s.metaHandler.Scan(); err != nil {
			panic(err)
		}
	}()
	// Start the worker for handling blob upload
	for i := 0; i < 25; i++ {
		go s.processBlobs()
	}
}

// KvStore return a kv client, written for embedded client
func (s *Server) KvStore() *embed.KvStore {
	return embed.NewKvStore(s.DB, s.KvUpdate, s.Router)
}

func (s *Server) BlobStore() *embed.BlobStore {
	return embed.NewBlobStore(s.blobs, s.Router)
}

func (s *Server) Run() error {
	// Start the HTTP API
	r := api.New(s.wg, s.DB, s.KvUpdate, s.Router, s.blobs)
	http.Handle("/", r)
	log.Printf("server: HTTP API listening on 0.0.0.0:8050")
	go func() {
		if err := http.ListenAndServe(":8050", nil); err != nil {
			panic(err)
		}
	}()
	// Listen for shutdown signal
	cs := make(chan os.Signal, 1)
	signal.Notify(cs, os.Interrupt,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)
	for {
		select {
		case <-s.shutdown:
			break
		case sig := <-cs:
			log.Printf("server: Captured %v\n", sig)
			break
		}
		s.Close()
		os.Exit(0)
	}
}

func (s *Server) Close() {
	close(s.stop)
	s.wg.Wait()
	close(s.KvUpdate)
	s.DB.Close()
	for _, b := range s.Backends {
		b.Close()
	}
}
<commit_msg>bugfix embbed mode<commit_after>package server

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"sync"
	"syscall"

	"github.com/tsileo/blobstash/api"
	"github.com/tsileo/blobstash/backend"
	"github.com/tsileo/blobstash/config"
	"github.com/tsileo/blobstash/config/pathutil"
	"github.com/tsileo/blobstash/embed"
	"github.com/tsileo/blobstash/meta"
	"github.com/tsileo/blobstash/router"
	"github.com/tsileo/blobstash/vkv"
)

var Version = "0.0.0"

var DefaultConf = map[string]interface{}{
	"backends": map[string]interface{}{
		"blobs": map[string]interface{}{
			"backend-type": "blobsfile",
			"backend-args": map[string]interface{}{
				"path": "blobs",
			},
		},
	},
	"router": []interface{}{[]interface{}{"default", "blobs"}},
}

type Server struct {
	Router      *router.Router
	Backends    map[string]backend.BlobHandler
	DB          *vkv.DB
	metaHandler *meta.MetaHandler

	KvUpdate chan *vkv.KeyValue
	blobs    chan *router.Blob

	shutdown chan struct{}
	stop     chan struct{}
	wg       sync.WaitGroup
}

func New(conf map[string]interface{}) *Server {
	if conf == nil {
		conf = DefaultConf
	}
	vardir := pathutil.VarDir()
	os.MkdirAll(vardir, 0700)
	db, err := vkv.New(filepath.Join(vardir, "vkv.db"))
	if err != nil {
		panic(err)
	}
	server := &Server{
		Router:   router.New(conf["router"].([]interface{})),
		Backends: map[string]backend.BlobHandler{},
		DB:       db,
		KvUpdate: make(chan *vkv.KeyValue),
		shutdown: make(chan struct{}, 1),
		stop:     make(chan struct{}),
		blobs:    make(chan *router.Blob),
	}
	backends := conf["backends"].(map[string]interface{})
	for _, b := range server.Router.ResolveBackends() {
		server.Backends[b] = config.NewFromConfig(backends[b].(map[string]interface{}))
		server.Router.Backends[b] = server.Backends[b]
	}
	server.metaHandler = meta.New(server.Router, server.DB)
	return server
}

func (s *Server) processBlobs() {
	s.wg.Add(1)
	defer s.wg.Done()
	for {
		select {
		case blob := <-s.blobs:
			log.Printf("processBlobs: %+v", blob)
			backend := s.Router.Route(blob.Req)
			exists, err := backend.Exists(blob.Hash)
			if err != nil {
				panic(fmt.Errorf("processBlobs error: %v", err))
			}
			if !exists {
				if err := backend.Put(blob.Hash, blob.Blob); err != nil {
					panic(fmt.Errorf("processBlobs error: %v", err))
				}
			}
		case <-s.stop:
			return
		}
	}
}

func (s *Server) Embed() {
	// Start meta handler: watch for kv update and create meta blob
	go s.metaHandler.WatchKvUpdate(s.wg, s.blobs, s.KvUpdate)
	// Scan existing meta blob
	go func() {
		s.wg.Add(1)
		defer s.wg.Done()
		if err := s.metaHandler.Scan(); err != nil {
			panic(err)
		}
	}()
	// Start the worker for handling blob upload
	for i := 0; i < 25; i++ {
		go s.processBlobs()
	}
}

// KvStore return a kv client, written for embedded client
func (s *Server) KvStore() *embed.KvStore {
	return embed.NewKvStore(s.DB, s.KvUpdate, s.Router)
}

func (s *Server) BlobStore() *embed.BlobStore {
	return embed.NewBlobStore(s.blobs, s.Router)
}

func (s *Server) Run() error {
	// Start the HTTP API
	s.Embed()
	r := api.New(s.wg, s.DB, s.KvUpdate, s.Router, s.blobs)
	http.Handle("/", r)
	log.Printf("server: HTTP API listening on 0.0.0.0:8050")
	go func() {
		if err := http.ListenAndServe(":8050", nil); err != nil {
			panic(err)
		}
	}()
	// Listen for shutdown signal
	cs := make(chan os.Signal, 1)
	signal.Notify(cs, os.Interrupt,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)
	for {
		select {
		case <-s.shutdown:
			break
		case sig := <-cs:
			log.Printf("server: Captured %v\n", sig)
			break
		}
		s.Close()
		os.Exit(0)
	}
}

func (s *Server) Close() {
	close(s.stop)
	s.wg.Wait()
	close(s.KvUpdate)
	s.DB.Close()
	for _, b := range s.Backends {
		b.Close()
	}
}
<|endoftext|>