<commit_before>package config

import (
	"sync"

	"github.com/docker/libcompose/yaml"
)

// EnvironmentLookup defines methods to provides environment variable loading.
type EnvironmentLookup interface {
	Lookup(key, serviceName string, config *ServiceConfig) []string
}

// ResourceLookup defines methods to provides file loading.
type ResourceLookup interface {
	Lookup(file, relativeTo string) ([]byte, string, error)
	ResolvePath(path, inFile string) string
}

// ServiceConfigV1 holds version 1 of libcompose service configuration
type ServiceConfigV1 struct {
	Build         string               `yaml:"build,omitempty"`
	CapAdd        []string             `yaml:"cap_add,omitempty"`
	CapDrop       []string             `yaml:"cap_drop,omitempty"`
	CgroupParent  string               `yaml:"cgroup_parent,omitempty"`
	CPUQuota      int64                `yaml:"cpu_quota,omitempty"`
	CPUSet        string               `yaml:"cpuset,omitempty"`
	CPUShares     int64                `yaml:"cpu_shares,omitempty"`
	Command       yaml.Command         `yaml:"command,flow,omitempty"`
	ContainerName string               `yaml:"container_name,omitempty"`
	Devices       []string             `yaml:"devices,omitempty"`
	DNS           yaml.Stringorslice   `yaml:"dns,omitempty"`
	DNSSearch     yaml.Stringorslice   `yaml:"dns_search,omitempty"`
	Dockerfile    string               `yaml:"dockerfile,omitempty"`
	DomainName    string               `yaml:"domainname,omitempty"`
	Entrypoint    yaml.Command         `yaml:"entrypoint,flow,omitempty"`
	EnvFile       yaml.Stringorslice   `yaml:"env_file,omitempty"`
	Environment   yaml.MaporEqualSlice `yaml:"environment,omitempty"`
	Hostname      string               `yaml:"hostname,omitempty"`
	Image         string               `yaml:"image,omitempty"`
	Labels        yaml.SliceorMap      `yaml:"labels,omitempty"`
	Links         yaml.MaporColonSlice `yaml:"links,omitempty"`
	LogDriver     string               `yaml:"log_driver,omitempty"`
	MacAddress    string               `yaml:"mac_address,omitempty"`
	MemLimit      int64                `yaml:"mem_limit,omitempty"`
	MemSwapLimit  int64                `yaml:"memswap_limit,omitempty"`
	Name          string               `yaml:"name,omitempty"`
	Net           string               `yaml:"net,omitempty"`
	Pid           string               `yaml:"pid,omitempty"`
	Uts           string               `yaml:"uts,omitempty"`
	Ipc           string               `yaml:"ipc,omitempty"`
	Ports         []string             `yaml:"ports,omitempty"`
	Privileged    bool                 `yaml:"privileged,omitempty"`
	Restart       string               `yaml:"restart,omitempty"`
	ReadOnly      bool                 `yaml:"read_only,omitempty"`
	ShmSize       int64                `yaml:"shm_size,omitempty"`
	StdinOpen     bool                 `yaml:"stdin_open,omitempty"`
	SecurityOpt   []string             `yaml:"security_opt,omitempty"`
	Tty           bool                 `yaml:"tty,omitempty"`
	User          string               `yaml:"user,omitempty"`
	VolumeDriver  string               `yaml:"volume_driver,omitempty"`
	Volumes       []string             `yaml:"volumes,omitempty"`
	VolumesFrom   []string             `yaml:"volumes_from,omitempty"`
	WorkingDir    string               `yaml:"working_dir,omitempty"`
	Expose        []string             `yaml:"expose,omitempty"`
	ExternalLinks []string             `yaml:"external_links,omitempty"`
	LogOpt        map[string]string    `yaml:"log_opt,omitempty"`
	ExtraHosts    []string             `yaml:"extra_hosts,omitempty"`
	Ulimits       yaml.Ulimits         `yaml:"ulimits,omitempty"`
}

// Log holds v2 logging information
type Log struct {
	Driver  string            `yaml:"driver,omitempty"`
	Options map[string]string `yaml:"options,omitempty"`
}

// ServiceConfig holds version 2 of libcompose service configuration
type ServiceConfig struct {
	Build         yaml.Build           `yaml:"build,omitempty"`
	CapAdd        []string             `yaml:"cap_add,omitempty"`
	CapDrop       []string             `yaml:"cap_drop,omitempty"`
	CPUSet        string               `yaml:"cpuset,omitempty"`
	CPUShares     int64                `yaml:"cpu_shares,omitempty"`
	CPUQuota      int64                `yaml:"cpu_quota,omitempty"`
	Command       yaml.Command         `yaml:"command,flow,omitempty"`
	CgroupParent  string               `yaml:"cgroup_parent,omitempty"`
	ContainerName string               `yaml:"container_name,omitempty"`
	Devices       []string             `yaml:"devices,omitempty"`
	DependsOn     []string             `yaml:"depends_on,omitempty"`
	DNS           yaml.Stringorslice   `yaml:"dns,omitempty"`
	DNSSearch     yaml.Stringorslice   `yaml:"dns_search,omitempty"`
	DomainName    string               `yaml:"domain_name,omitempty"`
	Entrypoint    yaml.Command         `yaml:"entrypoint,flow,omitempty"`
	EnvFile       yaml.Stringorslice   `yaml:"env_file,omitempty"`
	Environment   yaml.MaporEqualSlice `yaml:"environment,omitempty"`
	Expose        []string             `yaml:"expose,omitempty"`
	Extends       yaml.MaporEqualSlice `yaml:"extends,omitempty"`
	ExternalLinks []string             `yaml:"external_links,omitempty"`
	ExtraHosts    []string             `yaml:"extra_hosts,omitempty"`
	Image         string               `yaml:"image,omitempty"`
	Hostname      string               `yaml:"hostname,omitempty"`
	Ipc           string               `yaml:"ipc,omitempty"`
	Labels        yaml.SliceorMap      `yaml:"labels,omitempty"`
	Links         yaml.MaporColonSlice `yaml:"links,omitempty"`
	Logging       Log                  `yaml:"logging,omitempty"`
	MacAddress    string               `yaml:"mac_address,omitempty"`
	MemLimit      int64                `yaml:"mem_limit,omitempty"`
	MemSwapLimit  int64                `yaml:"memswap_limit,omitempty"`
	NetworkMode   string               `yaml:"network_mode,omitempty"`
	Networks      *yaml.Networks       `yaml:"networks,omitempty"`
	Pid           string               `yaml:"pid,omitempty"`
	Ports         []string             `yaml:"ports,omitempty"`
	Privileged    bool                 `yaml:"privileged,omitempty"`
	SecurityOpt   []string             `yaml:"security_opt,omitempty"`
	ShmSize       int64                `yaml:"shm_size,omitempty"`
	StopSignal    string               `yaml:"stop_signal,omitempty"`
	VolumeDriver  string               `yaml:"volume_driver,omitempty"`
	Volumes       []string             `yaml:"volumes,omitempty"`
	VolumesFrom   []string             `yaml:"volumes_from,omitempty"`
	Uts           string               `yaml:"uts,omitempty"`
	Restart       string               `yaml:"restart,omitempty"`
	ReadOnly      bool                 `yaml:"read_only,omitempty"`
	StdinOpen     bool                 `yaml:"stdin_open,omitempty"`
	Tty           bool                 `yaml:"tty,omitempty"`
	User          string               `yaml:"user,omitempty"`
	WorkingDir    string               `yaml:"working_dir,omitempty"`
	Ulimits       yaml.Ulimits         `yaml:"ulimits,omitempty"`
}

// VolumeConfig holds v2 volume configuration
type VolumeConfig struct {
	Driver     string            `yaml:"driver,omitempty"`
	DriverOpts map[string]string `yaml:"driver_opts,omitempty"`
	External   yaml.External     `yaml:"external,omitempty"`
}

// Ipam holds v2 network IPAM information
type Ipam struct {
	Driver string       `yaml:"driver,omitempty"`
	Config []IpamConfig `yaml:"config,omitempty"`
}

// IpamConfig holds v2 network IPAM configuration information
type IpamConfig struct {
	Subnet     string            `yaml:"subnet,omitempty"`
	IPRange    string            `yaml:"ip_range,omitempty"`
	Gateway    string            `yaml:"gateway,omitempty"`
	AuxAddress map[string]string `yaml:"aux_addresses,omitempty"`
}

// NetworkConfig holds v2 network configuration
type NetworkConfig struct {
	Driver     string            `yaml:"driver,omitempty"`
	DriverOpts map[string]string `yaml:"driver_opts,omitempty"`
	External   yaml.External     `yaml:"external,omitempty"`
	Ipam       Ipam              `yaml:"ipam,omitempty"`
}

// Config holds libcompose top level configuration
type Config struct {
	Version  string                    `yaml:"version,omitempty"`
	Services RawServiceMap             `yaml:"services,omitempty"`
	Volumes  map[string]*VolumeConfig  `yaml:"volumes,omitempty"`
	Networks map[string]*NetworkConfig `yaml:"networks,omitempty"`
}

// NewServiceConfigs initializes a new Configs struct
func NewServiceConfigs() *ServiceConfigs {
	return &ServiceConfigs{
		m: make(map[string]*ServiceConfig),
	}
}

// ServiceConfigs holds a concurrent safe map of ServiceConfig
type ServiceConfigs struct {
	m  map[string]*ServiceConfig
	mu sync.RWMutex
}

// Has checks if the config map has the specified name
func (c *ServiceConfigs) Has(name string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	_, ok := c.m[name]
	return ok
}

// Get returns the config and the presence of the specified name
func (c *ServiceConfigs) Get(name string) (*ServiceConfig, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	service, ok := c.m[name]
	return service, ok
}

// Add add the specifed config with the specified name
func (c *ServiceConfigs) Add(name string, service *ServiceConfig) {
	c.mu.Lock()
	c.m[name] = service
	c.mu.Unlock()
}

// Len returns the len of the configs
func (c *ServiceConfigs) Len() int {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return len(c.m)
}

// Keys returns the names of the config
func (c *ServiceConfigs) Keys() []string {
	keys := []string{}
	c.mu.RLock()
	defer c.mu.RUnlock()
	for name := range c.m {
		keys = append(keys, name)
	}
	return keys
}

// RawService is represent a Service in map form unparsed
type RawService map[string]interface{}

// RawServiceMap is a collection of RawServices
type RawServiceMap map[string]RawService

// ParseOptions are a set of options to customize the parsing process
type ParseOptions struct {
	Interpolate bool
	Validate    bool
	Preprocess  func(RawServiceMap) (RawServiceMap, error)
	Postprocess func(map[string]*ServiceConfig) (map[string]*ServiceConfig, error)
}
<commit_msg>Fix 'domainname' key<commit_after>package config

import (
	"sync"

	"github.com/docker/libcompose/yaml"
)

// EnvironmentLookup defines methods to provides environment variable loading.
type EnvironmentLookup interface {
	Lookup(key, serviceName string, config *ServiceConfig) []string
}

// ResourceLookup defines methods to provides file loading.
type ResourceLookup interface {
	Lookup(file, relativeTo string) ([]byte, string, error)
	ResolvePath(path, inFile string) string
}

// ServiceConfigV1 holds version 1 of libcompose service configuration
type ServiceConfigV1 struct {
	Build         string               `yaml:"build,omitempty"`
	CapAdd        []string             `yaml:"cap_add,omitempty"`
	CapDrop       []string             `yaml:"cap_drop,omitempty"`
	CgroupParent  string               `yaml:"cgroup_parent,omitempty"`
	CPUQuota      int64                `yaml:"cpu_quota,omitempty"`
	CPUSet        string               `yaml:"cpuset,omitempty"`
	CPUShares     int64                `yaml:"cpu_shares,omitempty"`
	Command       yaml.Command         `yaml:"command,flow,omitempty"`
	ContainerName string               `yaml:"container_name,omitempty"`
	Devices       []string             `yaml:"devices,omitempty"`
	DNS           yaml.Stringorslice   `yaml:"dns,omitempty"`
	DNSSearch     yaml.Stringorslice   `yaml:"dns_search,omitempty"`
	Dockerfile    string               `yaml:"dockerfile,omitempty"`
	DomainName    string               `yaml:"domainname,omitempty"`
	Entrypoint    yaml.Command         `yaml:"entrypoint,flow,omitempty"`
	EnvFile       yaml.Stringorslice   `yaml:"env_file,omitempty"`
	Environment   yaml.MaporEqualSlice `yaml:"environment,omitempty"`
	Hostname      string               `yaml:"hostname,omitempty"`
	Image         string               `yaml:"image,omitempty"`
	Labels        yaml.SliceorMap      `yaml:"labels,omitempty"`
	Links         yaml.MaporColonSlice `yaml:"links,omitempty"`
	LogDriver     string               `yaml:"log_driver,omitempty"`
	MacAddress    string               `yaml:"mac_address,omitempty"`
	MemLimit      int64                `yaml:"mem_limit,omitempty"`
	MemSwapLimit  int64                `yaml:"memswap_limit,omitempty"`
	Name          string               `yaml:"name,omitempty"`
	Net           string               `yaml:"net,omitempty"`
	Pid           string               `yaml:"pid,omitempty"`
	Uts           string               `yaml:"uts,omitempty"`
	Ipc           string               `yaml:"ipc,omitempty"`
	Ports         []string             `yaml:"ports,omitempty"`
	Privileged    bool                 `yaml:"privileged,omitempty"`
	Restart       string               `yaml:"restart,omitempty"`
	ReadOnly      bool                 `yaml:"read_only,omitempty"`
	ShmSize       int64                `yaml:"shm_size,omitempty"`
	StdinOpen     bool                 `yaml:"stdin_open,omitempty"`
	SecurityOpt   []string             `yaml:"security_opt,omitempty"`
	Tty           bool                 `yaml:"tty,omitempty"`
	User          string               `yaml:"user,omitempty"`
	VolumeDriver  string               `yaml:"volume_driver,omitempty"`
	Volumes       []string             `yaml:"volumes,omitempty"`
	VolumesFrom   []string             `yaml:"volumes_from,omitempty"`
	WorkingDir    string               `yaml:"working_dir,omitempty"`
	Expose        []string             `yaml:"expose,omitempty"`
	ExternalLinks []string             `yaml:"external_links,omitempty"`
	LogOpt        map[string]string    `yaml:"log_opt,omitempty"`
	ExtraHosts    []string             `yaml:"extra_hosts,omitempty"`
	Ulimits       yaml.Ulimits         `yaml:"ulimits,omitempty"`
}

// Log holds v2 logging information
type Log struct {
	Driver  string            `yaml:"driver,omitempty"`
	Options map[string]string `yaml:"options,omitempty"`
}

// ServiceConfig holds version 2 of libcompose service configuration
type ServiceConfig struct {
	Build         yaml.Build           `yaml:"build,omitempty"`
	CapAdd        []string             `yaml:"cap_add,omitempty"`
	CapDrop       []string             `yaml:"cap_drop,omitempty"`
	CPUSet        string               `yaml:"cpuset,omitempty"`
	CPUShares     int64                `yaml:"cpu_shares,omitempty"`
	CPUQuota      int64                `yaml:"cpu_quota,omitempty"`
	Command       yaml.Command         `yaml:"command,flow,omitempty"`
	CgroupParent  string               `yaml:"cgroup_parent,omitempty"`
	ContainerName string               `yaml:"container_name,omitempty"`
	Devices       []string             `yaml:"devices,omitempty"`
	DependsOn     []string             `yaml:"depends_on,omitempty"`
	DNS           yaml.Stringorslice   `yaml:"dns,omitempty"`
	DNSSearch     yaml.Stringorslice   `yaml:"dns_search,omitempty"`
	DomainName    string               `yaml:"domainname,omitempty"`
	Entrypoint    yaml.Command         `yaml:"entrypoint,flow,omitempty"`
	EnvFile       yaml.Stringorslice   `yaml:"env_file,omitempty"`
	Environment   yaml.MaporEqualSlice `yaml:"environment,omitempty"`
	Expose        []string             `yaml:"expose,omitempty"`
	Extends       yaml.MaporEqualSlice `yaml:"extends,omitempty"`
	ExternalLinks []string             `yaml:"external_links,omitempty"`
	ExtraHosts    []string             `yaml:"extra_hosts,omitempty"`
	Image         string               `yaml:"image,omitempty"`
	Hostname      string               `yaml:"hostname,omitempty"`
	Ipc           string               `yaml:"ipc,omitempty"`
	Labels        yaml.SliceorMap      `yaml:"labels,omitempty"`
	Links         yaml.MaporColonSlice `yaml:"links,omitempty"`
	Logging       Log                  `yaml:"logging,omitempty"`
	MacAddress    string               `yaml:"mac_address,omitempty"`
	MemLimit      int64                `yaml:"mem_limit,omitempty"`
	MemSwapLimit  int64                `yaml:"memswap_limit,omitempty"`
	NetworkMode   string               `yaml:"network_mode,omitempty"`
	Networks      *yaml.Networks       `yaml:"networks,omitempty"`
	Pid           string               `yaml:"pid,omitempty"`
	Ports         []string             `yaml:"ports,omitempty"`
	Privileged    bool                 `yaml:"privileged,omitempty"`
	SecurityOpt   []string             `yaml:"security_opt,omitempty"`
	ShmSize       int64                `yaml:"shm_size,omitempty"`
	StopSignal    string               `yaml:"stop_signal,omitempty"`
	VolumeDriver  string               `yaml:"volume_driver,omitempty"`
	Volumes       []string             `yaml:"volumes,omitempty"`
	VolumesFrom   []string             `yaml:"volumes_from,omitempty"`
	Uts           string               `yaml:"uts,omitempty"`
	Restart       string               `yaml:"restart,omitempty"`
	ReadOnly      bool                 `yaml:"read_only,omitempty"`
	StdinOpen     bool                 `yaml:"stdin_open,omitempty"`
	Tty           bool                 `yaml:"tty,omitempty"`
	User          string               `yaml:"user,omitempty"`
	WorkingDir    string               `yaml:"working_dir,omitempty"`
	Ulimits       yaml.Ulimits         `yaml:"ulimits,omitempty"`
}

// VolumeConfig holds v2 volume configuration
type VolumeConfig struct {
	Driver     string            `yaml:"driver,omitempty"`
	DriverOpts map[string]string `yaml:"driver_opts,omitempty"`
	External   yaml.External     `yaml:"external,omitempty"`
}

// Ipam holds v2 network IPAM information
type Ipam struct {
	Driver string       `yaml:"driver,omitempty"`
	Config []IpamConfig `yaml:"config,omitempty"`
}

// IpamConfig holds v2 network IPAM configuration information
type IpamConfig struct {
	Subnet     string            `yaml:"subnet,omitempty"`
	IPRange    string            `yaml:"ip_range,omitempty"`
	Gateway    string            `yaml:"gateway,omitempty"`
	AuxAddress map[string]string `yaml:"aux_addresses,omitempty"`
}

// NetworkConfig holds v2 network configuration
type NetworkConfig struct {
	Driver     string            `yaml:"driver,omitempty"`
	DriverOpts map[string]string `yaml:"driver_opts,omitempty"`
	External   yaml.External     `yaml:"external,omitempty"`
	Ipam       Ipam              `yaml:"ipam,omitempty"`
}

// Config holds libcompose top level configuration
type Config struct {
	Version  string                    `yaml:"version,omitempty"`
	Services RawServiceMap             `yaml:"services,omitempty"`
	Volumes  map[string]*VolumeConfig  `yaml:"volumes,omitempty"`
	Networks map[string]*NetworkConfig `yaml:"networks,omitempty"`
}

// NewServiceConfigs initializes a new Configs struct
func NewServiceConfigs() *ServiceConfigs {
	return &ServiceConfigs{
		m: make(map[string]*ServiceConfig),
	}
}

// ServiceConfigs holds a concurrent safe map of ServiceConfig
type ServiceConfigs struct {
	m  map[string]*ServiceConfig
	mu sync.RWMutex
}

// Has checks if the config map has the specified name
func (c *ServiceConfigs) Has(name string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	_, ok := c.m[name]
	return ok
}

// Get returns the config and the presence of the specified name
func (c *ServiceConfigs) Get(name string) (*ServiceConfig, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	service, ok := c.m[name]
	return service, ok
}

// Add add the specifed config with the specified name
func (c *ServiceConfigs) Add(name string, service *ServiceConfig) {
	c.mu.Lock()
	c.m[name] = service
	c.mu.Unlock()
}

// Len returns the len of the configs
func (c *ServiceConfigs) Len() int {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return len(c.m)
}

// Keys returns the names of the config
func (c *ServiceConfigs) Keys() []string {
	keys := []string{}
	c.mu.RLock()
	defer c.mu.RUnlock()
	for name := range c.m {
		keys = append(keys, name)
	}
	return keys
}

// RawService is represent a Service in map form unparsed
type RawService map[string]interface{}

// RawServiceMap is a collection of RawServices
type RawServiceMap map[string]RawService

// ParseOptions are a set of options to customize the parsing process
type ParseOptions struct {
	Interpolate bool
	Validate    bool
	Preprocess  func(RawServiceMap) (RawServiceMap, error)
	Postprocess func(map[string]*ServiceConfig) (map[string]*ServiceConfig, error)
}
<|endoftext|>


<commit_before>package backend_utils

import (
	"encoding/json"
	"os"
	"log"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"github.com/grpc-ecosystem/go-grpc-middleware/validator"
	"io/ioutil"
	"crypto/rsa"
	"github.com/dgrijalva/jwt-go"
	"golang.org/x/net/context"
	"fmt"
	"google.golang.org/grpc/metadata"
	"github.com/grpc-ecosystem/go-grpc-middleware/auth"
	"database/sql"
	"errors"
)

type GrpcServerConfig struct {

	// Use TLS for encryption
	UseTls 		bool	`json:"use_tls"`
	CertFile 	string	`json:"cert_file"`
	KeyFile 	string 	`json:"key_file"`

	// Use JWT based authentication
	UseJwt		bool	`json:"use_jwt"`
	PubKeyFile	string	`json:"pub_key"`
	PrivKeyFile	string	`json:"priv_key"`

	UseValidator	bool	`json:"use_validator"`
	Port		int32	`json:"port"`
	LogLevel	int32	`json:"log_level"`

	// Non-json fields
	PubKey		*rsa.PublicKey
	PrivKey		*rsa.PrivateKey
	auth_func_set	bool
	auth_func 	func (context.Context) (context.Context, error)
}

type GrpcClientConfig struct {

	// Use TLS for encryption
	UseTls 			bool	`json:"use_tls"`
	CertFile 		string	`json:"cert_file"`

	UseJwt			bool	`json:"use_jwt"`

	ServerHostOverride 	string	`json:"server_host_override"`
	ServerAddr 		string	`json:"server_addr"`

	// Non-json fields
	JwtToken		string
}

type PostgresDBConfig struct {
	Username	string	`json:"username"`
	Password	string	`json:"password"`
	DBName		string	`json:"db_name"`
}

type EmailerConfig struct {
	SmtpAddr	string	`json:"smtp_addr"`
	SmtpPort	int	`json:"smtp_port"`
	Username	string	`json:"username"`
	Password	string	`json:"password"`
}

type Configurations struct {
	ServerConfig	GrpcServerConfig 	`json:"server_config"`
	ClientConfig 	[]GrpcClientConfig	`json:"client_config"`
	PostgresDB	PostgresDBConfig	`json:"postgres_db"`
	Emailer		EmailerConfig		`json:"emailer"`
	AuthConfig 	GrpcClientConfig	`json:"auth_config"`
}

func ReadConfFile(file_path string) (*Configurations, error) {

	file, err := os.Open(file_path)
	if err != nil {
		return nil, err
	}

	conf := new(Configurations)

	decoder := json.NewDecoder(file)
	err = decoder.Decode(&conf)
	if err != nil {
		return nil, err
	}

	log.Printf("Read Configurations:%v\n", conf)

	return conf, nil
}

func ParseJWTpubKeyFile(file_path string) (*rsa.PublicKey, error) {
	key, err := ioutil.ReadFile(file_path)
	if err != nil {
		log.Printf("Failed reading JWT public key file.ERR:%s\n", err)
		return nil, err
	}
	pub_key, err := jwt.ParseRSAPublicKeyFromPEM(key)
	if err != nil {
		log.Printf("Failed parsing public key.ERR:%s\n", err)
		return nil, err
	}
	return pub_key, nil
}

func ParseJWTprivKeyFile(file_path string) (*rsa.PrivateKey, error) {
	key, err := ioutil.ReadFile(file_path)
	if err != nil {
		log.Printf("Failed reading JWT public key file.ERR:%s\n", err)
		return nil, err
	}
	priv_key, err := jwt.ParseRSAPrivateKeyFromPEM(key)
	if err != nil {
		log.Printf("Failed parsing public key.ERR:%s\n", err)
		return nil, err
	}
	return priv_key, nil
}

func validateToken(token string, publicKey *rsa.PublicKey) (*jwt.Token, error) {
	jwtToken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
		if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
			log.Printf("Unexpected signing method: %v", t.Header["alg"])
			return nil, fmt.Errorf("invalid token")
		}
		return publicKey, nil
	})
	if err == nil && jwtToken.Valid {
		return jwtToken, nil
	}
	return nil, err
}

func (c *Configurations) DefaultAuthFunction(ctx context.Context) (context.Context, error) {

	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, ErrUnauthenticated("Metadata corrupted")
	}

	jwtToken, ok := md["authorization"]
	if !ok {
		return nil, ErrUnauthenticated("Authorization header not present")
	}

	token, err := validateToken(jwtToken[0], c.ServerConfig.PubKey)
	if err != nil {
		return nil, ErrUnauthenticated("Invalid token")
	}

	newCtx := context.WithValue(ctx, "jwt_token", token)
	return newCtx, nil
}

func (c *Configurations) WithAuthFunc(auth func (context.Context) (context.Context, error)) {

	if !c.ServerConfig.UseJwt {
		log.Fatal("Public key file not specified in config.")
	}

	var err error
	c.ServerConfig.PubKey, err = ParseJWTpubKeyFile(c.ServerConfig.PubKeyFile)
	if err != nil {
		log.Fatalf("Failed parsing public key.ERR:%s\n", err)
	}

	c.ServerConfig.auth_func = auth
	c.ServerConfig.auth_func_set = true
}

func (c *Configurations) withDefaultAuthFunc() {

	if !c.ServerConfig.UseJwt {
		log.Fatal("Public key file not specified in config.")
	}

	var err error
	c.ServerConfig.PubKey, err = ParseJWTpubKeyFile(c.ServerConfig.PubKeyFile)
	if err != nil {
		log.Fatalf("Failed parsing public key.ERR:%s\n", err)
	}

	c.ServerConfig.auth_func = c.DefaultAuthFunction
	c.ServerConfig.auth_func_set = true
}

func (c *Configurations) GetServerOpts() ([]grpc.ServerOption, error) {

	var opts []grpc.ServerOption

	if c.ServerConfig.UseTls {
		creds, err := credentials.NewServerTLSFromFile(c.ServerConfig.CertFile, c.ServerConfig.KeyFile)
		if err != nil {
			log.Printf("Failed creating TLS credentials.ERR:%s\n", err)
			return opts, err
		}

		opts = append(opts, grpc.Creds(creds))
	}

	if c.ServerConfig.UseJwt {
		if !c.ServerConfig.auth_func_set {
			c.withDefaultAuthFunc()
		}
		opts = append(opts, grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(c.ServerConfig.auth_func)))
		opts = append(opts, grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(c.ServerConfig.auth_func)))

	}

	if c.ServerConfig.UseValidator {
		opts = append(opts, grpc.StreamInterceptor(grpc_validator.StreamServerInterceptor()))
		opts = append(opts, grpc.UnaryInterceptor(grpc_validator.UnaryServerInterceptor()))
	}

	return opts, nil
}

func (c *Configurations) WithJWTToken(token string) {

	if c.ClientConfig == nil {
		log.Fatal("Grpc Client config not specified.")
	}

	token_set := false
	for i := range c.ClientConfig {
		if c.ClientConfig[i].UseJwt {
			c.ClientConfig[i].JwtToken = token
			token_set = true
		}
	}

	if !token_set {
		log.Fatal("No client with JWT enabled.")
	}
}

type JwtCredentials struct {
	credentials.PerRPCCredentials
	token string
}

func NewJwtCredentials(tok string) *JwtCredentials {
	creds := new(JwtCredentials)
	creds.token = tok
	return creds
}

// GetRequestMetadata gets the current request metadata
func (j *JwtCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {

	return map[string]string{
		"authorization": j.token,
	}, nil
}

// Jwt does not RequireTransportSecurity
func (j *JwtCredentials) RequireTransportSecurity() bool { return false }

func (c *GrpcClientConfig) GetClientOpts() ([]grpc.DialOption, error) {

	var opts []grpc.DialOption
	if c.UseTls {
		var sn string
		if c.ServerHostOverride != "" {
			sn = c.ServerHostOverride
		}

		var creds credentials.TransportCredentials
		if c.CertFile != "" {
			var err error
			creds, err = credentials.NewClientTLSFromFile(c.CertFile, sn)
			if err != nil {
				log.Printf("Failed to create TLS credentials. ERR:%s\n", err.Error())
				return nil, err
			}
		} else {
			creds = credentials.NewClientTLSFromCert(nil, sn)
		}
		opts = append(opts, grpc.WithTransportCredentials(creds))
	}

	if c.UseJwt {
		opts = append(opts, grpc.WithPerRPCCredentials(NewJwtCredentials(c.JwtToken)))
	}

	return opts, nil
}

func (dbConf *PostgresDBConfig) OpenDB() (*sql.DB, error) {

	dbP, err := sql.Open("postgres", "user=" + dbConf.Username + " dbname=" + dbConf.DBName +
			     " sslmode=disable")
	if err == nil {
		// Open doesn't really do anything. Ping is where we will know.
		err = dbP.Ping()
	}

	if err != nil {
		log.Printf("Failed opening DB Err:%s", err.Error())
		return nil, err
	}

	return dbP, nil
}

func (dbConf *PostgresDBConfig) CreatePQDB() (*sql.DB, error) {

	// If DB is already created, Use the same.
	dbP, err := dbConf.OpenDB()
	if err == nil {
		log.Println("DB has already been created.")
		return dbP, nil
	}

	cmd_result := ExecCommand("psql", "-U" + dbConf.Username, "-tc CREATE DATABASE " + dbConf.DBName)
	if cmd_result.Err != nil {
		log.Printf("Failed executing database create command Err:%s", cmd_result.Err.Error())
		return nil, cmd_result.Err
	}

	log.Printf("Database command result: OUT:%s ERR:%s", cmd_result.StdOut, cmd_result.StdErr)
	if len(cmd_result.StdErr) > 0 {
		log.Printf("Database create command retured error. %s", cmd_result.StdErr)
		return nil, errors.New(cmd_result.StdErr)
	}

	return dbConf.OpenDB()
}
<commit_msg>Adding some client related utilities. Single connection for mobile clients.<commit_after>package backend_utils

import (
	"encoding/json"
	"os"
	"log"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"github.com/grpc-ecosystem/go-grpc-middleware/validator"
	"io/ioutil"
	"crypto/rsa"
	"github.com/dgrijalva/jwt-go"
	"golang.org/x/net/context"
	"fmt"
	"google.golang.org/grpc/metadata"
	"github.com/grpc-ecosystem/go-grpc-middleware/auth"
	"database/sql"
	"errors"
)

type GrpcServerConfig struct {

	// Use TLS for encryption
	UseTls 		bool	`json:"use_tls"`
	CertFile 	string	`json:"cert_file"`
	KeyFile 	string 	`json:"key_file"`

	// Use JWT based authentication
	UseJwt		bool	`json:"use_jwt"`
	PubKeyFile	string	`json:"pub_key"`
	PrivKeyFile	string	`json:"priv_key"`

	UseValidator	bool	`json:"use_validator"`
	Port		int32	`json:"port"`
	LogLevel	int32	`json:"log_level"`

	// Non-json fields
	PubKey		*rsa.PublicKey
	PrivKey		*rsa.PrivateKey
	auth_func_set	bool
	auth_func 	func (context.Context) (context.Context, error)
}

type GrpcClientConfig struct {

	// Use TLS for encryption
	UseTls 			bool	`json:"use_tls"`
	CertFile 		string	`json:"cert_file"`

	UseJwt			bool	`json:"use_jwt"`

	ServerHostOverride 	string	`json:"server_host_override"`
	ServerAddr 		string	`json:"server_addr"`

	// Non-json fields
	JwtToken		string
}

type PostgresDBConfig struct {
	Username	string	`json:"username"`
	Password	string	`json:"password"`
	DBName		string	`json:"db_name"`
}

type EmailerConfig struct {
	SmtpAddr	string	`json:"smtp_addr"`
	SmtpPort	int	`json:"smtp_port"`
	Username	string	`json:"username"`
	Password	string	`json:"password"`
}

type Configurations struct {
	ServerConfig	GrpcServerConfig 	`json:"server_config"`
	ClientConfig 	[]GrpcClientConfig	`json:"client_config"`
	PostgresDB	PostgresDBConfig	`json:"postgres_db"`
	Emailer		EmailerConfig		`json:"emailer"`
	AuthConfig 	GrpcClientConfig	`json:"auth_config"`
}

func ReadConfFile(file_path string) (*Configurations, error) {

	file, err := os.Open(file_path)
	if err != nil {
		return nil, err
	}

	conf := new(Configurations)

	decoder := json.NewDecoder(file)
	err = decoder.Decode(&conf)
	if err != nil {
		return nil, err
	}

	log.Printf("Read Configurations:%v\n", conf)

	return conf, nil
}

func ParseJWTpubKeyFile(file_path string) (*rsa.PublicKey, error) {
	key, err := ioutil.ReadFile(file_path)
	if err != nil {
		log.Printf("Failed reading JWT public key file.ERR:%s\n", err)
		return nil, err
	}
	pub_key, err := jwt.ParseRSAPublicKeyFromPEM(key)
	if err != nil {
		log.Printf("Failed parsing public key.ERR:%s\n", err)
		return nil, err
	}
	return pub_key, nil
}

func ParseJWTprivKeyFile(file_path string) (*rsa.PrivateKey, error) {
	key, err := ioutil.ReadFile(file_path)
	if err != nil {
		log.Printf("Failed reading JWT public key file.ERR:%s\n", err)
		return nil, err
	}
	priv_key, err := jwt.ParseRSAPrivateKeyFromPEM(key)
	if err != nil {
		log.Printf("Failed parsing public key.ERR:%s\n", err)
		return nil, err
	}
	return priv_key, nil
}

func validateToken(token string, publicKey *rsa.PublicKey) (*jwt.Token, error) {
	jwtToken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
		if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
			log.Printf("Unexpected signing method: %v", t.Header["alg"])
			return nil, fmt.Errorf("Invalid token %s", token)
		}
		return publicKey, nil
	})
	if err == nil && jwtToken.Valid {
		return jwtToken, nil
	}
	return nil, err
}

func (c *Configurations) DefaultAuthFunction(ctx context.Context) (context.Context, error) {

	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, ErrUnauthenticated("Metadata corrupted")
	}

	jwtToken, ok := md["authorization"]
	if !ok {
		return nil, ErrUnauthenticated("Authorization header not present")
	}

	token, err := validateToken(jwtToken[0], c.ServerConfig.PubKey)
	if err != nil {
		return nil, ErrUnauthenticated("Invalid token")
	}

	newCtx := context.WithValue(ctx, "jwt_token", token)
	return newCtx, nil
}

func (c *Configurations) WithAuthFunc(auth func (context.Context) (context.Context, error)) {

	if !c.ServerConfig.UseJwt {
		log.Fatal("Public key file not specified in config.")
	}

	var err error
	c.ServerConfig.PubKey, err = ParseJWTpubKeyFile(c.ServerConfig.PubKeyFile)
	if err != nil {
		log.Fatalf("Failed parsing public key.ERR:%s\n", err)
	}

	c.ServerConfig.auth_func = auth
	c.ServerConfig.auth_func_set = true
}

func (c *Configurations) withDefaultAuthFunc() {

	if !c.ServerConfig.UseJwt {
		log.Fatal("Public key file not specified in config.")
	}

	var err error
	c.ServerConfig.PubKey, err = ParseJWTpubKeyFile(c.ServerConfig.PubKeyFile)
	if err != nil {
		log.Fatalf("Failed parsing public key.ERR:%s\n", err)
	}

	c.ServerConfig.auth_func = c.DefaultAuthFunction
	c.ServerConfig.auth_func_set = true
}

func (c *Configurations) GetServerOpts() ([]grpc.ServerOption, error) {

	var opts []grpc.ServerOption

	if c.ServerConfig.UseTls {
		creds, err := credentials.NewServerTLSFromFile(c.ServerConfig.CertFile, c.ServerConfig.KeyFile)
		if err != nil {
			log.Printf("Failed creating TLS credentials.ERR:%s\n", err)
			return opts, err
		}

		opts = append(opts, grpc.Creds(creds))
	}

	if c.ServerConfig.UseJwt {
		if !c.ServerConfig.auth_func_set {
			c.withDefaultAuthFunc()
		}
		opts = append(opts, grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(c.ServerConfig.auth_func)))
		opts = append(opts, grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(c.ServerConfig.auth_func)))

	}

	if c.ServerConfig.UseValidator {
		opts = append(opts, grpc.StreamInterceptor(grpc_validator.StreamServerInterceptor()))
		opts = append(opts, grpc.UnaryInterceptor(grpc_validator.UnaryServerInterceptor()))
	}

	return opts, nil
}

func (c *Configurations) WithJWTToken(token string) {

	if c.ClientConfig == nil {
		log.Fatal("Grpc Client config not specified.")
	}

	token_set := false
	for i := range c.ClientConfig {
		if c.ClientConfig[i].UseJwt {
			c.ClientConfig[i].JwtToken = token
			token_set = true
		}
	}

	if !token_set {
		log.Fatal("No client with JWT enabled.")
	}
}

type JwtCredentials struct {
	credentials.PerRPCCredentials
	token string
}

func NewJwtCredentials(tok string) *JwtCredentials {
	creds := new(JwtCredentials)
	creds.token = tok
	return creds
}

// GetRequestMetadata gets the current request metadata
func (j *JwtCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {

	return map[string]string{
		"authorization": j.token,
	}, nil
}

// Jwt does not RequireTransportSecurity
func (j *JwtCredentials) RequireTransportSecurity() bool { return false }

func (c *GrpcClientConfig) WithJWTToken(token string) *GrpcClientConfig {
	c.JwtToken = token
	return c
}

func (c *GrpcClientConfig) NewRPCConn() (*grpc.ClientConn, error) {

	opts, err := c.GetClientOpts()
	if err != nil {
		log.Printf("Failed to get client options. ERR:%s\n", err.Error())
		return nil, err
	}

	conn, err := grpc.Dial(c.ServerAddr, opts...)
	if err != nil {
		log.Printf("Failed to dial. ERR:%s\n", err.Error())
		return nil, err
	}

	return conn, nil
}

func (c *GrpcClientConfig) GetClientOpts() ([]grpc.DialOption, error) {

	var opts []grpc.DialOption
	if c.UseTls {
		var sn string
		if c.ServerHostOverride != "" {
			sn = c.ServerHostOverride
		}

		var creds credentials.TransportCredentials
		if c.CertFile != "" {
			var err error
			creds, err = credentials.NewClientTLSFromFile(c.CertFile, sn)
			if err != nil {
				log.Printf("Failed to create TLS credentials. ERR:%s\n", err.Error())
				return nil, err
			}
		} else {
			creds = credentials.NewClientTLSFromCert(nil, sn)
		}
		opts = append(opts, grpc.WithTransportCredentials(creds))
	}

	if c.UseJwt {
		if len(c.JwtToken) == 0 {
			log.Println("Token not specified for JWT.")
			return nil, errors.New("Token not specified for use of JWT.")
		}
		opts = append(opts, grpc.WithPerRPCCredentials(NewJwtCredentials(c.JwtToken)))
	}

	return opts, nil
}

func (dbConf *PostgresDBConfig) OpenDB() (*sql.DB, error) {

	dbP, err := sql.Open("postgres", "user=" + dbConf.Username + " dbname=" + dbConf.DBName +
			     " sslmode=disable")
	if err == nil {
		// Open doesn't really do anything. Ping is where we will know.
		err = dbP.Ping()
	}

	if err != nil {
		log.Printf("Failed opening DB Err:%s", err.Error())
		return nil, err
	}

	return dbP, nil
}

func (dbConf *PostgresDBConfig) CreatePQDB() (*sql.DB, error) {

	// If DB is already created, Use the same.
	dbP, err := dbConf.OpenDB()
	if err == nil {
		log.Println("DB has already been created.")
		return dbP, nil
	}

	cmd_result := ExecCommand("psql", "-U" + dbConf.Username, "-tc CREATE DATABASE " + dbConf.DBName)
	if cmd_result.Err != nil {
		log.Printf("Failed executing database create command Err:%s", cmd_result.Err.Error())
		return nil, cmd_result.Err
	}

	log.Printf("Database command result: OUT:%s ERR:%s", cmd_result.StdOut, cmd_result.StdErr)
	if len(cmd_result.StdErr) > 0 {
		log.Printf("Database create command retured error. %s", cmd_result.StdErr)
		return nil, errors.New(cmd_result.StdErr)
	}

	return dbConf.OpenDB()
}
<|endoftext|>


<commit_before>// Package fakesqldb provides a MySQL server for tests.
package fakesqldb

import (
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/youtube/vitess/go/mysqlconn"
	"github.com/youtube/vitess/go/sqldb"
	"github.com/youtube/vitess/go/sqltypes"
)

// DB is a fake database and all its methods are thread safe.  It
// creates a mysqlconn.Listener and implements the mysqlconn.Handler interface.
type DB struct {
	// Fields set at construction time.

	// t is our testing.T instance
	t *testing.T

	// listener is our mysqlconn.Listener.
	listener *mysqlconn.Listener

	// name is the name of this DB. Set to 'fakesqldb' by default.
	// Use SetName() to change.
	name string

	// acceptWG is set when we listen, and can be waited on to
	// make sure we don't accept any more.
	acceptWG sync.WaitGroup

	// Fields set at runtime.

	// mu protects all the following fields.
	mu sync.Mutex
	// isConnFail trigger a panic in the connection handler.
	isConnFail bool
	// shouldClose, if true, tells ComQuery() to close the connection when
	// processing the next query. This will trigger a MySQL client error with
	// errno 2013 ("server lost").
	shouldClose bool
	// data maps tolower(query) to a result.
	data map[string]*sqltypes.Result
	// rejectedData maps tolower(query) to an error.
	rejectedData map[string]error
	// patternData is a list of regexp to results.
	patternData []exprResult
	// queryCalled keeps track of how many times a query was called.
	queryCalled map[string]int
	// connections tracks all open connections.
	// The key for the map is the value of mysql.Conn.ConnectionID.
	connections map[uint32]*mysqlconn.Conn
}

type exprResult struct {
	expr   *regexp.Regexp
	result *sqltypes.Result
}

// New creates a server, and starts listening.
func New(t *testing.T) *DB {
	// Create our DB.
	db := &DB{
		t:            t,
		name:         "fakesqldb",
		data:         make(map[string]*sqltypes.Result),
		rejectedData: make(map[string]error),
		queryCalled:  make(map[string]int),
		connections:  make(map[uint32]*mysqlconn.Conn),
	}

	// Start listening.
	var err error
	db.listener, err = mysqlconn.NewListener("tcp", ":0", db)
	if err != nil {
		t.Fatalf("NewListener failed: %v", err)
	}

	db.listener.PasswordMap["user1"] = "password1"
	db.acceptWG.Add(1)
	go func() {
		defer db.acceptWG.Done()
		db.listener.Accept()
	}()

	// Return the db and connection parameters.
	return db
}

// SetName sets the name of the DB. to differentiate them in tests if needed.
func (db *DB) SetName(name string) *DB {
	db.name = name
	return db
}

// Close closes the Listener and waits for it to stop accepting.
func (db *DB) Close() {
	db.listener.Close()
	db.acceptWG.Wait()

	db.CloseAllConnections()
}

// CloseAllConnections can be used to provoke MySQL client errors for open
// connections.
// Make sure to call WaitForShutdown() as well.
func (db *DB) CloseAllConnections() {
	db.mu.Lock()
	defer db.mu.Unlock()

	for _, c := range db.connections {
		c.Close()
	}
}

// WaitForClose should be used after CloseAllConnections() is closed and
// you want to provoke a MySQL client error with errno 2006.
//
// If you don't call this function and execute the next query right away, you
// will very likely see errno 2013 instead due to timing issues.
// That's because the following can happen:
//
// 1. vttablet MySQL client is able to send the query to this fake server.
// 2. The fake server sees the query and calls the ComQuery() callback.
// 3. The fake server tries to write the response back on the connection.
// => This will finally fail because the connection is already closed.
// In this example, the client would have been able to send off the query and
// therefore return errno 2013 ("server lost").
// Instead, if step 1 already fails, the client returns errno 2006 ("gone away").
// By waiting for the connections to close, you make sure of that.
func (db *DB) WaitForClose(timeout time.Duration) error {
	start := time.Now()
	for {
		db.mu.Lock()
		count := len(db.connections)
		db.mu.Unlock()

		if count == 0 {
			return nil
		}
		if d := time.Since(start); d > timeout {
			return fmt.Errorf("connections were not correctly closed after %v: %v are left", d, count)
		}
		time.Sleep(1 * time.Microsecond)
	}
}

// Host returns the host we're listening on.
func (db *DB) Host() string {
	return db.listener.Addr().(*net.TCPAddr).IP.String()
}

// Port returns the port we're listening on.
func (db *DB) Port() int {
	return db.listener.Addr().(*net.TCPAddr).Port
}

// ConnParams returns the ConnParams to connect to the DB.
func (db *DB) ConnParams() *sqldb.ConnParams {
	return &sqldb.ConnParams{
		Host:    db.Host(),
		Port:    db.Port(),
		Uname:   "user1",
		Pass:    "password1",
		Charset: "utf8",
	}
}

//
// mysqlconn.Handler interface
//

// NewConnection is part of the mysqlconn.Handler interface.
func (db *DB) NewConnection(c *mysqlconn.Conn) {
	db.t.Logf("NewConnection(%v): client %v", db.name, c.ConnectionID)

	if db.IsConnFail() {
		panic(fmt.Errorf("simulating a connection failure"))
	}

	db.mu.Lock()
	defer db.mu.Unlock()

	if conn, ok := db.connections[c.ConnectionID]; ok {
		db.t.Fatalf("BUG: connection with id: %v is already active. existing conn: %v new conn: %v", c.ConnectionID, conn, c)
	}
	db.connections[c.ConnectionID] = c
}

// ConnectionClosed is part of the mysqlconn.Handler interface.
func (db *DB) ConnectionClosed(c *mysqlconn.Conn) {
	db.t.Logf("ConnectionClosed(%v): client %v", db.name, c.ConnectionID)

	db.mu.Lock()
	defer db.mu.Unlock()

	if _, ok := db.connections[c.ConnectionID]; !ok {
		db.t.Fatalf("BUG: Cannot delete connection from list of open connections because it is not registered. ID: %v Conn: %v", c.ConnectionID, c)
	}
	delete(db.connections, c.ConnectionID)
}

// ComQuery is part of the mysqlconn.Handler interface.
func (db *DB) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error) {
	db.t.Logf("ComQuery(%v): client %v: %v", db.name, c.ConnectionID, query)

	key := strings.ToLower(query)
	db.mu.Lock()
	defer db.mu.Unlock()
	db.queryCalled[key]++

	// Check if we should close the connection and provoke errno 2013.
	if db.shouldClose {
		c.Close()
		return nil, nil
	}

	// Using special handling for 'SET NAMES utf8'.  The driver
	// may send this at connection time, and we don't want it to
	// interfere.
	if key == "set names utf8" {
		return &sqltypes.Result{}, nil
	}

	// check if we should reject it.
	if err, ok := db.rejectedData[key]; ok {
		return nil, err
	}

	// Check explicit queries from AddQuery().
	result, ok := db.data[key]
	if ok {
		return result, nil
	}

	// Check query patterns from AddQueryPattern().
	for _, pat := range db.patternData {
		if pat.expr.MatchString(query) {
			return pat.result, nil
		}
	}

	// Nothing matched.
	return nil, fmt.Errorf("query: %s is not supported on %v", query, db.name)
}

//
// Methods to add expected queries and results.
//

// AddQuery adds a query and its expected result.
func (db *DB) AddQuery(query string, expectedResult *sqltypes.Result) {
	if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 {
		panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", query))
	}
	result := &sqltypes.Result{}
	*result = *expectedResult
	db.mu.Lock()
	defer db.mu.Unlock()
	key := strings.ToLower(query)
	db.data[key] = result
	db.queryCalled[key] = 0
}

// AddQueryPattern adds an expected result for a set of queries.
// These patterns are checked if no exact matches from AddQuery() are found.
// This function forces the addition of begin/end anchors (^$) and turns on
// case-insensitive matching mode.
func (db *DB) AddQueryPattern(queryPattern string, expectedResult *sqltypes.Result) {
	if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 {
		panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", queryPattern))
	}
	expr := regexp.MustCompile("(?is)^" + queryPattern + "$")
	result := *expectedResult
	db.mu.Lock()
	defer db.mu.Unlock()
	db.patternData = append(db.patternData, exprResult{expr, &result})
}

// DeleteQuery deletes query from the fake DB.
func (db *DB) DeleteQuery(query string) {
	db.mu.Lock()
	defer db.mu.Unlock()
	key := strings.ToLower(query)
	delete(db.data, key)
	delete(db.queryCalled, key)
}

// AddRejectedQuery adds a query which will be rejected at execution time.
func (db *DB) AddRejectedQuery(query string, err error) {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.rejectedData[strings.ToLower(query)] = err
}

// DeleteRejectedQuery deletes query from the fake DB.
func (db *DB) DeleteRejectedQuery(query string) {
	db.mu.Lock()
	defer db.mu.Unlock()
	delete(db.rejectedData, strings.ToLower(query))
}

// GetQueryCalledNum returns how many times db executes a certain query.
func (db *DB) GetQueryCalledNum(query string) int {
	db.mu.Lock()
	defer db.mu.Unlock()
	num, ok := db.queryCalled[strings.ToLower(query)]
	if !ok {
		return 0
	}
	return num
}

// EnableConnFail makes connection to this fake DB fail.
func (db *DB) EnableConnFail() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.isConnFail = true
}

// DisableConnFail makes connection to this fake DB success.
func (db *DB) DisableConnFail() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.isConnFail = false
}

// IsConnFail tests whether there is a connection failure.
func (db *DB) IsConnFail() bool {
	db.mu.Lock()
	defer db.mu.Unlock()
	return db.isConnFail
}

// EnableShouldClose closes the connection when processing the next query.
func (db *DB) EnableShouldClose() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.shouldClose = true
}
<commit_msg>mysqlconn: Return empty struct instead of nil when the fake should close the connection.<commit_after>// Package fakesqldb provides a MySQL server for tests.
package fakesqldb

import (
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/youtube/vitess/go/mysqlconn"
	"github.com/youtube/vitess/go/sqldb"
	"github.com/youtube/vitess/go/sqltypes"
)

// DB is a fake database and all its methods are thread safe.  It
// creates a mysqlconn.Listener and implements the mysqlconn.Handler interface.
type DB struct {
	// Fields set at construction time.

	// t is our testing.T instance
	t *testing.T

	// listener is our mysqlconn.Listener.
	listener *mysqlconn.Listener

	// name is the name of this DB. Set to 'fakesqldb' by default.
	// Use SetName() to change.
	name string

	// acceptWG is set when we listen, and can be waited on to
	// make sure we don't accept any more.
	acceptWG sync.WaitGroup

	// Fields set at runtime.

	// mu protects all the following fields.
	mu sync.Mutex
	// isConnFail trigger a panic in the connection handler.
	isConnFail bool
	// shouldClose, if true, tells ComQuery() to close the connection when
	// processing the next query. This will trigger a MySQL client error with
	// errno 2013 ("server lost").
	shouldClose bool
	// data maps tolower(query) to a result.
	data map[string]*sqltypes.Result
	// rejectedData maps tolower(query) to an error.
	rejectedData map[string]error
	// patternData is a list of regexp to results.
	patternData []exprResult
	// queryCalled keeps track of how many times a query was called.
	queryCalled map[string]int
	// connections tracks all open connections.
	// The key for the map is the value of mysql.Conn.ConnectionID.
	connections map[uint32]*mysqlconn.Conn
}

type exprResult struct {
	expr   *regexp.Regexp
	result *sqltypes.Result
}

// New creates a server, and starts listening.
func New(t *testing.T) *DB {
	// Create our DB.
	db := &DB{
		t:            t,
		name:         "fakesqldb",
		data:         make(map[string]*sqltypes.Result),
		rejectedData: make(map[string]error),
		queryCalled:  make(map[string]int),
		connections:  make(map[uint32]*mysqlconn.Conn),
	}

	// Start listening.
	var err error
	db.listener, err = mysqlconn.NewListener("tcp", ":0", db)
	if err != nil {
		t.Fatalf("NewListener failed: %v", err)
	}

	db.listener.PasswordMap["user1"] = "password1"
	db.acceptWG.Add(1)
	go func() {
		defer db.acceptWG.Done()
		db.listener.Accept()
	}()

	// Return the db and connection parameters.
	return db
}

// SetName sets the name of the DB. to differentiate them in tests if needed.
func (db *DB) SetName(name string) *DB {
	db.name = name
	return db
}

// Close closes the Listener and waits for it to stop accepting.
func (db *DB) Close() {
	db.listener.Close()
	db.acceptWG.Wait()

	db.CloseAllConnections()
}

// CloseAllConnections can be used to provoke MySQL client errors for open
// connections.
// Make sure to call WaitForShutdown() as well.
func (db *DB) CloseAllConnections() {
	db.mu.Lock()
	defer db.mu.Unlock()

	for _, c := range db.connections {
		c.Close()
	}
}

// WaitForClose should be used after CloseAllConnections() is closed and
// you want to provoke a MySQL client error with errno 2006.
//
// If you don't call this function and execute the next query right away, you
// will very likely see errno 2013 instead due to timing issues.
// That's because the following can happen:
//
// 1. vttablet MySQL client is able to send the query to this fake server.
// 2. The fake server sees the query and calls the ComQuery() callback.
// 3. The fake server tries to write the response back on the connection.
// => This will finally fail because the connection is already closed.
// In this example, the client would have been able to send off the query and
// therefore return errno 2013 ("server lost").
// Instead, if step 1 already fails, the client returns errno 2006 ("gone away").
// By waiting for the connections to close, you make sure of that.
func (db *DB) WaitForClose(timeout time.Duration) error {
	start := time.Now()
	for {
		db.mu.Lock()
		count := len(db.connections)
		db.mu.Unlock()

		if count == 0 {
			return nil
		}
		if d := time.Since(start); d > timeout {
			return fmt.Errorf("connections were not correctly closed after %v: %v are left", d, count)
		}
		time.Sleep(1 * time.Microsecond)
	}
}

// Host returns the host we're listening on.
func (db *DB) Host() string {
	return db.listener.Addr().(*net.TCPAddr).IP.String()
}

// Port returns the port we're listening on.
func (db *DB) Port() int {
	return db.listener.Addr().(*net.TCPAddr).Port
}

// ConnParams returns the ConnParams to connect to the DB.
func (db *DB) ConnParams() *sqldb.ConnParams {
	return &sqldb.ConnParams{
		Host:    db.Host(),
		Port:    db.Port(),
		Uname:   "user1",
		Pass:    "password1",
		Charset: "utf8",
	}
}

//
// mysqlconn.Handler interface
//

// NewConnection is part of the mysqlconn.Handler interface.
func (db *DB) NewConnection(c *mysqlconn.Conn) {
	db.t.Logf("NewConnection(%v): client %v", db.name, c.ConnectionID)

	if db.IsConnFail() {
		panic(fmt.Errorf("simulating a connection failure"))
	}

	db.mu.Lock()
	defer db.mu.Unlock()

	if conn, ok := db.connections[c.ConnectionID]; ok {
		db.t.Fatalf("BUG: connection with id: %v is already active. existing conn: %v new conn: %v", c.ConnectionID, conn, c)
	}
	db.connections[c.ConnectionID] = c
}

// ConnectionClosed is part of the mysqlconn.Handler interface.
func (db *DB) ConnectionClosed(c *mysqlconn.Conn) {
	db.t.Logf("ConnectionClosed(%v): client %v", db.name, c.ConnectionID)

	db.mu.Lock()
	defer db.mu.Unlock()

	if _, ok := db.connections[c.ConnectionID]; !ok {
		db.t.Fatalf("BUG: Cannot delete connection from list of open connections because it is not registered. ID: %v Conn: %v", c.ConnectionID, c)
	}
	delete(db.connections, c.ConnectionID)
}

// ComQuery is part of the mysqlconn.Handler interface.
func (db *DB) ComQuery(c *mysqlconn.Conn, query string) (*sqltypes.Result, error) {
	db.t.Logf("ComQuery(%v): client %v: %v", db.name, c.ConnectionID, query)

	key := strings.ToLower(query)
	db.mu.Lock()
	defer db.mu.Unlock()
	db.queryCalled[key]++

	// Check if we should close the connection and provoke errno 2013.
	if db.shouldClose {
		c.Close()
		return &sqltypes.Result{}, nil
	}

	// Using special handling for 'SET NAMES utf8'.  The driver
	// may send this at connection time, and we don't want it to
	// interfere.
	if key == "set names utf8" {
		return &sqltypes.Result{}, nil
	}

	// check if we should reject it.
	if err, ok := db.rejectedData[key]; ok {
		return nil, err
	}

	// Check explicit queries from AddQuery().
	result, ok := db.data[key]
	if ok {
		return result, nil
	}

	// Check query patterns from AddQueryPattern().
	for _, pat := range db.patternData {
		if pat.expr.MatchString(query) {
			return pat.result, nil
		}
	}

	// Nothing matched.
	return nil, fmt.Errorf("query: %s is not supported on %v", query, db.name)
}

//
// Methods to add expected queries and results.
//

// AddQuery adds a query and its expected result.
func (db *DB) AddQuery(query string, expectedResult *sqltypes.Result) {
	if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 {
		panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", query))
	}
	result := &sqltypes.Result{}
	*result = *expectedResult
	db.mu.Lock()
	defer db.mu.Unlock()
	key := strings.ToLower(query)
	db.data[key] = result
	db.queryCalled[key] = 0
}

// AddQueryPattern adds an expected result for a set of queries.
// These patterns are checked if no exact matches from AddQuery() are found.
// This function forces the addition of begin/end anchors (^$) and turns on
// case-insensitive matching mode.
func (db *DB) AddQueryPattern(queryPattern string, expectedResult *sqltypes.Result) {
	if len(expectedResult.Rows) > 0 && len(expectedResult.Fields) == 0 {
		panic(fmt.Errorf("Please add Fields to this Result so it's valid: %v", queryPattern))
	}
	expr := regexp.MustCompile("(?is)^" + queryPattern + "$")
	result := *expectedResult
	db.mu.Lock()
	defer db.mu.Unlock()
	db.patternData = append(db.patternData, exprResult{expr, &result})
}

// DeleteQuery deletes query from the fake DB.
func (db *DB) DeleteQuery(query string) {
	db.mu.Lock()
	defer db.mu.Unlock()
	key := strings.ToLower(query)
	delete(db.data, key)
	delete(db.queryCalled, key)
}

// AddRejectedQuery adds a query which will be rejected at execution time.
func (db *DB) AddRejectedQuery(query string, err error) {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.rejectedData[strings.ToLower(query)] = err
}

// DeleteRejectedQuery deletes query from the fake DB.
func (db *DB) DeleteRejectedQuery(query string) {
	db.mu.Lock()
	defer db.mu.Unlock()
	delete(db.rejectedData, strings.ToLower(query))
}

// GetQueryCalledNum returns how many times db executes a certain query.
func (db *DB) GetQueryCalledNum(query string) int {
	db.mu.Lock()
	defer db.mu.Unlock()
	num, ok := db.queryCalled[strings.ToLower(query)]
	if !ok {
		return 0
	}
	return num
}

// EnableConnFail makes connection to this fake DB fail.
func (db *DB) EnableConnFail() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.isConnFail = true
}

// DisableConnFail makes connection to this fake DB success.
func (db *DB) DisableConnFail() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.isConnFail = false
}

// IsConnFail tests whether there is a connection failure.
func (db *DB) IsConnFail() bool {
	db.mu.Lock()
	defer db.mu.Unlock()
	return db.isConnFail
}

// EnableShouldClose closes the connection when processing the next query.
func (db *DB) EnableShouldClose() {
	db.mu.Lock()
	defer db.mu.Unlock()
	db.shouldClose = true
}
<|endoftext|>


<commit_before>// The erisdb package contains tendermint-specific services that goes with the
// server.
package erisdb

import (
  "encoding/hex"
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"path"
	"strings"
	"sync"

	// tendermint support libs
	. "github.com/tendermint/go-common"
	cfg "github.com/tendermint/go-config"
	dbm "github.com/tendermint/go-db"
	"github.com/tendermint/go-events"
	rpcserver "github.com/tendermint/go-rpc/server"
	"github.com/tendermint/go-wire"
	"github.com/tendermint/log15"

	// for inproc tendermint
	"github.com/tendermint/go-p2p"
	"github.com/tendermint/tendermint/node"
	"github.com/tendermint/tendermint/proxy"
	"github.com/tendermint/tendermint/types"
	tmspcli "github.com/tendermint/tmsp/client"

	// tmsp server
	tmsp "github.com/tendermint/tmsp/server"

	edbcfg "github.com/eris-ltd/eris-db/config"
	ep "github.com/eris-ltd/eris-db/erisdb/pipe"
	rpccore "github.com/eris-ltd/eris-db/rpc/core"
	"github.com/eris-ltd/eris-db/server"
	sm "github.com/eris-ltd/eris-db/state"
	stypes "github.com/eris-ltd/eris-db/state/types"
	edbapp "github.com/eris-ltd/eris-db/tmsp"

	tmcfg "github.com/tendermint/tendermint/config/tendermint" // for inproc only!
)

var log = log15.New("module", "eris/erisdb_server")
var tmConfig cfg.Config

// This function returns a properly configured ErisDb server process,
// with a tmsp listener for talking to tendermint core.
// To start listening for incoming HTTP requests on the Rest server, call 'Start()' on the process.
// Make sure to register any start event listeners first
func ServeErisDB(workDir string, inProc bool) (*server.ServeProcess, error) {
	log.Info("ErisDB Serve initializing.")
	errEns := EnsureDir(workDir, 0777)

	if errEns != nil {
		return nil, errEns
	}

	// there are two types of config we need to load,
	// one for the erisdb server and one for tendermint.
	// even if consensus isn't in process, the tendermint libs (eg. db)
	// expect tendermint/go-config to be setup.
	// Regardless, both configs are expected in the same file (root/config.toml)
	// Some of this stuff is implicit and maybe a little confusing,
	// but cfg mgmt across projects probably often is!

	// Get an erisdb configuration
	var edbConf *edbcfg.ErisDBConfig
	edbConfPath := path.Join(workDir, "config.toml")
	if !FileExists(edbConfPath) {
		log.Info("No server configuration, using default.")
		log.Info("Writing to: " + edbConfPath)
		edbConf = edbcfg.DefaultErisDBConfig()
		errW := edbcfg.WriteErisDBConfig(edbConfPath, edbConf)
		if errW != nil {
			panic(errW)
		}
	} else {
		var errRSC error
		edbConf, errRSC = edbcfg.ReadErisDBConfig(edbConfPath)
		if errRSC != nil {
			log.Error("Server config file error.", "error", errRSC.Error())
		}
	}

	// Get tendermint configuration
	tmConfig = tmcfg.GetConfig(workDir)

	// tmConfig.Set("tm.version", TENDERMINT_VERSION) // ?
	cfg.ApplyConfig(tmConfig) // Notify in proc tendermint of new config

	// Load the application state
	// The app state used to be managed by tendermint node,
	// but is now managed by ErisDB.
	// The tendermint core only stores the blockchain (history of txs)
	stateDB := dbm.GetDB("app_state", edbConf.DB.Backend, workDir+"/data")
	state := sm.LoadState(stateDB)
	var genDoc *stypes.GenesisDoc
	if state == nil {
		genDoc, state = sm.MakeGenesisStateFromFile(stateDB, workDir+"/genesis.json")
		state.Save()
		buf, n, err := new(bytes.Buffer), new(int), new(error)
		wire.WriteJSON(genDoc, buf, n, err)
		stateDB.Set(stypes.GenDocKey, buf.Bytes())
		if *err != nil {
			Exit(Fmt("Unable to write gendoc to db: %v", err))
		}
	} else {
		genDocBytes := stateDB.Get(stypes.GenDocKey)
		err := new(error)
		wire.ReadJSONPtr(&genDoc, genDocBytes, err)
		if *err != nil {
			Exit(Fmt("Unable to read gendoc from db: %v", err))
		}
	}
	// add the chainid
	config.Set("erisdb.chain_id", state.ChainID)

	// *****************************
	// erisdb-tmsp app

	// start the event switch for state related events
	// (transactions to/from acconts, etc)
	evsw := events.NewEventSwitch()
	evsw.Start()

	// create the app
	app := edbapp.NewErisDBApp(state, evsw)

	// so we know where to find the consensus host (for eg. blockchain/consensus rpcs)
	app.SetHostAddress(edbConf.Tendermint.Host)
	if inProc {
		fmt.Println("Starting tm node in proc")
		// will also start the go-rpc server (46657 api)
		startTMNode(app, workDir)
	} else {
		fmt.Println("Starting tmsp listener")
		// Start the tmsp listener for state update commands
		go func() {
			// TODO config
			_, err := tmsp.NewServer(edbConf.TMSP.Listener, app)
			if err != nil {
				// TODO: play nice
				Exit(err.Error())
			}
		}()
	}

	// *****************************
	// Boot the erisdb restful API servers

	// Load supporting objects.
	pipe := ep.NewPipe(app, evsw)
	codec := &TCodec{}
	evtSubs := NewEventSubscriptions(pipe.Events())
	// The services.
	tmwss := NewErisDbWsService(codec, pipe)
	tmjs := NewErisDbJsonService(codec, pipe, evtSubs)
	// The servers.
	jsonServer := NewJsonRpcServer(tmjs)
	restServer := NewRestServer(codec, pipe, evtSubs)
	wsServer := server.NewWebSocketServer(edbConf.Server.WebSocket.MaxWebSocketSessions, tmwss)
	// Create a server process.
	proc := server.NewServeProcess(&edbConf.Server, jsonServer, restServer, wsServer)

	return proc, nil
}

// start an inproc tendermint node
func startTMNode(app *edbapp.ErisDBApp, workDir string) {
	// get the genesis
	genDocFile := config.GetString("tm.genesis_file")
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
	}
	genDoc := types.GenesisDocFromJSON(jsonBlob)
	if genDoc.ChainID == "" {
		PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile))
	}
	config.Set("tm.chain_id", genDoc.ChainID)
	config.Set("tm.genesis_doc", genDoc)

	// Get PrivValidator
	privValidatorFile := config.GetString("tm.priv_validator_file")
	privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
	nd := node.NewNode(privValidator, func(addr string, hash []byte) proxy.AppConn {
		// TODO: Check the hash
		return tmspcli.NewLocalClient(new(sync.Mutex), app)
	})

	l := p2p.NewDefaultListener("tcp", config.GetString("tm.node_laddr"), config.GetBool("tm.skip_upnp"))
	nd.AddListener(l)
	if err := nd.Start(); err != nil {
		Exit(Fmt("Failed to start node: %v", err))
	}

	log.Notice("Started node", "nodeInfo", nd.NodeInfo())

	// If seedNode is provided by config, dial out.
	if config.GetString("tm.seeds") != "" {
		seeds := strings.Split(config.GetString("tm.seeds"), ",")
		nd.DialSeeds(seeds)
	}

	// Run the RPC server.
	if config.GetString("tm.rpc_laddr") != "" {
		_, err := StartRPC(nd, app)
		if err != nil {
			PanicCrisis(err)
		}
	}
}

func StartRPC(n *node.Node, edbApp *edbapp.ErisDBApp) ([]net.Listener, error) {
	rpccore.SetErisDBApp(edbApp)
	rpccore.SetBlockStore(n.BlockStore())
	rpccore.SetConsensusState(n.ConsensusState())
	rpccore.SetConsensusReactor(n.ConsensusReactor())
	rpccore.SetMempoolReactor(n.MempoolReactor())
	rpccore.SetSwitch(n.Switch())
	rpccore.SetPrivValidator(n.PrivValidator())
	rpccore.SetGenDoc(LoadGenDoc())

	listenAddrs := strings.Split(config.GetString("tm.rpc_laddr"), ",")

	// we may expose the rpc over both a unix and tcp socket
	listeners := make([]net.Listener, len(listenAddrs))
	for i, listenAddr := range listenAddrs {
		mux := http.NewServeMux()
		wm := rpcserver.NewWebsocketManager(rpccore.Routes, n.EventSwitch())
		mux.HandleFunc("/websocket", wm.WebsocketHandler)
		rpcserver.RegisterRPCFuncs(mux, rpccore.Routes)
		listener, err := rpcserver.StartHTTPServer(listenAddr, mux)
		if err != nil {
			return nil, err
		}
		listeners[i] = listener
	}
	return listeners, nil
}

func LoadGenDoc() *stypes.GenesisDoc {
	genDocFile := config.GetString("tm.genesis_file")
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
	}
	return stypes.GenesisDocFromJSON(jsonBlob)
}

type ErisSigner struct {
	Host       string
	Address    string
	SessionKey string
}

func NewErisSigner(host, address string) *ErisSigner {
	if !strings.HasPrefix(host, "http://") {
		host = fmt.Sprintf("http://%s", host)
	}
	return &ErisSigner{Host: host, Address: address}
}

func (es *ErisSigner) Sign(msg []byte) acm.SignatureEd25519 {
	msgHex := hex.EncodeToString(msg)
	sig, err := core.Sign(msgHex, es.Address, es.Host)
	if err != nil {
		panic(err)
	}
	return acm.SignatureEd25519(sig)
}
<commit_msg>remove ErisSigner struct<commit_after>// The erisdb package contains tendermint-specific services that goes with the
// server.
package erisdb

import (
  "encoding/hex"
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"path"
	"strings"
	"sync"

	// tendermint support libs
	. "github.com/tendermint/go-common"
	cfg "github.com/tendermint/go-config"
	dbm "github.com/tendermint/go-db"
	"github.com/tendermint/go-events"
	rpcserver "github.com/tendermint/go-rpc/server"
	"github.com/tendermint/go-wire"
	"github.com/tendermint/log15"

	// for inproc tendermint
	"github.com/tendermint/go-p2p"
	"github.com/tendermint/tendermint/node"
	"github.com/tendermint/tendermint/proxy"
	"github.com/tendermint/tendermint/types"
	tmspcli "github.com/tendermint/tmsp/client"

	// tmsp server
	tmsp "github.com/tendermint/tmsp/server"

	edbcfg "github.com/eris-ltd/eris-db/config"
	ep "github.com/eris-ltd/eris-db/erisdb/pipe"
	rpccore "github.com/eris-ltd/eris-db/rpc/core"
	"github.com/eris-ltd/eris-db/server"
	sm "github.com/eris-ltd/eris-db/state"
	stypes "github.com/eris-ltd/eris-db/state/types"
	edbapp "github.com/eris-ltd/eris-db/tmsp"

	tmcfg "github.com/tendermint/tendermint/config/tendermint" // for inproc only!
)

var log = log15.New("module", "eris/erisdb_server")
var tmConfig cfg.Config

// This function returns a properly configured ErisDb server process,
// with a tmsp listener for talking to tendermint core.
// To start listening for incoming HTTP requests on the Rest server, call 'Start()' on the process.
// Make sure to register any start event listeners first
func ServeErisDB(workDir string, inProc bool) (*server.ServeProcess, error) {
	log.Info("ErisDB Serve initializing.")
	errEns := EnsureDir(workDir, 0777)

	if errEns != nil {
		return nil, errEns
	}

	// there are two types of config we need to load,
	// one for the erisdb server and one for tendermint.
	// even if consensus isn't in process, the tendermint libs (eg. db)
	// expect tendermint/go-config to be setup.
	// Regardless, both configs are expected in the same file (root/config.toml)
	// Some of this stuff is implicit and maybe a little confusing,
	// but cfg mgmt across projects probably often is!

	// Get an erisdb configuration
	var edbConf *edbcfg.ErisDBConfig
	edbConfPath := path.Join(workDir, "config.toml")
	if !FileExists(edbConfPath) {
		log.Info("No server configuration, using default.")
		log.Info("Writing to: " + edbConfPath)
		edbConf = edbcfg.DefaultErisDBConfig()
		errW := edbcfg.WriteErisDBConfig(edbConfPath, edbConf)
		if errW != nil {
			panic(errW)
		}
	} else {
		var errRSC error
		edbConf, errRSC = edbcfg.ReadErisDBConfig(edbConfPath)
		if errRSC != nil {
			log.Error("Server config file error.", "error", errRSC.Error())
		}
	}

	// Get tendermint configuration
	tmConfig = tmcfg.GetConfig(workDir)

	// tmConfig.Set("tm.version", TENDERMINT_VERSION) // ?
	cfg.ApplyConfig(tmConfig) // Notify in proc tendermint of new config

	// Load the application state
	// The app state used to be managed by tendermint node,
	// but is now managed by ErisDB.
	// The tendermint core only stores the blockchain (history of txs)
	stateDB := dbm.GetDB("app_state", edbConf.DB.Backend, workDir+"/data")
	state := sm.LoadState(stateDB)
	var genDoc *stypes.GenesisDoc
	if state == nil {
		genDoc, state = sm.MakeGenesisStateFromFile(stateDB, workDir+"/genesis.json")
		state.Save()
		buf, n, err := new(bytes.Buffer), new(int), new(error)
		wire.WriteJSON(genDoc, buf, n, err)
		stateDB.Set(stypes.GenDocKey, buf.Bytes())
		if *err != nil {
			Exit(Fmt("Unable to write gendoc to db: %v", err))
		}
	} else {
		genDocBytes := stateDB.Get(stypes.GenDocKey)
		err := new(error)
		wire.ReadJSONPtr(&genDoc, genDocBytes, err)
		if *err != nil {
			Exit(Fmt("Unable to read gendoc from db: %v", err))
		}
	}
	// add the chainid
	config.Set("erisdb.chain_id", state.ChainID)

	// *****************************
	// erisdb-tmsp app

	// start the event switch for state related events
	// (transactions to/from acconts, etc)
	evsw := events.NewEventSwitch()
	evsw.Start()

	// create the app
	app := edbapp.NewErisDBApp(state, evsw)

	// so we know where to find the consensus host (for eg. blockchain/consensus rpcs)
	app.SetHostAddress(edbConf.Tendermint.Host)
	if inProc {
		fmt.Println("Starting tm node in proc")
		// will also start the go-rpc server (46657 api)
		startTMNode(app, workDir)
	} else {
		fmt.Println("Starting tmsp listener")
		// Start the tmsp listener for state update commands
		go func() {
			// TODO config
			_, err := tmsp.NewServer(edbConf.TMSP.Listener, app)
			if err != nil {
				// TODO: play nice
				Exit(err.Error())
			}
		}()
	}

	// *****************************
	// Boot the erisdb restful API servers

	// Load supporting objects.
	pipe := ep.NewPipe(app, evsw)
	codec := &TCodec{}
	evtSubs := NewEventSubscriptions(pipe.Events())
	// The services.
	tmwss := NewErisDbWsService(codec, pipe)
	tmjs := NewErisDbJsonService(codec, pipe, evtSubs)
	// The servers.
	jsonServer := NewJsonRpcServer(tmjs)
	restServer := NewRestServer(codec, pipe, evtSubs)
	wsServer := server.NewWebSocketServer(edbConf.Server.WebSocket.MaxWebSocketSessions, tmwss)
	// Create a server process.
	proc := server.NewServeProcess(&edbConf.Server, jsonServer, restServer, wsServer)

	return proc, nil
}

// start an inproc tendermint node
func startTMNode(app *edbapp.ErisDBApp, workDir string) {
	// get the genesis
	genDocFile := config.GetString("tm.genesis_file")
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
	}
	genDoc := types.GenesisDocFromJSON(jsonBlob)
	if genDoc.ChainID == "" {
		PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile))
	}
	config.Set("tm.chain_id", genDoc.ChainID)
	config.Set("tm.genesis_doc", genDoc)

	// Get PrivValidator
	privValidatorFile := config.GetString("tm.priv_validator_file")
	privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
	nd := node.NewNode(privValidator, func(addr string, hash []byte) proxy.AppConn {
		// TODO: Check the hash
		return tmspcli.NewLocalClient(new(sync.Mutex), app)
	})

	l := p2p.NewDefaultListener("tcp", config.GetString("tm.node_laddr"), config.GetBool("tm.skip_upnp"))
	nd.AddListener(l)
	if err := nd.Start(); err != nil {
		Exit(Fmt("Failed to start node: %v", err))
	}

	log.Notice("Started node", "nodeInfo", nd.NodeInfo())

	// If seedNode is provided by config, dial out.
	if config.GetString("tm.seeds") != "" {
		seeds := strings.Split(config.GetString("tm.seeds"), ",")
		nd.DialSeeds(seeds)
	}

	// Run the RPC server.
	if config.GetString("tm.rpc_laddr") != "" {
		_, err := StartRPC(nd, app)
		if err != nil {
			PanicCrisis(err)
		}
	}
}

func StartRPC(n *node.Node, edbApp *edbapp.ErisDBApp) ([]net.Listener, error) {
	rpccore.SetErisDBApp(edbApp)
	rpccore.SetBlockStore(n.BlockStore())
	rpccore.SetConsensusState(n.ConsensusState())
	rpccore.SetConsensusReactor(n.ConsensusReactor())
	rpccore.SetMempoolReactor(n.MempoolReactor())
	rpccore.SetSwitch(n.Switch())
	rpccore.SetPrivValidator(n.PrivValidator())
	rpccore.SetGenDoc(LoadGenDoc())

	listenAddrs := strings.Split(config.GetString("tm.rpc_laddr"), ",")

	// we may expose the rpc over both a unix and tcp socket
	listeners := make([]net.Listener, len(listenAddrs))
	for i, listenAddr := range listenAddrs {
		mux := http.NewServeMux()
		wm := rpcserver.NewWebsocketManager(rpccore.Routes, n.EventSwitch())
		mux.HandleFunc("/websocket", wm.WebsocketHandler)
		rpcserver.RegisterRPCFuncs(mux, rpccore.Routes)
		listener, err := rpcserver.StartHTTPServer(listenAddr, mux)
		if err != nil {
			return nil, err
		}
		listeners[i] = listener
	}
	return listeners, nil
}

func LoadGenDoc() *stypes.GenesisDoc {
	genDocFile := config.GetString("tm.genesis_file")
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
	}
	return stypes.GenesisDocFromJSON(jsonBlob)
}
<|endoftext|>


<commit_before>package protocol

import "time"

// DefaultMaxCongestionWindow is the default for the max congestion window
const DefaultMaxCongestionWindow = 1000

// InitialCongestionWindow is the initial congestion window in QUIC packets
const InitialCongestionWindow = 32

// MaxUndecryptablePackets limits the number of undecryptable packets that a
// session queues for later until it sends a public reset.
const MaxUndecryptablePackets = 10

// AckSendDelay is the maximal time delay applied to packets containing only ACKs
const AckSendDelay = 5 * time.Millisecond

// ReceiveStreamFlowControlWindow is the stream-level flow control window for receiving data
// This is the value that Google servers are using
const ReceiveStreamFlowControlWindow ByteCount = (1 << 10) * 32 // 32 kB

// ReceiveConnectionFlowControlWindow is the connection-level flow control window for receiving data
// This is the value that Google servers are using
const ReceiveConnectionFlowControlWindow ByteCount = (1 << 10) * 48 // 48 kB

// MaxReceiveStreamFlowControlWindow is the maximum stream-level flow control window for receiving data
// This is the value that Google servers are using
const MaxReceiveStreamFlowControlWindow ByteCount = 1 * (1 << 20) // 1 MB

// MaxReceiveConnectionFlowControlWindow is the connection-level flow control window for receiving data
// This is the value that Google servers are using
const MaxReceiveConnectionFlowControlWindow ByteCount = 1.5 * (1 << 20) // 1.5 MB

// MaxStreamsPerConnection is the maximum value accepted for the number of streams per connection
const MaxStreamsPerConnection = 100

// MaxIncomingDynamicStreamsPerConnection is the maximum value accepted for the incoming number of dynamic streams per connection
const MaxIncomingDynamicStreamsPerConnection = 100

// MaxStreamsMultiplier is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this procentual increase and the absolute increment specified by MaxStreamsMinimumIncrement is used.
const MaxStreamsMultiplier = 1.1

// MaxStreamsMinimumIncrement is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this absolute increment and the procentual increase specified by MaxStreamsMultiplier is used.
const MaxStreamsMinimumIncrement = 10

// MaxNewStreamIDDelta is the maximum difference between and a newly opened Stream and the highest StreamID that a client has ever opened
// note that the number of streams is half this value, since the client can only open streams with open StreamID
const MaxNewStreamIDDelta = 4 * MaxStreamsPerConnection

// MaxSessionUnprocessedPackets is the max number of packets stored in each session that are not yet processed.
const MaxSessionUnprocessedPackets = DefaultMaxCongestionWindow

// RetransmissionThreshold + 1 is the number of times a packet has to be NACKed so that it gets retransmitted
const RetransmissionThreshold = 3

// SkipPacketAveragePeriodLength is the average period length in which one packet number is skipped to prevent an Optimistic ACK attack
const SkipPacketAveragePeriodLength PacketNumber = 500

// MaxTrackedSkippedPackets is the maximum number of skipped packet numbers the SentPacketHandler keep track of for Optimistic ACK attack mitigation
const MaxTrackedSkippedPackets = 10

// STKExpiryTimeSec is the valid time of a source address token in seconds
const STKExpiryTimeSec = 24 * 60 * 60

// MaxTrackedSentPackets is maximum number of sent packets saved for either later retransmission or entropy calculation
const MaxTrackedSentPackets = 2 * DefaultMaxCongestionWindow

// MaxTrackedReceivedPackets is the maximum number of received packets saved for doing the entropy calculations
const MaxTrackedReceivedPackets = 2 * DefaultMaxCongestionWindow

// MaxTrackedReceivedAckRanges is the maximum number of ACK ranges tracked
const MaxTrackedReceivedAckRanges = DefaultMaxCongestionWindow

// MaxPacketsReceivedBeforeAckSend is the number of packets that can be received before an ACK frame is sent
const MaxPacketsReceivedBeforeAckSend = 20

// RetransmittablePacketsBeforeAck is the number of retransmittable that an ACK is sent for
const RetransmittablePacketsBeforeAck = 2

// MaxStreamFrameSorterGaps is the maximum number of gaps between received StreamFrames
// prevents DoS attacks against the streamFrameSorter
const MaxStreamFrameSorterGaps = 1000

// CryptoMaxParams is the upper limit for the number of parameters in a crypto message.
// Value taken from Chrome.
const CryptoMaxParams = 128

// CryptoParameterMaxLength is the upper limit for the length of a parameter in a crypto message.
const CryptoParameterMaxLength = 2000

// EphermalKeyLifetime is the lifetime of the ephermal key during the handshake, see handshake.getEphermalKEX.
const EphermalKeyLifetime = time.Minute

// InitialIdleTimeout is the timeout before the handshake succeeds.
const InitialIdleTimeout = 5 * time.Second

// DefaultIdleTimeout is the default idle timeout.
const DefaultIdleTimeout = 30 * time.Second

// MaxIdleTimeout is the maximum idle timeout that can be negotiated.
const MaxIdleTimeout = 1 * time.Minute

// MaxTimeForCryptoHandshake is the default timeout for a connection until the crypto handshake succeeds.
const MaxTimeForCryptoHandshake = 10 * time.Second

// ClosedSessionDeleteTimeout the server ignores packets arriving on a connection that is already closed
// after this time all information about the old connection will be deleted
const ClosedSessionDeleteTimeout = time.Minute

// NumCachedCertificates is the number of cached compressed certificate chains, each taking ~1K space
const NumCachedCertificates = 128
<commit_msg>increase the ACK send delay to the value Chromium is using<commit_after>package protocol

import "time"

// DefaultMaxCongestionWindow is the default for the max congestion window
const DefaultMaxCongestionWindow = 1000

// InitialCongestionWindow is the initial congestion window in QUIC packets
const InitialCongestionWindow = 32

// MaxUndecryptablePackets limits the number of undecryptable packets that a
// session queues for later until it sends a public reset.
const MaxUndecryptablePackets = 10

// AckSendDelay is the maximum delay that can be applied to an ACK for a retransmittable packet
// This is the value Chromium is using
const AckSendDelay = 25 * time.Millisecond

// ReceiveStreamFlowControlWindow is the stream-level flow control window for receiving data
// This is the value that Google servers are using
const ReceiveStreamFlowControlWindow ByteCount = (1 << 10) * 32 // 32 kB

// ReceiveConnectionFlowControlWindow is the connection-level flow control window for receiving data
// This is the value that Google servers are using
const ReceiveConnectionFlowControlWindow ByteCount = (1 << 10) * 48 // 48 kB

// MaxReceiveStreamFlowControlWindow is the maximum stream-level flow control window for receiving data
// This is the value that Google servers are using
const MaxReceiveStreamFlowControlWindow ByteCount = 1 * (1 << 20) // 1 MB

// MaxReceiveConnectionFlowControlWindow is the connection-level flow control window for receiving data
// This is the value that Google servers are using
const MaxReceiveConnectionFlowControlWindow ByteCount = 1.5 * (1 << 20) // 1.5 MB

// MaxStreamsPerConnection is the maximum value accepted for the number of streams per connection
const MaxStreamsPerConnection = 100

// MaxIncomingDynamicStreamsPerConnection is the maximum value accepted for the incoming number of dynamic streams per connection
const MaxIncomingDynamicStreamsPerConnection = 100

// MaxStreamsMultiplier is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this procentual increase and the absolute increment specified by MaxStreamsMinimumIncrement is used.
const MaxStreamsMultiplier = 1.1

// MaxStreamsMinimumIncrement is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this absolute increment and the procentual increase specified by MaxStreamsMultiplier is used.
const MaxStreamsMinimumIncrement = 10

// MaxNewStreamIDDelta is the maximum difference between and a newly opened Stream and the highest StreamID that a client has ever opened
// note that the number of streams is half this value, since the client can only open streams with open StreamID
const MaxNewStreamIDDelta = 4 * MaxStreamsPerConnection

// MaxSessionUnprocessedPackets is the max number of packets stored in each session that are not yet processed.
const MaxSessionUnprocessedPackets = DefaultMaxCongestionWindow

// RetransmissionThreshold + 1 is the number of times a packet has to be NACKed so that it gets retransmitted
const RetransmissionThreshold = 3

// SkipPacketAveragePeriodLength is the average period length in which one packet number is skipped to prevent an Optimistic ACK attack
const SkipPacketAveragePeriodLength PacketNumber = 500

// MaxTrackedSkippedPackets is the maximum number of skipped packet numbers the SentPacketHandler keep track of for Optimistic ACK attack mitigation
const MaxTrackedSkippedPackets = 10

// STKExpiryTimeSec is the valid time of a source address token in seconds
const STKExpiryTimeSec = 24 * 60 * 60

// MaxTrackedSentPackets is maximum number of sent packets saved for either later retransmission or entropy calculation
const MaxTrackedSentPackets = 2 * DefaultMaxCongestionWindow

// MaxTrackedReceivedPackets is the maximum number of received packets saved for doing the entropy calculations
const MaxTrackedReceivedPackets = 2 * DefaultMaxCongestionWindow

// MaxTrackedReceivedAckRanges is the maximum number of ACK ranges tracked
const MaxTrackedReceivedAckRanges = DefaultMaxCongestionWindow

// MaxPacketsReceivedBeforeAckSend is the number of packets that can be received before an ACK frame is sent
const MaxPacketsReceivedBeforeAckSend = 20

// RetransmittablePacketsBeforeAck is the number of retransmittable that an ACK is sent for
const RetransmittablePacketsBeforeAck = 2

// MaxStreamFrameSorterGaps is the maximum number of gaps between received StreamFrames
// prevents DoS attacks against the streamFrameSorter
const MaxStreamFrameSorterGaps = 1000

// CryptoMaxParams is the upper limit for the number of parameters in a crypto message.
// Value taken from Chrome.
const CryptoMaxParams = 128

// CryptoParameterMaxLength is the upper limit for the length of a parameter in a crypto message.
const CryptoParameterMaxLength = 2000

// EphermalKeyLifetime is the lifetime of the ephermal key during the handshake, see handshake.getEphermalKEX.
const EphermalKeyLifetime = time.Minute

// InitialIdleTimeout is the timeout before the handshake succeeds.
const InitialIdleTimeout = 5 * time.Second

// DefaultIdleTimeout is the default idle timeout.
const DefaultIdleTimeout = 30 * time.Second

// MaxIdleTimeout is the maximum idle timeout that can be negotiated.
const MaxIdleTimeout = 1 * time.Minute

// MaxTimeForCryptoHandshake is the default timeout for a connection until the crypto handshake succeeds.
const MaxTimeForCryptoHandshake = 10 * time.Second

// ClosedSessionDeleteTimeout the server ignores packets arriving on a connection that is already closed
// after this time all information about the old connection will be deleted
const ClosedSessionDeleteTimeout = time.Minute

// NumCachedCertificates is the number of cached compressed certificate chains, each taking ~1K space
const NumCachedCertificates = 128
<|endoftext|>


<commit_before>package tango

import (
	"testing"
	"bytes"
	"strings"
	"net/http/httptest"
	"net/http"
)

type CtxAction struct {
	Ctx
}

func (p *CtxAction) Get() {
	p.Ctx.Write([]byte("context"))
}

func TestContext1(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	o := Classic()
	o.Get("/", new(CtxAction))

	req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
	if err != nil {
		t.Error(err)
	}

	o.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
	refute(t, len(buff.String()), 0)
	expect(t, buff.String(), "context")
}

type CtxJsonAction struct {
	Ctx
}

func (p *CtxJsonAction) Get() error {
	return p.Ctx.ServeJson(map[string]string{
		"get": "ctx",
		})
}

func TestContext2(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	o := Classic()
	o.Get("/", new(CtxJsonAction))

	req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
	if err != nil {
		t.Error(err)
	}

	o.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
	refute(t, len(buff.String()), 0)
	expect(t, strings.TrimSpace(buff.String()), `{"get":"ctx"}`)
}

type CtxXmlAction struct {
	Ctx
}

type XmlStruct struct {
	Content string
}

func (p *CtxXmlAction) Get() error {
	return p.Ctx.ServeXml(XmlStruct{"content"})
}

func TestContext3(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	o := Classic()
	o.Get("/", new(CtxXmlAction))

	req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
	if err != nil {
		t.Error(err)
	}

	o.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
	refute(t, len(buff.String()), 0)
	expect(t, strings.TrimSpace(buff.String()), `<XmlStruct><Content>content</Content></XmlStruct>`)
}

type CtxFileAction struct {
	Ctx
}

func (p *CtxFileAction) Get() error {
	return p.Ctx.ServeFile("./public/index.html")
}

func TestContext4(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	o := Classic()
	o.Get("/", new(CtxFileAction))

	req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
	if err != nil {
		t.Error(err)
	}

	o.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
	refute(t, len(buff.String()), 0)
	expect(t, strings.TrimSpace(buff.String()), `this is index.html`)
}<commit_msg>more tests<commit_after>package tango

import (
	"testing"
	"bytes"
	"strings"
	"net/http/httptest"
	"net/http"
)

type CtxAction struct {
	Ctx
}

func (p *CtxAction) Get() {
	p.Ctx.Write([]byte("context"))
}

func TestContext1(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	o := Classic()
	o.Get("/", new(CtxAction))

	req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
	if err != nil {
		t.Error(err)
	}

	o.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
	refute(t, len(buff.String()), 0)
	expect(t, buff.String(), "context")
}

type CtxJsonAction struct {
	Ctx
}

func (p *CtxJsonAction) Get() error {
	return p.Ctx.ServeJson(map[string]string{
		"get": "ctx",
		})
}

func TestContext2(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	o := Classic()
	o.Get("/", new(CtxJsonAction))

	req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
	if err != nil {
		t.Error(err)
	}

	o.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
	refute(t, len(buff.String()), 0)
	expect(t, strings.TrimSpace(buff.String()), `{"get":"ctx"}`)
}

type CtxXmlAction struct {
	Ctx
}

type XmlStruct struct {
	Content string
}

func (p *CtxXmlAction) Get() error {
	return p.Ctx.ServeXml(XmlStruct{"content"})
}

func TestContext3(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	o := Classic()
	o.Get("/", new(CtxXmlAction))

	req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
	if err != nil {
		t.Error(err)
	}

	o.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
	refute(t, len(buff.String()), 0)
	expect(t, strings.TrimSpace(buff.String()), `<XmlStruct><Content>content</Content></XmlStruct>`)
}

type CtxFileAction struct {
	Ctx
}

func (p *CtxFileAction) Get() error {
	return p.Ctx.ServeFile("./public/index.html")
}

func TestContext4(t *testing.T) {
	buff := bytes.NewBufferString("")
	recorder := httptest.NewRecorder()
	recorder.Body = buff

	o := Classic()
	o.Any("/", new(CtxFileAction))

	req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
	if err != nil {
		t.Error(err)
	}

	o.ServeHTTP(recorder, req)
	expect(t, recorder.Code, http.StatusOK)
	refute(t, len(buff.String()), 0)
	expect(t, strings.TrimSpace(buff.String()), `this is index.html`)
}<|endoftext|>


<commit_before>// Copyright 2017 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package oracle

import (
	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
	names "gopkg.in/juju/names.v2"
)

//
// These methods here belong of the environ.Networking interface
// their are provided here to tell the juju if the oracle provider supports different network
// options
// Netowrking interface defines methods that environmnets with networking capabilities must implement.
//
// Together these implements also the NetworkingEnviron interface
//

// Subnet returns basic information about subnets known by the oracle provider for the environmnet
func (e oracleEnviron) Subnets(inst instance.Id, subnetIds []network.Id) ([]network.SubnetInfo, error) {
	return nil, nil
}

// NetworkInterfaces requests information about the network interfaces on the given instance
func (e oracleEnviron) NetworkInterfaces(instId instance.Id) ([]network.InterfaceInfo, error) {
	return nil, nil
}

// SupportsSpaces returns whether the current oracle environment supports
// spaces. The returned error satisfies errors.IsNotSupported(),
// unless a general API failure occurs.
func (e oracleEnviron) SupportsSpaces() (bool, error) {
	return false, nil
}

// SupportsSpaceDiscovery returns whether the current environment
// supports discovering spaces from the oracle provider. The returned error
// satisfies errors.IsNotSupported(), unless a general API failure occurs.
func (e oracleEnviron) SupportsSpaceDiscovery() (bool, error) {
	return false, nil
}

// Spaces returns a slice of network.SpaceInfo with info, including
// details of all associated subnets, about all spaces known to the
// oracle provider that have subnets available.
func (e oracleEnviron) Spaces() ([]network.SpaceInfo, error) {
	return nil, nil
}

// AllocateContainerAddresses allocates a static address for each of the
// container NICs in preparedInfo, hosted by the hostInstanceID. Returns the
// network config including all allocated addresses on success.
func (e oracleEnviron) AllocateContainerAddresses(
	hostInstanceID instance.Id,
	containerTag names.MachineTag,
	preparedInfo []network.InterfaceInfo,
) ([]network.InterfaceInfo, error) {
	return nil, nil
}

// ReleaseContainerAddresses releases the previously allocated
// addresses matching the interface details passed in.
func (e oracleEnviron) ReleaseContainerAddresses(interfaces []network.ProviderInterfaceInfo) error {
	return nil
}
<commit_msg>More netowrking code<commit_after>// Copyright 2017 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package oracle

import (
	"net"
	"strconv"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
	names "gopkg.in/juju/names.v2"
)

// These methods here belong of the environ.Networking interface
// their are provided here to tell the juju
// if the oracle provider supports different network options
// Netowrking interface defines methods that environmnets with
// networking capabilities must implement.
// Together these implements also the NetworkingEnviron interface

// Subnet returns basic information about subnets
// known by the oracle provider for the environmnet
func (e oracleEnviron) Subnets(
	inst instance.Id,
	subnetIds []network.Id,
) ([]network.SubnetInfo, error) {

	return nil, nil
}

// NetworkInterfaces requests information about the
// network interfaces on the given instance
func (e oracleEnviron) NetworkInterfaces(
	instId instance.Id,
) ([]network.InterfaceInfo, error) {

	id := string(instId)

	instance, err := e.client.InstanceDetails(e.client.ComposeName(id))
	if err != nil {
		return nil, err
	}

	n := len(instance.Networking)
	if n == 0 {
		return nil, errors.Errorf(
			"Cannot get the network interfaces of the instance %s", id,
		)
	}

	interfaces := make([]network.InterfaceInfo, 0, n)
	//TODO(sgiulitti) find all the info and parse it
	for nicName := range instance.Networking {
		ni := network.InterfaceInfo{InterfaceName: nicName}

		// for every attribute we should check if
		// we are dealing with the nicName attributes
		for key, value := range instance.Attributes.Network {
			// if we are dealing with the correct one
			// we should extract and populate the InterfaceInfo struct
			if strings.Contains(key, nicName) {
				ni.DeviceIndex, err = strconv.Atoi(value.Vethernet_id)
				if err != nil {
					return nil, errors.Trace(err)
				}

				// get the mac and IP
				mac, address, err := getMacAndIP(value.Address)
				if err != nil {
					return nil, errors.Trace(err)
				}

				ni.MACAddress = mac
				ni.Address = network.NewAddress(address)

				break
			}
		}

		interfaces = append(interfaces, ni)
	}

	return interfaces, nil
}

// getMacAndIp picks and returns the correct mac and ip from the given slice
// if the slice does not contain a valid mac and ip it will return an error
func getMacAndIP(address []string) (mac string, ip string, err error) {

	if address == nil {
		return "", "", errors.New(
			"Empty address slice given",
		)
	}

	if len(address) != 2 {
		return "", "", errors.Errorf(
			"more than one mac and one ip in address %#v",
			address,
		)
	}

	for _, val := range address {

		valIp := net.ParseIP(val)
		if valIp != nil {
			ip = val
			continue
		}

		_, err = net.ParseMAC(val)
		if err != nil {
			return "", "", errors.Errorf(
				"The address is not an mac neighter an ip %s", val,
			)
		}

		mac = val
	}

	return mac, ip, nil
}

// SupportsSpaces returns whether the current oracle environment supports
// spaces. The returned error satisfies errors.IsNotSupported(),
// unless a general API failure occurs.
func (e oracleEnviron) SupportsSpaces() (bool, error) {
	return true, nil
}

// SupportsSpaceDiscovery returns whether the current environment
// supports discovering spaces from the oracle provider. The returned error
// satisfies errors.IsNotSupported(), unless a general API failure occurs.
func (e oracleEnviron) SupportsSpaceDiscovery() (bool, error) {
	return false, errors.NotSupportedf("space discovery")
}

// Spaces returns a slice of network.SpaceInfo with info, including
// details of all associated subnets, about all spaces known to the
// oracle provider that have subnets available.
func (e oracleEnviron) Spaces() ([]network.SpaceInfo, error) {
	return nil, nil
}

// AllocateContainerAddresses allocates a static address for each of the
// container NICs in preparedInfo, hosted by the hostInstanceID. Returns the
// network config including all allocated addresses on success.
func (e oracleEnviron) AllocateContainerAddresses(
	hostInstanceID instance.Id,
	containerTag names.MachineTag,
	preparedInfo []network.InterfaceInfo,
) ([]network.InterfaceInfo, error) {
	return nil, errors.NotSupportedf("containers")
}

// ReleaseContainerAddresses releases the previously allocated
// addresses matching the interface details passed in.
func (e oracleEnviron) ReleaseContainerAddresses(interfaces []network.ProviderInterfaceInfo) error {
	return errors.NotSupportedf("container")
}
<|endoftext|>