<commit_before>package daemon

import (
	"context"
	"fmt"
	"math/rand"
	"net"
	"sync/atomic"
	"time"

	"golang.org/x/net/ipv4"
	"golang.org/x/sys/unix"
	"google.golang.org/grpc"

	"github.com/datawire/dlib/dgroup"
	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/rpc/v2/daemon"
	"github.com/telepresenceio/telepresence/rpc/v2/manager"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/connpool"
	"github.com/telepresenceio/telepresence/v2/pkg/iputil"
	"github.com/telepresenceio/telepresence/v2/pkg/tun"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/buffer"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/icmp"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/ip"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/tcp"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/udp"
)

// tunRouter is a router for outbound traffic that is centered around a TUN device. It's similar to a
// TUN-to-SOCKS5 but uses a bidirectional gRPC tunnel instead of SOCKS when communicating with the
// traffic-manager. The addresses of the device are derived from IP addresses sent to it from the user
// daemon (which in turn receives them from the cluster).
//
// Data sent to the device is received as L3 IP-packages and parsed into L4 UDP and TCP before they
// are dispatched over the tunnel. Returned payloads are wrapped as IP-packages before written
// back to the device.
//
// Connection pooling:
//
// For UDP and TCP packages, a ConnID is created which uniquely identifies a combination of protocol,
// source IP, source port, destination IP, and destination port. A handler is then obtained that matches
// that ID (active handlers are cached in a connpool.Pool) and the package is then sent to that handler.
// The handler typically sends the ConnID and the payload of the package over to the traffic-manager
// using the gRPC ConnTunnel. At the receiving en din the traffic-manager, a similar connpool.Pool obtains
// a corresponding handler which manages a net.Conn matching the ConnID in the cluster.
//
// Negotiation:
//
// UDP is of course very simple. It's fire and forget. There's no negotiation whatsoever.
//
// TCP requires a complete workflow engine on the TUN-device side (see tcp.Handler). All TCP negotiation,
// takes place in the client and the same bidirectional tunnel is then used to send both TCP and UDP
// packages to the manager. TCP will send some control packages. One to verify that a connection can
// be established at the manager side, and one when the connection is closed (from either side).
type tunRouter struct {
	// dev is the TUN device that gets configured with the subnets found in the cluster
	dev *tun.Device

	// managerClient provides the gRPC tunnel to the traffic-manager
	managerClient manager.ManagerClient

	// connStream is the bidirectional gRPC tunnel to the traffic-manager
	connStream *connpool.Stream

	// connPool contains handlers that represent active connections. Those handlers
	// are obtained using a connpool.ConnID.
	handlers *connpool.Pool

	// toTunCh  is where handlers post packages intended to be written to the TUN device
	toTunCh chan ip.Packet

	// fragmentMap is when concatenating ipv4 fragments
	fragmentMap map[uint16][]*buffer.Data

	// dnsIP is the IP of the DNS server attached to the TUN device. This is currently only
	// used in conjunction with systemd.resolved. The current MacOS and the overriding solution
	// will dispatch directly to the local DNS service without going through the TUN device but
	// that may change later if we decide to dispatch to the DNS-server in the cluster.
	dnsIP   net.IP
	dnsPort uint16

	// dnsLocalAddr is the address of the local DNS server
	dnsLocalAddr *net.UDPAddr

	// closing is set during shutdown and can have the values:
	//   0 = running
	//   1 = closing
	//   2 = closed
	closing int32

	// session contains the manager session
	session *manager.SessionInfo

	// mgrConfigured will be closed as soon as the connector has sent over the correct port to
	// the traffic manager and the managerClient has been connected.
	mgrConfigured <-chan struct{}

	// rndSource is the source for the random number generator in the TCP handlers
	rndSource rand.Source
}

func newTunRouter(managerConfigured <-chan struct{}) (*tunRouter, error) {
	td, err := tun.OpenTun()
	if err != nil {
		return nil, err
	}
	return &tunRouter{
		dev:           td,
		handlers:      connpool.NewPool(),
		toTunCh:       make(chan ip.Packet, 100),
		mgrConfigured: managerConfigured,
		fragmentMap:   make(map[uint16][]*buffer.Data),
		rndSource:     rand.NewSource(time.Now().UnixNano()),
	}, nil
}

func (t *tunRouter) configureDNS(_ context.Context, dnsIP net.IP, dnsPort uint16, dnsLocalAddr *net.UDPAddr) error {
	t.dnsIP = dnsIP
	t.dnsPort = dnsPort
	t.dnsLocalAddr = dnsLocalAddr
	return nil
}

func (t *tunRouter) setOutboundInfo(ctx context.Context, mi *daemon.OutboundInfo) (err error) {
	if t.managerClient == nil {
		// First check. Establish connection
		tos := &client.GetConfig(ctx).Timeouts
		tc, cancel := context.WithTimeout(ctx, tos.TrafficManagerAPI)
		defer cancel()

		var conn *grpc.ClientConn
		conn, err = grpc.DialContext(tc, fmt.Sprintf("127.0.0.1:%d", mi.ManagerPort),
			grpc.WithInsecure(),
			grpc.WithNoProxy(),
			grpc.WithBlock())
		if err != nil {
			return client.CheckTimeout(tc, &tos.TrafficManagerAPI, err)
		}
		t.session = mi.Session
		t.managerClient = manager.NewManagerClient(conn)

		cidr := iputil.IPNetFromRPC(mi.ServiceSubnet)
		dlog.Infof(ctx, "Adding service subnet %s", cidr)
		if err = t.dev.AddSubnet(ctx, cidr); err != nil {
			return err
		}

		for _, sn := range mi.PodSubnets {
			cidr = iputil.IPNetFromRPC(sn)
			dlog.Infof(ctx, "Adding pod subnet %s", cidr)
			if err = t.dev.AddSubnet(ctx, cidr); err != nil {
				return err
			}
		}
	}
	return nil
}

func (t *tunRouter) stop(c context.Context) {
	cc, cancel := context.WithTimeout(c, time.Second)
	defer cancel()
	go func() {
		atomic.StoreInt32(&t.closing, 1)
		t.handlers.CloseAll(cc)
		cancel()
	}()
	<-cc.Done()
	atomic.StoreInt32(&t.closing, 2)
	t.dev.Close()
}

var blockedUDPPorts = map[uint16]bool{
	137: true, // NETBIOS Name Service
	138: true, // NETBIOS Datagram Service
	139: true, // NETBIOS
}

func (t *tunRouter) run(c context.Context) error {
	g := dgroup.NewGroup(c, dgroup.GroupConfig{})

	// writer
	g.Go("TUN writer", func(c context.Context) error {
		for atomic.LoadInt32(&t.closing) < 2 {
			select {
			case <-c.Done():
				return nil
			case pkt := <-t.toTunCh:
				dlog.Debugf(c, "-> TUN %s", pkt)
				_, err := t.dev.WritePacket(pkt.Data())
				pkt.SoftRelease()
				if err != nil {
					if atomic.LoadInt32(&t.closing) == 2 || c.Err() != nil {
						err = nil
					}
					return err
				}
			}
		}
		return nil
	})

	g.Go("MGR stream", func(c context.Context) error {
		dlog.Debug(c, "Waiting until manager gRPC is configured")
		select {
		case <-c.Done():
			return nil
		case <-t.mgrConfigured:
		}

		tunnel, err := t.managerClient.ConnTunnel(c)
		if err != nil {
			return err
		}
		if err = tunnel.Send(connpool.SessionInfoControl(t.session).TunnelMessage()); err != nil {
			return err
		}
		t.connStream = connpool.NewStream(tunnel)
		dlog.Debug(c, "MGR read loop starting")
		return t.connStream.DialLoop(c, &t.closing, t.handlers)
	})

	g.Go("TUN reader", func(c context.Context) error {
		dlog.Debug(c, "Waiting until manager gRPC is configured")
		select {
		case <-c.Done():
			return nil
		case <-t.mgrConfigured:
		}

		dlog.Debug(c, "TUN read loop starting")
		for atomic.LoadInt32(&t.closing) < 2 {
			data := buffer.DataPool.Get(buffer.DataPool.MTU)
			for {
				n, err := t.dev.ReadPacket(data)
				if err != nil {
					buffer.DataPool.Put(data)
					if c.Err() != nil || atomic.LoadInt32(&t.closing) == 2 {
						return nil
					}
					return fmt.Errorf("read packet error: %w", err)
				}
				if n > 0 {
					data.SetLength(n)
					break
				}
			}
			t.handlePacket(c, data)
		}
		return nil
	})
	return g.Wait()
}

func (t *tunRouter) handlePacket(c context.Context, data *buffer.Data) {
	defer func() {
		if data != nil {
			buffer.DataPool.Put(data)
		}
	}()

	ipHdr, err := ip.ParseHeader(data.Buf())
	if err != nil {
		dlog.Error(c, "Unable to parse package header")
		return
	}

	if ipHdr.PayloadLen() > buffer.DataPool.MTU-ipHdr.HeaderLen() {
		// Package is too large for us.
		t.toTunCh <- icmp.DestinationUnreachablePacket(ipHdr, icmp.MustFragment)
		return
	}

	if ipHdr.Version() == ipv4.Version {
		v4Hdr := ipHdr.(ip.V4Header)
		if v4Hdr.Flags()&ipv4.MoreFragments != 0 || v4Hdr.FragmentOffset() != 0 {
			data = v4Hdr.ConcatFragments(data, t.fragmentMap)
			if data == nil {
				return
			}
			v4Hdr = data.Buf()
		}
	} // TODO: similar for ipv6 using segments

	switch ipHdr.L4Protocol() {
	case unix.IPPROTO_TCP:
		t.tcp(c, tcp.PacketFromData(ipHdr, data))
		data = nil
	case unix.IPPROTO_UDP:
		dst := ipHdr.Destination()
		if dst.IsLinkLocalUnicast() || dst.IsLinkLocalMulticast() {
			// Just ignore at this point.
			return
		}
		if ip4 := dst.To4(); ip4 != nil && ip4[2] == 0 && ip4[3] == 0 {
			// Write to the a subnet's zero address. Not sure why this is happening but there's no point in
			// passing them on.
			t.toTunCh <- icmp.DestinationUnreachablePacket(ipHdr, icmp.HostUnreachable)
			return
		}
		dg := udp.DatagramFromData(ipHdr, data)
		if blockedUDPPorts[dg.Header().SourcePort()] || blockedUDPPorts[dg.Header().DestinationPort()] {
			t.toTunCh <- icmp.DestinationUnreachablePacket(ipHdr, icmp.PortUnreachable)
			return
		}
		data = nil
		t.udp(c, dg)
	case unix.IPPROTO_ICMP:
	case unix.IPPROTO_ICMPV6:
		pkt := icmp.PacketFromData(ipHdr, data)
		dlog.Debugf(c, "<- TUN %s", pkt)
	default:
		// An L4 protocol that we don't handle.
		t.toTunCh <- icmp.DestinationUnreachablePacket(ipHdr, icmp.ProtocolUnreachable)
	}
}

func (t *tunRouter) tcp(c context.Context, pkt tcp.Packet) {
	ipHdr := pkt.IPHeader()
	tcpHdr := pkt.Header()
	connID := connpool.NewConnID(unix.IPPROTO_TCP, ipHdr.Source(), ipHdr.Destination(), tcpHdr.SourcePort(), tcpHdr.DestinationPort())
	wf, _, err := t.handlers.Get(c, connID, func(c context.Context, remove func()) (connpool.Handler, error) {
		return tcp.NewHandler(t.connStream, &t.closing, t.toTunCh, connID, remove, t.rndSource), nil
	})
	if err != nil {
		dlog.Error(c, err)
		return
	}
	wf.(tcp.PacketHandler).HandlePacket(c, pkt)
}

func (t *tunRouter) udp(c context.Context, dg udp.Datagram) {
	ipHdr := dg.IPHeader()
	udpHdr := dg.Header()
	connID := connpool.NewConnID(unix.IPPROTO_UDP, ipHdr.Source(), ipHdr.Destination(), udpHdr.SourcePort(), udpHdr.DestinationPort())
	uh, _, err := t.handlers.Get(c, connID, func(c context.Context, remove func()) (connpool.Handler, error) {
		if udpHdr.DestinationPort() == t.dnsPort && ipHdr.Destination().Equal(t.dnsIP) {
			return udp.NewDnsInterceptor(t.connStream, t.toTunCh, connID, remove, t.dnsLocalAddr)
		}
		return udp.NewHandler(t.connStream, t.toTunCh, connID, remove), nil
	})
	if err != nil {
		dlog.Error(c, err)
		return
	}
	uh.(udp.DatagramHandler).NewDatagram(c, dg)
}
<commit_msg>Ignore packages sent to TUN that aren't relevant.<commit_after>package daemon

import (
	"context"
	"fmt"
	"math/rand"
	"net"
	"sync/atomic"
	"time"

	"golang.org/x/net/ipv4"
	"golang.org/x/sys/unix"
	"google.golang.org/grpc"

	"github.com/datawire/dlib/dgroup"
	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/rpc/v2/daemon"
	"github.com/telepresenceio/telepresence/rpc/v2/manager"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/connpool"
	"github.com/telepresenceio/telepresence/v2/pkg/iputil"
	"github.com/telepresenceio/telepresence/v2/pkg/tun"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/buffer"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/icmp"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/ip"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/tcp"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/udp"
)

// tunRouter is a router for outbound traffic that is centered around a TUN device. It's similar to a
// TUN-to-SOCKS5 but uses a bidirectional gRPC tunnel instead of SOCKS when communicating with the
// traffic-manager. The addresses of the device are derived from IP addresses sent to it from the user
// daemon (which in turn receives them from the cluster).
//
// Data sent to the device is received as L3 IP-packages and parsed into L4 UDP and TCP before they
// are dispatched over the tunnel. Returned payloads are wrapped as IP-packages before written
// back to the device.
//
// Connection pooling:
//
// For UDP and TCP packages, a ConnID is created which uniquely identifies a combination of protocol,
// source IP, source port, destination IP, and destination port. A handler is then obtained that matches
// that ID (active handlers are cached in a connpool.Pool) and the package is then sent to that handler.
// The handler typically sends the ConnID and the payload of the package over to the traffic-manager
// using the gRPC ConnTunnel. At the receiving en din the traffic-manager, a similar connpool.Pool obtains
// a corresponding handler which manages a net.Conn matching the ConnID in the cluster.
//
// Negotiation:
//
// UDP is of course very simple. It's fire and forget. There's no negotiation whatsoever.
//
// TCP requires a complete workflow engine on the TUN-device side (see tcp.Handler). All TCP negotiation,
// takes place in the client and the same bidirectional tunnel is then used to send both TCP and UDP
// packages to the manager. TCP will send some control packages. One to verify that a connection can
// be established at the manager side, and one when the connection is closed (from either side).
type tunRouter struct {
	// dev is the TUN device that gets configured with the subnets found in the cluster
	dev *tun.Device

	// managerClient provides the gRPC tunnel to the traffic-manager
	managerClient manager.ManagerClient

	// connStream is the bidirectional gRPC tunnel to the traffic-manager
	connStream *connpool.Stream

	// connPool contains handlers that represent active connections. Those handlers
	// are obtained using a connpool.ConnID.
	handlers *connpool.Pool

	// toTunCh  is where handlers post packages intended to be written to the TUN device
	toTunCh chan ip.Packet

	// fragmentMap is when concatenating ipv4 fragments
	fragmentMap map[uint16][]*buffer.Data

	// dnsIP is the IP of the DNS server attached to the TUN device. This is currently only
	// used in conjunction with systemd.resolved. The current MacOS and the overriding solution
	// will dispatch directly to the local DNS service without going through the TUN device but
	// that may change later if we decide to dispatch to the DNS-server in the cluster.
	dnsIP   net.IP
	dnsPort uint16

	// dnsLocalAddr is the address of the local DNS server
	dnsLocalAddr *net.UDPAddr

	// closing is set during shutdown and can have the values:
	//   0 = running
	//   1 = closing
	//   2 = closed
	closing int32

	// session contains the manager session
	session *manager.SessionInfo

	// mgrConfigured will be closed as soon as the connector has sent over the correct port to
	// the traffic manager and the managerClient has been connected.
	mgrConfigured <-chan struct{}

	// rndSource is the source for the random number generator in the TCP handlers
	rndSource rand.Source
}

func newTunRouter(managerConfigured <-chan struct{}) (*tunRouter, error) {
	td, err := tun.OpenTun()
	if err != nil {
		return nil, err
	}
	return &tunRouter{
		dev:           td,
		handlers:      connpool.NewPool(),
		toTunCh:       make(chan ip.Packet, 100),
		mgrConfigured: managerConfigured,
		fragmentMap:   make(map[uint16][]*buffer.Data),
		rndSource:     rand.NewSource(time.Now().UnixNano()),
	}, nil
}

func (t *tunRouter) configureDNS(_ context.Context, dnsIP net.IP, dnsPort uint16, dnsLocalAddr *net.UDPAddr) error {
	t.dnsIP = dnsIP
	t.dnsPort = dnsPort
	t.dnsLocalAddr = dnsLocalAddr
	return nil
}

func (t *tunRouter) setOutboundInfo(ctx context.Context, mi *daemon.OutboundInfo) (err error) {
	if t.managerClient == nil {
		// First check. Establish connection
		tos := &client.GetConfig(ctx).Timeouts
		tc, cancel := context.WithTimeout(ctx, tos.TrafficManagerAPI)
		defer cancel()

		var conn *grpc.ClientConn
		conn, err = grpc.DialContext(tc, fmt.Sprintf("127.0.0.1:%d", mi.ManagerPort),
			grpc.WithInsecure(),
			grpc.WithNoProxy(),
			grpc.WithBlock())
		if err != nil {
			return client.CheckTimeout(tc, &tos.TrafficManagerAPI, err)
		}
		t.session = mi.Session
		t.managerClient = manager.NewManagerClient(conn)

		cidr := iputil.IPNetFromRPC(mi.ServiceSubnet)
		dlog.Infof(ctx, "Adding service subnet %s", cidr)
		if err = t.dev.AddSubnet(ctx, cidr); err != nil {
			return err
		}

		for _, sn := range mi.PodSubnets {
			cidr = iputil.IPNetFromRPC(sn)
			dlog.Infof(ctx, "Adding pod subnet %s", cidr)
			if err = t.dev.AddSubnet(ctx, cidr); err != nil {
				return err
			}
		}
	}
	return nil
}

func (t *tunRouter) stop(c context.Context) {
	cc, cancel := context.WithTimeout(c, time.Second)
	defer cancel()
	go func() {
		atomic.StoreInt32(&t.closing, 1)
		t.handlers.CloseAll(cc)
		cancel()
	}()
	<-cc.Done()
	atomic.StoreInt32(&t.closing, 2)
	t.dev.Close()
}

var blockedUDPPorts = map[uint16]bool{
	137: true, // NETBIOS Name Service
	138: true, // NETBIOS Datagram Service
	139: true, // NETBIOS
}

func (t *tunRouter) run(c context.Context) error {
	g := dgroup.NewGroup(c, dgroup.GroupConfig{})

	// writer
	g.Go("TUN writer", func(c context.Context) error {
		for atomic.LoadInt32(&t.closing) < 2 {
			select {
			case <-c.Done():
				return nil
			case pkt := <-t.toTunCh:
				dlog.Debugf(c, "-> TUN %s", pkt)
				_, err := t.dev.WritePacket(pkt.Data())
				pkt.SoftRelease()
				if err != nil {
					if atomic.LoadInt32(&t.closing) == 2 || c.Err() != nil {
						err = nil
					}
					return err
				}
			}
		}
		return nil
	})

	g.Go("MGR stream", func(c context.Context) error {
		dlog.Debug(c, "Waiting until manager gRPC is configured")
		select {
		case <-c.Done():
			return nil
		case <-t.mgrConfigured:
		}

		tunnel, err := t.managerClient.ConnTunnel(c)
		if err != nil {
			return err
		}
		if err = tunnel.Send(connpool.SessionInfoControl(t.session).TunnelMessage()); err != nil {
			return err
		}
		t.connStream = connpool.NewStream(tunnel)
		dlog.Debug(c, "MGR read loop starting")
		return t.connStream.DialLoop(c, &t.closing, t.handlers)
	})

	g.Go("TUN reader", func(c context.Context) error {
		dlog.Debug(c, "Waiting until manager gRPC is configured")
		select {
		case <-c.Done():
			return nil
		case <-t.mgrConfigured:
		}

		dlog.Debug(c, "TUN read loop starting")
		for atomic.LoadInt32(&t.closing) < 2 {
			data := buffer.DataPool.Get(buffer.DataPool.MTU)
			for {
				n, err := t.dev.ReadPacket(data)
				if err != nil {
					buffer.DataPool.Put(data)
					if c.Err() != nil || atomic.LoadInt32(&t.closing) == 2 {
						return nil
					}
					return fmt.Errorf("read packet error: %w", err)
				}
				if n > 0 {
					data.SetLength(n)
					break
				}
			}
			t.handlePacket(c, data)
		}
		return nil
	})
	return g.Wait()
}

func (t *tunRouter) handlePacket(c context.Context, data *buffer.Data) {
	defer func() {
		if data != nil {
			buffer.DataPool.Put(data)
		}
	}()

	ipHdr, err := ip.ParseHeader(data.Buf())
	if err != nil {
		dlog.Error(c, "Unable to parse package header")
		return
	}

	if ipHdr.PayloadLen() > buffer.DataPool.MTU-ipHdr.HeaderLen() {
		// Package is too large for us.
		t.toTunCh <- icmp.DestinationUnreachablePacket(ipHdr, icmp.MustFragment)
		return
	}

	if ipHdr.Version() == ipv4.Version {
		v4Hdr := ipHdr.(ip.V4Header)
		if v4Hdr.Flags()&ipv4.MoreFragments != 0 || v4Hdr.FragmentOffset() != 0 {
			data = v4Hdr.ConcatFragments(data, t.fragmentMap)
			if data == nil {
				return
			}
			v4Hdr = data.Buf()
		}
	} // TODO: similar for ipv6 using segments

	switch ipHdr.L4Protocol() {
	case unix.IPPROTO_TCP:
		t.tcp(c, tcp.PacketFromData(ipHdr, data))
		data = nil
	case unix.IPPROTO_UDP:
		dst := ipHdr.Destination()
		if !dst.IsGlobalUnicast() {
			// Just ignore at this point.
			return
		}
		if ip4 := dst.To4(); ip4 != nil && ip4[2] == 0 && ip4[3] == 0 {
			// Write to the a subnet's zero address. Not sure why this is happening but there's no point in
			// passing them on.
			t.toTunCh <- icmp.DestinationUnreachablePacket(ipHdr, icmp.HostUnreachable)
			return
		}
		dg := udp.DatagramFromData(ipHdr, data)
		if blockedUDPPorts[dg.Header().SourcePort()] || blockedUDPPorts[dg.Header().DestinationPort()] {
			t.toTunCh <- icmp.DestinationUnreachablePacket(ipHdr, icmp.PortUnreachable)
			return
		}
		data = nil
		t.udp(c, dg)
	case unix.IPPROTO_ICMP:
	case unix.IPPROTO_ICMPV6:
		pkt := icmp.PacketFromData(ipHdr, data)
		dlog.Debugf(c, "<- TUN %s", pkt)
	default:
		// An L4 protocol that we don't handle.
		t.toTunCh <- icmp.DestinationUnreachablePacket(ipHdr, icmp.ProtocolUnreachable)
	}
}

func (t *tunRouter) tcp(c context.Context, pkt tcp.Packet) {
	ipHdr := pkt.IPHeader()
	tcpHdr := pkt.Header()
	if tcpHdr.DestinationPort() == t.dnsPort && ipHdr.Destination().Equal(t.dnsIP) {
		// Ignore TCP packages intended for the DNS resolver for now
		// TODO: Add support to DNS over TCP. The github.com/miekg/dns can do that.
		return
	}

	connID := connpool.NewConnID(unix.IPPROTO_TCP, ipHdr.Source(), ipHdr.Destination(), tcpHdr.SourcePort(), tcpHdr.DestinationPort())
	wf, _, err := t.handlers.Get(c, connID, func(c context.Context, remove func()) (connpool.Handler, error) {
		return tcp.NewHandler(t.connStream, &t.closing, t.toTunCh, connID, remove, t.rndSource), nil
	})
	if err != nil {
		dlog.Error(c, err)
		return
	}
	wf.(tcp.PacketHandler).HandlePacket(c, pkt)
}

func (t *tunRouter) udp(c context.Context, dg udp.Datagram) {
	ipHdr := dg.IPHeader()
	udpHdr := dg.Header()
	connID := connpool.NewConnID(unix.IPPROTO_UDP, ipHdr.Source(), ipHdr.Destination(), udpHdr.SourcePort(), udpHdr.DestinationPort())
	uh, _, err := t.handlers.Get(c, connID, func(c context.Context, remove func()) (connpool.Handler, error) {
		if udpHdr.DestinationPort() == t.dnsPort && ipHdr.Destination().Equal(t.dnsIP) {
			return udp.NewDnsInterceptor(t.connStream, t.toTunCh, connID, remove, t.dnsLocalAddr)
		}
		return udp.NewHandler(t.connStream, t.toTunCh, connID, remove), nil
	})
	if err != nil {
		dlog.Error(c, err)
		return
	}
	uh.(udp.DatagramHandler).NewDatagram(c, dg)
}
<|endoftext|>


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

SPDX-License-Identifier: Apache-2.0
*/

package config

import (
	"bytes"
	"errors"
	"testing"

	"github.com/golang/protobuf/proto"
	cb "github.com/hyperledger/fabric-protos-go/common"
	mb "github.com/hyperledger/fabric-protos-go/msp"
	"github.com/hyperledger/fabric/common/tools/protolator"
	. "github.com/onsi/gomega"
)

func TestNewApplicationGroup(t *testing.T) {
	t.Parallel()

	gt := NewGomegaWithT(t)

	application := baseApplication()

	mspConfig := &mb.MSPConfig{}

	applicationGroup, err := newApplicationGroup(application, mspConfig)
	gt.Expect(err).NotTo(HaveOccurred())

	// ApplicationGroup checks
	gt.Expect(len(applicationGroup.Groups)).To(Equal(2))
	gt.Expect(applicationGroup.Groups["Org1"]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Values)).To(Equal(2))
	gt.Expect(applicationGroup.Values[ACLsKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Values[CapabilitiesKey]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Policies)).To(Equal(3))
	gt.Expect(applicationGroup.Policies[AdminsPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Policies[ReadersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Policies[WritersPolicyKey]).NotTo(BeNil())

	// ApplicationOrgGroup checks
	gt.Expect(len(applicationGroup.Groups["Org1"].Groups)).To(Equal(0))
	gt.Expect(len(applicationGroup.Groups["Org1"].Values)).To(Equal(2))
	gt.Expect(applicationGroup.Groups["Org1"].Values[MSPKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Values[AnchorPeersKey]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Groups["Org1"].Policies)).To(Equal(5))
	gt.Expect(applicationGroup.Groups["Org1"].Policies[AdminsPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Policies[ReadersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Policies[WritersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Policies[EndorsementPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Policies[LifecycleEndorsementPolicyKey]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Groups["Org2"].Groups)).To(Equal(0))
	gt.Expect(len(applicationGroup.Groups["Org2"].Values)).To(Equal(2))
	gt.Expect(applicationGroup.Groups["Org2"].Values[MSPKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Values[AnchorPeersKey]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Groups["Org2"].Policies)).To(Equal(5))
	gt.Expect(applicationGroup.Groups["Org2"].Policies[AdminsPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Policies[ReadersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Policies[WritersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Policies[EndorsementPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Policies[LifecycleEndorsementPolicyKey]).NotTo(BeNil())
}

func TestNewApplicationGroupFailure(t *testing.T) {
	t.Parallel()

	tests := []struct {
		testName       string
		applicationMod func(*Application)
		expectedErr    error
	}{
		{
			testName: "When application group policy is empty",
			applicationMod: func(a *Application) {
				a.Policies = nil
			},
			expectedErr: errors.New("no policies defined"),
		},
		{
			testName: "When adding policies to application group",
			applicationMod: func(a *Application) {
				a.Organizations[0].Policies = nil
			},
			expectedErr: errors.New("org group 'Org1': no policies defined"),
		},
	}

	for _, tt := range tests {
		tt := tt // capture range variable
		t.Run(tt.testName, func(t *testing.T) {
			t.Parallel()

			gt := NewGomegaWithT(t)

			application := baseApplication()
			tt.applicationMod(application)

			mspConfig := &mb.MSPConfig{}

			configGrp, err := newApplicationGroup(application, mspConfig)
			gt.Expect(err).To(MatchError(tt.expectedErr))
			gt.Expect(configGrp).To(BeNil())
		})
	}
}

func TestNewApplicationGroupSkipAsForeign(t *testing.T) {
	t.Parallel()

	gt := NewGomegaWithT(t)

	application := baseApplication()
	application.Organizations[0].SkipAsForeign = true
	application.Organizations[1].SkipAsForeign = true

	mspConfig := &mb.MSPConfig{}

	applicationGroup, err := newApplicationGroup(application, mspConfig)
	gt.Expect(err).NotTo(HaveOccurred())
	gt.Expect(applicationGroup.Groups["Org1"]).To(Equal(&cb.ConfigGroup{
		ModPolicy: AdminsPolicyKey,
		Groups:    make(map[string]*cb.ConfigGroup),
		Values:    make(map[string]*cb.ConfigValue),
		Policies:  make(map[string]*cb.ConfigPolicy),
	}))
	gt.Expect(applicationGroup.Groups["Org2"]).To(Equal(&cb.ConfigGroup{
		ModPolicy: AdminsPolicyKey,
		Groups:    make(map[string]*cb.ConfigGroup),
		Values:    make(map[string]*cb.ConfigValue),
		Policies:  make(map[string]*cb.ConfigPolicy),
	}))
}

func TestRemoveAnchorPeer(t *testing.T) {
	t.Parallel()

	gt := NewGomegaWithT(t)

	baseApplicationConf := baseApplication()

	applicationGroup, err := NewApplicationGroup(baseApplicationConf, &mb.MSPConfig{})
	gt.Expect(err).NotTo(HaveOccurred())

	config := &cb.Config{
		ChannelGroup: &cb.ConfigGroup{
			Groups: map[string]*cb.ConfigGroup{
				"Application": applicationGroup,
			},
		},
	}

	expectedUpdatedConfigJSON := `
{
	"channel_group": {
		"groups": {
			"Application": {
				"groups": {
					"Org1": {
						"groups": {},
						"mod_policy": "Admins",
						"policies": {
							"Admins": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Admins"
									}
								},
								"version": "0"
							},
							"Endorsement": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Endorsement"
									}
								},
								"version": "0"
							},
							"LifecycleEndorsement": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Endorsement"
									}
								},
								"version": "0"
							},
							"Readers": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "ANY",
									"sub_policy": "Readers"
									}
								},
								"version": "0"
							},
							"Writers": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "ANY",
									"sub_policy": "Writers"
									}
								},
								"version": "0"
							}
						},
						"values": {
							"AnchorPeers": {
								"mod_policy": "Admins",
								"value": {
									"anchor_peers": []
								},
								"version": "0"
							},
							"MSP": {
								"mod_policy": "Admins",
								"value": {
									"config": null,
									"type": 0
								},
								"version": "0"
							}
						},
						"version": "0"
					},
					"Org2": {
						"groups": {},
						"mod_policy": "Admins",
						"policies": {
							"Admins": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Admins"
									}
								},
								"version": "0"
							},
							"Endorsement": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Endorsement"
									}
								},
								"version": "0"
							},
							"LifecycleEndorsement": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Endorsement"
									}
								},
								"version": "0"
							},
							"Readers": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "ANY",
									"sub_policy": "Readers"
									}
								},
								"version": "0"
							},
							"Writers": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "ANY",
									"sub_policy": "Writers"
									}
								},
								"version": "0"
							}
						},
						"values": {
							"AnchorPeers": {
								"mod_policy": "Admins",
								"value": {
									"anchor_peers": []
								},
								"version": "0"
							},
							"MSP": {
								"mod_policy": "Admins",
								"value": {
									"config": null,
									"type": 0
								},
								"version": "0"
							}
						},
						"version": "0"
					}
				},
				"mod_policy": "Admins",
				"policies": {
					"Admins": {
						"mod_policy": "Admins",
						"policy": {
							"type": 3,
							"value": {
								"rule": "MAJORITY",
								"sub_policy": "Admins"
							}
						},
						"version": "0"
					},
					"Readers": {
						"mod_policy": "Admins",
						"policy": {
							"type": 3,
							"value": {
								"rule": "ANY",
								"sub_policy": "Readers"
							}
						},
						"version": "0"
					},
					"Writers": {
						"mod_policy": "Admins",
						"policy": {
							"type": 3,
							"value": {
								"rule": "ANY",
								"sub_policy": "Writers"
							}
						},
						"version": "0"
					}
				},
				"values": {
					"ACLs": {
						"mod_policy": "Admins",
						"value": {
							"acls": {
								"acl1": {
									"policy_ref": "hi"
								}
							}
						},
						"version": "0"
					},
					"Capabilities": {
						"mod_policy": "Admins",
						"value": {
							"capabilities": {
								"V1_3": {}
							}
						},
						"version": "0"
					}
				},
				"version": "0"
			}
		},
		"mod_policy": "",
		"policies": {},
		"values": {},
		"version": "0"
	},
	"sequence": "0"
}
	`

	expectedUpdatedConfig := &cb.Config{}

	err = protolator.DeepUnmarshalJSON(bytes.NewBufferString(expectedUpdatedConfigJSON), expectedUpdatedConfig)
	gt.Expect(err).ToNot(HaveOccurred())

	err = RemoveAnchorPeer(config, "Org1", baseApplicationConf.Organizations[0].AnchorPeers[0])
	gt.Expect(err).NotTo(HaveOccurred())

	err = RemoveAnchorPeer(config, "Org2", baseApplicationConf.Organizations[1].AnchorPeers[0])
	gt.Expect(err).NotTo(HaveOccurred())

	gt.Expect(proto.Equal(config, expectedUpdatedConfig)).To(BeTrue())
}

func TestRemoveAnchorPeerFailure(t *testing.T) {
	t.Parallel()

	tests := []struct {
		testName           string
		orgName            string
		anchorPeerToRemove *AnchorPeer
		expectedErr        string
	}{
		{
			testName:           "When the org for the application does not exist",
			orgName:            "BadOrg",
			anchorPeerToRemove: &AnchorPeer{Host: "host1", Port: 123},
			expectedErr:        "application org BadOrg does not exist in channel config",
		},
		{
			testName:           "When the anchor peer being removed doesn't exist in the org",
			orgName:            "Org1",
			anchorPeerToRemove: &AnchorPeer{Host: "host2", Port: 123},
			expectedErr:        "could not find anchor peer with host: host2, port: 123 in existing anchor peers",
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.testName, func(t *testing.T) {
			t.Parallel()

			gt := NewGomegaWithT(t)

			baseApplicationConf := baseApplication()

			applicationGroup, err := NewApplicationGroup(baseApplicationConf, &mb.MSPConfig{})
			gt.Expect(err).NotTo(HaveOccurred())

			config := &cb.Config{
				ChannelGroup: &cb.ConfigGroup{
					Groups: map[string]*cb.ConfigGroup{
						"Application": applicationGroup,
					},
				},
			}

			err = RemoveAnchorPeer(config, tt.orgName, tt.anchorPeerToRemove)
			gt.Expect(err).To(MatchError(tt.expectedErr))
		})
	}
}

func baseApplication() *Application {
	return &Application{
		Policies: standardPolicies(),
		Organizations: []*Organization{
			{
				Name:     "Org1",
				ID:       "Org1MSP",
				Policies: applicationOrgStandardPolicies(),
				AnchorPeers: []*AnchorPeer{
					{Host: "host1", Port: 123},
				},
			},
			{
				Name:     "Org2",
				ID:       "Org2MSP",
				Policies: applicationOrgStandardPolicies(),
				AnchorPeers: []*AnchorPeer{
					{Host: "host2", Port: 123},
				},
			},
		},
		Capabilities: map[string]bool{
			"V1_3": true,
		},
		ACLs: map[string]string{
			"acl1": "hi",
		},
	}
}
<commit_msg>Fix conflict in pkg/config test<commit_after>/*
Copyright IBM Corp All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package config

import (
	"bytes"
	"errors"
	"testing"

	"github.com/golang/protobuf/proto"
	cb "github.com/hyperledger/fabric-protos-go/common"
	mb "github.com/hyperledger/fabric-protos-go/msp"
	"github.com/hyperledger/fabric/common/tools/protolator"
	. "github.com/onsi/gomega"
)

func TestNewApplicationGroup(t *testing.T) {
	t.Parallel()

	gt := NewGomegaWithT(t)

	application := baseApplication()

	mspConfig := &mb.MSPConfig{}

	applicationGroup, err := newApplicationGroup(application, mspConfig)
	gt.Expect(err).NotTo(HaveOccurred())

	// ApplicationGroup checks
	gt.Expect(len(applicationGroup.Groups)).To(Equal(2))
	gt.Expect(applicationGroup.Groups["Org1"]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Values)).To(Equal(2))
	gt.Expect(applicationGroup.Values[ACLsKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Values[CapabilitiesKey]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Policies)).To(Equal(3))
	gt.Expect(applicationGroup.Policies[AdminsPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Policies[ReadersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Policies[WritersPolicyKey]).NotTo(BeNil())

	// ApplicationOrgGroup checks
	gt.Expect(len(applicationGroup.Groups["Org1"].Groups)).To(Equal(0))
	gt.Expect(len(applicationGroup.Groups["Org1"].Values)).To(Equal(2))
	gt.Expect(applicationGroup.Groups["Org1"].Values[MSPKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Values[AnchorPeersKey]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Groups["Org1"].Policies)).To(Equal(5))
	gt.Expect(applicationGroup.Groups["Org1"].Policies[AdminsPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Policies[ReadersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Policies[WritersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Policies[EndorsementPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org1"].Policies[LifecycleEndorsementPolicyKey]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Groups["Org2"].Groups)).To(Equal(0))
	gt.Expect(len(applicationGroup.Groups["Org2"].Values)).To(Equal(2))
	gt.Expect(applicationGroup.Groups["Org2"].Values[MSPKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Values[AnchorPeersKey]).NotTo(BeNil())
	gt.Expect(len(applicationGroup.Groups["Org2"].Policies)).To(Equal(5))
	gt.Expect(applicationGroup.Groups["Org2"].Policies[AdminsPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Policies[ReadersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Policies[WritersPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Policies[EndorsementPolicyKey]).NotTo(BeNil())
	gt.Expect(applicationGroup.Groups["Org2"].Policies[LifecycleEndorsementPolicyKey]).NotTo(BeNil())
}

func TestNewApplicationGroupFailure(t *testing.T) {
	t.Parallel()

	tests := []struct {
		testName       string
		applicationMod func(*Application)
		expectedErr    error
	}{
		{
			testName: "When application group policy is empty",
			applicationMod: func(a *Application) {
				a.Policies = nil
			},
			expectedErr: errors.New("no policies defined"),
		},
		{
			testName: "When adding policies to application group",
			applicationMod: func(a *Application) {
				a.Organizations[0].Policies = nil
			},
			expectedErr: errors.New("org group 'Org1': no policies defined"),
		},
	}

	for _, tt := range tests {
		tt := tt // capture range variable
		t.Run(tt.testName, func(t *testing.T) {
			t.Parallel()

			gt := NewGomegaWithT(t)

			application := baseApplication()
			tt.applicationMod(application)

			mspConfig := &mb.MSPConfig{}

			configGrp, err := newApplicationGroup(application, mspConfig)
			gt.Expect(err).To(MatchError(tt.expectedErr))
			gt.Expect(configGrp).To(BeNil())
		})
	}
}

func TestNewApplicationGroupSkipAsForeign(t *testing.T) {
	t.Parallel()

	gt := NewGomegaWithT(t)

	application := baseApplication()
	application.Organizations[0].SkipAsForeign = true
	application.Organizations[1].SkipAsForeign = true

	mspConfig := &mb.MSPConfig{}

	applicationGroup, err := newApplicationGroup(application, mspConfig)
	gt.Expect(err).NotTo(HaveOccurred())
	gt.Expect(applicationGroup.Groups["Org1"]).To(Equal(&cb.ConfigGroup{
		ModPolicy: AdminsPolicyKey,
		Groups:    make(map[string]*cb.ConfigGroup),
		Values:    make(map[string]*cb.ConfigValue),
		Policies:  make(map[string]*cb.ConfigPolicy),
	}))
	gt.Expect(applicationGroup.Groups["Org2"]).To(Equal(&cb.ConfigGroup{
		ModPolicy: AdminsPolicyKey,
		Groups:    make(map[string]*cb.ConfigGroup),
		Values:    make(map[string]*cb.ConfigValue),
		Policies:  make(map[string]*cb.ConfigPolicy),
	}))
}

func TestRemoveAnchorPeer(t *testing.T) {
	t.Parallel()

	gt := NewGomegaWithT(t)

	baseApplicationConf := baseApplication()

	applicationGroup, err := newApplicationGroup(baseApplicationConf, &mb.MSPConfig{})
	gt.Expect(err).NotTo(HaveOccurred())

	config := &cb.Config{
		ChannelGroup: &cb.ConfigGroup{
			Groups: map[string]*cb.ConfigGroup{
				"Application": applicationGroup,
			},
		},
	}

	expectedUpdatedConfigJSON := `
{
	"channel_group": {
		"groups": {
			"Application": {
				"groups": {
					"Org1": {
						"groups": {},
						"mod_policy": "Admins",
						"policies": {
							"Admins": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Admins"
									}
								},
								"version": "0"
							},
							"Endorsement": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Endorsement"
									}
								},
								"version": "0"
							},
							"LifecycleEndorsement": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Endorsement"
									}
								},
								"version": "0"
							},
							"Readers": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "ANY",
									"sub_policy": "Readers"
									}
								},
								"version": "0"
							},
							"Writers": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "ANY",
									"sub_policy": "Writers"
									}
								},
								"version": "0"
							}
						},
						"values": {
							"AnchorPeers": {
								"mod_policy": "Admins",
								"value": {
									"anchor_peers": []
								},
								"version": "0"
							},
							"MSP": {
								"mod_policy": "Admins",
								"value": {
									"config": null,
									"type": 0
								},
								"version": "0"
							}
						},
						"version": "0"
					},
					"Org2": {
						"groups": {},
						"mod_policy": "Admins",
						"policies": {
							"Admins": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Admins"
									}
								},
								"version": "0"
							},
							"Endorsement": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Endorsement"
									}
								},
								"version": "0"
							},
							"LifecycleEndorsement": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "MAJORITY",
									"sub_policy": "Endorsement"
									}
								},
								"version": "0"
							},
							"Readers": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "ANY",
									"sub_policy": "Readers"
									}
								},
								"version": "0"
							},
							"Writers": {
								"mod_policy": "Admins",
								"policy": {
									"type": 3,
									"value": {
									"rule": "ANY",
									"sub_policy": "Writers"
									}
								},
								"version": "0"
							}
						},
						"values": {
							"AnchorPeers": {
								"mod_policy": "Admins",
								"value": {
									"anchor_peers": []
								},
								"version": "0"
							},
							"MSP": {
								"mod_policy": "Admins",
								"value": {
									"config": null,
									"type": 0
								},
								"version": "0"
							}
						},
						"version": "0"
					}
				},
				"mod_policy": "Admins",
				"policies": {
					"Admins": {
						"mod_policy": "Admins",
						"policy": {
							"type": 3,
							"value": {
								"rule": "MAJORITY",
								"sub_policy": "Admins"
							}
						},
						"version": "0"
					},
					"Readers": {
						"mod_policy": "Admins",
						"policy": {
							"type": 3,
							"value": {
								"rule": "ANY",
								"sub_policy": "Readers"
							}
						},
						"version": "0"
					},
					"Writers": {
						"mod_policy": "Admins",
						"policy": {
							"type": 3,
							"value": {
								"rule": "ANY",
								"sub_policy": "Writers"
							}
						},
						"version": "0"
					}
				},
				"values": {
					"ACLs": {
						"mod_policy": "Admins",
						"value": {
							"acls": {
								"acl1": {
									"policy_ref": "hi"
								}
							}
						},
						"version": "0"
					},
					"Capabilities": {
						"mod_policy": "Admins",
						"value": {
							"capabilities": {
								"V1_3": {}
							}
						},
						"version": "0"
					}
				},
				"version": "0"
			}
		},
		"mod_policy": "",
		"policies": {},
		"values": {},
		"version": "0"
	},
	"sequence": "0"
}
	`

	expectedUpdatedConfig := &cb.Config{}

	err = protolator.DeepUnmarshalJSON(bytes.NewBufferString(expectedUpdatedConfigJSON), expectedUpdatedConfig)
	gt.Expect(err).ToNot(HaveOccurred())

	err = RemoveAnchorPeer(config, "Org1", baseApplicationConf.Organizations[0].AnchorPeers[0])
	gt.Expect(err).NotTo(HaveOccurred())

	err = RemoveAnchorPeer(config, "Org2", baseApplicationConf.Organizations[1].AnchorPeers[0])
	gt.Expect(err).NotTo(HaveOccurred())

	gt.Expect(proto.Equal(config, expectedUpdatedConfig)).To(BeTrue())
}

func TestRemoveAnchorPeerFailure(t *testing.T) {
	t.Parallel()

	tests := []struct {
		testName           string
		orgName            string
		anchorPeerToRemove *AnchorPeer
		expectedErr        string
	}{
		{
			testName:           "When the org for the application does not exist",
			orgName:            "BadOrg",
			anchorPeerToRemove: &AnchorPeer{Host: "host1", Port: 123},
			expectedErr:        "application org BadOrg does not exist in channel config",
		},
		{
			testName:           "When the anchor peer being removed doesn't exist in the org",
			orgName:            "Org1",
			anchorPeerToRemove: &AnchorPeer{Host: "host2", Port: 123},
			expectedErr:        "could not find anchor peer with host: host2, port: 123 in existing anchor peers",
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.testName, func(t *testing.T) {
			t.Parallel()

			gt := NewGomegaWithT(t)

			baseApplicationConf := baseApplication()

			applicationGroup, err := newApplicationGroup(baseApplicationConf, &mb.MSPConfig{})
			gt.Expect(err).NotTo(HaveOccurred())

			config := &cb.Config{
				ChannelGroup: &cb.ConfigGroup{
					Groups: map[string]*cb.ConfigGroup{
						"Application": applicationGroup,
					},
				},
			}

			err = RemoveAnchorPeer(config, tt.orgName, tt.anchorPeerToRemove)
			gt.Expect(err).To(MatchError(tt.expectedErr))
		})
	}
}

func baseApplication() *Application {
	return &Application{
		Policies: standardPolicies(),
		Organizations: []*Organization{
			{
				Name:     "Org1",
				ID:       "Org1MSP",
				Policies: applicationOrgStandardPolicies(),
				AnchorPeers: []*AnchorPeer{
					{Host: "host1", Port: 123},
				},
			},
			{
				Name:     "Org2",
				ID:       "Org2MSP",
				Policies: applicationOrgStandardPolicies(),
				AnchorPeers: []*AnchorPeer{
					{Host: "host2", Port: 123},
				},
			},
		},
		Capabilities: map[string]bool{
			"V1_3": true,
		},
		ACLs: map[string]string{
			"acl1": "hi",
		},
	}
}
<|endoftext|>


<commit_before>package rule

import (
	"io"
	"strings"
)

// DefaultRuleSet is the list of rules that are built into the inspector
const defaultRuleSet = `---
# Python 2.5+ is installed on all nodes
# This is required by ansible
- kind: Python2Version
  when: []
  supportedVersions:
   - Python 2.5
   - Python 2.6
   - Python 2.7

# Executables required by kubelet
- kind: ExecutableInPath
  when: ["master","worker"]
  executable: iptables
- kind: ExecutableInPath
  when: ["master","worker"]
  executable: iptables-save
- kind: ExecutableInPath
  when: ["master","worker"]
  executable: iptables-restore

# Ports used by etcd are available
- kind: TCPPortAvailable
  when: ["etcd"]
  port: 2379
- kind: TCPPortAvailable
  when: ["etcd"]
  port: 6666
- kind: TCPPortAvailable
  when: ["etcd"]
  port: 2380
- kind: TCPPortAvailable
  when: ["etcd"]
  port: 6660

# Ports used by etcd are accessible
- kind: TCPPortAccessible
  when: ["etcd"]
  port: 2379
  timeout: 5s
- kind: TCPPortAccessible
  when: ["etcd"]
  port: 6666
  timeout: 5s
- kind: TCPPortAccessible
  when: ["etcd"]
  port: 2380
  timeout: 5s
- kind: TCPPortAccessible
  when: ["etcd"]
  port: 6660
  timeout: 5s

# Ports used by K8s master are available
- kind: TCPPortAvailable
  when: ["master"]
  port: 6443
- kind: TCPPortAvailable
  when: ["master"]
  port: 8080

# Ports used by K8s master are accessible
# Port 8080 is not accessible from outside
- kind: TCPPortAccessible
  when: ["master"]
  port: 6443
  timeout: 5s

# Port used by Docker registry
- kind: TCPPortAvailable
  when: ["master"]
  port: 8443
- kind: TCPPortAccessible
  when: ["master"]
  port: 8443
  timeout: 5s

# Port used by Ingress
- kind: TCPPortAvailable
  when: ["ingress"]
  port: 80
- kind: TCPPortAccessible
  when: ["ingress"]
  port: 80
  timeout: 5s
- kind: TCPPortAvailable
  when: ["ingress"]
  port: 443
- kind: TCPPortAccessible
  when: ["ingress"]
  port: 443
  timeout: 5s

- kind: PackageAvailable
  when: ["etcd", "ubuntu"]
  packageName: kismatic-etcd
  packageVersion: 1.5.1-3
- kind: PackageAvailable
  when: ["master","ubuntu"]
  packageName: kismatic-kubernetes-master
  packageVersion: 1.5.1-3
- kind: PackageAvailable
  when: ["worker","ubuntu"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1-3
- kind: PackageAvailable
  when: ["ingress","ubuntu"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1-3

- kind: PackageAvailable
  when: ["etcd", "centos"]
  packageName: kismatic-etcd
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["master","centos"]
  packageName: kismatic-kubernetes-master
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["worker","centos"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["ingress","centos"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1

- kind: PackageAvailable
  when: ["etcd", "rhel"]
  packageName: kismatic-etcd
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["master","rhel"]
  packageName: kismatic-kubernetes-master
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["worker","rhel"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["ingress","rhel"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1

# Gluster packages
- kind: PackageAvailable
  when: ["storage", "centos"]
  packageName: glusterfs-server
  packageVersion: 3.8.7-1.el7
- kind: PackageAvailable
  when: ["storage", "rhel"]
  packageName: glusterfs-server
  packageVersion: 3.8.7-1.el7
- kind: PackageAvailable
  when: ["storage", "ubuntu"]
  packageName: glusterfs-server
  packageVersion: 3.8.7-ubuntu1~xenial1

# Ports required for NFS
- kind: TCPPortAvailable
  when: ["storage"]
  port: 111
- kind: TCPPortAccessible
  when: ["storage"]
  port: 111
  timeout: 5s
- kind: TCPPortAvailable
  when: ["storage"]
  port: 2049
- kind: TCPPortAccessible
  when: ["storage"]
  port: 2049
  timeout: 5s
- kind: TCPPortAvailable
  when: ["storage"]
  port: 38465
- kind: TCPPortAccessible
  when: ["storage"]
  port: 38465
  timeout: 5s
- kind: TCPPortAvailable
  when: ["storage"]
  port: 38466
- kind: TCPPortAccessible
  when: ["storage"]
  port: 38466
  timeout: 5s
- kind: TCPPortAvailable
  when: ["storage"]
  port: 38467
- kind: TCPPortAccessible
  when: ["storage"]
  port: 38467
  timeout: 5s
`

// DefaultRules returns the list of rules that are built into the inspector
func DefaultRules() []Rule {
	rules, err := UnmarshalRulesYAML([]byte(defaultRuleSet))
	if err != nil {
		// The default rules should not contain errors
		// If they do, panic so that we catch them during tests
		panic(err)
	}
	return rules
}

// DumpDefaultRules writes the default rule set to a file
func DumpDefaultRules(writer io.Writer) error {
	_, err := io.Copy(writer, strings.NewReader(defaultRuleSet))
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Add worker pkg check for storage nodes<commit_after>package rule

import (
	"io"
	"strings"
)

// DefaultRuleSet is the list of rules that are built into the inspector
const defaultRuleSet = `---
# Python 2.5+ is installed on all nodes
# This is required by ansible
- kind: Python2Version
  when: []
  supportedVersions:
   - Python 2.5
   - Python 2.6
   - Python 2.7

# Executables required by kubelet
- kind: ExecutableInPath
  when: ["master","worker"]
  executable: iptables
- kind: ExecutableInPath
  when: ["master","worker"]
  executable: iptables-save
- kind: ExecutableInPath
  when: ["master","worker"]
  executable: iptables-restore

# Ports used by etcd are available
- kind: TCPPortAvailable
  when: ["etcd"]
  port: 2379
- kind: TCPPortAvailable
  when: ["etcd"]
  port: 6666
- kind: TCPPortAvailable
  when: ["etcd"]
  port: 2380
- kind: TCPPortAvailable
  when: ["etcd"]
  port: 6660

# Ports used by etcd are accessible
- kind: TCPPortAccessible
  when: ["etcd"]
  port: 2379
  timeout: 5s
- kind: TCPPortAccessible
  when: ["etcd"]
  port: 6666
  timeout: 5s
- kind: TCPPortAccessible
  when: ["etcd"]
  port: 2380
  timeout: 5s
- kind: TCPPortAccessible
  when: ["etcd"]
  port: 6660
  timeout: 5s

# Ports used by K8s master are available
- kind: TCPPortAvailable
  when: ["master"]
  port: 6443
- kind: TCPPortAvailable
  when: ["master"]
  port: 8080

# Ports used by K8s master are accessible
# Port 8080 is not accessible from outside
- kind: TCPPortAccessible
  when: ["master"]
  port: 6443
  timeout: 5s

# Port used by Docker registry
- kind: TCPPortAvailable
  when: ["master"]
  port: 8443
- kind: TCPPortAccessible
  when: ["master"]
  port: 8443
  timeout: 5s

# Port used by Ingress
- kind: TCPPortAvailable
  when: ["ingress"]
  port: 80
- kind: TCPPortAccessible
  when: ["ingress"]
  port: 80
  timeout: 5s
- kind: TCPPortAvailable
  when: ["ingress"]
  port: 443
- kind: TCPPortAccessible
  when: ["ingress"]
  port: 443
  timeout: 5s

- kind: PackageAvailable
  when: ["etcd", "ubuntu"]
  packageName: kismatic-etcd
  packageVersion: 1.5.1-3
- kind: PackageAvailable
  when: ["master","ubuntu"]
  packageName: kismatic-kubernetes-master
  packageVersion: 1.5.1-3
- kind: PackageAvailable
  when: ["worker","ubuntu"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1-3
- kind: PackageAvailable
  when: ["ingress","ubuntu"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1-3
- kind: PackageAvailable
  when: ["storage","ubuntu"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1-3

- kind: PackageAvailable
  when: ["etcd", "centos"]
  packageName: kismatic-etcd
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["master","centos"]
  packageName: kismatic-kubernetes-master
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["worker","centos"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["ingress","centos"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["storage","centos"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1

- kind: PackageAvailable
  when: ["etcd", "rhel"]
  packageName: kismatic-etcd
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["master","rhel"]
  packageName: kismatic-kubernetes-master
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["worker","rhel"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["ingress","rhel"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1
- kind: PackageAvailable
  when: ["storage","rhel"]
  packageName: kismatic-kubernetes-node
  packageVersion: 1.5.1_3-1

# Gluster packages
- kind: PackageAvailable
  when: ["storage", "centos"]
  packageName: glusterfs-server
  packageVersion: 3.8.7-1.el7
- kind: PackageAvailable
  when: ["storage", "rhel"]
  packageName: glusterfs-server
  packageVersion: 3.8.7-1.el7
- kind: PackageAvailable
  when: ["storage", "ubuntu"]
  packageName: glusterfs-server
  packageVersion: 3.8.7-ubuntu1~xenial1

# Ports required for NFS
- kind: TCPPortAvailable
  when: ["storage"]
  port: 111
- kind: TCPPortAccessible
  when: ["storage"]
  port: 111
  timeout: 5s
- kind: TCPPortAvailable
  when: ["storage"]
  port: 2049
- kind: TCPPortAccessible
  when: ["storage"]
  port: 2049
  timeout: 5s
- kind: TCPPortAvailable
  when: ["storage"]
  port: 38465
- kind: TCPPortAccessible
  when: ["storage"]
  port: 38465
  timeout: 5s
- kind: TCPPortAvailable
  when: ["storage"]
  port: 38466
- kind: TCPPortAccessible
  when: ["storage"]
  port: 38466
  timeout: 5s
- kind: TCPPortAvailable
  when: ["storage"]
  port: 38467
- kind: TCPPortAccessible
  when: ["storage"]
  port: 38467
  timeout: 5s
`

// DefaultRules returns the list of rules that are built into the inspector
func DefaultRules() []Rule {
	rules, err := UnmarshalRulesYAML([]byte(defaultRuleSet))
	if err != nil {
		// The default rules should not contain errors
		// If they do, panic so that we catch them during tests
		panic(err)
	}
	return rules
}

// DumpDefaultRules writes the default rule set to a file
func DumpDefaultRules(writer io.Writer) error {
	_, err := io.Copy(writer, strings.NewReader(defaultRuleSet))
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package jwt

var signingMethods = map[string]func() SigningMethod{}

// Implement SigningMethod to add new methods for signing or verifying tokens.
type SigningMethod interface {
	Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid
	Sign(signingString string, key interface{}) (string, error)    // Returns encoded signature or error
	Alg() string                                                   // returns the alg identifier for this method (example: 'HS256')
}

// Register the "alg" name and a factory function for signing method.
// This is typically done during init() in the method's implementation
func RegisterSigningMethod(alg string, f func() SigningMethod) {
	signingMethods[alg] = f
}

// Get a signing method from an "alg" string
func GetSigningMethod(alg string) (method SigningMethod) {
	if methodF, ok := signingMethods[alg]; ok {
		method = methodF()
	}
	return
}
<commit_msg>mutex around signing method registration. shouldn't matter, but couldn't hurt<commit_after>package jwt

import (
	"sync"
)

var signingMethods = map[string]func() SigningMethod{}
var signingMethodLock = new(sync.RWMutex)

// Implement SigningMethod to add new methods for signing or verifying tokens.
type SigningMethod interface {
	Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid
	Sign(signingString string, key interface{}) (string, error)    // Returns encoded signature or error
	Alg() string                                                   // returns the alg identifier for this method (example: 'HS256')
}

// Register the "alg" name and a factory function for signing method.
// This is typically done during init() in the method's implementation
func RegisterSigningMethod(alg string, f func() SigningMethod) {
	signingMethodLock.Lock()
	defer signingMethodLock.Unlock()

	signingMethods[alg] = f
}

// Get a signing method from an "alg" string
func GetSigningMethod(alg string) (method SigningMethod) {
	signingMethodLock.RLock()
	defer signingMethodLock.RUnlock()

	if methodF, ok := signingMethods[alg]; ok {
		method = methodF()
	}
	return
}
<|endoftext|>


<commit_before>package sqlstore

import (
	"bytes"
	"fmt"
	"strings"
	"time"

	"github.com/go-xorm/xorm"
	"github.com/grafana/grafana/pkg/bus"
	m "github.com/grafana/grafana/pkg/models"
)

func init() {
	bus.AddHandler("sql", SaveAlerts)
	bus.AddHandler("sql", HandleAlertsQuery)
	bus.AddHandler("sql", GetAlertById)
	bus.AddHandler("sql", DeleteAlertById)
	bus.AddHandler("sql", GetAllAlertQueryHandler)
	bus.AddHandler("sql", SetAlertState)
	bus.AddHandler("sql", GetAlertStatesForDashboard)
	bus.AddHandler("sql", PauseAlert)
	bus.AddHandler("sql", PauseAllAlerts)
}

func GetAlertById(query *m.GetAlertByIdQuery) error {
	alert := m.Alert{}
	has, err := x.Id(query.Id).Get(&alert)
	if !has {
		return fmt.Errorf("could not find alert")
	}
	if err != nil {
		return err
	}

	query.Result = &alert
	return nil
}

func GetAllAlertQueryHandler(query *m.GetAllAlertsQuery) error {
	var alerts []*m.Alert
	err := x.Sql("select * from alert").Find(&alerts)
	if err != nil {
		return err
	}

	query.Result = alerts
	return nil
}

func deleteAlertByIdInternal(alertId int64, reason string, sess *xorm.Session) error {
	sqlog.Debug("Deleting alert", "id", alertId, "reason", reason)

	if _, err := sess.Exec("DELETE FROM alert WHERE id = ?", alertId); err != nil {
		return err
	}

	if _, err := sess.Exec("DELETE FROM annotation WHERE alert_id = ?", alertId); err != nil {
		return err
	}

	return nil
}

func DeleteAlertById(cmd *m.DeleteAlertCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		return deleteAlertByIdInternal(cmd.AlertId, "DeleteAlertCommand", sess)
	})
}

func HandleAlertsQuery(query *m.GetAlertsQuery) error {
	var sql bytes.Buffer
	params := make([]interface{}, 0)

	sql.WriteString(`SELECT *
						from alert
						`)

	sql.WriteString(`WHERE org_id = ?`)
	params = append(params, query.OrgId)

	if query.DashboardId != 0 {
		sql.WriteString(` AND dashboard_id = ?`)
		params = append(params, query.DashboardId)
	}

	if query.PanelId != 0 {
		sql.WriteString(` AND panel_id = ?`)
		params = append(params, query.PanelId)
	}

	if len(query.State) > 0 && query.State[0] != "ALL" {
		sql.WriteString(` AND (`)
		for i, v := range query.State {
			if i > 0 {
				sql.WriteString(" OR ")
			}
			sql.WriteString("state = ? ")
			params = append(params, v)
		}
		sql.WriteString(")")
	}

	if query.Limit != 0 {
		sql.WriteString(" LIMIT ?")
		params = append(params, query.Limit)
	}

	sql.WriteString(" ORDER BY name ASC")

	alerts := make([]*m.Alert, 0)
	if err := x.Sql(sql.String(), params...).Find(&alerts); err != nil {
		return err
	}

	for i := range alerts {
		if alerts[i].ExecutionError == " " {
			alerts[i].ExecutionError = ""
		}
	}

	query.Result = alerts
	return nil
}

func DeleteAlertDefinition(dashboardId int64, sess *xorm.Session) error {
	alerts := make([]*m.Alert, 0)
	sess.Where("dashboard_id = ?", dashboardId).Find(&alerts)

	for _, alert := range alerts {
		deleteAlertByIdInternal(alert.Id, "Dashboard deleted", sess)
	}

	return nil
}

func SaveAlerts(cmd *m.SaveAlertsCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		existingAlerts, err := GetAlertsByDashboardId2(cmd.DashboardId, sess)
		if err != nil {
			return err
		}

		if err := upsertAlerts(existingAlerts, cmd, sess); err != nil {
			return err
		}

		if err := deleteMissingAlerts(existingAlerts, cmd, sess); err != nil {
			return err
		}

		return nil
	})
}

func upsertAlerts(existingAlerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xorm.Session) error {
	for _, alert := range cmd.Alerts {
		update := false
		var alertToUpdate *m.Alert

		for _, k := range existingAlerts {
			if alert.PanelId == k.PanelId {
				update = true
				alert.Id = k.Id
				alertToUpdate = k
				break
			}
		}

		if update {
			if alertToUpdate.ContainsUpdates(alert) {
				alert.Updated = time.Now()
				alert.State = alertToUpdate.State
				_, err := sess.Id(alert.Id).Update(alert)
				if err != nil {
					return err
				}

				sqlog.Debug("Alert updated", "name", alert.Name, "id", alert.Id)
			}
		} else {
			alert.Updated = time.Now()
			alert.Created = time.Now()
			alert.State = m.AlertStatePending
			alert.NewStateDate = time.Now()

			_, err := sess.Insert(alert)
			if err != nil {
				return err
			}

			sqlog.Debug("Alert inserted", "name", alert.Name, "id", alert.Id)
		}
	}

	return nil
}

func deleteMissingAlerts(alerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xorm.Session) error {
	for _, missingAlert := range alerts {
		missing := true

		for _, k := range cmd.Alerts {
			if missingAlert.PanelId == k.PanelId {
				missing = false
				break
			}
		}

		if missing {
			deleteAlertByIdInternal(missingAlert.Id, "Removed from dashboard", sess)
		}
	}

	return nil
}

func GetAlertsByDashboardId2(dashboardId int64, sess *xorm.Session) ([]*m.Alert, error) {
	alerts := make([]*m.Alert, 0)
	err := sess.Where("dashboard_id = ?", dashboardId).Find(&alerts)

	if err != nil {
		return []*m.Alert{}, err
	}

	return alerts, nil
}

func SetAlertState(cmd *m.SetAlertStateCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		alert := m.Alert{}

		if has, err := sess.Id(cmd.AlertId).Get(&alert); err != nil {
			return err
		} else if !has {
			return fmt.Errorf("Could not find alert")
		}

		if alert.State == m.AlertStatePaused {
			return m.ErrCannotChangeStateOnPausedAlert
		}

		if alert.State == cmd.State {
			return m.ErrRequiresNewState
		}

		alert.State = cmd.State
		alert.StateChanges += 1
		alert.NewStateDate = time.Now()
		alert.EvalData = cmd.EvalData

		if cmd.Error == "" {
			alert.ExecutionError = " " //without this space, xorm skips updating this field
		} else {
			alert.ExecutionError = cmd.Error
		}

		sess.Id(alert.Id).Update(&alert)
		return nil
	})
}

func PauseAlert(cmd *m.PauseAlertCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		if len(cmd.AlertIds) == 0 {
			return fmt.Errorf("command contains no alertids")
		}

		var buffer bytes.Buffer
		params := make([]interface{}, 0)

		buffer.WriteString(`UPDATE alert SET state = ?`)
		if cmd.Paused {
			params = append(params, string(m.AlertStatePaused))
		} else {
			params = append(params, string(m.AlertStatePending))
		}

		buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`)
		for _, v := range cmd.AlertIds {
			params = append(params, v)
		}

		res, err := sess.Exec(buffer.String(), params...)
		if err != nil {
			return err
		}
		cmd.ResultCount, _ = res.RowsAffected()
		return nil
	})
}

func PauseAllAlerts(cmd *m.PauseAllAlertCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		var newState string
		if cmd.Paused {
			newState = string(m.AlertStatePaused)
		} else {
			newState = string(m.AlertStatePending)
		}

		res, err := sess.Exec(`UPDATE alert SET state = ?`, newState)
		if err != nil {
			return err
		}
		cmd.ResultCount, _ = res.RowsAffected()
		return nil
	})
}

func GetAlertStatesForDashboard(query *m.GetAlertStatesForDashboardQuery) error {
	var rawSql = `SELECT
	                id,
	                dashboard_id,
	                panel_id,
	                state,
	                new_state_date
	                FROM alert
	                WHERE org_id = ? AND dashboard_id = ?`

	query.Result = make([]*m.AlertStateInfoDTO, 0)
	err := x.Sql(rawSql, query.OrgId, query.DashboardId).Find(&query.Result)

	return err
}
<commit_msg>fix: minor fix for bug when saving alert with empty message, fixes #7927<commit_after>package sqlstore

import (
	"bytes"
	"fmt"
	"strings"
	"time"

	"github.com/go-xorm/xorm"
	"github.com/grafana/grafana/pkg/bus"
	m "github.com/grafana/grafana/pkg/models"
)

func init() {
	bus.AddHandler("sql", SaveAlerts)
	bus.AddHandler("sql", HandleAlertsQuery)
	bus.AddHandler("sql", GetAlertById)
	bus.AddHandler("sql", DeleteAlertById)
	bus.AddHandler("sql", GetAllAlertQueryHandler)
	bus.AddHandler("sql", SetAlertState)
	bus.AddHandler("sql", GetAlertStatesForDashboard)
	bus.AddHandler("sql", PauseAlert)
	bus.AddHandler("sql", PauseAllAlerts)
}

func GetAlertById(query *m.GetAlertByIdQuery) error {
	alert := m.Alert{}
	has, err := x.Id(query.Id).Get(&alert)
	if !has {
		return fmt.Errorf("could not find alert")
	}
	if err != nil {
		return err
	}

	query.Result = &alert
	return nil
}

func GetAllAlertQueryHandler(query *m.GetAllAlertsQuery) error {
	var alerts []*m.Alert
	err := x.Sql("select * from alert").Find(&alerts)
	if err != nil {
		return err
	}

	query.Result = alerts
	return nil
}

func deleteAlertByIdInternal(alertId int64, reason string, sess *xorm.Session) error {
	sqlog.Debug("Deleting alert", "id", alertId, "reason", reason)

	if _, err := sess.Exec("DELETE FROM alert WHERE id = ?", alertId); err != nil {
		return err
	}

	if _, err := sess.Exec("DELETE FROM annotation WHERE alert_id = ?", alertId); err != nil {
		return err
	}

	return nil
}

func DeleteAlertById(cmd *m.DeleteAlertCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		return deleteAlertByIdInternal(cmd.AlertId, "DeleteAlertCommand", sess)
	})
}

func HandleAlertsQuery(query *m.GetAlertsQuery) error {
	var sql bytes.Buffer
	params := make([]interface{}, 0)

	sql.WriteString(`SELECT *
						from alert
						`)

	sql.WriteString(`WHERE org_id = ?`)
	params = append(params, query.OrgId)

	if query.DashboardId != 0 {
		sql.WriteString(` AND dashboard_id = ?`)
		params = append(params, query.DashboardId)
	}

	if query.PanelId != 0 {
		sql.WriteString(` AND panel_id = ?`)
		params = append(params, query.PanelId)
	}

	if len(query.State) > 0 && query.State[0] != "ALL" {
		sql.WriteString(` AND (`)
		for i, v := range query.State {
			if i > 0 {
				sql.WriteString(" OR ")
			}
			sql.WriteString("state = ? ")
			params = append(params, v)
		}
		sql.WriteString(")")
	}

	if query.Limit != 0 {
		sql.WriteString(" LIMIT ?")
		params = append(params, query.Limit)
	}

	sql.WriteString(" ORDER BY name ASC")

	alerts := make([]*m.Alert, 0)
	if err := x.Sql(sql.String(), params...).Find(&alerts); err != nil {
		return err
	}

	for i := range alerts {
		if alerts[i].ExecutionError == " " {
			alerts[i].ExecutionError = ""
		}
	}

	query.Result = alerts
	return nil
}

func DeleteAlertDefinition(dashboardId int64, sess *xorm.Session) error {
	alerts := make([]*m.Alert, 0)
	sess.Where("dashboard_id = ?", dashboardId).Find(&alerts)

	for _, alert := range alerts {
		deleteAlertByIdInternal(alert.Id, "Dashboard deleted", sess)
	}

	return nil
}

func SaveAlerts(cmd *m.SaveAlertsCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		existingAlerts, err := GetAlertsByDashboardId2(cmd.DashboardId, sess)
		if err != nil {
			return err
		}

		if err := upsertAlerts(existingAlerts, cmd, sess); err != nil {
			return err
		}

		if err := deleteMissingAlerts(existingAlerts, cmd, sess); err != nil {
			return err
		}

		return nil
	})
}

func upsertAlerts(existingAlerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xorm.Session) error {
	for _, alert := range cmd.Alerts {
		update := false
		var alertToUpdate *m.Alert

		for _, k := range existingAlerts {
			if alert.PanelId == k.PanelId {
				update = true
				alert.Id = k.Id
				alertToUpdate = k
				break
			}
		}

		if update {
			if alertToUpdate.ContainsUpdates(alert) {
				alert.Updated = time.Now()
				alert.State = alertToUpdate.State
				sess.MustCols("message")
				_, err := sess.Id(alert.Id).Update(alert)
				if err != nil {
					return err
				}

				sqlog.Debug("Alert updated", "name", alert.Name, "id", alert.Id)
			}
		} else {
			alert.Updated = time.Now()
			alert.Created = time.Now()
			alert.State = m.AlertStatePending
			alert.NewStateDate = time.Now()

			_, err := sess.Insert(alert)
			if err != nil {
				return err
			}

			sqlog.Debug("Alert inserted", "name", alert.Name, "id", alert.Id)
		}
	}

	return nil
}

func deleteMissingAlerts(alerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xorm.Session) error {
	for _, missingAlert := range alerts {
		missing := true

		for _, k := range cmd.Alerts {
			if missingAlert.PanelId == k.PanelId {
				missing = false
				break
			}
		}

		if missing {
			deleteAlertByIdInternal(missingAlert.Id, "Removed from dashboard", sess)
		}
	}

	return nil
}

func GetAlertsByDashboardId2(dashboardId int64, sess *xorm.Session) ([]*m.Alert, error) {
	alerts := make([]*m.Alert, 0)
	err := sess.Where("dashboard_id = ?", dashboardId).Find(&alerts)

	if err != nil {
		return []*m.Alert{}, err
	}

	return alerts, nil
}

func SetAlertState(cmd *m.SetAlertStateCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		alert := m.Alert{}

		if has, err := sess.Id(cmd.AlertId).Get(&alert); err != nil {
			return err
		} else if !has {
			return fmt.Errorf("Could not find alert")
		}

		if alert.State == m.AlertStatePaused {
			return m.ErrCannotChangeStateOnPausedAlert
		}

		if alert.State == cmd.State {
			return m.ErrRequiresNewState
		}

		alert.State = cmd.State
		alert.StateChanges += 1
		alert.NewStateDate = time.Now()
		alert.EvalData = cmd.EvalData

		if cmd.Error == "" {
			alert.ExecutionError = " " //without this space, xorm skips updating this field
		} else {
			alert.ExecutionError = cmd.Error
		}

		sess.Id(alert.Id).Update(&alert)
		return nil
	})
}

func PauseAlert(cmd *m.PauseAlertCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		if len(cmd.AlertIds) == 0 {
			return fmt.Errorf("command contains no alertids")
		}

		var buffer bytes.Buffer
		params := make([]interface{}, 0)

		buffer.WriteString(`UPDATE alert SET state = ?`)
		if cmd.Paused {
			params = append(params, string(m.AlertStatePaused))
		} else {
			params = append(params, string(m.AlertStatePending))
		}

		buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`)
		for _, v := range cmd.AlertIds {
			params = append(params, v)
		}

		res, err := sess.Exec(buffer.String(), params...)
		if err != nil {
			return err
		}
		cmd.ResultCount, _ = res.RowsAffected()
		return nil
	})
}

func PauseAllAlerts(cmd *m.PauseAllAlertCommand) error {
	return inTransaction(func(sess *xorm.Session) error {
		var newState string
		if cmd.Paused {
			newState = string(m.AlertStatePaused)
		} else {
			newState = string(m.AlertStatePending)
		}

		res, err := sess.Exec(`UPDATE alert SET state = ?`, newState)
		if err != nil {
			return err
		}
		cmd.ResultCount, _ = res.RowsAffected()
		return nil
	})
}

func GetAlertStatesForDashboard(query *m.GetAlertStatesForDashboardQuery) error {
	var rawSql = `SELECT
	                id,
	                dashboard_id,
	                panel_id,
	                state,
	                new_state_date
	                FROM alert
	                WHERE org_id = ? AND dashboard_id = ?`

	query.Result = make([]*m.AlertStateInfoDTO, 0)
	err := x.Sql(rawSql, query.OrgId, query.DashboardId).Find(&query.Result)

	return err
}
<|endoftext|>


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

package parser

import (
	"bytes"
	"fmt"

	"github.com/pkg/errors"
)

// IndexedVarContainer provides the implementation of TypeCheck, Eval, and
// String for IndexedVars.
// If an object that wishes to implement this interface has lost the
// textual name that an IndexedVar originates from, it can use the
// ordinal column reference syntax: fmt.Fprintf(buf, "@%d", idx)
type IndexedVarContainer interface {
	IndexedVarEval(idx int, ctx *EvalContext) (Datum, error)
	IndexedVarResolvedType(idx int) Type
	IndexedVarFormat(buf *bytes.Buffer, f FmtFlags, idx int)
}

// IndexedVar is a VariableExpr that can be used as a leaf in expressions; it
// represents a dynamic value. It defers calls to TypeCheck, Eval, String to an
// IndexedVarContainer.
type IndexedVar struct {
	Idx       int
	container IndexedVarContainer
}

var _ TypedExpr = &IndexedVar{}
var _ VariableExpr = &IndexedVar{}

// Variable is a dummy function part of the VariableExpr interface.
func (*IndexedVar) Variable() {}

// Walk is part of the Expr interface.
func (v *IndexedVar) Walk(_ Visitor) Expr {
	return v
}

// TypeCheck is part of the Expr interface.
func (v *IndexedVar) TypeCheck(_ *SemaContext, desired Type) (TypedExpr, error) {
	if v.container == nil {
		// A more technically correct message would be to say that the
		// reference is unbound and thus cannot be typed. However this is
		// a tad bit too technical for the average SQL use case and
		// instead we acknowledge that we only get here if someone has
		// used a column reference in a place where it's not allowed by
		// the docs, so just say that instead.
		return nil, errors.Errorf("column reference %s not allowed in this context", v)
	}
	return v, nil
}

// Eval is part of the TypedExpr interface.
func (v *IndexedVar) Eval(ctx *EvalContext) (Datum, error) {
	if v.container == nil {
		panic("indexed var must be bound to a container before evaluation")
	}
	return v.container.IndexedVarEval(v.Idx, ctx)
}

// ResolvedType is part of the TypedExpr interface.
func (v *IndexedVar) ResolvedType() Type {
	if v.container == nil {
		panic("indexed var must be bound to a container before type resolution")
	}
	return v.container.IndexedVarResolvedType(v.Idx)
}

// Format implements the NodeFormatter interface.
func (v *IndexedVar) Format(buf *bytes.Buffer, f FmtFlags) {
	if f.indexedVarFormat != nil {
		f.indexedVarFormat(buf, f, v.container, v.Idx)
	} else if f.symbolicVars || v.container == nil {
		fmt.Fprintf(buf, "@%d", v.Idx+1)
	} else {
		v.container.IndexedVarFormat(buf, f, v.Idx)
	}
}

// NewOrdinalReference is a helper routine to create a standalone
// IndexedVar with the given index value. This needs to undergo
// BindIfUnbound() below before it can be fully used.
func NewOrdinalReference(r int) *IndexedVar {
	return &IndexedVar{Idx: r, container: nil}
}

// IndexedVarHelper is a structure that helps with initialization of IndexedVars.
type IndexedVarHelper struct {
	vars      []IndexedVar
	container IndexedVarContainer
}

// BindIfUnbound attaches an IndexedVar to an existing container.
// This is needed for standalone column ordinals created during parsing.
func (h *IndexedVarHelper) BindIfUnbound(ivar *IndexedVar) error {
	if ivar.container != nil {
		return nil
	}
	if ivar.Idx < 0 || ivar.Idx >= len(h.vars) {
		return errors.Errorf("invalid column ordinal: @%d", ivar.Idx+1)
	}
	// This container must also remember it has "seen" the variable
	// so that IndexedVarUsed() below returns the right results.
	// The IndexedVar() method ensures this.
	*ivar = *h.IndexedVar(ivar.Idx)
	return nil
}

// MakeIndexedVarHelper initializes an IndexedVarHelper structure.
func MakeIndexedVarHelper(container IndexedVarContainer, numVars int) IndexedVarHelper {
	return IndexedVarHelper{vars: make([]IndexedVar, numVars), container: container}
}

// AssertSameContainer checks that the indexed var refers to the same container.
func (h *IndexedVarHelper) AssertSameContainer(ivar *IndexedVar) {
	if ivar.container != h.container {
		panic(fmt.Sprintf("indexed var linked to different container (%T) %+v, expected (%T) %+v",
			ivar.container, ivar.container, h.container, h.container))
	}
}

func (h *IndexedVarHelper) checkIndex(idx int) {
	if idx < 0 || idx >= len(h.vars) {
		panic(fmt.Sprintf("invalid var index %d (columns: %d)", idx, len(h.vars)))
	}
}

// NumVars returns the number of variables the IndexedVarHelper was initialized
// for.
func (h *IndexedVarHelper) NumVars() int {
	return len(h.vars)
}

// IndexedVar returns an IndexedVar for the given index. The index must be
// valid.
func (h *IndexedVarHelper) IndexedVar(idx int) *IndexedVar {
	h.checkIndex(idx)
	v := &h.vars[idx]
	if v.container == nil {
		v.Idx = idx
		v.container = h.container
	}
	return v
}

// IndexedVarUsed returns true if IndexedVar() was called for the given index.
// The index must be valid.
func (h *IndexedVarHelper) IndexedVarUsed(idx int) bool {
	h.checkIndex(idx)
	return h.vars[idx].container != nil
}

// InvalidColIdx is the index value of a non-initialized IndexedVar.
const InvalidColIdx = -1

// GetIndexedVars transfers ownership of the array of initialized
// IndexedVars to the caller; unused vars are guaranteed to have an
// invalid index. The helper cannot be used any more after the
// ownership has been transferred.
func (h *IndexedVarHelper) GetIndexedVars() []IndexedVar {
	for i := range h.vars {
		if h.vars[i].container == nil {
			h.vars[i].Idx = InvalidColIdx
		}
	}
	ret := h.vars
	h.vars = nil
	return ret
}

// Reset re-initializes an IndexedVarHelper structure with the same
// number of slots. After a helper has been reset, all the expressions
// that were linked to the helper before it was reset must be
// re-bound, e.g. using Rebind(). Resetting is useful to ensure that
// the helper's knowledge of which IndexedVars are actually used by
// linked expressions is up to date, especially after
// optimizations/transforms which eliminate sub-expressions. The
// optimizations performed by setNeededColumns() work then best.
func (h *IndexedVarHelper) Reset() {
	h.vars = make([]IndexedVar, len(h.vars))
}

// Rebind collects all the IndexedVars in the given expression
// and re-binds them to this helper.
func (h *IndexedVarHelper) Rebind(expr TypedExpr, alsoReset, normalizeToNonNil bool) TypedExpr {
	if alsoReset {
		h.Reset()
	}
	if expr == nil || expr == DBoolTrue {
		if normalizeToNonNil {
			return DBoolTrue
		}
		return nil
	}
	ret, _ := WalkExpr(h, expr)
	return ret.(TypedExpr)
}

var _ Visitor = &IndexedVarHelper{}

// VisitPre implements the Visitor interface.
func (h *IndexedVarHelper) VisitPre(expr Expr) (recurse bool, newExpr Expr) {
	if iv, ok := expr.(*IndexedVar); ok {
		return false, h.IndexedVar(iv.Idx)
	}
	return true, expr
}

// VisitPost implements the Visitor interface.
func (*IndexedVarHelper) VisitPost(expr Expr) Expr { return expr }
<commit_msg>sql: add comment to IndexedVarHelper<commit_after>// Copyright 2016 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Radu Berinde (radu@cockroachlabs.com)

package parser

import (
	"bytes"
	"fmt"

	"github.com/pkg/errors"
)

// IndexedVarContainer provides the implementation of TypeCheck, Eval, and
// String for IndexedVars.
// If an object that wishes to implement this interface has lost the
// textual name that an IndexedVar originates from, it can use the
// ordinal column reference syntax: fmt.Fprintf(buf, "@%d", idx)
type IndexedVarContainer interface {
	IndexedVarEval(idx int, ctx *EvalContext) (Datum, error)
	IndexedVarResolvedType(idx int) Type
	IndexedVarFormat(buf *bytes.Buffer, f FmtFlags, idx int)
}

// IndexedVar is a VariableExpr that can be used as a leaf in expressions; it
// represents a dynamic value. It defers calls to TypeCheck, Eval, String to an
// IndexedVarContainer.
type IndexedVar struct {
	Idx       int
	container IndexedVarContainer
}

var _ TypedExpr = &IndexedVar{}
var _ VariableExpr = &IndexedVar{}

// Variable is a dummy function part of the VariableExpr interface.
func (*IndexedVar) Variable() {}

// Walk is part of the Expr interface.
func (v *IndexedVar) Walk(_ Visitor) Expr {
	return v
}

// TypeCheck is part of the Expr interface.
func (v *IndexedVar) TypeCheck(_ *SemaContext, desired Type) (TypedExpr, error) {
	if v.container == nil {
		// A more technically correct message would be to say that the
		// reference is unbound and thus cannot be typed. However this is
		// a tad bit too technical for the average SQL use case and
		// instead we acknowledge that we only get here if someone has
		// used a column reference in a place where it's not allowed by
		// the docs, so just say that instead.
		return nil, errors.Errorf("column reference %s not allowed in this context", v)
	}
	return v, nil
}

// Eval is part of the TypedExpr interface.
func (v *IndexedVar) Eval(ctx *EvalContext) (Datum, error) {
	if v.container == nil {
		panic("indexed var must be bound to a container before evaluation")
	}
	return v.container.IndexedVarEval(v.Idx, ctx)
}

// ResolvedType is part of the TypedExpr interface.
func (v *IndexedVar) ResolvedType() Type {
	if v.container == nil {
		panic("indexed var must be bound to a container before type resolution")
	}
	return v.container.IndexedVarResolvedType(v.Idx)
}

// Format implements the NodeFormatter interface.
func (v *IndexedVar) Format(buf *bytes.Buffer, f FmtFlags) {
	if f.indexedVarFormat != nil {
		f.indexedVarFormat(buf, f, v.container, v.Idx)
	} else if f.symbolicVars || v.container == nil {
		fmt.Fprintf(buf, "@%d", v.Idx+1)
	} else {
		v.container.IndexedVarFormat(buf, f, v.Idx)
	}
}

// NewOrdinalReference is a helper routine to create a standalone
// IndexedVar with the given index value. This needs to undergo
// BindIfUnbound() below before it can be fully used.
func NewOrdinalReference(r int) *IndexedVar {
	return &IndexedVar{Idx: r, container: nil}
}

// IndexedVarHelper wraps an IndexedVarContainer (an interface) and creates
// IndexedVars bound to that container.
//
// It also keeps track of which indexes from the container are used by
// expressions.
type IndexedVarHelper struct {
	vars      []IndexedVar
	container IndexedVarContainer
}

// BindIfUnbound attaches an IndexedVar to an existing container.
// This is needed for standalone column ordinals created during parsing.
func (h *IndexedVarHelper) BindIfUnbound(ivar *IndexedVar) error {
	if ivar.container != nil {
		return nil
	}
	if ivar.Idx < 0 || ivar.Idx >= len(h.vars) {
		return errors.Errorf("invalid column ordinal: @%d", ivar.Idx+1)
	}
	// This container must also remember it has "seen" the variable
	// so that IndexedVarUsed() below returns the right results.
	// The IndexedVar() method ensures this.
	*ivar = *h.IndexedVar(ivar.Idx)
	return nil
}

// MakeIndexedVarHelper initializes an IndexedVarHelper structure.
func MakeIndexedVarHelper(container IndexedVarContainer, numVars int) IndexedVarHelper {
	return IndexedVarHelper{vars: make([]IndexedVar, numVars), container: container}
}

// AssertSameContainer checks that the indexed var refers to the same container.
func (h *IndexedVarHelper) AssertSameContainer(ivar *IndexedVar) {
	if ivar.container != h.container {
		panic(fmt.Sprintf("indexed var linked to different container (%T) %+v, expected (%T) %+v",
			ivar.container, ivar.container, h.container, h.container))
	}
}

func (h *IndexedVarHelper) checkIndex(idx int) {
	if idx < 0 || idx >= len(h.vars) {
		panic(fmt.Sprintf("invalid var index %d (columns: %d)", idx, len(h.vars)))
	}
}

// NumVars returns the number of variables the IndexedVarHelper was initialized
// for.
func (h *IndexedVarHelper) NumVars() int {
	return len(h.vars)
}

// IndexedVar returns an IndexedVar for the given index. The index must be
// valid.
func (h *IndexedVarHelper) IndexedVar(idx int) *IndexedVar {
	h.checkIndex(idx)
	v := &h.vars[idx]
	if v.container == nil {
		v.Idx = idx
		v.container = h.container
	}
	return v
}

// IndexedVarUsed returns true if IndexedVar() was called for the given index.
// The index must be valid.
func (h *IndexedVarHelper) IndexedVarUsed(idx int) bool {
	h.checkIndex(idx)
	return h.vars[idx].container != nil
}

// InvalidColIdx is the index value of a non-initialized IndexedVar.
const InvalidColIdx = -1

// GetIndexedVars transfers ownership of the array of initialized
// IndexedVars to the caller; unused vars are guaranteed to have an
// invalid index. The helper cannot be used any more after the
// ownership has been transferred.
func (h *IndexedVarHelper) GetIndexedVars() []IndexedVar {
	for i := range h.vars {
		if h.vars[i].container == nil {
			h.vars[i].Idx = InvalidColIdx
		}
	}
	ret := h.vars
	h.vars = nil
	return ret
}

// Reset re-initializes an IndexedVarHelper structure with the same
// number of slots. After a helper has been reset, all the expressions
// that were linked to the helper before it was reset must be
// re-bound, e.g. using Rebind(). Resetting is useful to ensure that
// the helper's knowledge of which IndexedVars are actually used by
// linked expressions is up to date, especially after
// optimizations/transforms which eliminate sub-expressions. The
// optimizations performed by setNeededColumns() work then best.
//
// TODO(knz): groupNode and windowNode hold on to IndexedVar's after a Reset().
func (h *IndexedVarHelper) Reset() {
	h.vars = make([]IndexedVar, len(h.vars))
}

// Rebind collects all the IndexedVars in the given expression
// and re-binds them to this helper.
func (h *IndexedVarHelper) Rebind(expr TypedExpr, alsoReset, normalizeToNonNil bool) TypedExpr {
	if alsoReset {
		h.Reset()
	}
	if expr == nil || expr == DBoolTrue {
		if normalizeToNonNil {
			return DBoolTrue
		}
		return nil
	}
	ret, _ := WalkExpr(h, expr)
	return ret.(TypedExpr)
}

var _ Visitor = &IndexedVarHelper{}

// VisitPre implements the Visitor interface.
func (h *IndexedVarHelper) VisitPre(expr Expr) (recurse bool, newExpr Expr) {
	if iv, ok := expr.(*IndexedVar); ok {
		return false, h.IndexedVar(iv.Idx)
	}
	return true, expr
}

// VisitPost implements the Visitor interface.
func (*IndexedVarHelper) VisitPost(expr Expr) Expr { return expr }
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 shovey

import (
	"github.com/ctdk/goiardi/config"
	"github.com/ctdk/goiardi/datastore"
	"github.com/ctdk/goiardi/node"
	"github.com/ctdk/goiardi/serfin"
	"github.com/codeskyblue/go-uuid"
	"github.com/ctdk/goiardi/util"
	serfclient "github.com/hashicorp/serf/client"
	"net/http"
	"regexp"
	"strconv"
	"time"
)

type Shovey struct {
	RunID string `json:"id"`
	NodeNames []string `json:"nodes"`
	Command string `json:"command"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Status string `json:"status"`
	Timeout time.Duration `json:"timeout"`
	Quorum string `json:"quorum"`
	NodeRuns []*ShoveyRun
	Nodes []*node.Node
}

type ShoveyRun struct {
	ID int
	ShoveyUUID string
	NodeName string
	Status string
	AckTime time.Time
	EndTime time.Time
}

func New(command string, timeout int, quorumStr string, nodes []*node.Node) (*Shovey, util.Gerror) {
	runID := uuid.New()
	nodeNames := make([]string, len(nodes))
	for i, n := range nodes {
		nodeNames[i] = n.Name
	}
	s := &Shovey{ RunID: runID, NodeNames: nodeNames, Command: command, Timeout: time.Duration(timeout), Quorum: quorumStr, Status: "submitted" }
	if config.UsingDB() {

	}
	s.CreatedAt = time.Now()
	s.UpdatedAt = time.Now()

	ds := datastore.New()
	ds.Set("shovey", runID, s)

	// TODO: send jobs to nodes, try and get quorum

	return s, nil
}

func (s *Shovey) save() util.Gerror {
	if config.UsingDB() {

	}
	s.UpdatedAt = time.Now()

	ds := datastore.New()
	ds.Set("shovey", s.RunID, s)

	return nil
}

func Get(runID string) (*Shovey, util.Gerror) {
	if config.UsingDB() {

	}
	var shove *Shovey
	ds := datastore.New()
	s, found := ds.Get("shovey", runID)
	if s != nil {
		shove = s.(*Shovey)
	}
	if !found {
		err := util.Errorf("shovey job %s not found", runID)
		err.SetStatus(http.StatusNotFound)
		return nil, err
	}
	return shove, nil
}

func Cancel(runID string) util.Gerror {
	s, err := Get(runID)
	if err != nil {
		return err
	}
	s.Status = "cancelled"
	err = s.save()
	if err != nil {
		return err
	}

	// TODO: cancel jobs on nodes

	return nil
}

func (s *Shovey) startJobs() error {
	// determine if we meet the quorum
	// First is this a percentage or absolute quorum
	qnum, err := getQuorum(s.Quorum, len(s.Nodes))
	if err != nil {
		return err
	}
	// query node statuses to see if enough are up

	// if that all worked, ping the nodes over serf & listen for replies.
	// If enough reply, send the commands

	return nil
}

func getQuorum(quorum string, numNodes int) (int, error) {
	var qnum float64

	if numNodes == 0 {
		err := fmt.Errorf("There's no nodes to make a quorum")
		return 0, nil
	}

	m := regexp.MustCompile(`^(\d+\.?\d?)%$`)
	z := m.FindStringSubmatch(quorum)
	if z != nil {
		q, err := strconv.ParseFloat(z[1], 64)
		if err != nil {
			return 0, err
		}
		qnum = math.Ceil((q / 100.0) * numNodes)
	} else {
		var err error
		qnum, err = strconv.ParseFloat(quorum, 64)
		if err != nil {
			return 0, err
		}
		if qnum > numNodes {
			err := fmt.Errorf("%d nodes were required for the quorum, but only %d matched the criteria given", qnum, numNodes)
			return 0, err
		}
	}

	return int(qnum), nil
}
<commit_msg>Checkpoint<commit_after>/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 shovey

import (
	"encoding/json"
	"github.com/ctdk/goiardi/config"
	"github.com/ctdk/goiardi/datastore"
	"github.com/ctdk/goiardi/node"
	"github.com/ctdk/goiardi/serfin"
	"github.com/codeskyblue/go-uuid"
	"github.com/ctdk/goiardi/util"
	serfclient "github.com/hashicorp/serf/client"
	"net/http"
	"regexp"
	"runtime"
	"strconv"
	"time"
)

type Shovey struct {
	RunID string `json:"id"`
	NodeNames []string `json:"nodes"`
	Command string `json:"command"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Status string `json:"status"`
	Timeout time.Duration `json:"timeout"`
	Quorum string `json:"quorum"`
	NodeRuns []*ShoveyRun
	Nodes []*node.Node
}

type ShoveyRun struct {
	ID int
	ShoveyUUID string
	NodeName string
	Status string
	AckTime time.Time
	EndTime time.Time
}

func New(command string, timeout int, quorumStr string, nodes []*node.Node) (*Shovey, util.Gerror) {
	runID := uuid.New()
	nodeNames := make([]string, len(nodes))
	for i, n := range nodes {
		nodeNames[i] = n.Name
	}
	s := &Shovey{ RunID: runID, NodeNames: nodeNames, Command: command, Timeout: time.Duration(timeout), Quorum: quorumStr, Status: "submitted" }
	if config.UsingDB() {

	}
	s.CreatedAt = time.Now()
	s.UpdatedAt = time.Now()

	ds := datastore.New()
	ds.Set("shovey", runID, s)

	// TODO: send jobs to nodes, try and get quorum

	return s, nil
}

func (s *Shovey) save() util.Gerror {
	if config.UsingDB() {

	}
	s.UpdatedAt = time.Now()

	ds := datastore.New()
	ds.Set("shovey", s.RunID, s)

	return nil
}

func Get(runID string) (*Shovey, util.Gerror) {
	if config.UsingDB() {

	}
	var shove *Shovey
	ds := datastore.New()
	s, found := ds.Get("shovey", runID)
	if s != nil {
		shove = s.(*Shovey)
	}
	if !found {
		err := util.Errorf("shovey job %s not found", runID)
		err.SetStatus(http.StatusNotFound)
		return nil, err
	}
	return shove, nil
}

func Cancel(runID string) util.Gerror {
	s, err := Get(runID)
	if err != nil {
		return err
	}
	s.Status = "cancelled"
	err = s.save()
	if err != nil {
		return err
	}

	// TODO: cancel jobs on nodes

	return nil
}

func (s *Shovey) startJobs() error {
	// determine if we meet the quorum
	// First is this a percentage or absolute quorum
	qnum, err := getQuorum(s.Quorum, len(s.Nodes))
	if err != nil {
		return err
	}
	// query node statuses to see if enough are up
	upNodes := node.GetNodesByStatus("up")
	if len(upNodes) < qnum {
		err = fmt.Errorf("Not enough nodes were up to execute job %s - got %d, needed at least %d", s.RunID, len(upNodes), qnum)
	}

	// if that all worked, send the commands
	errch := make(chan error, 1)
	go func() {
		tagNodes := make([]string, len(upNodes))
		for i, n := range upNodes {
			tagNodes[i] = n.Name
			sr := &ShoveyRun{ ShoveyUUID: s.RunID, NodeName: n.Name, Status: "created" }
			sr.save()
		}
		// make sure this is the right amount of buffering
		payload := make(map[string]string)
		payload["run_id"] = s.RunID
		payload["command"] = s.Command
		jsonPayload := json.Marshal(payload)
		ackCh := make(chan string, len(tagNodes))
		respCh := make(chan serfclient.NodeResponse, len(tagNodes))
		q := &serfclient.QueryParam{ Name: "shovey", Payload: jsonPayload, FilterNodes: tagNodes, Timeout: s.Timeout, RequestAck: true, AckCh: ackCh, RespCh: respCh }
		qerr := serfclient.Query(q)
		if qerr != nil {
			errch <- qerr
			return
		}

		for {
			select {
			case a := <-ackCh:

			case r := <-respCh:

			case <- time.After(s.Timeout * time.Second):
				break
			}
		}
	}()
	err <-errch
	if err != nil {
		return err
	}

	return nil
}

func getQuorum(quorum string, numNodes int) (int, error) {
	var qnum float64

	if numNodes == 0 {
		err := fmt.Errorf("There's no nodes to make a quorum")
		return 0, nil
	}

	m := regexp.MustCompile(`^(\d+\.?\d?)%$`)
	z := m.FindStringSubmatch(quorum)
	if z != nil {
		q, err := strconv.ParseFloat(z[1], 64)
		if err != nil {
			return 0, err
		}
		qnum = math.Ceil((q / 100.0) * numNodes)
	} else {
		var err error
		qnum, err = strconv.ParseFloat(quorum, 64)
		if err != nil {
			return 0, err
		}
		if qnum > numNodes {
			err := fmt.Errorf("%d nodes were required for the quorum, but only %d matched the criteria given", qnum, numNodes)
			return 0, err
		}
	}

	return int(qnum), nil
}
<|endoftext|>


<commit_before>package ble

import (
	"testing"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/gobottest"
)

var _ gobot.Adaptor = (*ClientAdaptor)(nil)

func initTestBLEClientAdaptor() *ClientAdaptor {
	a := NewClientAdaptor("D7:99:5A:26:EC:38")
	return a
}

func TestBLEClientAdaptor(t *testing.T) {
	a := NewClientAdaptor("D7:99:5A:26:EC:38")
	gobottest.Assert(t, a.Address(), "D7:99:5A:26:EC:38")
}
<commit_msg>ble: add a little bit of additional test coverage<commit_after>package ble

import (
	"testing"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/gobottest"
)

var _ gobot.Adaptor = (*ClientAdaptor)(nil)

func initTestBLEClientAdaptor() *ClientAdaptor {
	a := NewClientAdaptor("D7:99:5A:26:EC:38")
	return a
}

func TestBLEClientAdaptor(t *testing.T) {
	a := NewClientAdaptor("D7:99:5A:26:EC:38")
	gobottest.Assert(t, a.Address(), "D7:99:5A:26:EC:38")
	gobottest.Assert(t, a.Name(), "BLECLient")
}

func TestBLEClientAdaptorName(t *testing.T) {
	a := NewClientAdaptor("D7:99:5A:26:EC:38")
	a.SetName("awesome")
	gobottest.Assert(t, a.Name(), "awesome")
}
<|endoftext|>