<commit_before>package libnetwork

import (
	"encoding/json"
	"fmt"
	"net"

	"github.com/docker/libnetwork/driverapi"
	"github.com/docker/libnetwork/types"
)

// EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
type EndpointInfo interface {
	// Iface returns InterfaceInfo, go interface that can be used
	// to get more information on the interface which was assigned to
	// the endpoint by the driver. This can be used after the
	// endpoint has been created.
	Iface() InterfaceInfo

	// Gateway returns the IPv4 gateway assigned by the driver.
	// This will only return a valid value if a container has joined the endpoint.
	Gateway() net.IP

	// GatewayIPv6 returns the IPv6 gateway assigned by the driver.
	// This will only return a valid value if a container has joined the endpoint.
	GatewayIPv6() net.IP

	// StaticRoutes returns the list of static routes configured by the network
	// driver when the container joins a network
	StaticRoutes() []*types.StaticRoute

	// Sandbox returns the attached sandbox if there, nil otherwise.
	Sandbox() Sandbox
}

// InterfaceInfo provides an interface to retrieve interface addresses bound to the endpoint.
type InterfaceInfo interface {
	// MacAddress returns the MAC address assigned to the endpoint.
	MacAddress() net.HardwareAddr

	// Address returns the IPv4 address assigned to the endpoint.
	Address() *net.IPNet

	// AddressIPv6 returns the IPv6 address assigned to the endpoint.
	AddressIPv6() *net.IPNet

	// LinkLocalAddresses returns the list of link-local (IPv4/IPv6) addresses assigned to the endpoint.
	LinkLocalAddresses() []*net.IPNet
}

type endpointInterface struct {
	mac       net.HardwareAddr
	addr      *net.IPNet
	addrv6    *net.IPNet
	llAddrs   []*net.IPNet
	srcName   string
	dstPrefix string
	routes    []*net.IPNet
	v4PoolID  string
	v6PoolID  string
}

func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
	epMap := make(map[string]interface{})
	if epi.mac != nil {
		epMap["mac"] = epi.mac.String()
	}
	if epi.addr != nil {
		epMap["addr"] = epi.addr.String()
	}
	if epi.addrv6 != nil {
		epMap["addrv6"] = epi.addrv6.String()
	}
	if len(epi.llAddrs) != 0 {
		list := make([]string, 0, len(epi.llAddrs))
		for _, ll := range epi.llAddrs {
			list = append(list, ll.String())
		}
		epMap["llAddrs"] = list
	}
	epMap["srcName"] = epi.srcName
	epMap["dstPrefix"] = epi.dstPrefix
	var routes []string
	for _, route := range epi.routes {
		routes = append(routes, route.String())
	}
	epMap["routes"] = routes
	epMap["v4PoolID"] = epi.v4PoolID
	epMap["v6PoolID"] = epi.v6PoolID
	return json.Marshal(epMap)
}

func (epi *endpointInterface) UnmarshalJSON(b []byte) error {
	var (
		err   error
		epMap map[string]interface{}
	)
	if err = json.Unmarshal(b, &epMap); err != nil {
		return err
	}
	if v, ok := epMap["mac"]; ok {
		if epi.mac, err = net.ParseMAC(v.(string)); err != nil {
			return types.InternalErrorf("failed to decode endpoint interface mac address after json unmarshal: %s", v.(string))
		}
	}
	if v, ok := epMap["addr"]; ok {
		if epi.addr, err = types.ParseCIDR(v.(string)); err != nil {
			return types.InternalErrorf("failed to decode endpoint interface ipv4 address after json unmarshal: %v", err)
		}
	}
	if v, ok := epMap["addrv6"]; ok {
		if epi.addrv6, err = types.ParseCIDR(v.(string)); err != nil {
			return types.InternalErrorf("failed to decode endpoint interface ipv6 address after json unmarshal: %v", err)
		}
	}
	if v, ok := epMap["llAddrs"]; ok {
		list := v.([]interface{})
		epi.llAddrs = make([]*net.IPNet, 0, len(list))
		for _, llS := range list {
			ll, err := types.ParseCIDR(llS.(string))
			if err != nil {
				return types.InternalErrorf("failed to decode endpoint interface link-local address (%v) after json unmarshal: %v", llS, err)
			}
			epi.llAddrs = append(epi.llAddrs, ll)
		}
	}
	epi.srcName = epMap["srcName"].(string)
	epi.dstPrefix = epMap["dstPrefix"].(string)

	rb, _ := json.Marshal(epMap["routes"])
	var routes []string
	json.Unmarshal(rb, &routes)
	epi.routes = make([]*net.IPNet, 0)
	for _, route := range routes {
		ip, ipr, err := net.ParseCIDR(route)
		if err == nil {
			ipr.IP = ip
			epi.routes = append(epi.routes, ipr)
		}
	}
	epi.v4PoolID = epMap["v4PoolID"].(string)
	epi.v6PoolID = epMap["v6PoolID"].(string)

	return nil
}

func (epi *endpointInterface) CopyTo(dstEpi *endpointInterface) error {
	dstEpi.mac = types.GetMacCopy(epi.mac)
	dstEpi.addr = types.GetIPNetCopy(epi.addr)
	dstEpi.addrv6 = types.GetIPNetCopy(epi.addrv6)
	dstEpi.srcName = epi.srcName
	dstEpi.dstPrefix = epi.dstPrefix
	dstEpi.v4PoolID = epi.v4PoolID
	dstEpi.v6PoolID = epi.v6PoolID
	if len(epi.llAddrs) != 0 {
		dstEpi.llAddrs = make([]*net.IPNet, 0, len(epi.llAddrs))
		dstEpi.llAddrs = append(dstEpi.llAddrs, epi.llAddrs...)
	}

	for _, route := range epi.routes {
		dstEpi.routes = append(dstEpi.routes, types.GetIPNetCopy(route))
	}

	return nil
}

type endpointJoinInfo struct {
	gw                    net.IP
	gw6                   net.IP
	StaticRoutes          []*types.StaticRoute
	driverTableEntries    []*tableEntry
	disableGatewayService bool
}

type tableEntry struct {
	tableName string
	key       string
	value     []byte
}

func (ep *endpoint) Info() EndpointInfo {
	if ep.sandboxID != "" {
		return ep
	}
	n, err := ep.getNetworkFromStore()
	if err != nil {
		return nil
	}

	ep, err = n.getEndpointFromStore(ep.ID())
	if err != nil {
		return nil
	}

	sb, ok := ep.getSandbox()
	if !ok {
		// endpoint hasn't joined any sandbox.
		// Just return the endpoint
		return ep
	}

	if epi := sb.getEndpoint(ep.ID()); epi != nil {
		return epi
	}

	return nil
}

func (ep *endpoint) Iface() InterfaceInfo {
	ep.Lock()
	defer ep.Unlock()

	if ep.iface != nil {
		return ep.iface
	}

	return nil
}

func (ep *endpoint) Interface() driverapi.InterfaceInfo {
	ep.Lock()
	defer ep.Unlock()

	if ep.iface != nil {
		return ep.iface
	}

	return nil
}

func (epi *endpointInterface) SetMacAddress(mac net.HardwareAddr) error {
	if epi.mac != nil {
		return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", epi.mac, mac)
	}
	if mac == nil {
		return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface")
	}
	epi.mac = types.GetMacCopy(mac)
	return nil
}

func (epi *endpointInterface) SetIPAddress(address *net.IPNet) error {
	if address.IP == nil {
		return types.BadRequestErrorf("tried to set nil IP address to endpoint interface")
	}
	if address.IP.To4() == nil {
		return setAddress(&epi.addrv6, address)
	}
	return setAddress(&epi.addr, address)
}

func setAddress(ifaceAddr **net.IPNet, address *net.IPNet) error {
	if *ifaceAddr != nil {
		return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address)
	}
	*ifaceAddr = types.GetIPNetCopy(address)
	return nil
}

func (epi *endpointInterface) MacAddress() net.HardwareAddr {
	return types.GetMacCopy(epi.mac)
}

func (epi *endpointInterface) Address() *net.IPNet {
	return types.GetIPNetCopy(epi.addr)
}

func (epi *endpointInterface) AddressIPv6() *net.IPNet {
	return types.GetIPNetCopy(epi.addrv6)
}

func (epi *endpointInterface) LinkLocalAddresses() []*net.IPNet {
	return epi.llAddrs
}

func (epi *endpointInterface) SetNames(srcName string, dstPrefix string) error {
	epi.srcName = srcName
	epi.dstPrefix = dstPrefix
	return nil
}

func (ep *endpoint) InterfaceName() driverapi.InterfaceNameInfo {
	ep.Lock()
	defer ep.Unlock()

	if ep.iface != nil {
		return ep.iface
	}

	return nil
}

func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
	ep.Lock()
	defer ep.Unlock()

	r := types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop}

	if routeType == types.NEXTHOP {
		// If the route specifies a next-hop, then it's loosely routed (i.e. not bound to a particular interface).
		ep.joinInfo.StaticRoutes = append(ep.joinInfo.StaticRoutes, &r)
	} else {
		// If the route doesn't specify a next-hop, it must be a connected route, bound to an interface.
		ep.iface.routes = append(ep.iface.routes, r.Destination)
	}
	return nil
}

func (ep *endpoint) AddTableEntry(tableName, key string, value []byte) error {
	ep.Lock()
	defer ep.Unlock()

	ep.joinInfo.driverTableEntries = append(ep.joinInfo.driverTableEntries, &tableEntry{
		tableName: tableName,
		key:       key,
		value:     value,
	})

	return nil
}

func (ep *endpoint) Sandbox() Sandbox {
	cnt, ok := ep.getSandbox()
	if !ok {
		return nil
	}
	return cnt
}

func (ep *endpoint) StaticRoutes() []*types.StaticRoute {
	ep.Lock()
	defer ep.Unlock()

	if ep.joinInfo == nil {
		return nil
	}

	return ep.joinInfo.StaticRoutes
}

func (ep *endpoint) Gateway() net.IP {
	ep.Lock()
	defer ep.Unlock()

	if ep.joinInfo == nil {
		return net.IP{}
	}

	return types.GetIPCopy(ep.joinInfo.gw)
}

func (ep *endpoint) GatewayIPv6() net.IP {
	ep.Lock()
	defer ep.Unlock()

	if ep.joinInfo == nil {
		return net.IP{}
	}

	return types.GetIPCopy(ep.joinInfo.gw6)
}

func (ep *endpoint) SetGateway(gw net.IP) error {
	ep.Lock()
	defer ep.Unlock()

	ep.joinInfo.gw = types.GetIPCopy(gw)
	return nil
}

func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error {
	ep.Lock()
	defer ep.Unlock()

	ep.joinInfo.gw6 = types.GetIPCopy(gw6)
	return nil
}

func (ep *endpoint) retrieveFromStore() (*endpoint, error) {
	n, err := ep.getNetworkFromStore()
	if err != nil {
		return nil, fmt.Errorf("could not find network in store to get latest endpoint %s: %v", ep.Name(), err)
	}
	return n.getEndpointFromStore(ep.ID())
}

func (ep *endpoint) DisableGatewayService() {
	ep.Lock()
	defer ep.Unlock()

	ep.joinInfo.disableGatewayService = true
}

func (epj *endpointJoinInfo) MarshalJSON() ([]byte, error) {
	epMap := make(map[string]interface{})
	if epj.gw != nil {
		epMap["gw"] = epj.gw.String()
	}
	if epj.gw6 != nil {
		epMap["gw6"] = epj.gw6.String()
	}
	epMap["disableGatewayService"] = epj.disableGatewayService
	epMap["StaticRoutes"] = epj.StaticRoutes
	return json.Marshal(epMap)
}

func (epj *endpointJoinInfo) UnmarshalJSON(b []byte) error {
	var (
		err   error
		epMap map[string]interface{}
	)
	if err = json.Unmarshal(b, &epMap); err != nil {
		return err
	}
	if v, ok := epMap["gw"]; ok {
		epj.gw6 = net.ParseIP(v.(string))
	}
	if v, ok := epMap["gw6"]; ok {
		epj.gw6 = net.ParseIP(v.(string))
	}
	epj.disableGatewayService = epMap["disableGatewayService"].(bool)

	var tStaticRoute []types.StaticRoute
	if v, ok := epMap["StaticRoutes"]; ok {
		tb, _ := json.Marshal(v)
		var tStaticRoute []types.StaticRoute
		json.Unmarshal(tb, &tStaticRoute)
	}
	var StaticRoutes []*types.StaticRoute
	for _, r := range tStaticRoute {
		StaticRoutes = append(StaticRoutes, &r)
	}
	epj.StaticRoutes = StaticRoutes

	return nil
}

func (epj *endpointJoinInfo) CopyTo(dstEpj *endpointJoinInfo) error {
	dstEpj.disableGatewayService = epj.disableGatewayService
	dstEpj.StaticRoutes = make([]*types.StaticRoute, len(epj.StaticRoutes))
	copy(dstEpj.StaticRoutes, epj.StaticRoutes)
	dstEpj.driverTableEntries = make([]*tableEntry, len(epj.driverTableEntries))
	copy(dstEpj.driverTableEntries, epj.driverTableEntries)
	dstEpj.gw = types.GetIPCopy(epj.gw)
	dstEpj.gw = types.GetIPCopy(epj.gw6)
	return nil
}
<commit_msg>Fixes bug which makes restoring endpoint mess<commit_after>package libnetwork

import (
	"encoding/json"
	"fmt"
	"net"

	"github.com/docker/libnetwork/driverapi"
	"github.com/docker/libnetwork/types"
)

// EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
type EndpointInfo interface {
	// Iface returns InterfaceInfo, go interface that can be used
	// to get more information on the interface which was assigned to
	// the endpoint by the driver. This can be used after the
	// endpoint has been created.
	Iface() InterfaceInfo

	// Gateway returns the IPv4 gateway assigned by the driver.
	// This will only return a valid value if a container has joined the endpoint.
	Gateway() net.IP

	// GatewayIPv6 returns the IPv6 gateway assigned by the driver.
	// This will only return a valid value if a container has joined the endpoint.
	GatewayIPv6() net.IP

	// StaticRoutes returns the list of static routes configured by the network
	// driver when the container joins a network
	StaticRoutes() []*types.StaticRoute

	// Sandbox returns the attached sandbox if there, nil otherwise.
	Sandbox() Sandbox
}

// InterfaceInfo provides an interface to retrieve interface addresses bound to the endpoint.
type InterfaceInfo interface {
	// MacAddress returns the MAC address assigned to the endpoint.
	MacAddress() net.HardwareAddr

	// Address returns the IPv4 address assigned to the endpoint.
	Address() *net.IPNet

	// AddressIPv6 returns the IPv6 address assigned to the endpoint.
	AddressIPv6() *net.IPNet

	// LinkLocalAddresses returns the list of link-local (IPv4/IPv6) addresses assigned to the endpoint.
	LinkLocalAddresses() []*net.IPNet
}

type endpointInterface struct {
	mac       net.HardwareAddr
	addr      *net.IPNet
	addrv6    *net.IPNet
	llAddrs   []*net.IPNet
	srcName   string
	dstPrefix string
	routes    []*net.IPNet
	v4PoolID  string
	v6PoolID  string
}

func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
	epMap := make(map[string]interface{})
	if epi.mac != nil {
		epMap["mac"] = epi.mac.String()
	}
	if epi.addr != nil {
		epMap["addr"] = epi.addr.String()
	}
	if epi.addrv6 != nil {
		epMap["addrv6"] = epi.addrv6.String()
	}
	if len(epi.llAddrs) != 0 {
		list := make([]string, 0, len(epi.llAddrs))
		for _, ll := range epi.llAddrs {
			list = append(list, ll.String())
		}
		epMap["llAddrs"] = list
	}
	epMap["srcName"] = epi.srcName
	epMap["dstPrefix"] = epi.dstPrefix
	var routes []string
	for _, route := range epi.routes {
		routes = append(routes, route.String())
	}
	epMap["routes"] = routes
	epMap["v4PoolID"] = epi.v4PoolID
	epMap["v6PoolID"] = epi.v6PoolID
	return json.Marshal(epMap)
}

func (epi *endpointInterface) UnmarshalJSON(b []byte) error {
	var (
		err   error
		epMap map[string]interface{}
	)
	if err = json.Unmarshal(b, &epMap); err != nil {
		return err
	}
	if v, ok := epMap["mac"]; ok {
		if epi.mac, err = net.ParseMAC(v.(string)); err != nil {
			return types.InternalErrorf("failed to decode endpoint interface mac address after json unmarshal: %s", v.(string))
		}
	}
	if v, ok := epMap["addr"]; ok {
		if epi.addr, err = types.ParseCIDR(v.(string)); err != nil {
			return types.InternalErrorf("failed to decode endpoint interface ipv4 address after json unmarshal: %v", err)
		}
	}
	if v, ok := epMap["addrv6"]; ok {
		if epi.addrv6, err = types.ParseCIDR(v.(string)); err != nil {
			return types.InternalErrorf("failed to decode endpoint interface ipv6 address after json unmarshal: %v", err)
		}
	}
	if v, ok := epMap["llAddrs"]; ok {
		list := v.([]interface{})
		epi.llAddrs = make([]*net.IPNet, 0, len(list))
		for _, llS := range list {
			ll, err := types.ParseCIDR(llS.(string))
			if err != nil {
				return types.InternalErrorf("failed to decode endpoint interface link-local address (%v) after json unmarshal: %v", llS, err)
			}
			epi.llAddrs = append(epi.llAddrs, ll)
		}
	}
	epi.srcName = epMap["srcName"].(string)
	epi.dstPrefix = epMap["dstPrefix"].(string)

	rb, _ := json.Marshal(epMap["routes"])
	var routes []string
	json.Unmarshal(rb, &routes)
	epi.routes = make([]*net.IPNet, 0)
	for _, route := range routes {
		ip, ipr, err := net.ParseCIDR(route)
		if err == nil {
			ipr.IP = ip
			epi.routes = append(epi.routes, ipr)
		}
	}
	epi.v4PoolID = epMap["v4PoolID"].(string)
	epi.v6PoolID = epMap["v6PoolID"].(string)

	return nil
}

func (epi *endpointInterface) CopyTo(dstEpi *endpointInterface) error {
	dstEpi.mac = types.GetMacCopy(epi.mac)
	dstEpi.addr = types.GetIPNetCopy(epi.addr)
	dstEpi.addrv6 = types.GetIPNetCopy(epi.addrv6)
	dstEpi.srcName = epi.srcName
	dstEpi.dstPrefix = epi.dstPrefix
	dstEpi.v4PoolID = epi.v4PoolID
	dstEpi.v6PoolID = epi.v6PoolID
	if len(epi.llAddrs) != 0 {
		dstEpi.llAddrs = make([]*net.IPNet, 0, len(epi.llAddrs))
		dstEpi.llAddrs = append(dstEpi.llAddrs, epi.llAddrs...)
	}

	for _, route := range epi.routes {
		dstEpi.routes = append(dstEpi.routes, types.GetIPNetCopy(route))
	}

	return nil
}

type endpointJoinInfo struct {
	gw                    net.IP
	gw6                   net.IP
	StaticRoutes          []*types.StaticRoute
	driverTableEntries    []*tableEntry
	disableGatewayService bool
}

type tableEntry struct {
	tableName string
	key       string
	value     []byte
}

func (ep *endpoint) Info() EndpointInfo {
	if ep.sandboxID != "" {
		return ep
	}
	n, err := ep.getNetworkFromStore()
	if err != nil {
		return nil
	}

	ep, err = n.getEndpointFromStore(ep.ID())
	if err != nil {
		return nil
	}

	sb, ok := ep.getSandbox()
	if !ok {
		// endpoint hasn't joined any sandbox.
		// Just return the endpoint
		return ep
	}

	if epi := sb.getEndpoint(ep.ID()); epi != nil {
		return epi
	}

	return nil
}

func (ep *endpoint) Iface() InterfaceInfo {
	ep.Lock()
	defer ep.Unlock()

	if ep.iface != nil {
		return ep.iface
	}

	return nil
}

func (ep *endpoint) Interface() driverapi.InterfaceInfo {
	ep.Lock()
	defer ep.Unlock()

	if ep.iface != nil {
		return ep.iface
	}

	return nil
}

func (epi *endpointInterface) SetMacAddress(mac net.HardwareAddr) error {
	if epi.mac != nil {
		return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", epi.mac, mac)
	}
	if mac == nil {
		return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface")
	}
	epi.mac = types.GetMacCopy(mac)
	return nil
}

func (epi *endpointInterface) SetIPAddress(address *net.IPNet) error {
	if address.IP == nil {
		return types.BadRequestErrorf("tried to set nil IP address to endpoint interface")
	}
	if address.IP.To4() == nil {
		return setAddress(&epi.addrv6, address)
	}
	return setAddress(&epi.addr, address)
}

func setAddress(ifaceAddr **net.IPNet, address *net.IPNet) error {
	if *ifaceAddr != nil {
		return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address)
	}
	*ifaceAddr = types.GetIPNetCopy(address)
	return nil
}

func (epi *endpointInterface) MacAddress() net.HardwareAddr {
	return types.GetMacCopy(epi.mac)
}

func (epi *endpointInterface) Address() *net.IPNet {
	return types.GetIPNetCopy(epi.addr)
}

func (epi *endpointInterface) AddressIPv6() *net.IPNet {
	return types.GetIPNetCopy(epi.addrv6)
}

func (epi *endpointInterface) LinkLocalAddresses() []*net.IPNet {
	return epi.llAddrs
}

func (epi *endpointInterface) SetNames(srcName string, dstPrefix string) error {
	epi.srcName = srcName
	epi.dstPrefix = dstPrefix
	return nil
}

func (ep *endpoint) InterfaceName() driverapi.InterfaceNameInfo {
	ep.Lock()
	defer ep.Unlock()

	if ep.iface != nil {
		return ep.iface
	}

	return nil
}

func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
	ep.Lock()
	defer ep.Unlock()

	r := types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop}

	if routeType == types.NEXTHOP {
		// If the route specifies a next-hop, then it's loosely routed (i.e. not bound to a particular interface).
		ep.joinInfo.StaticRoutes = append(ep.joinInfo.StaticRoutes, &r)
	} else {
		// If the route doesn't specify a next-hop, it must be a connected route, bound to an interface.
		ep.iface.routes = append(ep.iface.routes, r.Destination)
	}
	return nil
}

func (ep *endpoint) AddTableEntry(tableName, key string, value []byte) error {
	ep.Lock()
	defer ep.Unlock()

	ep.joinInfo.driverTableEntries = append(ep.joinInfo.driverTableEntries, &tableEntry{
		tableName: tableName,
		key:       key,
		value:     value,
	})

	return nil
}

func (ep *endpoint) Sandbox() Sandbox {
	cnt, ok := ep.getSandbox()
	if !ok {
		return nil
	}
	return cnt
}

func (ep *endpoint) StaticRoutes() []*types.StaticRoute {
	ep.Lock()
	defer ep.Unlock()

	if ep.joinInfo == nil {
		return nil
	}

	return ep.joinInfo.StaticRoutes
}

func (ep *endpoint) Gateway() net.IP {
	ep.Lock()
	defer ep.Unlock()

	if ep.joinInfo == nil {
		return net.IP{}
	}

	return types.GetIPCopy(ep.joinInfo.gw)
}

func (ep *endpoint) GatewayIPv6() net.IP {
	ep.Lock()
	defer ep.Unlock()

	if ep.joinInfo == nil {
		return net.IP{}
	}

	return types.GetIPCopy(ep.joinInfo.gw6)
}

func (ep *endpoint) SetGateway(gw net.IP) error {
	ep.Lock()
	defer ep.Unlock()

	ep.joinInfo.gw = types.GetIPCopy(gw)
	return nil
}

func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error {
	ep.Lock()
	defer ep.Unlock()

	ep.joinInfo.gw6 = types.GetIPCopy(gw6)
	return nil
}

func (ep *endpoint) retrieveFromStore() (*endpoint, error) {
	n, err := ep.getNetworkFromStore()
	if err != nil {
		return nil, fmt.Errorf("could not find network in store to get latest endpoint %s: %v", ep.Name(), err)
	}
	return n.getEndpointFromStore(ep.ID())
}

func (ep *endpoint) DisableGatewayService() {
	ep.Lock()
	defer ep.Unlock()

	ep.joinInfo.disableGatewayService = true
}

func (epj *endpointJoinInfo) MarshalJSON() ([]byte, error) {
	epMap := make(map[string]interface{})
	if epj.gw != nil {
		epMap["gw"] = epj.gw.String()
	}
	if epj.gw6 != nil {
		epMap["gw6"] = epj.gw6.String()
	}
	epMap["disableGatewayService"] = epj.disableGatewayService
	epMap["StaticRoutes"] = epj.StaticRoutes
	return json.Marshal(epMap)
}

func (epj *endpointJoinInfo) UnmarshalJSON(b []byte) error {
	var (
		err   error
		epMap map[string]interface{}
	)
	if err = json.Unmarshal(b, &epMap); err != nil {
		return err
	}
	if v, ok := epMap["gw"]; ok {
		epj.gw = net.ParseIP(v.(string))
	}
	if v, ok := epMap["gw6"]; ok {
		epj.gw6 = net.ParseIP(v.(string))
	}
	epj.disableGatewayService = epMap["disableGatewayService"].(bool)

	var tStaticRoute []types.StaticRoute
	if v, ok := epMap["StaticRoutes"]; ok {
		tb, _ := json.Marshal(v)
		var tStaticRoute []types.StaticRoute
		json.Unmarshal(tb, &tStaticRoute)
	}
	var StaticRoutes []*types.StaticRoute
	for _, r := range tStaticRoute {
		StaticRoutes = append(StaticRoutes, &r)
	}
	epj.StaticRoutes = StaticRoutes

	return nil
}

func (epj *endpointJoinInfo) CopyTo(dstEpj *endpointJoinInfo) error {
	dstEpj.disableGatewayService = epj.disableGatewayService
	dstEpj.StaticRoutes = make([]*types.StaticRoute, len(epj.StaticRoutes))
	copy(dstEpj.StaticRoutes, epj.StaticRoutes)
	dstEpj.driverTableEntries = make([]*tableEntry, len(epj.driverTableEntries))
	copy(dstEpj.driverTableEntries, epj.driverTableEntries)
	dstEpj.gw = types.GetIPCopy(epj.gw)
	dstEpj.gw6 = types.GetIPCopy(epj.gw6)
	return nil
}
<|endoftext|>


<commit_before>package engine

type Parser interface {
	Parse() ([]Kombinacia, error)
}

type (
	Kombinacia []int

	Cislovacka int

	Cifrovacky [10]byte
)
<commit_msg>engine: definovanie zakldanych interface<commit_after>package engine

// Kombinacia reprezentuje kombinaciu cisiel.
// Cisla musia byt vacsie ako 1 a mensie ako 'm'. Velkost kombinacie musi byt 'n'
// a nesmie sa menit.
type Kombinacia []int

// Rc je cislo c. c moze mat hodnotu od 1 do n.
type Rc interface {
	// Rp vrati pocetnost cisla c.
	Rp(c int) int

	// Rh vrati hodnotu cisla c vypocitanu pomocou vzorca H(c, 1, Rp(c), n, m).
	Rh(c int) float64
}

// STLc je cislo c v stlpci s.
type STLc interface {
	// STLp vrati pocetnost cisla c.
	STLp(c int, s int) int

	// STLh vypocita hodnotu cisla pomocou vzorca H(c, s, STLp(c), n, m).
	STLh(c int, s int) float64
}

// Cislo je cislo v riadku a slpci s hodnotami R a STL.
type Cislo interface {
	Rc
	STLc
}

// Rk definuje sucet hodnot Rp.
type Rk interface {
	// R vrati sucet hodnot Rp Kombinacie.
	R(Kombinacia) float64
}

// STLk definuje sucet hodnot STLp.
type STLk interface {
	// STL vrati sucet hodnot STLp Kombinacie.
	STL(Kombinacia) float64
}

// RSTLk kombinuje sucty R a STL.
type RSTLk interface {
	Rk
	STLk
}

// Xk vypocita hodnotu Hrx, pripadne HHrx.
type Xk interface {
	// Ak je zadana Kombinacia k vypocita hodnotu po presune cisiel.
	// V pripade k == nil, vrati aktualnu hodnotu hrx, hhrx.
	X(k Kombinacia) float64
}

// Hrx je rozhranie ktore zdruzuje hodnoty R a STL vypocitane pre 1-DO a OD-DO.
type Hrx interface {
	Cislo
	RSTLk
	Xk
}

// Uc je uzatvaracie cislo pre OD-DO.
type Uc struct {
	Riadok int
	Cislo  int
}
<|endoftext|>


<commit_before>package engine

import (
	"errors"
	"github.com/malbrecht/chess"
	"time"
)

var (
	// ErrTimeout indicates a timeout in communicating with the engine.
	ErrTimeout = errors.New("timeout in engine communication")
	// ErrExited indicates that the engine was closed.
	ErrExited = errors.New("engine was closed")
)

// Engine provides a generic interface to a running chess engine.
type Engine interface {
	// SetPosition sets the position to search.
	SetPosition(board *chess.Board)

	// Search starts an infinite search of the position; that is, until
	// Stop is called. During the search, Info's will be sent on the
	// channel that is returned. The last Info on the channel, and only the
	// last, will either return a non-nil Info.Err or an Info.BestMove,
	// after which the channel is closed.
	Search() <-chan Info

	// SearchDepth is like Search but tells the engine to stop at a certain
	// depth.
	SearchDepth(depth int) <-chan Info

	// SearchTime is like Search but tells the engine to stop after the
	// given time.
	SearchTime(t time.Duration) <-chan Info

	// SearchClock is like Search but informs the engine of the time
	// controls of the game and lets the engine decide how much time to
	// use. movesToGo is the number of moves to the next time control.
	SearchClock(wtime, btime, winc, binc time.Duration, movesToGo int) <-chan Info

	// Stop stops a search started by one of the SearchXXX functions.
	Stop()

	// Quit quits the engine process.
	Quit()

	// Ping pings the engine process to check that it is still responding.
	Ping() error

	// Options returns the settable options of the engine.
	Options() map[string]Option
}

// Pv holds the details of a principal variation from an engine search.
type Pv struct {
	Moves      []chess.Move // principal variation moves
	Score      int          // centipawns or moves-to-mate; positive is good for white
	Mate       bool         // if yes then Score is moves-to-mate
	Upperbound bool         // Score is a upperbound
	Lowerbound bool         // Score is a lowerbound
	Rank       int          // 0-based rank of the pv in a MultiPV search
}

// Stats holds statistics from an engine search.
type Stats struct {
	Depth    int           // depth in plies
	SelDepth int           // selective depth
	Nodes    int           // number of nodes searched so far
	Time     time.Duration // amount of time searched so far
}

// Info represents a generic information "event" sent over an Info channel
// while an engine search is in progress.
type Info interface {
	// Err returns an error if one occured. This should be the first thing
	// to check, before calling the other methods.
	Err() error

	// BestMove returns the best move found by the engine. It returns !ok
	// if no best move has been found yet.
	BestMove() (move chess.Move, ok bool)

	// Pv returns the principal variation of this Info. It can be nil if no
	// pv information is available.
	Pv() *Pv

	// Stats returns statistics of the search so far. Any of the Stats
	// fields can be zero, meaning the information is not present (or
	// actually zero).
	Stats() *Stats
}

// Engine options

// Option represents a generic settable engine option.
type Option interface {
	String() string        // current value
	StringDefault() string // default value
	Set(value string)      // change the value
}

// StringOption represents string option.
type StringOption interface {
	Option
}

// IntOption represents a number option, possibly with a limited range.
type IntOption interface {
	Int() int     // current value
	SetInt(int)   // change value
	Default() int // default value
	Min() int     // minimum value
	Max() int     // maximum value (0 = no maximum)
}

// BoolOption represents a togglable option.
type BoolOption interface {
	Bool() bool    // current value
	SetBool(bool)  // change value
	Default() bool // default value
}
<commit_msg>add package comment<commit_after>// package engine defines a generic interface for communicating with chess
// engines.
package engine

import (
	"errors"
	"github.com/malbrecht/chess"
	"time"
)

var (
	// ErrTimeout indicates a timeout in communicating with the engine.
	ErrTimeout = errors.New("timeout in engine communication")
	// ErrExited indicates that the engine was closed.
	ErrExited = errors.New("engine was closed")
)

// Engine provides a generic interface to a running chess engine.
type Engine interface {
	// SetPosition sets the position to search.
	SetPosition(board *chess.Board)

	// Search starts an infinite search of the position; that is, until
	// Stop is called. During the search, Info's will be sent on the
	// channel that is returned. The last Info on the channel, and only the
	// last, will either return a non-nil Info.Err or an Info.BestMove,
	// after which the channel is closed.
	Search() <-chan Info

	// SearchDepth is like Search but tells the engine to stop at a certain
	// depth.
	SearchDepth(depth int) <-chan Info

	// SearchTime is like Search but tells the engine to stop after the
	// given time.
	SearchTime(t time.Duration) <-chan Info

	// SearchClock is like Search but informs the engine of the time
	// controls of the game and lets the engine decide how much time to
	// use. movesToGo is the number of moves to the next time control.
	SearchClock(wtime, btime, winc, binc time.Duration, movesToGo int) <-chan Info

	// Stop stops a search started by one of the SearchXXX functions.
	Stop()

	// Quit quits the engine process.
	Quit()

	// Ping pings the engine process to check that it is still responding.
	Ping() error

	// Options returns the settable options of the engine.
	Options() map[string]Option
}

// Pv holds the details of a principal variation from an engine search.
type Pv struct {
	Moves      []chess.Move // principal variation moves
	Score      int          // centipawns or moves-to-mate; positive is good for white
	Mate       bool         // if yes then Score is moves-to-mate
	Upperbound bool         // Score is a upperbound
	Lowerbound bool         // Score is a lowerbound
	Rank       int          // 0-based rank of the pv in a MultiPV search
}

// Stats holds statistics from an engine search.
type Stats struct {
	Depth    int           // depth in plies
	SelDepth int           // selective depth
	Nodes    int           // number of nodes searched so far
	Time     time.Duration // amount of time searched so far
}

// Info represents a generic information "event" sent over an Info channel
// while an engine search is in progress.
type Info interface {
	// Err returns an error if one occured. This should be the first thing
	// to check, before calling the other methods.
	Err() error

	// BestMove returns the best move found by the engine. It returns !ok
	// if no best move has been found yet.
	BestMove() (move chess.Move, ok bool)

	// Pv returns the principal variation of this Info. It can be nil if no
	// pv information is available.
	Pv() *Pv

	// Stats returns statistics of the search so far. Any of the Stats
	// fields can be zero, meaning the information is not present (or
	// actually zero).
	Stats() *Stats
}

// Engine options

// Option represents a generic settable engine option.
type Option interface {
	String() string        // current value
	StringDefault() string // default value
	Set(value string)      // change the value
}

// StringOption represents string option.
type StringOption interface {
	Option
}

// IntOption represents a number option, possibly with a limited range.
type IntOption interface {
	Int() int     // current value
	SetInt(int)   // change value
	Default() int // default value
	Min() int     // minimum value
	Max() int     // maximum value (0 = no maximum)
}

// BoolOption represents a togglable option.
type BoolOption interface {
	Bool() bool    // current value
	SetBool(bool)  // change value
	Default() bool // default value
}
<|endoftext|>


<commit_before>package engine

import (
	"errors"
	"time"

	log "github.com/coreos/fleet/Godeps/_workspace/src/github.com/golang/glog"

	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/machine"
	"github.com/coreos/fleet/registry"
)

type Engine struct {
	registry registry.Registry
	machine  machine.Machine
	// keeps a picture of the load in the cluster for more intelligent scheduling
	clust *cluster
}

func New(reg registry.Registry, mach machine.Machine) *Engine {
	return &Engine{reg, mach, newCluster()}
}

func (e *Engine) Run(stop chan bool) {
	ticker := time.Tick(time.Second * 5)

	work := func() {
		lock := e.registry.LockEngine(e.machine.State().ID)
		if lock == nil {
			log.V(1).Info("Unable to acquire engine lock")
			return
		}

		defer lock.Unlock()
		e.CheckForWork()
	}

	for {
		select {
		case <-stop:
			log.V(1).Info("Engine exiting due to stop signal")
			return
		case <-ticker:
			log.V(1).Info("Engine tick")
			work()
		}
	}
}

// CheckForWork attempts to rectify the current state of all Jobs in the cluster
// with their target states wherever discrepancies are identified.
func (e *Engine) CheckForWork() {
	log.Infof("Polling etcd for actionable Jobs")

	for _, jo := range e.registry.UnresolvedJobOffers() {
		bids, err := e.registry.Bids(&jo)
		if err != nil {
			log.Errorf("Failed determining open JobBids for JobOffer(%s): %v", jo.Job.Name, err)
			continue
		}
		if len(bids) == 0 {
			log.V(1).Infof("No bids found for open JobOffer(%s), ignoring", jo.Job.Name)
			continue
		}

		log.Infof("Resolving JobOffer(%s), scheduling to Machine(%s)", bids[0].JobName, bids[0].MachineID)
		if e.ResolveJobOffer(bids[0].JobName, bids[0].MachineID); err != nil {
			log.Infof("Failed scheduling Job(%s) to Machine(%s)", bids[0].JobName, bids[0].MachineID)
		} else {
			log.Infof("Scheduled Job(%s) to Machine(%s)", bids[0].JobName, bids[0].MachineID)
		}
	}

	jobs, _ := e.registry.Jobs()
	for _, j := range jobs {
		if j.State == nil || j.TargetState == *j.State {
			continue
		}

		if *j.State == job.JobStateInactive {
			log.Infof("Offering Job(%s)", j.Name)
			e.OfferJob(j)
		} else if j.TargetState == job.JobStateInactive {
			log.Infof("Unscheduling Job(%s)", j.Name)
			e.registry.ClearJobTarget(j.Name, j.TargetMachineID)
		}
	}
}

func (e *Engine) OfferJob(j job.Job) error {
	log.V(1).Infof("Attempting to lock Job(%s)", j.Name)

	mutex := e.registry.LockJob(j.Name, e.machine.State().ID)
	if mutex == nil {
		log.V(1).Infof("Could not lock Job(%s)", j.Name)
		return errors.New("could not lock Job")
	}
	defer mutex.Unlock()

	log.V(1).Infof("Claimed Job(%s)", j.Name)

	machineIDs, err := e.partitionCluster(&j)
	if err != nil {
		log.Errorf("failed partitioning cluster for Job(%s): %v", j.Name, err)
		return err
	}

	offer := job.NewOfferFromJob(j, machineIDs)

	err = e.registry.CreateJobOffer(offer)
	if err == nil {
		log.Infof("Published JobOffer(%s)", offer.Job.Name)
	}

	return err
}

func (e *Engine) ResolveJobOffer(jobName string, machID string) error {
	log.V(1).Infof("Attempting to lock JobOffer(%s)", jobName)
	mutex := e.registry.LockJobOffer(jobName, e.machine.State().ID)

	if mutex == nil {
		log.V(1).Infof("Could not lock JobOffer(%s)", jobName)
		return errors.New("could not lock JobOffer")
	}
	defer mutex.Unlock()

	log.V(1).Infof("Claimed JobOffer(%s)", jobName)

	err := e.registry.ResolveJobOffer(jobName)
	if err != nil {
		log.Errorf("Failed resolving JobOffer(%s): %v", jobName, err)
		return err
	}

	err = e.registry.ScheduleJob(jobName, machID)
	if err != nil {
		log.Errorf("Failed scheduling Job(%s): %v", jobName, err)
		return err
	}

	log.Infof("Scheduled Job(%s) to Machine(%s)", jobName, machID)
	return nil
}
<commit_msg>engine: refactor reconcile routine<commit_after>package engine

import (
	"time"

	log "github.com/coreos/fleet/Godeps/_workspace/src/github.com/golang/glog"

	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/machine"
	"github.com/coreos/fleet/registry"
)

const (
	// time between triggering reconciliation routine
	reconcileInterval = 2 * time.Second
)

type Engine struct {
	registry registry.Registry
	machine  machine.Machine
	// keeps a picture of the load in the cluster for more intelligent scheduling
	clust *cluster
}

func New(reg registry.Registry, mach machine.Machine) *Engine {
	return &Engine{reg, mach, newCluster()}
}

func (e *Engine) Run(stop chan bool) {
	ticker := time.Tick(reconcileInterval)

	work := func() {
		lock := e.registry.LockEngine(e.machine.State().ID)
		if lock == nil {
			log.V(1).Info("Unable to acquire engine lock")
			return
		}

		defer lock.Unlock()
		e.Reconcile()
	}

	for {
		select {
		case <-stop:
			log.V(1).Info("Engine exiting due to stop signal")
			return
		case <-ticker:
			log.V(1).Info("Engine tick")
			work()
		}
	}
}

// Reconcile attempts to advance the state of Jobs and JobOffers
// towards their desired states wherever discrepancies are identified.
func (e *Engine) Reconcile() {
	log.V(1).Infof("Polling Registry for actionable work")

	jobs, err := e.registry.Jobs()
	if err != nil {
		log.Errorf("Failed fetching Jobs from Registry: %v", err)
		return
	}

	machines, err := e.registry.Machines()
	if err != nil {
		log.Errorf("Failed fetching Machines from Registry: %v", err)
		return
	}

	mMap := make(map[string]*machine.MachineState, len(machines))
	for i := range machines {
		m := machines[i]
		mMap[m.ID] = &m
	}

	offers := e.registry.UnresolvedJobOffers()
	oMap := make(map[string]*job.JobOffer, len(offers))
	for i := range offers {
		o := offers[i]
		oMap[o.Job.Name] = &o
	}

	// Jobs will be sorted into three categories:
	// 1. Jobs where TargetState is inactive
	inactive := make([]*job.Job, 0)
	// 2. Jobs where TargetState is active, and the Job has been scheduled
	activeScheduled := make([]*job.Job, 0)
	// 3. Jobs where TargetState is active, and the Job has not been scheduled
	activeNotScheduled := make([]*job.Job, 0)

	for i := range jobs {
		j := jobs[i]
		if j.TargetState == job.JobStateInactive {
			inactive = append(inactive, &j)
		} else if j.Scheduled() {
			activeScheduled = append(activeScheduled, &j)
		} else {
			activeNotScheduled = append(activeNotScheduled, &j)
		}
	}

	// resolveJobOffer removes the referenced Job's corresponding
	// JobOffer from the local cache before marking it as resolved
	// in the Registry
	resolveJobOffer := func(jName string) {
		delete(oMap, jName)

		err := e.registry.ResolveJobOffer(jName)
		if err != nil {
			log.Errorf("Failed resolving JobOffer(%s): %v", jName, err)
		} else {
			log.Infof("Resolved JobOffer(%s)", jName)
		}
	}

	// unscheduleJob clears the current target of the provided Job from
	// the Registry
	unscheduleJob := func(j *job.Job) (err error) {
		err = e.registry.ClearJobTarget(j.Name, j.TargetMachineID)
		if err != nil {
			log.Errorf("Failed clearing target Machine(%s) of Job(%s): %v", j.TargetMachineID, j.Name, err)
		} else {
			log.Infof("Unscheduled Job(%s) from Machine(%s)", j.Name, j.TargetMachineID)
		}

		return
	}

	// maybeScheduleJob attempts to schedule the given Job only if one or more
	// bids have been submitted
	maybeScheduleJob := func(jName string) error {
		bids, err := e.registry.Bids(oMap[jName])
		if err != nil {
			log.Errorf("Failed determining open JobBids for JobOffer(%s): %v", jName, err)
			return err
		}

		if len(bids) == 0 {
			log.Infof("No bids found for unresolved JobOffer(%s), unable to resolve", jName)
			return nil
		}

		choice := bids[0]

		err = e.registry.ScheduleJob(jName, choice.MachineID)
		if err != nil {
			log.Errorf("Failed scheduling Job(%s) to Machine(%s): %v", choice.JobName, choice.MachineID, err)
		} else {
			log.Infof("Scheduled Job(%s) to Machine(%s)", jName, choice.MachineID)
		}

		return err
	}

	// offerExists returns true if the referenced Job has a corresponding
	// offer in the local JobOffer cache
	offerExists := func(jobName string) bool {
		_, ok := oMap[jobName]
		return ok
	}

	// machinePresent determines if the referenced Machine appears to be a
	// current member of the cluster based on the local cache
	machinePresent := func(machID string) bool {
		_, ok := mMap[machID]
		return ok
	}

	for _, j := range inactive {
		if j.Scheduled() {
			log.Infof("Unscheduling Job(%s) from Machine(%s) since target state is inactive %s", j.Name, j.TargetMachineID)
			unscheduleJob(j)
		}

		if offerExists(j.Name) {
			log.Infof("Resolving extraneous JobOffer(%s) since target state is inactive", j.Name)
			resolveJobOffer(j.Name)
		}
	}

	for _, j := range activeScheduled {
		if machinePresent(j.TargetMachineID) {
			if offerExists(j.Name) {
				log.Infof("Resolving extraneous JobOffer(%s) since Job is already scheduled", j.Name)
				resolveJobOffer(j.Name)
			}
		} else {
			log.Infof("Unscheduling Job(%s) since target Machine(%s) seems to have gone away", j.Name, j.TargetMachineID)
			err := unscheduleJob(j)
			if err != nil {
				continue
			}

			if !offerExists(j.Name) {
				log.Infof("Offering Job(%s) since target state %s and Job not scheduled", j.Name, j.TargetState)
				e.offerJob(j)
			}
		}
	}

	for _, j := range activeNotScheduled {
		if !offerExists(j.Name) {
			log.Infof("Offering Job(%s) since target state %s and Job not scheduled", j.Name, j.TargetState)
			e.offerJob(j)
			continue
		}

		log.Infof("Attempting to schedule Job(%s) since target state %s and Job not scheduled", j.Name, j.TargetState)

		err := maybeScheduleJob(j.Name)
		if err != nil {
			continue
		}

		resolveJobOffer(j.Name)
	}

	// Deal with remaining JobOffers that do not have a corresponding Job
	for jName, _ := range oMap {
		log.Infof("Removing extraneous JobOffer(%s) since corresponding Job does not exist", jName)
		resolveJobOffer(jName)
	}
}

func (e *Engine) offerJob(j *job.Job) {
	machineIDs, err := e.partitionCluster(j)
	if err != nil {
		log.Errorf("Failed partitioning cluster for Job(%s): %v", j.Name, err)
	}

	offer := job.NewOfferFromJob(*j, machineIDs)
	err = e.registry.CreateJobOffer(offer)
	if err != nil {
		log.Errorf("Failed publishing JobOffer(%s): %v", j.Name, err)
	} else {
		log.Infof("Published JobOffer(%s)", j.Name)
	}
}
<|endoftext|>


<commit_before>package errors

import (
	"bytes"
	"fmt"
	"io"
	"runtime"
	"sync"
	"unicode/utf8"
)

var ShowStackTraceOnError bool

type Error interface {
	Message() string
	StackTrace() string
	Cause() error
	Error() string
	ErrorWithStackTrace() string
}

type BaseError struct {
	message    string
	stackTrace string
	cause      error
}

func New(msg string) *BaseError {
	return _new(nil, msg, 3)
}

func Errorf(format string, args ...interface{}) *BaseError {
	return _new(nil, fmt.Sprintf(format, args...), 3)
}

func _new(cause error, msg string, skipStack int) *BaseError {
	err := BaseError{
		message: msg,
		cause:   cause,
	}
	err.stackTrace = StackTrace(skipStack, 2048)
	return &err
}

func (e *BaseError) Message() string {
	return e.message
}

func (e *BaseError) StackTrace() string {
	return e.stackTrace
}

func (e *BaseError) Cause() error {
	return e.cause
}

func (e *BaseError) Error() string {
	var msg string
	if e.message != "" {
		msg = e.message
	} else if e.cause != nil {
		msg = e.cause.Error()
	}
	if ShowStackTraceOnError {
		return fmt.Sprintf("message: %s\nstacktrace:\n%s", msg, e.ErrorWithStackTrace())
	} else {
		return msg
	}
}

func (e *BaseError) ErrorWithStackTrace() string {
	var buf bytes.Buffer
	buf.WriteString(e.message)
	buf.WriteString("\n")
	buf.WriteString(e.stackTrace)
	if e.cause != nil {
		buf.WriteString("  caused by \n")
		buf.WriteString(e.cause.Error())
		buf.WriteString("\n")
	}
	return buf.String()
}

func Wrap(e error, msg string) *BaseError {
	if e == nil {
		return nil
	}
	return _new(e, msg, 3)
}

func Wrapf(e error, msg string, args ...interface{}) *BaseError {
	if e == nil {
		return nil
	}
	return _new(e, fmt.Sprintf(msg, args...), 3)
}

func WrapOr(e error) *BaseError {
	if e == nil {
		return nil
	}
	if ee, ok := e.(*BaseError); ok {
		return ee
	}
	return _new(e, "", 3)
}

func Root(e error) error {
	for {
		err, ok := e.(Error)
		if !ok {
			return e
		}
		cause := err.Cause()
		if cause == nil {
			return err
		}
		e = cause
	}
}

func Find(e error, f func(e error) bool) error {
	for {
		if f(e) {
			return e
		}
		err, ok := e.(Error)
		if !ok {
			return nil
		}
		cause := err.Cause()
		if cause == nil {
			return nil
		}
		e = cause
	}
}

type MultiError []error

func (me MultiError) Error() string {
	var buf bytes.Buffer
	for _, err := range me {
		buf.WriteString(err.Error())
		buf.WriteString("\n")
	}
	return buf.String()
}

func (me MultiError) HasAdditional() interface{} {
	return me
}

func StackTrace(skip, maxBytes int) string {
	// this func is debug purpose and ignores errors

	buf := make([]byte, maxBytes)
	n := runtime.Stack(buf, false)
	var gotall bool
	if n < len(buf) {
		buf = buf[:n]
		gotall = true
	} else {
		for !utf8.Valid(buf) || len(buf) == 0 {
			buf = buf[:len(buf)-1]
		}
	}

	var w bytes.Buffer

	writeOrSkip := func(buf []byte, w io.Writer, line int) {
		if line == 1 || line > 1+skip*2 {
			w.Write(buf)
		}
	}

	line := 1
	for {
		lf := bytes.IndexByte(buf, '\n')
		if lf < 0 {
			writeOrSkip(buf, &w, line)
			break
		}
		writeOrSkip(buf[:lf+1], &w, line)
		buf = buf[lf+1:]
		line++
	}

	if !gotall {
		w.WriteString("\n        ... (omitted)")
	}
	w.WriteString("\n")

	return w.String()
}

// SyncMultiError describes synchronized MultiError.
// Note: wrapped MultiError is not synchronized if you write directly.
type SyncMultiError struct {
	MultiError
	sync.Mutex
}

// Append append an error.
func (sm *SyncMultiError) Append(err error) {
	sm.Lock()
	defer sm.Unlock()
	sm.MultiError = append(sm.MultiError, err)
}

// Len returns length of errors.
func (sm *SyncMultiError) Len() int {
	return len(sm.MultiError)
}
<commit_msg>change retval of err-wrapping func from err struct ptr to error IF<commit_after>package errors

import (
	"bytes"
	"fmt"
	"io"
	"runtime"
	"sync"
	"unicode/utf8"
)

var ShowStackTraceOnError bool

type Error interface {
	Message() string
	StackTrace() string
	Cause() error
	Error() string
	ErrorWithStackTrace() string
}

type BaseError struct {
	message    string
	stackTrace string
	cause      error
}

func New(msg string) *BaseError {
	return _new(nil, msg, 3)
}

func Errorf(format string, args ...interface{}) *BaseError {
	return _new(nil, fmt.Sprintf(format, args...), 3)
}

func _new(cause error, msg string, skipStack int) *BaseError {
	err := BaseError{
		message: msg,
		cause:   cause,
	}
	err.stackTrace = StackTrace(skipStack, 2048)
	return &err
}

func (e *BaseError) Message() string {
	return e.message
}

func (e *BaseError) StackTrace() string {
	return e.stackTrace
}

func (e *BaseError) Cause() error {
	return e.cause
}

func (e *BaseError) Error() string {
	var msg string
	if e.message != "" {
		msg = e.message
	} else if e.cause != nil {
		msg = e.cause.Error()
	}
	if ShowStackTraceOnError {
		return fmt.Sprintf("message: %s\nstacktrace:\n%s", msg, e.ErrorWithStackTrace())
	} else {
		return msg
	}
}

func (e *BaseError) ErrorWithStackTrace() string {
	var buf bytes.Buffer
	buf.WriteString(e.message)
	buf.WriteString("\n")
	buf.WriteString(e.stackTrace)
	if e.cause != nil {
		buf.WriteString("  caused by \n")
		buf.WriteString(e.cause.Error())
		buf.WriteString("\n")
	}
	return buf.String()
}

func Wrap(e error, msg string) error {
	if e == nil {
		return nil
	}
	return _new(e, msg, 3)
}

func Wrapf(e error, msg string, args ...interface{}) error {
	if e == nil {
		return nil
	}
	return _new(e, fmt.Sprintf(msg, args...), 3)
}

func WrapOr(e error) error {
	if e == nil {
		return nil
	}
	if ee, ok := e.(*BaseError); ok {
		return ee
	}
	return _new(e, "", 3)
}

func Root(e error) error {
	for {
		err, ok := e.(Error)
		if !ok {
			return e
		}
		cause := err.Cause()
		if cause == nil {
			return err
		}
		e = cause
	}
}

func Find(e error, f func(e error) bool) error {
	for {
		if f(e) {
			return e
		}
		err, ok := e.(Error)
		if !ok {
			return nil
		}
		cause := err.Cause()
		if cause == nil {
			return nil
		}
		e = cause
	}
}

type MultiError []error

func (me MultiError) Error() string {
	var buf bytes.Buffer
	for _, err := range me {
		buf.WriteString(err.Error())
		buf.WriteString("\n")
	}
	return buf.String()
}

func (me MultiError) HasAdditional() interface{} {
	return me
}

func StackTrace(skip, maxBytes int) string {
	// this func is debug purpose and ignores errors

	buf := make([]byte, maxBytes)
	n := runtime.Stack(buf, false)
	var gotall bool
	if n < len(buf) {
		buf = buf[:n]
		gotall = true
	} else {
		for !utf8.Valid(buf) || len(buf) == 0 {
			buf = buf[:len(buf)-1]
		}
	}

	var w bytes.Buffer

	writeOrSkip := func(buf []byte, w io.Writer, line int) {
		if line == 1 || line > 1+skip*2 {
			w.Write(buf)
		}
	}

	line := 1
	for {
		lf := bytes.IndexByte(buf, '\n')
		if lf < 0 {
			writeOrSkip(buf, &w, line)
			break
		}
		writeOrSkip(buf[:lf+1], &w, line)
		buf = buf[lf+1:]
		line++
	}

	if !gotall {
		w.WriteString("\n        ... (omitted)")
	}
	w.WriteString("\n")

	return w.String()
}

// SyncMultiError describes synchronized MultiError.
// Note: wrapped MultiError is not synchronized if you write directly.
type SyncMultiError struct {
	MultiError
	sync.Mutex
}

// Append append an error.
func (sm *SyncMultiError) Append(err error) {
	sm.Lock()
	defer sm.Unlock()
	sm.MultiError = append(sm.MultiError, err)
}

// Len returns length of errors.
func (sm *SyncMultiError) Len() int {
	return len(sm.MultiError)
}
<|endoftext|>


<commit_before>// Copyright 2013-3014 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package datetime

import (
	"log"
	"strings"
	"time"
)

/*
Takes a date/time string and attempts to parse it and return a newly formatted
date/time that looks like YYYY-MM-DD HH:MM:SS
*/
func ParseDateTime(dateString string) string {
	outputForm := "2006-01-02 15:04:05"
	firstForm := "Mon, 02 Jan 2006 15:04:05 -0700 MST"
	secondForm := "Mon, 02 Jan 2006 15:04:05 -0700 (MST)"
	thirdForm := "Mon, 2 Jan 2006 15:04:05 -0700 (MST)"

	dateString = strings.TrimSpace(dateString)
	result := ""

	t, err := time.Parse(firstForm, dateString)
	if err != nil {
		t, err = time.Parse(secondForm, dateString)
		if err != nil {
			t, err = time.Parse(thirdForm, dateString)
			if err != nil {
				log.Printf("libmailslurper: ERROR - Parsing date: %s\n", err)
				result = dateString
			} else {
				result = t.Format(outputForm)
			}
		} else {
			result = t.Format(outputForm)
		}
	} else {
		result = t.Format(outputForm)
	}

	return result
}
<commit_msg>Refactored date parsing to be cleaner. Now checking for a 4th input format<commit_after>// Copyright 2013-3014 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package datetime

import (
	"log"
	"strings"
	"time"
)

var dateInputFormats = []string{
	"Mon, 02 Jan 2006 15:04:05 -0700 MST",
	"Mon, 02 Jan 2006 15:04:05 -0700 (MST)",
	"Mon, 2 Jan 2006 15:04:05 -0700 (MST)",
	"02 Jan 2006 15:04:05 -0700",
}

/*
Takes a date/time string and attempts to parse it and return a newly formatted
date/time that looks like YYYY-MM-DD HH:MM:SS
*/
func ParseDateTime(dateString string) string {
	outputFormat := "2006-01-02 15:04:05"
	var parsedTime time.Time
	var err error

	dateString = strings.TrimSpace(dateString)
	result := ""

	for _, inputFormat := range dateInputFormats {
		if parsedTime, err = time.Parse(inputFormat, dateString); err != nil {
			result = parsedTime.Format(outputFormat)
			break
		}
	}

	if result == "" {
		log.Printf("libmailslurper: ERROR - Parsing date %s", dateString)
		result = dateString
	}

	return result
}
<|endoftext|>


<commit_before>package dynect

import (
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
)

// ConvenientClient A client with extra helper methods for common actions
type ConvenientClient struct {
	Client
}

// NewConvenientClient Creates a new ConvenientClient
func NewConvenientClient(customerName string) *ConvenientClient {
	return &ConvenientClient{
		Client{
			CustomerName: customerName,
			Transport:    &http.Transport{Proxy: http.ProxyFromEnvironment},
		}}
}

// CreateZone method to create a zone
func (c *ConvenientClient) CreateZone(zone, rname, serialStyle, ttl string) error {
	url := fmt.Sprintf("Zone/%s/", zone)
	data := &CreateZoneBlock{
		RName:       rname,
		SerialStyle: serialStyle,
		TTL:         ttl,
	}

	if err := c.Do("POST", url, data, nil); err != nil {
		return fmt.Errorf("Failed to create zone: %s", err)
	}

	return nil
}

// GetZone method to read a zone
func (c *ConvenientClient) GetZone(z *Zone) error {
	url := fmt.Sprintf("Zone/%s", z.Zone)
	data := &ZoneResponse{}

	if err := c.Do("GET", url, nil, data); err != nil {
		return fmt.Errorf("Failed to get zone: %s", err)
	}

	z.Serial = strconv.Itoa(data.Data.Serial)
	z.SerialStyle = data.Data.SerialStyle
	z.Zone = data.Data.Zone
	z.Type = data.Data.ZoneType

	return nil
}

// PublishZone Publish a specific zone and the changes for the current session
func (c *ConvenientClient) PublishZone(zone string) error {
	url := fmt.Sprintf("Zone/%s", zone)
	data := &PublishZoneBlock{
		Publish: true,
	}

	if err := c.Do("PUT", url, data, nil); err != nil {
		return fmt.Errorf("Failed to publish zone: %s", err)
	}

	return nil
}

// DeleteZoneNode method to delete everything in a zone
func (c *ConvenientClient) DeleteZoneNode(zone string) error {
	parentZone := strings.Join(strings.Split(zone, ".")[1:], ".")
	url := fmt.Sprintf("Node/%s/%s", parentZone, zone)

	if err := c.Do("DELETE", url, nil, nil); err != nil {
		return fmt.Errorf("Failed to delete zone node: %s", err)
	}

	return nil
}

// DeleteZone method to delete a zone
func (c *ConvenientClient) DeleteZone(zone string) error {
	url := fmt.Sprintf("Zone/%s/", zone)

	if err := c.Do("DELETE", url, nil, nil); err != nil {
		return fmt.Errorf("Failed to delete zone: %s", err)
	}

	return nil
}

// GetRecordID finds the dns record ID by fetching all records for a FQDN
func (c *ConvenientClient) GetRecordID(record *Record) error {
	finalID := ""
	url := fmt.Sprintf("AllRecord/%s/%s", record.Zone, record.FQDN)
	var records AllRecordsResponse
	err := c.Do("GET", url, nil, &records)
	if err != nil {
		return fmt.Errorf("Failed to find Dyn record id: %s", err)
	}
	for _, recordURL := range records.Data {
		id := strings.TrimPrefix(recordURL, fmt.Sprintf("/REST/%sRecord/%s/%s/", record.Type, record.Zone, record.FQDN))
		if !strings.Contains(id, "/") && id != "" {
			finalID = id
			log.Printf("[INFO] Found Dyn record ID: %s", id)
		}
	}
	if finalID == "" {
		return fmt.Errorf("Failed to find Dyn record id!")
	}

	record.ID = finalID
	return nil
}

// CreateRecord Method to create a DNS record
func (c *ConvenientClient) CreateRecord(record *Record) error {
	if record.FQDN == "" && record.Name == "" {
		record.FQDN = record.Zone
	} else if record.FQDN == "" {
		record.FQDN = fmt.Sprintf("%s.%s", record.Name, record.Zone)
	}
	rdata, err := buildRData(record)
	if err != nil {
		return fmt.Errorf("Failed to create Dyn RData: %s", err)
	}
	url := fmt.Sprintf("%sRecord/%s/%s", record.Type, record.Zone, record.FQDN)
	data := &RecordRequest{
		RData: rdata,
		TTL:   record.TTL,
	}
	return c.Do("POST", url, data, nil)
}

// UpdateRecord Method to update a DNS record
func (c *ConvenientClient) UpdateRecord(record *Record) error {
	if record.FQDN == "" {
		record.FQDN = fmt.Sprintf("%s.%s", record.Name, record.Zone)
	}
	rdata, err := buildRData(record)
	if err != nil {
		return fmt.Errorf("Failed to create Dyn RData: %s", err)
	}
	url := fmt.Sprintf("%sRecord/%s/%s/%s", record.Type, record.Zone, record.FQDN, record.ID)
	data := &RecordRequest{
		RData: rdata,
		TTL:   record.TTL,
	}
	return c.Do("PUT", url, data, nil)
}

// DeleteRecord Method to delete a DNS record
func (c *ConvenientClient) DeleteRecord(record *Record) error {
	if record.FQDN == "" {
		record.FQDN = fmt.Sprintf("%s.%s", record.Name, record.Zone)
	}
	// safety check that we have an ID, otherwise we could accidentally delete everything
	if record.ID == "" {
		return fmt.Errorf("No ID found! We can't continue!")
	}
	url := fmt.Sprintf("%sRecord/%s/%s/%s", record.Type, record.Zone, record.FQDN, record.ID)
	return c.Do("DELETE", url, nil, nil)
}

// GetRecord Method to get record details
func (c *ConvenientClient) GetRecord(record *Record) error {
	url := fmt.Sprintf("%sRecord/%s/%s/%s", record.Type, record.Zone, record.FQDN, record.ID)
	var rec RecordResponse
	err := c.Do("GET", url, nil, &rec)
	if err != nil {
		return err
	}

	record.Zone = rec.Data.Zone
	record.FQDN = rec.Data.FQDN
	record.Name = strings.TrimSuffix(rec.Data.FQDN, "."+rec.Data.Zone)
	record.Type = rec.Data.RecordType
	record.TTL = strconv.Itoa(rec.Data.TTL)

	switch rec.Data.RecordType {
	case "A", "AAAA":
		record.Value = rec.Data.RData.Address
	case "ALIAS":
		record.Value = rec.Data.RData.Alias
	case "CNAME":
		record.Value = rec.Data.RData.CName
	case "MX":
		record.Value = fmt.Sprintf("%d %s", rec.Data.RData.Preference, rec.Data.RData.Exchange)
	case "NS":
		record.Value = rec.Data.RData.NSDName
	case "SOA":
		record.Value = rec.Data.RData.RName
	case "TXT", "SPF":
		record.Value = rec.Data.RData.TxtData
	default:
		fmt.Println("unknown response", rec)
		return fmt.Errorf("Invalid Dyn record type: %s", rec.Data.RecordType)
	}

	return nil
}

func buildRData(r *Record) (DataBlock, error) {
	var rdata DataBlock

	switch r.Type {
	case "A", "AAAA":
		rdata = DataBlock{
			Address: r.Value,
		}
	case "ALIAS":
		rdata = DataBlock{
			Alias: r.Value,
		}
	case "CNAME":
		rdata = DataBlock{
			CName: r.Value,
		}
	case "MX":
		rdata = DataBlock{}
		fmt.Sscanf(r.Value, "%d %s", &rdata.Preference, &rdata.Exchange)
	case "NS":
		rdata = DataBlock{
			NSDName: r.Value,
		}
	case "SOA":
		rdata = DataBlock{
			RName: r.Value,
		}
	case "TXT", "SPF":
		rdata = DataBlock{
			TxtData: r.Value,
		}
	default:
		return rdata, fmt.Errorf("Invalid Dyn record type: %s", r.Type)
	}

	return rdata, nil
}
<commit_msg>support SRV records in convienient client<commit_after>package dynect

import (
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
)

// ConvenientClient A client with extra helper methods for common actions
type ConvenientClient struct {
	Client
}

// NewConvenientClient Creates a new ConvenientClient
func NewConvenientClient(customerName string) *ConvenientClient {
	return &ConvenientClient{
		Client{
			CustomerName: customerName,
			Transport:    &http.Transport{Proxy: http.ProxyFromEnvironment},
		}}
}

// CreateZone method to create a zone
func (c *ConvenientClient) CreateZone(zone, rname, serialStyle, ttl string) error {
	url := fmt.Sprintf("Zone/%s/", zone)
	data := &CreateZoneBlock{
		RName:       rname,
		SerialStyle: serialStyle,
		TTL:         ttl,
	}

	if err := c.Do("POST", url, data, nil); err != nil {
		return fmt.Errorf("Failed to create zone: %s", err)
	}

	return nil
}

// GetZone method to read a zone
func (c *ConvenientClient) GetZone(z *Zone) error {
	url := fmt.Sprintf("Zone/%s", z.Zone)
	data := &ZoneResponse{}

	if err := c.Do("GET", url, nil, data); err != nil {
		return fmt.Errorf("Failed to get zone: %s", err)
	}

	z.Serial = strconv.Itoa(data.Data.Serial)
	z.SerialStyle = data.Data.SerialStyle
	z.Zone = data.Data.Zone
	z.Type = data.Data.ZoneType

	return nil
}

// PublishZone Publish a specific zone and the changes for the current session
func (c *ConvenientClient) PublishZone(zone string) error {
	url := fmt.Sprintf("Zone/%s", zone)
	data := &PublishZoneBlock{
		Publish: true,
	}

	if err := c.Do("PUT", url, data, nil); err != nil {
		return fmt.Errorf("Failed to publish zone: %s", err)
	}

	return nil
}

// DeleteZoneNode method to delete everything in a zone
func (c *ConvenientClient) DeleteZoneNode(zone string) error {
	parentZone := strings.Join(strings.Split(zone, ".")[1:], ".")
	url := fmt.Sprintf("Node/%s/%s", parentZone, zone)

	if err := c.Do("DELETE", url, nil, nil); err != nil {
		return fmt.Errorf("Failed to delete zone node: %s", err)
	}

	return nil
}

// DeleteZone method to delete a zone
func (c *ConvenientClient) DeleteZone(zone string) error {
	url := fmt.Sprintf("Zone/%s/", zone)

	if err := c.Do("DELETE", url, nil, nil); err != nil {
		return fmt.Errorf("Failed to delete zone: %s", err)
	}

	return nil
}

// GetRecordID finds the dns record ID by fetching all records for a FQDN
func (c *ConvenientClient) GetRecordID(record *Record) error {
	finalID := ""
	url := fmt.Sprintf("AllRecord/%s/%s", record.Zone, record.FQDN)
	var records AllRecordsResponse
	err := c.Do("GET", url, nil, &records)
	if err != nil {
		return fmt.Errorf("Failed to find Dyn record id: %s", err)
	}
	for _, recordURL := range records.Data {
		id := strings.TrimPrefix(recordURL, fmt.Sprintf("/REST/%sRecord/%s/%s/", record.Type, record.Zone, record.FQDN))
		if !strings.Contains(id, "/") && id != "" {
			finalID = id
			log.Printf("[INFO] Found Dyn record ID: %s", id)
		}
	}
	if finalID == "" {
		return fmt.Errorf("Failed to find Dyn record id!")
	}

	record.ID = finalID
	return nil
}

// CreateRecord Method to create a DNS record
func (c *ConvenientClient) CreateRecord(record *Record) error {
	if record.FQDN == "" && record.Name == "" {
		record.FQDN = record.Zone
	} else if record.FQDN == "" {
		record.FQDN = fmt.Sprintf("%s.%s", record.Name, record.Zone)
	}
	rdata, err := buildRData(record)
	if err != nil {
		return fmt.Errorf("Failed to create Dyn RData: %s", err)
	}
	url := fmt.Sprintf("%sRecord/%s/%s", record.Type, record.Zone, record.FQDN)
	data := &RecordRequest{
		RData: rdata,
		TTL:   record.TTL,
	}
	return c.Do("POST", url, data, nil)
}

// UpdateRecord Method to update a DNS record
func (c *ConvenientClient) UpdateRecord(record *Record) error {
	if record.FQDN == "" {
		record.FQDN = fmt.Sprintf("%s.%s", record.Name, record.Zone)
	}
	rdata, err := buildRData(record)
	if err != nil {
		return fmt.Errorf("Failed to create Dyn RData: %s", err)
	}
	url := fmt.Sprintf("%sRecord/%s/%s/%s", record.Type, record.Zone, record.FQDN, record.ID)
	data := &RecordRequest{
		RData: rdata,
		TTL:   record.TTL,
	}
	return c.Do("PUT", url, data, nil)
}

// DeleteRecord Method to delete a DNS record
func (c *ConvenientClient) DeleteRecord(record *Record) error {
	if record.FQDN == "" {
		record.FQDN = fmt.Sprintf("%s.%s", record.Name, record.Zone)
	}
	// safety check that we have an ID, otherwise we could accidentally delete everything
	if record.ID == "" {
		return fmt.Errorf("No ID found! We can't continue!")
	}
	url := fmt.Sprintf("%sRecord/%s/%s/%s", record.Type, record.Zone, record.FQDN, record.ID)
	return c.Do("DELETE", url, nil, nil)
}

// GetRecord Method to get record details
func (c *ConvenientClient) GetRecord(record *Record) error {
	url := fmt.Sprintf("%sRecord/%s/%s/%s", record.Type, record.Zone, record.FQDN, record.ID)
	var rec RecordResponse
	err := c.Do("GET", url, nil, &rec)
	if err != nil {
		return err
	}

	record.Zone = rec.Data.Zone
	record.FQDN = rec.Data.FQDN
	record.Name = strings.TrimSuffix(rec.Data.FQDN, "."+rec.Data.Zone)
	record.Type = rec.Data.RecordType
	record.TTL = strconv.Itoa(rec.Data.TTL)

	switch rec.Data.RecordType {
	case "A", "AAAA":
		record.Value = rec.Data.RData.Address
	case "ALIAS":
		record.Value = rec.Data.RData.Alias
	case "CNAME":
		record.Value = rec.Data.RData.CName
	case "MX":
		record.Value = fmt.Sprintf("%d %s", rec.Data.RData.Preference, rec.Data.RData.Exchange)
	case "NS":
		record.Value = rec.Data.RData.NSDName
	case "SOA":
		record.Value = rec.Data.RData.RName
	case "TXT", "SPF":
		record.Value = rec.Data.RData.TxtData
	case "SRV":
		record.Value = fmt.Sprintf("%d %d %d %s", rec.Data.RData.Priority, rec.Data.RData.Weight, rec.Data.RData.Port, rec.Data.RData.Target)
	default:
		fmt.Println("unknown response", rec)
		return fmt.Errorf("Invalid Dyn record type: %s", rec.Data.RecordType)
	}

	return nil
}

func buildRData(r *Record) (DataBlock, error) {
	var rdata DataBlock

	switch r.Type {
	case "A", "AAAA":
		rdata = DataBlock{
			Address: r.Value,
		}
	case "ALIAS":
		rdata = DataBlock{
			Alias: r.Value,
		}
	case "CNAME":
		rdata = DataBlock{
			CName: r.Value,
		}
	case "MX":
		rdata = DataBlock{}
		fmt.Sscanf(r.Value, "%d %s", &rdata.Preference, &rdata.Exchange)
	case "NS":
		rdata = DataBlock{
			NSDName: r.Value,
		}
	case "SOA":
		rdata = DataBlock{
			RName: r.Value,
		}
	case "TXT", "SPF":
		rdata = DataBlock{
			TxtData: r.Value,
		}
	case "SRV":
		rdata = DataBlock{}
		fmt.Sscanf(r.Value, "%d %d %d %s", &rdata.Priority, &rdata.Weight, &rdata.Port, &rdata.Target)
	default:
		return rdata, fmt.Errorf("Invalid Dyn record type: %s", r.Type)
	}

	return rdata, nil
}
<|endoftext|>


<commit_before>package overlap

import (
	"fmt"
	"testing"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/e2e/e2eutil"
	"github.com/hashicorp/nomad/helper/uuid"
	"github.com/hashicorp/nomad/testutil"
	"github.com/shoenig/test/must"
)

// TestOverlap asserts that the resources used by an allocation are not
// considered free until their ClientStatus is terminal.
//
// See: https://github.com/hashicorp/nomad/issues/10440
func TestOverlap(t *testing.T) {
	nomadClient := e2eutil.NomadClient(t)
	e2eutil.WaitForLeader(t, nomadClient)

	getJob := func() (*api.Job, string) {
		job, err := e2eutil.Parse2(t, "testdata/overlap.nomad")
		must.NoError(t, err)
		jobID := *job.ID + uuid.Short()
		job.ID = &jobID
		return job, *job.ID
	}
	job1, jobID1 := getJob()

	// Register initial job that should block subsequent job's placement until
	// its shutdown_delay is up.
	_, _, err := nomadClient.Jobs().Register(job1, nil)
	must.NoError(t, err)
	defer e2eutil.WaitForJobStopped(t, nomadClient, jobID1)

	var origAlloc *api.AllocationListStub
	testutil.Wait(t, func() (bool, error) {
		time.Sleep(time.Second)

		a, _, err := nomadClient.Jobs().Allocations(jobID1, false, nil)
		must.NoError(t, err)
		if n := len(a); n == 0 {
			evalOut := e2eutil.DumpEvals(nomadClient, jobID1)
			return false, fmt.Errorf("timed out before an allocation was found for %s. Evals:\n%s", jobID1, evalOut)
		}
		must.Len(t, 1, a)

		origAlloc = a[0]
		return origAlloc.ClientStatus == "running", fmt.Errorf("timed out before alloc %s for %s was running: %s",
			origAlloc.ID, jobID1, origAlloc.ClientStatus)
	})

	// Capture node so we can ensure 2nd job is blocked by first
	node, _, err := nomadClient.Nodes().Info(origAlloc.NodeID, nil)
	must.NoError(t, err)

	// Stop job but don't wait for ClientStatus terminal
	_, _, err = nomadClient.Jobs().Deregister(jobID1, false, nil)
	must.NoError(t, err)
	minStopTime := time.Now().Add(job1.TaskGroups[0].Tasks[0].ShutdownDelay)

	testutil.Wait(t, func() (bool, error) {
		a, _, err := nomadClient.Allocations().Info(origAlloc.ID, nil)
		must.NoError(t, err)
		ds, cs := a.DesiredStatus, a.ClientStatus
		return ds == "stop" && cs == "running", fmt.Errorf("expected alloc %s to be stop|running but found %s|%s",
			a.ID, ds, cs)
	})

	// Start replacement job on same node and assert it is blocked
	job2, jobID2 := getJob()
	job2.Constraints = append(job2.Constraints, api.NewConstraint("${node.unique.id}", "=", origAlloc.NodeID))
	job2.TaskGroups[0].Tasks[0].ShutdownDelay = 0 // no need on the followup
	availCPU := int(node.NodeResources.Cpu.CpuShares - int64(node.ReservedResources.Cpu.CpuShares))
	job2.TaskGroups[0].Tasks[0].Resources.CPU = &availCPU // require job1 to free resources

	resp, _, err := nomadClient.Jobs().Register(job2, nil)
	must.NoError(t, err)
	defer e2eutil.WaitForJobStopped(t, nomadClient, jobID2)

	testutil.Wait(t, func() (bool, error) {
		e, _, err := nomadClient.Evaluations().Info(resp.EvalID, nil)
		must.NoError(t, err)
		if e == nil {
			return false, fmt.Errorf("eval %s does not exist yet", resp.EvalID)
		}
		return e.BlockedEval != "", fmt.Errorf("expected a blocked eval to be created but found: %#v", *e)
	})

	// Wait for job1's ShutdownDelay for origAlloc.ClientStatus to go terminal
	sleepyTime := minStopTime.Sub(time.Now())
	if sleepyTime > 0 {
		t.Logf("Followup job %s blocked. Sleeping for the rest of %s's shutdown_delay (%.3s/%s)",
			*job2.ID, *job1.ID, sleepyTime, job1.TaskGroups[0].Tasks[0].ShutdownDelay)
		time.Sleep(sleepyTime)
	}

	testutil.Wait(t, func() (bool, error) {
		a, _, err := nomadClient.Allocations().Info(origAlloc.ID, nil)
		must.NoError(t, err)
		return a.ClientStatus == "complete", fmt.Errorf("expected original alloc %s to be complete but is %s",
			a.ID, a.ClientStatus)
	})

	// Assert replacement job unblocked and running
	testutil.Wait(t, func() (bool, error) {
		a, _, err := nomadClient.Jobs().Allocations(jobID2, false, nil)
		must.NoError(t, err)
		if n := len(a); n == 0 {
			return false, fmt.Errorf("timed out before an allocation was found for %s", jobID2)
		}
		must.Len(t, 1, a)

		return a[0].ClientStatus == "running", fmt.Errorf("timed out before alloc %s for %s was running: %s",
			a[0].ID, jobID2, a[0].ClientStatus)
	})
}
<commit_msg>test: expand timing and debugging for overlap test (#14920)<commit_after>package overlap

import (
	"fmt"
	"testing"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/e2e/e2eutil"
	"github.com/hashicorp/nomad/helper/uuid"
	"github.com/hashicorp/nomad/testutil"
	"github.com/shoenig/test/must"
)

// TestOverlap asserts that the resources used by an allocation are not
// considered free until their ClientStatus is terminal.
//
// See: https://github.com/hashicorp/nomad/issues/10440
func TestOverlap(t *testing.T) {
	nomadClient := e2eutil.NomadClient(t)
	e2eutil.WaitForLeader(t, nomadClient)

	getJob := func() (*api.Job, string) {
		job, err := e2eutil.Parse2(t, "testdata/overlap.nomad")
		must.NoError(t, err)
		jobID := *job.ID + uuid.Short()
		job.ID = &jobID
		return job, *job.ID
	}
	job1, jobID1 := getJob()

	// Register initial job that should block subsequent job's placement until
	// its shutdown_delay is up.
	_, _, err := nomadClient.Jobs().Register(job1, nil)
	must.NoError(t, err)
	defer e2eutil.WaitForJobStopped(t, nomadClient, jobID1)

	var origAlloc *api.AllocationListStub
	testutil.Wait(t, func() (bool, error) {
		time.Sleep(time.Second)

		a, _, err := nomadClient.Jobs().Allocations(jobID1, false, nil)
		must.NoError(t, err)
		if n := len(a); n == 0 {
			evalOut := e2eutil.DumpEvals(nomadClient, jobID1)
			return false, fmt.Errorf("timed out before an allocation was found for %s. Evals:\n%s", jobID1, evalOut)
		}
		must.Len(t, 1, a)

		origAlloc = a[0]
		return origAlloc.ClientStatus == "running", fmt.Errorf("timed out before alloc %s for %s was running: %s",
			origAlloc.ID, jobID1, origAlloc.ClientStatus)
	})

	// Capture node so we can ensure 2nd job is blocked by first
	node, _, err := nomadClient.Nodes().Info(origAlloc.NodeID, nil)
	must.NoError(t, err)

	// Stop job but don't wait for ClientStatus terminal
	_, _, err = nomadClient.Jobs().Deregister(jobID1, false, nil)
	must.NoError(t, err)
	minStopTime := time.Now().Add(job1.TaskGroups[0].Tasks[0].ShutdownDelay)

	testutil.Wait(t, func() (bool, error) {
		a, _, err := nomadClient.Allocations().Info(origAlloc.ID, nil)
		must.NoError(t, err)
		ds, cs := a.DesiredStatus, a.ClientStatus
		return ds == "stop" && cs == "running", fmt.Errorf("expected alloc %s to be stop|running but found %s|%s",
			a.ID, ds, cs)
	})

	// Start replacement job on same node and assert it is blocked
	job2, jobID2 := getJob()
	job2.Constraints = append(job2.Constraints, api.NewConstraint("${node.unique.id}", "=", origAlloc.NodeID))
	job2.TaskGroups[0].Tasks[0].ShutdownDelay = 0 // no need on the followup
	availCPU := int(node.NodeResources.Cpu.CpuShares - int64(node.ReservedResources.Cpu.CpuShares))
	job2.TaskGroups[0].Tasks[0].Resources.CPU = &availCPU // require job1 to free resources

	resp, _, err := nomadClient.Jobs().Register(job2, nil)
	must.NoError(t, err)
	defer e2eutil.WaitForJobStopped(t, nomadClient, jobID2)

	testutil.Wait(t, func() (bool, error) {
		e, _, err := nomadClient.Evaluations().Info(resp.EvalID, nil)
		must.NoError(t, err)
		if e == nil {
			return false, fmt.Errorf("eval %s does not exist yet", resp.EvalID)
		}
		return e.BlockedEval != "", fmt.Errorf("expected a blocked eval to be created but found: %#v", *e)
	})

	// Wait for job1's ShutdownDelay for origAlloc.ClientStatus to go terminal
	sleepyTime := minStopTime.Sub(time.Now())
	if sleepyTime > 0 {
		t.Logf("Followup job %s blocked. Sleeping for the rest of %s's shutdown_delay (%.3s/%s)",
			*job2.ID, *job1.ID, sleepyTime, job1.TaskGroups[0].Tasks[0].ShutdownDelay)
		time.Sleep(sleepyTime)
	}

	testutil.Wait(t, func() (bool, error) {
		a, _, err := nomadClient.Allocations().Info(origAlloc.ID, nil)
		must.NoError(t, err)
		return a.ClientStatus == "complete", fmt.Errorf("expected original alloc %s to be complete but is %s",
			a.ID, a.ClientStatus)
	})

	// Assert replacement job unblocked and running
	testutil.Wait(t, func() (bool, error) {
		time.Sleep(time.Second)

		a, _, err := nomadClient.Jobs().Allocations(jobID2, false, nil)
		must.NoError(t, err)
		if n := len(a); n == 0 {
			evalOut := e2eutil.DumpEvals(nomadClient, jobID2)
			return false, fmt.Errorf("timed out before an allocation was found for %s; Evals:\n%s", jobID2, evalOut)
		}
		must.Len(t, 1, a)

		return a[0].ClientStatus == "running", fmt.Errorf("timed out before alloc %s for %s was running: %s",
			a[0].ID, jobID2, a[0].ClientStatus)
	})
}
<|endoftext|>


<commit_before>package notifier

import (
	"bytes"
	"fmt"
	"os"
	"sort"
	"strings"
	"sync"
	"text/template"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/fetcher"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/sendgrid/sendgrid-go"
	"github.com/sendgrid/sendgrid-go/helpers/mail"
	"github.com/uber-go/zap"
)

type Notifier struct {
	db            *gorm.DB
	fetcher       *fetcher.TeacherLessonFetcher
	dryRun        bool
	lessonService *model.LessonService
	teachers      map[uint32]*model.Teacher
}

func NewNotifier(db *gorm.DB, concurrency int, dryRun bool) *Notifier {
	if concurrency < 1 {
		concurrency = 1
	}
	return &Notifier{
		db:       db,
		fetcher:  fetcher.NewTeacherLessonFetcher(nil, concurrency, logger.App),
		dryRun:   dryRun,
		teachers: make(map[uint32]*model.Teacher, 1000),
	}
}

func (n *Notifier) SendNotification(user *model.User) error {
	followingTeacherService := model.NewFollowingTeacherService(n.db)
	n.lessonService = model.NewLessonService(n.db)

	teacherIDs, err := followingTeacherService.FindTeacherIDsByUserID(user.ID)
	if err != nil {
		return errors.Wrapperf(err, "Failed to FindTeacherIDsByUserID(): userID=%v", user.ID)
	}

	availableLessonsPerTeacher := make(map[uint32][]*model.Lesson, 1000)
	allFetchedLessons := make([]*model.Lesson, 0, 5000)
	wg := &sync.WaitGroup{}
	for _, teacherID := range teacherIDs {
		wg.Add(1)
		go func(teacherID uint32) {
			defer wg.Done()
			teacher, fetchedLessons, newAvailableLessons, err := n.fetchAndExtractNewAvailableLessons(teacherID)
			if err != nil {
				switch err.(type) {
				case *errors.NotFound:
					// TODO: update teacher table flag
					// TODO: Not need to log
					logger.App.Warn("Cannot find teacher", zap.Uint("teacherID", uint(teacherID)))
				default:
					logger.App.Error("Cannot fetch teacher", zap.Uint("teacherID", uint(teacherID)), zap.Error(err))
				}
				return
			}

			allFetchedLessons = append(allFetchedLessons, fetchedLessons...)
			n.teachers[teacherID] = teacher
			if len(newAvailableLessons) > 0 {
				availableLessonsPerTeacher[teacherID] = newAvailableLessons
			}
		}(teacherID)

		if err != nil {
			return err
		}
	}
	wg.Wait()

	if err := n.sendNotificationToUser(user, availableLessonsPerTeacher); err != nil {
		return err
	}

	if !n.dryRun {
		n.lessonService.UpdateLessons(allFetchedLessons)
	}

	time.Sleep(500 * time.Millisecond)

	return nil
}

// Returns teacher, fetchedLessons, newAvailableLessons, error
func (n *Notifier) fetchAndExtractNewAvailableLessons(teacherID uint32) (
	*model.Teacher, []*model.Lesson, []*model.Lesson, error,
) {
	teacher, fetchedLessons, err := n.fetcher.Fetch(teacherID)
	if err != nil {
		logger.App.Error(
			"TeacherLessonFetcher.Fetch",
			zap.Uint("teacherID", uint(teacherID)), zap.Error(err),
		)
		return nil, nil, nil, err
	}
	logger.App.Info(
		"TeacherLessonFetcher.Fetch",
		zap.Uint("teacherID", uint(teacher.ID)),
		zap.String("teacherName", teacher.Name),
		zap.Int("fetchedLessons", len(fetchedLessons)),
	)

	//fmt.Printf("fetchedLessons ---\n")
	//for _, l := range fetchedLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}

	now := time.Now()
	fromDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, config.LocalTimezone())
	toDate := fromDate.Add(24 * 6 * time.Hour)
	lastFetchedLessons, err := n.lessonService.FindLessons(teacher.ID, fromDate, toDate)
	if err != nil {
		return nil, nil, nil, err
	}
	//fmt.Printf("lastFetchedLessons ---\n")
	//for _, l := range lastFetchedLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}

	newAvailableLessons := n.lessonService.GetNewAvailableLessons(lastFetchedLessons, fetchedLessons)
	//fmt.Printf("newAvailableLessons ---\n")
	//for _, l := range newAvailableLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}
	return teacher, fetchedLessons, newAvailableLessons, nil
}

func (n *Notifier) sendNotificationToUser(
	user *model.User,
	lessonsPerTeacher map[uint32][]*model.Lesson,
) error {
	lessonsCount := 0
	var teacherIDs []int
	for teacherID, lessons := range lessonsPerTeacher {
		teacherIDs = append(teacherIDs, int(teacherID))
		lessonsCount += len(lessons)
	}
	if lessonsCount == 0 {
		// Don't send notification
		return nil
	}

	sort.Ints(teacherIDs)
	var teacherIDs2 []uint32
	var teacherNames []string
	for _, id := range teacherIDs {
		teacherIDs2 = append(teacherIDs2, uint32(id))
		teacherNames = append(teacherNames, n.teachers[uint32(id)].Name)
	}

	t := template.New("email")
	t = template.Must(t.Parse(getEmailTemplateJP()))
	type TemplateData struct {
		TeacherIDs        []uint32
		Teachers          map[uint32]*model.Teacher
		LessonsPerTeacher map[uint32][]*model.Lesson
		WebURL            string
	}
	data := &TemplateData{
		TeacherIDs:        teacherIDs2,
		Teachers:          n.teachers,
		LessonsPerTeacher: lessonsPerTeacher,
		WebURL:            config.WebURL(),
	}

	var body bytes.Buffer
	if err := t.Execute(&body, data); err != nil {
		return errors.InternalWrapf(err, "Failed to execute template.")
	}
	//fmt.Printf("--- mail ---\n%s", body.String())

	//subject := "Schedule of teacher " + strings.Join(teacherNames, ", ")
	subject := strings.Join(teacherNames, ", ") + "の空きレッスンがあります"
	sender := &EmailNotificationSender{}
	return sender.Send(user, subject, body.String())
}

func getEmailTemplateJP() string {
	return strings.TrimSpace(`
----- 不具合のお知らせ -----
2017年のレッスンがある講師をフォローしている場合、
通知のメールが10分ごとに必ず送られてしまうという不具合がありました。
不具合は修正済みです。ご迷惑をおかけして申し訳ありませんでした。
{{ $space1 := "" }}
{{ $space2 := "" }}

{{- range $teacherID := .TeacherIDs }}
{{- $teacher := index $.Teachers $teacherID -}}
--- {{ $teacher.Name }} ---
  {{- $lessons := index $.LessonsPerTeacher $teacherID }}
  {{- range $lesson := $lessons }}
{{ $lesson.Datetime.Format "2006-01-02 15:04" }}
  {{- end }}

レッスンの予約はこちらから:
PC
Mobile

{{ end }}
空きレッスンの通知の解除はこちら
	`)
}

func getEmailTemplateEN() string {
	return strings.TrimSpace(`
{{- range $teacherID := .TeacherIDs }}
{{- $teacher := index $.Teachers $teacherID -}}
--- {{ $teacher.Name }} ---
  {{- $lessons := index $.LessonsPerTeacher $teacherID }}
  {{- range $lesson := $lessons }}
{{ $lesson.Datetime.Format "2006-01-02 15:04" }}
  {{- end }}

Reserve here:
PC
Mobile
{{ end }}
Click here if you want to stop notification of the teacher.
	`)
}

func (n *Notifier) Close() {
	n.fetcher.Close()
}

type NotificationSender interface {
	Send(user *model.User, subject, body string) error
}

type EmailNotificationSender struct{}

func (s *EmailNotificationSender) Send(user *model.User, subject, body string) error {
	from := mail.NewEmail("lekcije", "lekcije@lekcije.com")
	to := mail.NewEmail(user.Name, user.Email.Raw())
	content := mail.NewContent("text/html", strings.Replace(body, "\n", "
", -1))
	m := mail.NewV3MailInit(from, subject, to, content)

	req := sendgrid.GetRequest(
		os.Getenv("SENDGRID_API_KEY"),
		"/v3/mail/send",
		"https://api.sendgrid.com",
	)
	req.Method = "POST"
	req.Body = mail.GetRequestBody(m)
	resp, err := sendgrid.API(req)
	if err != nil {
		return errors.InternalWrapf(err, "Failed to send email by sendgrid")
	}
	if resp.StatusCode >= 300 {
		message := fmt.Sprintf(
			"Failed to send email by sendgrid: statusCode=%v, body=%v",
			resp.StatusCode, strings.Replace(resp.Body, "\n", "\\n", -1),
		)
		logger.App.Error(message)
		return errors.InternalWrapf(
			err,
			"Failed to send email by sendgrid: statusCode=%v",
			resp.StatusCode,
		)
	}

	return nil
}
<commit_msg>Remove bug information<commit_after>package notifier

import (
	"bytes"
	"fmt"
	"os"
	"sort"
	"strings"
	"sync"
	"text/template"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/fetcher"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/sendgrid/sendgrid-go"
	"github.com/sendgrid/sendgrid-go/helpers/mail"
	"github.com/uber-go/zap"
)

type Notifier struct {
	db            *gorm.DB
	fetcher       *fetcher.TeacherLessonFetcher
	dryRun        bool
	lessonService *model.LessonService
	teachers      map[uint32]*model.Teacher
}

func NewNotifier(db *gorm.DB, concurrency int, dryRun bool) *Notifier {
	if concurrency < 1 {
		concurrency = 1
	}
	return &Notifier{
		db:       db,
		fetcher:  fetcher.NewTeacherLessonFetcher(nil, concurrency, logger.App),
		dryRun:   dryRun,
		teachers: make(map[uint32]*model.Teacher, 1000),
	}
}

func (n *Notifier) SendNotification(user *model.User) error {
	followingTeacherService := model.NewFollowingTeacherService(n.db)
	n.lessonService = model.NewLessonService(n.db)

	teacherIDs, err := followingTeacherService.FindTeacherIDsByUserID(user.ID)
	if err != nil {
		return errors.Wrapperf(err, "Failed to FindTeacherIDsByUserID(): userID=%v", user.ID)
	}

	availableLessonsPerTeacher := make(map[uint32][]*model.Lesson, 1000)
	allFetchedLessons := make([]*model.Lesson, 0, 5000)
	wg := &sync.WaitGroup{}
	for _, teacherID := range teacherIDs {
		wg.Add(1)
		go func(teacherID uint32) {
			defer wg.Done()
			teacher, fetchedLessons, newAvailableLessons, err := n.fetchAndExtractNewAvailableLessons(teacherID)
			if err != nil {
				switch err.(type) {
				case *errors.NotFound:
					// TODO: update teacher table flag
					// TODO: Not need to log
					logger.App.Warn("Cannot find teacher", zap.Uint("teacherID", uint(teacherID)))
				default:
					logger.App.Error("Cannot fetch teacher", zap.Uint("teacherID", uint(teacherID)), zap.Error(err))
				}
				return
			}

			allFetchedLessons = append(allFetchedLessons, fetchedLessons...)
			n.teachers[teacherID] = teacher
			if len(newAvailableLessons) > 0 {
				availableLessonsPerTeacher[teacherID] = newAvailableLessons
			}
		}(teacherID)

		if err != nil {
			return err
		}
	}
	wg.Wait()

	if err := n.sendNotificationToUser(user, availableLessonsPerTeacher); err != nil {
		return err
	}

	if !n.dryRun {
		n.lessonService.UpdateLessons(allFetchedLessons)
	}

	time.Sleep(500 * time.Millisecond)

	return nil
}

// Returns teacher, fetchedLessons, newAvailableLessons, error
func (n *Notifier) fetchAndExtractNewAvailableLessons(teacherID uint32) (
	*model.Teacher, []*model.Lesson, []*model.Lesson, error,
) {
	teacher, fetchedLessons, err := n.fetcher.Fetch(teacherID)
	if err != nil {
		logger.App.Error(
			"TeacherLessonFetcher.Fetch",
			zap.Uint("teacherID", uint(teacherID)), zap.Error(err),
		)
		return nil, nil, nil, err
	}
	logger.App.Info(
		"TeacherLessonFetcher.Fetch",
		zap.Uint("teacherID", uint(teacher.ID)),
		zap.String("teacherName", teacher.Name),
		zap.Int("fetchedLessons", len(fetchedLessons)),
	)

	//fmt.Printf("fetchedLessons ---\n")
	//for _, l := range fetchedLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}

	now := time.Now()
	fromDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, config.LocalTimezone())
	toDate := fromDate.Add(24 * 6 * time.Hour)
	lastFetchedLessons, err := n.lessonService.FindLessons(teacher.ID, fromDate, toDate)
	if err != nil {
		return nil, nil, nil, err
	}
	//fmt.Printf("lastFetchedLessons ---\n")
	//for _, l := range lastFetchedLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}

	newAvailableLessons := n.lessonService.GetNewAvailableLessons(lastFetchedLessons, fetchedLessons)
	//fmt.Printf("newAvailableLessons ---\n")
	//for _, l := range newAvailableLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}
	return teacher, fetchedLessons, newAvailableLessons, nil
}

func (n *Notifier) sendNotificationToUser(
	user *model.User,
	lessonsPerTeacher map[uint32][]*model.Lesson,
) error {
	lessonsCount := 0
	var teacherIDs []int
	for teacherID, lessons := range lessonsPerTeacher {
		teacherIDs = append(teacherIDs, int(teacherID))
		lessonsCount += len(lessons)
	}
	if lessonsCount == 0 {
		// Don't send notification
		return nil
	}

	sort.Ints(teacherIDs)
	var teacherIDs2 []uint32
	var teacherNames []string
	for _, id := range teacherIDs {
		teacherIDs2 = append(teacherIDs2, uint32(id))
		teacherNames = append(teacherNames, n.teachers[uint32(id)].Name)
	}

	t := template.New("email")
	t = template.Must(t.Parse(getEmailTemplateJP()))
	type TemplateData struct {
		TeacherIDs        []uint32
		Teachers          map[uint32]*model.Teacher
		LessonsPerTeacher map[uint32][]*model.Lesson
		WebURL            string
	}
	data := &TemplateData{
		TeacherIDs:        teacherIDs2,
		Teachers:          n.teachers,
		LessonsPerTeacher: lessonsPerTeacher,
		WebURL:            config.WebURL(),
	}

	var body bytes.Buffer
	if err := t.Execute(&body, data); err != nil {
		return errors.InternalWrapf(err, "Failed to execute template.")
	}
	//fmt.Printf("--- mail ---\n%s", body.String())

	//subject := "Schedule of teacher " + strings.Join(teacherNames, ", ")
	subject := strings.Join(teacherNames, ", ") + "の空きレッスンがあります"
	sender := &EmailNotificationSender{}
	return sender.Send(user, subject, body.String())
}

func getEmailTemplateJP() string {
	return strings.TrimSpace(`
{{- range $teacherID := .TeacherIDs }}
{{- $teacher := index $.Teachers $teacherID -}}
--- {{ $teacher.Name }} ---
  {{- $lessons := index $.LessonsPerTeacher $teacherID }}
  {{- range $lesson := $lessons }}
{{ $lesson.Datetime.Format "2006-01-02 15:04" }}
  {{- end }}

レッスンの予約はこちらから:
PC
Mobile

{{ end }}
空きレッスンの通知の解除はこちら
	`)
}

func getEmailTemplateEN() string {
	return strings.TrimSpace(`
{{- range $teacherID := .TeacherIDs }}
{{- $teacher := index $.Teachers $teacherID -}}
--- {{ $teacher.Name }} ---
  {{- $lessons := index $.LessonsPerTeacher $teacherID }}
  {{- range $lesson := $lessons }}
{{ $lesson.Datetime.Format "2006-01-02 15:04" }}
  {{- end }}

Reserve here:
PC
Mobile
{{ end }}
Click here if you want to stop notification of the teacher.
	`)
}

func (n *Notifier) Close() {
	n.fetcher.Close()
}

type NotificationSender interface {
	Send(user *model.User, subject, body string) error
}

type EmailNotificationSender struct{}

func (s *EmailNotificationSender) Send(user *model.User, subject, body string) error {
	from := mail.NewEmail("lekcije", "lekcije@lekcije.com")
	to := mail.NewEmail(user.Name, user.Email.Raw())
	content := mail.NewContent("text/html", strings.Replace(body, "\n", "
", -1))
	m := mail.NewV3MailInit(from, subject, to, content)

	req := sendgrid.GetRequest(
		os.Getenv("SENDGRID_API_KEY"),
		"/v3/mail/send",
		"https://api.sendgrid.com",
	)
	req.Method = "POST"
	req.Body = mail.GetRequestBody(m)
	resp, err := sendgrid.API(req)
	if err != nil {
		return errors.InternalWrapf(err, "Failed to send email by sendgrid")
	}
	if resp.StatusCode >= 300 {
		message := fmt.Sprintf(
			"Failed to send email by sendgrid: statusCode=%v, body=%v",
			resp.StatusCode, strings.Replace(resp.Body, "\n", "\\n", -1),
		)
		logger.App.Error(message)
		return errors.InternalWrapf(
			err,
			"Failed to send email by sendgrid: statusCode=%v",
			resp.StatusCode,
		)
	}

	return nil
}
<|endoftext|>


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

package server

import (
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/google/trillian/crypto"
	"github.com/google/trillian/extension"
	"github.com/google/trillian/log"
	"github.com/google/trillian/merkle"
	"github.com/google/trillian/util"
)

// SequencerManager provides sequencing operations for a collection of Logs.
type SequencerManager struct {
	keyManager  crypto.KeyManager
	guardWindow time.Duration
	registry    extension.Registry
}

// NewSequencerManager creates a new SequencerManager instance based on the provided KeyManager instance
// and guard window.
func NewSequencerManager(km crypto.KeyManager, registry extension.Registry, gw time.Duration) *SequencerManager {
	return &SequencerManager{
		keyManager:  km,
		guardWindow: gw,
		registry:    registry,
	}
}

// Name returns the name of the object.
func (s SequencerManager) Name() string {
	return "Sequencer"
}

// ExecutePass performs sequencing for the specified set of Logs.
func (s SequencerManager) ExecutePass(logIDs []int64, logctx LogOperationManagerContext) {
	if logctx.numSequencers == 0 {
		glog.Warning("Called ExecutePass with numSequencers == 0, assuming 1")
		logctx.numSequencers = 1
	}
	glog.V(1).Infof("Beginning sequencing run for %v active log(s) using %d sequencers", len(logIDs), logctx.numSequencers)

	startBatch := time.Now()

	var mu sync.Mutex
	successCount := 0
	leavesAdded := 0

	storage, err := s.registry.GetLogStorage()
	if err != nil {
		glog.Warningf("Failed to acquire log storage: %v", err)
		return
	}

	var wg sync.WaitGroup
	toSeq := make(chan int64, len(logIDs))

	for _, logID := range logIDs {
		toSeq <- logID
	}
	close(toSeq)

	for i := 0; i < logctx.numSequencers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for {
				logID, more := <-toSeq
				if !more {
					return
				}

				start := time.Now()

				// TODO(Martin2112): Honor the sequencing enabled in log parameters, needs an API change
				// so deferring it
				ctx := util.NewLogContext(logctx.ctx, logID)

				// TODO(Martin2112): Allow for different tree hashers to be used by different logs
				hasher, err := merkle.Factory(merkle.RFC6962SHA256Type)
				if err != nil {
					glog.Errorf("Unknown hash strategy for log %d: %v", logID, err)
					continue
				}
				sequencer := log.NewSequencer(hasher, logctx.timeSource, storage, s.keyManager)
				sequencer.SetGuardWindow(s.guardWindow)

				leaves, err := sequencer.SequenceBatch(ctx, logID, logctx.batchSize)
				if err != nil {
					glog.Warningf("%v: Error trying to sequence batch for: %v", logID, err)
					continue
				}
				d := time.Now().Sub(start).Seconds()
				glog.Infof("%v: sequenced %d leaves in %.2f seconds (%.2f qps)", logID, leaves, d, float64(leaves)/d)

				mu.Lock()
				defer mu.Unlock()
				successCount++
				leavesAdded += leaves
			}
		}()
	}

	wg.Wait()
	d := time.Now().Sub(startBatch).Seconds()

	mu.Lock()
	defer mu.Unlock()
	glog.V(1).Infof("Sequencing group run completed in %.2f seconds: %v succeeded, %v failed, %v leaves integrated", d, successCount, len(logIDs)-successCount, leavesAdded)
}
<commit_msg>Don't deadlock in sequencer (#380)<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/google/trillian/crypto"
	"github.com/google/trillian/extension"
	"github.com/google/trillian/log"
	"github.com/google/trillian/merkle"
	"github.com/google/trillian/util"
)

// SequencerManager provides sequencing operations for a collection of Logs.
type SequencerManager struct {
	keyManager  crypto.KeyManager
	guardWindow time.Duration
	registry    extension.Registry
}

// NewSequencerManager creates a new SequencerManager instance based on the provided KeyManager instance
// and guard window.
func NewSequencerManager(km crypto.KeyManager, registry extension.Registry, gw time.Duration) *SequencerManager {
	return &SequencerManager{
		keyManager:  km,
		guardWindow: gw,
		registry:    registry,
	}
}

// Name returns the name of the object.
func (s SequencerManager) Name() string {
	return "Sequencer"
}

// ExecutePass performs sequencing for the specified set of Logs.
func (s SequencerManager) ExecutePass(logIDs []int64, logctx LogOperationManagerContext) {
	if logctx.numSequencers == 0 {
		glog.Warning("Called ExecutePass with numSequencers == 0, assuming 1")
		logctx.numSequencers = 1
	}
	glog.V(1).Infof("Beginning sequencing run for %v active log(s) using %d sequencers", len(logIDs), logctx.numSequencers)

	startBatch := time.Now()

	var mu sync.Mutex
	successCount := 0
	leavesAdded := 0

	storage, err := s.registry.GetLogStorage()
	if err != nil {
		glog.Warningf("Failed to acquire log storage: %v", err)
		return
	}

	var wg sync.WaitGroup
	toSeq := make(chan int64, len(logIDs))

	for _, logID := range logIDs {
		toSeq <- logID
	}
	close(toSeq)

	for i := 0; i < logctx.numSequencers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for {
				logID, more := <-toSeq
				if !more {
					return
				}

				start := time.Now()

				// TODO(Martin2112): Honor the sequencing enabled in log parameters, needs an API change
				// so deferring it
				ctx := util.NewLogContext(logctx.ctx, logID)

				// TODO(Martin2112): Allow for different tree hashers to be used by different logs
				hasher, err := merkle.Factory(merkle.RFC6962SHA256Type)
				if err != nil {
					glog.Errorf("Unknown hash strategy for log %d: %v", logID, err)
					continue
				}
				sequencer := log.NewSequencer(hasher, logctx.timeSource, storage, s.keyManager)
				sequencer.SetGuardWindow(s.guardWindow)

				leaves, err := sequencer.SequenceBatch(ctx, logID, logctx.batchSize)
				if err != nil {
					glog.Warningf("%v: Error trying to sequence batch for: %v", logID, err)
					continue
				}
				d := time.Now().Sub(start).Seconds()
				glog.Infof("%v: sequenced %d leaves in %.2f seconds (%.2f qps)", logID, leaves, d, float64(leaves)/d)

				mu.Lock()
				successCount++
				leavesAdded += leaves
				mu.Unlock()
			}
		}()
	}

	wg.Wait()
	d := time.Now().Sub(startBatch).Seconds()

	mu.Lock()
	defer mu.Unlock()
	glog.V(1).Infof("Sequencing group run completed in %.2f seconds: %v succeeded, %v failed, %v leaves integrated", d, successCount, len(logIDs)-successCount, leavesAdded)
}
<|endoftext|>