<commit_before>/*
   Copyright The containerd Authors.

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

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

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

package cni

import (
	"context"
	"net"
	"testing"

	cnilibrary "github.com/containernetworking/cni/libcni"
	"github.com/containernetworking/cni/pkg/types"
	types020 "github.com/containernetworking/cni/pkg/types/020"
	types040 "github.com/containernetworking/cni/pkg/types/040"
	types100 "github.com/containernetworking/cni/pkg/types/100"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// TestLibCNIType020 tests the cni version 0.2.0 plugin
// config and parses the result into structured data
func TestLibCNIType020(t *testing.T) {
	// Get the default CNI config
	l := defaultCNIConfig()

	// Create a fake cni config directory and file
	cniDir, confDir := makeFakeCNIConfig(t)
	defer tearDownCNIConfig(t, cniDir)
	l.pluginConfDir = confDir
	// Set the minimum network count as 2 for this test
	l.networkCount = 2
	err := l.Load(WithAllConf)
	assert.NoError(t, err)

	err = l.Status()
	assert.NoError(t, err)

	mockCNI := &MockCNI{}
	l.networks[0].cni = mockCNI
	expectedRT := &cnilibrary.RuntimeConf{ContainerID: "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth0",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[0].config, expectedRT).Return(&types020.Result{
		CNIVersion: "0.2.0",
		IP4: &types020.IPConfig{
			IP: net.IPNet{
				IP: []byte{10, 0, 0, 1},
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[0].config, expectedRT).Return(nil)

	l.networks[1].cni = mockCNI
	expectedRT = &cnilibrary.RuntimeConf{ContainerID: "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth1",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[1].config, expectedRT).Return(&types020.Result{
		CNIVersion: "0.2.0",
		IP4: &types020.IPConfig{
			IP: net.IPNet{
				IP: []byte{10, 0, 0, 2},
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[1].config, expectedRT).Return(nil)

	ctx := context.Background()

	r, err := l.Setup(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)
	assert.Contains(t, r.Interfaces, "eth0")
	assert.NotNil(t, r.Interfaces["eth0"].IPConfigs)
	assert.Equal(t, r.Interfaces["eth0"].IPConfigs[0].IP.String(), "10.0.0.1")
	assert.Equal(t, r.Interfaces["eth0"].IPConfigs[1].IP.String(), "10.0.0.2")
	err = l.Remove(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)

	c := l.GetConfig()
	assert.NotNil(t, c)
	assert.NotNil(t, c.Prefix)
	assert.Equal(t, "eth", c.Prefix)
	assert.NotNil(t, c.PluginDirs)
	assert.Equal(t, DefaultCNIDir, c.PluginDirs[0])
	assert.NotNil(t, c.PluginConfDir)
	assert.Equal(t, confDir, c.PluginConfDir)
	assert.NotNil(t, c.Networks)
	assert.Equal(t, "plugin1", c.Networks[0].Config.Name)
	assert.Equal(t, "eth0", c.Networks[0].IFName)
}

// TestLibCNIType040 tests the cni version 0.4.0 plugin
// config and parses the result into structured data
func TestLibCNIType040(t *testing.T) {
	// Get the default CNI config
	l := defaultCNIConfig()
	// Create a fake cni config directory and file
	cniDir, confDir := makeFakeCNIConfig(t)
	defer tearDownCNIConfig(t, cniDir)
	l.pluginConfDir = confDir
	// Set the minimum network count as 2 for this test
	l.networkCount = 2
	err := l.Load(WithAllConf)
	assert.NoError(t, err)

	err = l.Status()
	assert.NoError(t, err)

	mockCNI := &MockCNI{}
	l.networks[0].cni = mockCNI
	l.networks[1].cni = mockCNI
	ipv4, err := types.ParseCIDR("10.0.0.1/24")
	assert.NoError(t, err)
	expectedRT := &cnilibrary.RuntimeConf{
		ContainerID:    "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth0",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[0].config, expectedRT).Return(&types040.Result{
		CNIVersion: "0.3.1", // covered by types040
		Interfaces: []*types040.Interface{
			{
				Name: "eth0",
			},
		},
		IPs: []*types040.IPConfig{
			{
				Version:   "4",
				Interface: types100.Int(0),
				Address:   *ipv4,
				Gateway:   net.ParseIP("10.0.0.255"),
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[0].config, expectedRT).Return(nil)

	ipv4, err = types.ParseCIDR("10.0.0.2/24")
	assert.NoError(t, err)
	l.networks[1].cni = mockCNI
	expectedRT = &cnilibrary.RuntimeConf{
		ContainerID:    "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth1",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[1].config, expectedRT).Return(&types040.Result{
		CNIVersion: "0.3.1", // covered by types040
		Interfaces: []*types040.Interface{
			{
				Name: "eth1",
			},
		},
		IPs: []*types040.IPConfig{
			{
				Version:   "4",
				Interface: types100.Int(0),
				Address:   *ipv4,
				Gateway:   net.ParseIP("10.0.0.2"),
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[1].config, expectedRT).Return(nil)

	ctx := context.Background()
	r, err := l.Setup(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)
	assert.Contains(t, r.Interfaces, "eth0")
	assert.NotNil(t, r.Interfaces["eth0"].IPConfigs)
	assert.Equal(t, r.Interfaces["eth0"].IPConfigs[0].IP.String(), "10.0.0.1")
	assert.Contains(t, r.Interfaces, "eth1")
	assert.NotNil(t, r.Interfaces["eth1"].IPConfigs)
	assert.Equal(t, r.Interfaces["eth1"].IPConfigs[0].IP.String(), "10.0.0.2")
	err = l.Remove(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)
}

type MockCNI struct {
	mock.Mock
}

func (m *MockCNI) AddNetwork(ctx context.Context, net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) (types.Result, error) {
	args := m.Called(net, rt)
	return args.Get(0).(types.Result), args.Error(1)
}

func (m *MockCNI) DelNetwork(ctx context.Context, net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) error {
	args := m.Called(net, rt)
	return args.Error(0)
}

func (m *MockCNI) DelNetworkList(ctx context.Context, net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) error {
	args := m.Called(net, rt)
	return args.Error(0)
}

func (m *MockCNI) AddNetworkList(ctx context.Context, net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) (types.Result, error) {
	args := m.Called(net, rt)
	return args.Get(0).(types.Result), args.Error(1)
}

func (m *MockCNI) CheckNetworkList(ctx context.Context, net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) error {
	args := m.Called(net, rt)
	return args.Error(0)
}

func (m *MockCNI) CheckNetwork(ctx context.Context, net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) error {
	args := m.Called(net, rt)
	return args.Error(0)
}

func (m *MockCNI) GetNetworkCachedConfig(net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) ([]byte, *cnilibrary.RuntimeConf, error) {
	args := m.Called(net, rt)
	return args.Get(0).([]byte), args.Get(1).(*cnilibrary.RuntimeConf), args.Error(1)
}

func (m *MockCNI) GetNetworkCachedResult(net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) (types.Result, error) {
	args := m.Called(net, rt)
	return args.Get(0).(types.Result), args.Error(1)
}

func (m *MockCNI) ValidateNetworkList(ctx context.Context, net *cnilibrary.NetworkConfigList) ([]string, error) {
	args := m.Called(net)
	return args.Get(0).([]string), args.Error(1)
}

func (m *MockCNI) ValidateNetwork(ctx context.Context, net *cnilibrary.NetworkConfig) ([]string, error) {
	args := m.Called(net)
	return args.Get(0).([]string), args.Error(1)
}

func (m *MockCNI) GetNetworkListCachedConfig(net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) ([]byte, *cnilibrary.RuntimeConf, error) {
	args := m.Called(net, rt)
	return args.Get(0).([]byte), args.Get(1).(*cnilibrary.RuntimeConf), args.Error(1)
}

func (m *MockCNI) GetNetworkListCachedResult(net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) (types.Result, error) {
	args := m.Called(net, rt)
	return args.Get(0).(types.Result), args.Error(1)
}
<commit_msg>test: add TestLibCNIType100<commit_after>/*
   Copyright The containerd Authors.

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

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

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

package cni

import (
	"context"
	"net"
	"testing"

	cnilibrary "github.com/containernetworking/cni/libcni"
	"github.com/containernetworking/cni/pkg/types"
	types020 "github.com/containernetworking/cni/pkg/types/020"
	types040 "github.com/containernetworking/cni/pkg/types/040"
	types100 "github.com/containernetworking/cni/pkg/types/100"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// TestLibCNIType020 tests the cni version 0.2.0 plugin
// config and parses the result into structured data
func TestLibCNIType020(t *testing.T) {
	// Get the default CNI config
	l := defaultCNIConfig()

	// Create a fake cni config directory and file
	cniDir, confDir := makeFakeCNIConfig(t)
	defer tearDownCNIConfig(t, cniDir)
	l.pluginConfDir = confDir
	// Set the minimum network count as 2 for this test
	l.networkCount = 2
	err := l.Load(WithAllConf)
	assert.NoError(t, err)

	err = l.Status()
	assert.NoError(t, err)

	mockCNI := &MockCNI{}
	l.networks[0].cni = mockCNI
	expectedRT := &cnilibrary.RuntimeConf{ContainerID: "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth0",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[0].config, expectedRT).Return(&types020.Result{
		CNIVersion: "0.2.0",
		IP4: &types020.IPConfig{
			IP: net.IPNet{
				IP: []byte{10, 0, 0, 1},
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[0].config, expectedRT).Return(nil)

	l.networks[1].cni = mockCNI
	expectedRT = &cnilibrary.RuntimeConf{ContainerID: "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth1",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[1].config, expectedRT).Return(&types020.Result{
		CNIVersion: "0.2.0",
		IP4: &types020.IPConfig{
			IP: net.IPNet{
				IP: []byte{10, 0, 0, 2},
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[1].config, expectedRT).Return(nil)

	ctx := context.Background()

	r, err := l.Setup(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)
	assert.Contains(t, r.Interfaces, "eth0")
	assert.NotNil(t, r.Interfaces["eth0"].IPConfigs)
	assert.Equal(t, r.Interfaces["eth0"].IPConfigs[0].IP.String(), "10.0.0.1")
	assert.Equal(t, r.Interfaces["eth0"].IPConfigs[1].IP.String(), "10.0.0.2")
	err = l.Remove(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)

	c := l.GetConfig()
	assert.NotNil(t, c)
	assert.NotNil(t, c.Prefix)
	assert.Equal(t, "eth", c.Prefix)
	assert.NotNil(t, c.PluginDirs)
	assert.Equal(t, DefaultCNIDir, c.PluginDirs[0])
	assert.NotNil(t, c.PluginConfDir)
	assert.Equal(t, confDir, c.PluginConfDir)
	assert.NotNil(t, c.Networks)
	assert.Equal(t, "plugin1", c.Networks[0].Config.Name)
	assert.Equal(t, "eth0", c.Networks[0].IFName)
}

// TestLibCNIType040 tests the cni version 0.4.0 plugin
// config and parses the result into structured data
func TestLibCNIType040(t *testing.T) {
	// Get the default CNI config
	l := defaultCNIConfig()
	// Create a fake cni config directory and file
	cniDir, confDir := makeFakeCNIConfig(t)
	defer tearDownCNIConfig(t, cniDir)
	l.pluginConfDir = confDir
	// Set the minimum network count as 2 for this test
	l.networkCount = 2
	err := l.Load(WithAllConf)
	assert.NoError(t, err)

	err = l.Status()
	assert.NoError(t, err)

	mockCNI := &MockCNI{}
	l.networks[0].cni = mockCNI
	l.networks[1].cni = mockCNI
	ipv4, err := types.ParseCIDR("10.0.0.1/24")
	assert.NoError(t, err)
	expectedRT := &cnilibrary.RuntimeConf{
		ContainerID:    "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth0",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[0].config, expectedRT).Return(&types040.Result{
		CNIVersion: "0.3.1", // covered by types040
		Interfaces: []*types040.Interface{
			{
				Name: "eth0",
			},
		},
		IPs: []*types040.IPConfig{
			{
				Version:   "4",
				Interface: types100.Int(0),
				Address:   *ipv4,
				Gateway:   net.ParseIP("10.0.0.255"),
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[0].config, expectedRT).Return(nil)

	ipv4, err = types.ParseCIDR("10.0.0.2/24")
	assert.NoError(t, err)
	l.networks[1].cni = mockCNI
	expectedRT = &cnilibrary.RuntimeConf{
		ContainerID:    "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth1",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[1].config, expectedRT).Return(&types040.Result{
		CNIVersion: "0.3.1", // covered by types040
		Interfaces: []*types040.Interface{
			{
				Name: "eth1",
			},
		},
		IPs: []*types040.IPConfig{
			{
				Version:   "4",
				Interface: types100.Int(0),
				Address:   *ipv4,
				Gateway:   net.ParseIP("10.0.0.2"),
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[1].config, expectedRT).Return(nil)

	ctx := context.Background()
	r, err := l.Setup(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)
	assert.Contains(t, r.Interfaces, "eth0")
	assert.NotNil(t, r.Interfaces["eth0"].IPConfigs)
	assert.Equal(t, r.Interfaces["eth0"].IPConfigs[0].IP.String(), "10.0.0.1")
	assert.Contains(t, r.Interfaces, "eth1")
	assert.NotNil(t, r.Interfaces["eth1"].IPConfigs)
	assert.Equal(t, r.Interfaces["eth1"].IPConfigs[0].IP.String(), "10.0.0.2")
	err = l.Remove(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)
}

// TestLibCNIType100 tests the cni version 1.0.0 plugin
// config and parses the result into structured data
func TestLibCNIType100(t *testing.T) {
	// Get the default CNI config
	l := defaultCNIConfig()
	// Create a fake cni config directory and file
	cniDir, confDir := makeFakeCNIConfig(t)
	defer tearDownCNIConfig(t, cniDir)
	l.pluginConfDir = confDir
	// Set the minimum network count as 2 for this test
	l.networkCount = 2
	err := l.Load(WithAllConf)
	assert.NoError(t, err)

	err = l.Status()
	assert.NoError(t, err)

	mockCNI := &MockCNI{}
	l.networks[0].cni = mockCNI
	l.networks[1].cni = mockCNI
	ipv4, err := types.ParseCIDR("10.0.0.1/24")
	assert.NoError(t, err)
	expectedRT := &cnilibrary.RuntimeConf{
		ContainerID:    "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth0",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[0].config, expectedRT).Return(&types100.Result{
		CNIVersion: "1.0.0",
		Interfaces: []*types100.Interface{
			{
				Name: "eth0",
			},
		},
		IPs: []*types100.IPConfig{
			{
				Interface: types100.Int(0),
				Address:   *ipv4,
				Gateway:   net.ParseIP("10.0.0.255"),
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[0].config, expectedRT).Return(nil)

	ipv4, err = types.ParseCIDR("10.0.0.2/24")
	assert.NoError(t, err)
	l.networks[1].cni = mockCNI
	expectedRT = &cnilibrary.RuntimeConf{
		ContainerID:    "container-id1",
		NetNS:          "/proc/12345/ns/net",
		IfName:         "eth1",
		Args:           [][2]string(nil),
		CapabilityArgs: map[string]interface{}{},
	}
	mockCNI.On("AddNetworkList", l.networks[1].config, expectedRT).Return(&types100.Result{
		CNIVersion: "1.0.0",
		Interfaces: []*types100.Interface{
			{
				Name: "eth1",
			},
		},
		IPs: []*types100.IPConfig{
			{
				Interface: types100.Int(0),
				Address:   *ipv4,
				Gateway:   net.ParseIP("10.0.0.2"),
			},
		},
	}, nil)
	mockCNI.On("DelNetworkList", l.networks[1].config, expectedRT).Return(nil)

	ctx := context.Background()
	r, err := l.Setup(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)
	assert.Contains(t, r.Interfaces, "eth0")
	assert.NotNil(t, r.Interfaces["eth0"].IPConfigs)
	assert.Equal(t, r.Interfaces["eth0"].IPConfigs[0].IP.String(), "10.0.0.1")
	assert.Contains(t, r.Interfaces, "eth1")
	assert.NotNil(t, r.Interfaces["eth1"].IPConfigs)
	assert.Equal(t, r.Interfaces["eth1"].IPConfigs[0].IP.String(), "10.0.0.2")
	err = l.Remove(ctx, "container-id1", "/proc/12345/ns/net")
	assert.NoError(t, err)
}

type MockCNI struct {
	mock.Mock
}

func (m *MockCNI) AddNetwork(ctx context.Context, net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) (types.Result, error) {
	args := m.Called(net, rt)
	return args.Get(0).(types.Result), args.Error(1)
}

func (m *MockCNI) DelNetwork(ctx context.Context, net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) error {
	args := m.Called(net, rt)
	return args.Error(0)
}

func (m *MockCNI) DelNetworkList(ctx context.Context, net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) error {
	args := m.Called(net, rt)
	return args.Error(0)
}

func (m *MockCNI) AddNetworkList(ctx context.Context, net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) (types.Result, error) {
	args := m.Called(net, rt)
	return args.Get(0).(types.Result), args.Error(1)
}

func (m *MockCNI) CheckNetworkList(ctx context.Context, net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) error {
	args := m.Called(net, rt)
	return args.Error(0)
}

func (m *MockCNI) CheckNetwork(ctx context.Context, net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) error {
	args := m.Called(net, rt)
	return args.Error(0)
}

func (m *MockCNI) GetNetworkCachedConfig(net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) ([]byte, *cnilibrary.RuntimeConf, error) {
	args := m.Called(net, rt)
	return args.Get(0).([]byte), args.Get(1).(*cnilibrary.RuntimeConf), args.Error(1)
}

func (m *MockCNI) GetNetworkCachedResult(net *cnilibrary.NetworkConfig, rt *cnilibrary.RuntimeConf) (types.Result, error) {
	args := m.Called(net, rt)
	return args.Get(0).(types.Result), args.Error(1)
}

func (m *MockCNI) ValidateNetworkList(ctx context.Context, net *cnilibrary.NetworkConfigList) ([]string, error) {
	args := m.Called(net)
	return args.Get(0).([]string), args.Error(1)
}

func (m *MockCNI) ValidateNetwork(ctx context.Context, net *cnilibrary.NetworkConfig) ([]string, error) {
	args := m.Called(net)
	return args.Get(0).([]string), args.Error(1)
}

func (m *MockCNI) GetNetworkListCachedConfig(net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) ([]byte, *cnilibrary.RuntimeConf, error) {
	args := m.Called(net, rt)
	return args.Get(0).([]byte), args.Get(1).(*cnilibrary.RuntimeConf), args.Error(1)
}

func (m *MockCNI) GetNetworkListCachedResult(net *cnilibrary.NetworkConfigList, rt *cnilibrary.RuntimeConf) (types.Result, error) {
	args := m.Called(net, rt)
	return args.Get(0).(types.Result), args.Error(1)
}
<|endoftext|>


<commit_before>package monitor

import (
	"context"
	"io"
	"sync"

	"github.com/TheThingsNetwork/ttn/api"
	pb_gateway "github.com/TheThingsNetwork/ttn/api/gateway"
	"github.com/TheThingsNetwork/ttn/api/router"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/apex/log"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
)

// Client is a wrapper around MonitorClient
type Client struct {
	Log log.Interface

	client MonitorClient
	conn   *grpc.ClientConn
	addr   string

	reopening chan struct{}

	gateways map[string]GatewayClient
	sync.RWMutex
}

// NewClient is a wrapper for NewMonitorClient, initializes
// connection to MonitorServer on monitorAddr with default gRPC options
func NewClient(ctx log.Interface, monitorAddr string) (cl *Client, err error) {
	cl = &Client{
		Log:      ctx,
		addr:     monitorAddr,
		gateways: make(map[string]GatewayClient),
	}
	return cl, cl.Open()
}

func (cl *Client) Open() (err error) {
	cl.Lock()
	defer cl.Unlock()

	return cl.open()
}
func (cl *Client) open() (err error) {
	addr := cl.addr

	ctx := cl.Log.WithField("addr", addr)
	ctx.Debug("Opening monitor connection...")

	cl.conn, err = grpc.Dial(addr, append(api.DialOptions, grpc.WithInsecure())...)
	if err != nil {
		ctx.WithError(errors.FromGRPCError(err)).Warn("Failed to establish connection")
		return err
	}
	ctx.Debug("Connection established")

	cl.client = NewMonitorClient(cl.conn)
	return nil
}

// Close closes connection to the monitor
func (cl *Client) Close() (err error) {
	cl.Lock()
	defer cl.Unlock()

	return cl.close()
}
func (cl *Client) close() (err error) {
	cl.Log.Debug("Closing monitor connection...")
	for _, gtw := range cl.gateways {
		err = gtw.Close()
		if err != nil {
			cl.Log.WithError(err).WithField("GatewayID", gtw.(*gatewayClient).id).Warn("Failed to close streams")
		}
	}

	err = cl.conn.Close()
	if err != nil {
		return err
	}

	cl.conn = nil
	return nil
}

func (cl *Client) Reopen() (err error) {
	cl.Lock()
	defer cl.Unlock()

	return cl.reopen()
}
func (cl *Client) reopen() (err error) {
	cl.Log.Debug("Reopening monitor connection...")

	cl.reopening = make(chan struct{})
	defer func() {
		close(cl.reopening)
		cl.reopening = nil
	}()

	err = cl.close()
	if err != nil {
		return err
	}
	return cl.open()
}

func (cl *Client) IsReopening() bool {
	return cl.reopening != nil
}

func (cl *Client) IsConnected() bool {
	return cl.client != nil && cl.conn != nil
}

func (cl *Client) GatewayClient(id, token string) (gtwCl GatewayClient) {
	cl.RLock()
	gtwCl, ok := cl.gateways[id]
	cl.RUnlock()
	if !ok {
		cl.Lock()
		gtwCl = &gatewayClient{
			Log: cl.Log.WithField("GatewayID", id),

			client: cl,

			id:    id,
			token: token,
		}
		cl.gateways[id] = gtwCl
		cl.Unlock()
	}
	return gtwCl
}

type gatewayClient struct {
	client *Client

	Log log.Interface

	id, token string

	status struct {
		stream Monitor_GatewayStatusClient
		sync.RWMutex
	}

	uplink struct {
		stream Monitor_GatewayUplinkClient
		sync.RWMutex
	}

	downlink struct {
		stream Monitor_GatewayDownlinkClient
		sync.RWMutex
	}
}

// GatewayClient is used as the main client for Gateways to communicate with the Router
type GatewayClient interface {
	SendStatus(status *pb_gateway.Status) (err error)
	SendUplink(msg *router.UplinkMessage) (err error)
	SendDownlink(msg *router.DownlinkMessage) (err error)
	Close() (err error)
}

func (cl *gatewayClient) SendStatus(status *pb_gateway.Status) (err error) {
	defer func() {
		if err != nil {
			cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to send status to monitor")

			if code := grpc.Code(err); code == codes.Unavailable || code == codes.Internal {
				cl.client.Lock()
				defer cl.client.Unlock()

				if !cl.client.IsReopening() {
					err = cl.client.reopen()
				}
			}
		} else {
			cl.Log.Debug("Sent status to monitor")
		}
	}()

	cl.status.RLock()
	stream := cl.status.stream
	cl.status.RUnlock()

	if stream == nil {
		cl.status.Lock()
		if stream = cl.status.stream; stream == nil {
			stream, err = cl.setupStatus()
			if err != nil {
				cl.status.Unlock()
				cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to open new monitor status stream")
				return err
			}

			cl.Log.Debug("Opened new monitor status stream")
		}
		cl.status.Unlock()
	}

	if err = stream.Send(status); err == io.EOF {
		cl.Log.Warn("Monitor status stream closed")
		cl.status.Lock()
		if cl.status.stream == stream {
			cl.status.stream = nil
		}
		cl.status.Unlock()
		return nil
	}
	return err
}

func (cl *gatewayClient) SendUplink(uplink *router.UplinkMessage) (err error) {
	defer func() {
		if err != nil {
			cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to send uplink to monitor")

			if code := grpc.Code(err); code == codes.Unavailable || code == codes.Internal {
				cl.Log.Debug("error is internal")
				cl.Log.Debug("Locking...")
				cl.client.Lock()
				cl.Log.Debug("Locked...")

				cl.Log.Debug("Check if reopening...")
				if !cl.client.IsReopening() {
					cl.Log.Debug("Not reopening...")
					err = cl.client.reopen()
				}
				cl.Log.Debug("Unlocking...")
				cl.client.Unlock()
				cl.Log.Debug("Unlocked...")
				cl.Log.Debugf("return %s", err)
			}
		} else {
			cl.Log.Debug("Sent uplink to monitor")
		}
	}()

	cl.uplink.RLock()
	stream := cl.uplink.stream
	cl.uplink.RUnlock()

	if stream == nil {
		cl.uplink.Lock()
		if stream = cl.uplink.stream; stream == nil {
			stream, err = cl.setupUplink()
			if err != nil {
				cl.uplink.Unlock()
				cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to open new monitor uplink stream")
				return err
			}

			cl.Log.Debug("Opened new monitor uplink stream")
		}
		cl.uplink.Unlock()
	}

	if err = stream.Send(uplink); err == io.EOF {
		cl.Log.Warn("Monitor uplink stream closed")
		cl.uplink.Lock()
		if cl.uplink.stream == stream {
			cl.uplink.stream = nil
		}
		cl.uplink.Unlock()
		return nil
	}
	return err
}

func (cl *gatewayClient) SendDownlink(downlink *router.DownlinkMessage) (err error) {
	defer func() {
		if err != nil {
			cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to send downlink to monitor")

			if code := grpc.Code(err); code == codes.Unavailable || code == codes.Internal {
				cl.client.Lock()
				defer cl.client.Unlock()

				if !cl.client.IsReopening() {
					err = cl.client.reopen()
				}
			}
		} else {
			cl.Log.Debug("Sent downlink to monitor")
		}
	}()

	cl.downlink.RLock()
	stream := cl.downlink.stream
	cl.downlink.RUnlock()

	if stream == nil {
		cl.downlink.Lock()
		if stream = cl.downlink.stream; stream == nil {
			stream, err = cl.setupDownlink()
			if err != nil {
				cl.downlink.Unlock()
				cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to open new monitor downlink stream")
				return err
			}

			cl.Log.Debug("Opened new monitor downlink stream")
		}
		cl.downlink.Unlock()
	}

	if err = stream.Send(downlink); err == io.EOF {
		cl.Log.Warn("Monitor downlink stream closed")
		cl.downlink.Lock()
		if cl.downlink.stream == stream {
			cl.downlink.stream = nil
		}
		cl.downlink.Unlock()
		return nil
	}
	return err
}

func (cl *gatewayClient) Close() (err error) {
	wg := &sync.WaitGroup{}

	wg.Add(1)
	go func() {
		defer wg.Done()

		cl.Log.Debug("Status locking...")
		cl.status.Lock()
		cl.Log.Debug("Status locked")

		if cl.status.stream != nil {
			if cerr := cl.status.stream.CloseSend(); cerr != nil {
				cl.Log.WithError(cerr).Warn("Failed to close status stream")
				err = cerr
			}
			cl.status.stream = nil
		}
	}()
	defer cl.status.Unlock()

	wg.Add(1)
	go func() {
		defer wg.Done()

		cl.Log.Debug("Uplink locking...")
		cl.uplink.Lock()
		cl.Log.Debug("Uplink locked")

		if cl.uplink.stream != nil {
			if cerr := cl.uplink.stream.CloseSend(); cerr != nil {
				cl.Log.WithError(cerr).Warn("Failed to close uplink stream")
				err = cerr
			}
			cl.uplink.stream = nil
		}
	}()
	defer cl.uplink.Unlock()

	wg.Add(1)
	go func() {
		defer wg.Done()

		cl.downlink.Lock()

		if cl.downlink.stream != nil {
			if cerr := cl.downlink.stream.CloseSend(); cerr != nil {
				cl.Log.WithError(cerr).Warn("Failed to close downlink stream")
				err = cerr
			}
			cl.downlink.stream = nil
		}
	}()
	defer cl.downlink.Unlock()

	wg.Wait()
	return err
}

func (cl *gatewayClient) Context() (monitorContext context.Context) {
	return metadata.NewContext(context.Background(), metadata.Pairs(
		"id", cl.id,
		"token", cl.token,
	))
}

func (cl *gatewayClient) setupStatus() (stream Monitor_GatewayStatusClient, err error) {
	stream, err = cl.client.client.GatewayStatus(cl.Context())
	if err != nil {
		return nil, err
	}

	cl.status.stream = stream
	return stream, nil
}

func (cl *gatewayClient) setupUplink() (stream Monitor_GatewayUplinkClient, err error) {
	stream, err = cl.client.client.GatewayUplink(cl.Context())
	if err != nil {
		return nil, err
	}

	cl.uplink.stream = stream
	return stream, nil
}

func (cl *gatewayClient) setupDownlink() (stream Monitor_GatewayDownlinkClient, err error) {
	stream, err = cl.client.client.GatewayDownlink(cl.Context())
	if err != nil {
		return nil, err
	}

	cl.downlink.stream = stream
	return stream, nil
}
<commit_msg>monitor client: refactor, use sync.Once<commit_after>package monitor

import (
	"context"
	"io"
	"sync"

	"github.com/TheThingsNetwork/ttn/api"
	pb_gateway "github.com/TheThingsNetwork/ttn/api/gateway"
	"github.com/TheThingsNetwork/ttn/api/router"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/apex/log"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
)

// Client is a wrapper around MonitorClient
type Client struct {
	Ctx log.Interface

	client MonitorClient
	conn   *grpc.ClientConn
	addr   string

	once sync.Once

	gateways map[string]GatewayClient
	mutex    sync.RWMutex
}

// NewClient is a wrapper for NewMonitorClient, initializes
// connection to MonitorServer on monitorAddr with default gRPC options
func NewClient(ctx log.Interface, monitorAddr string) (cl *Client, err error) {
	cl = &Client{
		Ctx:      ctx,
		addr:     monitorAddr,
		gateways: make(map[string]GatewayClient),
	}
	return cl, cl.Open()
}

func (cl *Client) Open() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.open()
}
func (cl *Client) open() (err error) {
	addr := cl.addr
	ctx := cl.Ctx.WithField("addr", addr)

	defer func() {
		if err != nil {
			ctx.Warn("Failed to open monitor connection")
		} else {
			ctx.Info("Monitor connection opened")
		}
	}()

	ctx.Debug("Opening monitor connection...")

	cl.conn, err = grpc.Dial(addr, append(api.DialOptions, grpc.WithInsecure())...)
	if err != nil {
		ctx.WithError(errors.FromGRPCError(err)).Warn("Failed to establish connection to gRPC service")
		return err
	}

	cl.client = NewMonitorClient(cl.conn)
	return nil
}

// Close closes connection to the monitor
func (cl *Client) Close() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.close()
}
func (cl *Client) close() (err error) {
	defer func() {
		if err != nil {
			cl.Ctx.Warn("Failed to close monitor connection")
		} else {
			cl.Ctx.Info("Monitor connection closed")
		}
	}()

	for _, gtw := range cl.gateways {
		ctx := cl.Ctx.WithField("GatewayID", gtw.(*gatewayClient).id)

		ctx.Debug("Closing gateway streams...")
		err = gtw.Close()
		if err != nil {
			ctx.Warn("Failed to close gateway streams")
		}
	}

	cl.Ctx.Debug("Closing monitor connection...")
	err = cl.conn.Close()
	if err != nil {
		return err
	}

	cl.conn = nil
	return nil
}

func (cl *Client) Reopen() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.reopen()
}
func (cl *Client) reopen() (err error) {
	defer func() {
		if err != nil {
			cl.Ctx.Warn("Failed to reopen monitor connection")
		} else {
			cl.Ctx.Info("Monitor connection reopened")
		}
	}()

	cl.Ctx.Debug("Reopening monitor connection...")

	err = cl.close()
	if err != nil {
		return err
	}
	return cl.open()
}

func (cl *Client) IsConnected() bool {
	return cl.client != nil && cl.conn != nil
}

func (cl *Client) GatewayClient(id, token string) (gtwCl GatewayClient) {
	cl.mutex.RLock()
	gtwCl, ok := cl.gateways[id]
	cl.mutex.RUnlock()
	if !ok {
		cl.mutex.Lock()
		gtwCl = &gatewayClient{
			Log: cl.Ctx.WithField("GatewayID", id),

			client: cl,

			id:    id,
			token: token,
		}
		cl.gateways[id] = gtwCl
		cl.mutex.Unlock()
	}
	return gtwCl
}

type gatewayClient struct {
	client *Client

	Log log.Interface

	id, token string

	status struct {
		stream Monitor_GatewayStatusClient
		sync.RWMutex
	}

	uplink struct {
		stream Monitor_GatewayUplinkClient
		sync.RWMutex
	}

	downlink struct {
		stream Monitor_GatewayDownlinkClient
		sync.RWMutex
	}
}

// GatewayClient is used as the main client for Gateways to communicate with the Router
type GatewayClient interface {
	SendStatus(status *pb_gateway.Status) (err error)
	SendUplink(msg *router.UplinkMessage) (err error)
	SendDownlink(msg *router.DownlinkMessage) (err error)
	Close() (err error)
}

func (cl *gatewayClient) SendStatus(status *pb_gateway.Status) (err error) {
	cl.status.RLock()
	cl.client.mutex.RLock()

	once := cl.client.once
	stream := cl.status.stream

	cl.status.RUnlock()
	cl.client.mutex.RUnlock()

	defer func() {
		if err != nil {
			cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to send status to monitor")

			if code := grpc.Code(err); code == codes.Unavailable || code == codes.Internal {
				cl.client.mutex.Lock()
				defer cl.client.mutex.Unlock()

				once.Do(func() {
					err = cl.client.Reopen()

					cl.client.mutex.Lock()
					cl.client.once = sync.Once{}
					cl.client.mutex.Unlock()
				})
			}
		} else {
			cl.Log.Debug("Sent status to monitor")
		}
	}()

	if stream == nil {
		cl.status.Lock()
		if stream = cl.status.stream; stream == nil {
			stream, err = cl.setupStatus()
			if err != nil {
				cl.status.Unlock()
				return err
			}
		}
		cl.status.Unlock()
	}

	if err = stream.Send(status); err == io.EOF {
		cl.Log.Warn("Monitor status stream closed")
		cl.status.Lock()
		if cl.status.stream == stream {
			cl.status.stream = nil
		}
		cl.status.Unlock()
		return nil
	}
	return err
}

func (cl *gatewayClient) SendUplink(uplink *router.UplinkMessage) (err error) {
	cl.uplink.RLock()
	cl.client.mutex.RLock()

	once := cl.client.once
	stream := cl.uplink.stream

	cl.uplink.RUnlock()
	cl.client.mutex.RUnlock()

	defer func() {
		if err != nil {
			cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to send uplink to monitor")

			if code := grpc.Code(err); code == codes.Unavailable || code == codes.Internal {

				once.Do(func() {
					err = cl.client.Reopen()

					cl.client.mutex.Lock()
					cl.client.once = sync.Once{}
					cl.client.mutex.Unlock()
				})
			}
		} else {
			cl.Log.Debug("Sent uplink to monitor")
		}
	}()

	if stream == nil {
		cl.uplink.Lock()
		if stream = cl.uplink.stream; stream == nil {
			stream, err = cl.setupUplink()
			if err != nil {
				cl.uplink.Unlock()
				return err
			}
		}
		cl.uplink.Unlock()
	}

	if err = stream.Send(uplink); err == io.EOF {
		cl.Log.Warn("Monitor uplink stream closed")
		cl.uplink.Lock()
		if cl.uplink.stream == stream {
			cl.uplink.stream = nil
		}
		cl.uplink.Unlock()
		return nil
	}
	return err
}

func (cl *gatewayClient) SendDownlink(downlink *router.DownlinkMessage) (err error) {
	cl.downlink.RLock()
	cl.client.mutex.RLock()

	once := cl.client.once
	stream := cl.downlink.stream

	cl.downlink.RUnlock()
	cl.client.mutex.RUnlock()

	defer func() {
		if err != nil {
			cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to send downlink to monitor")

			if code := grpc.Code(err); code == codes.Unavailable || code == codes.Internal {
				once.Do(func() {
					err = cl.client.Reopen()

					cl.client.mutex.Lock()
					cl.client.once = sync.Once{}
					cl.client.mutex.Unlock()
				})
			}
		} else {
			cl.Log.Debug("Sent downlink to monitor")
		}
	}()

	if stream == nil {
		cl.downlink.Lock()
		if stream = cl.downlink.stream; stream == nil {
			stream, err = cl.setupDownlink()
			if err != nil {
				cl.downlink.Unlock()
				return err
			}
		}
		cl.downlink.Unlock()
	}

	if err = stream.Send(downlink); err == io.EOF {
		cl.Log.Warn("Monitor downlink stream closed")
		cl.downlink.Lock()
		if cl.downlink.stream == stream {
			cl.downlink.stream = nil
		}
		cl.downlink.Unlock()
		return nil
	}
	return err
}

func (cl *gatewayClient) Close() (err error) {
	wg := &sync.WaitGroup{}

	wg.Add(1)
	go func() {
		defer wg.Done()

		cl.status.Lock()

		if cl.status.stream != nil {
			if cerr := cl.closeStatus(); cerr != nil {
				err = cerr
			}
			cl.status.stream = nil
		}
	}()
	defer cl.status.Unlock()

	wg.Add(1)
	go func() {
		defer wg.Done()

		cl.uplink.Lock()

		if cl.uplink.stream != nil {
			if cerr := cl.closeUplink(); cerr != nil {
				err = cerr
			}
			cl.uplink.stream = nil
		}
	}()
	defer cl.uplink.Unlock()

	wg.Add(1)
	go func() {
		defer wg.Done()

		cl.downlink.Lock()

		if cl.downlink.stream != nil {
			cerr := cl.closeDownlink()
			if cerr != nil {
				err = cerr
			}
			cl.downlink.stream = nil
		}
	}()
	defer cl.downlink.Unlock()

	wg.Wait()
	return err
}

func (cl *gatewayClient) Context() (monitorContext context.Context) {
	return metadata.NewContext(context.Background(), metadata.Pairs(
		"id", cl.id,
		"token", cl.token,
	))
}

func (cl *gatewayClient) setupStatus() (stream Monitor_GatewayStatusClient, err error) {
	stream, err = cl.client.client.GatewayStatus(cl.Context())
	if err != nil {
		cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to open new monitor status stream")
		return nil, err
	}
	cl.Log.Debug("Opened new monitor status stream")

	cl.status.stream = stream
	return stream, nil
}
func (cl *gatewayClient) setupUplink() (stream Monitor_GatewayUplinkClient, err error) {
	stream, err = cl.client.client.GatewayUplink(cl.Context())
	if err != nil {
		cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to open new monitor uplink stream")
		return nil, err
	}
	cl.Log.Debug("Opened new monitor uplink stream")

	cl.uplink.stream = stream
	return stream, nil
}
func (cl *gatewayClient) setupDownlink() (stream Monitor_GatewayDownlinkClient, err error) {
	stream, err = cl.client.client.GatewayDownlink(cl.Context())
	if err != nil {
		cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to open new monitor downlink stream")
		return nil, err
	}
	cl.Log.Debug("Opened new monitor downlink stream")

	cl.downlink.stream = stream
	return stream, nil
}

func (cl *gatewayClient) closeStatus() (err error) {
	err = cl.status.stream.CloseSend()
	if err != nil {
		cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to close status stream")
	}
	cl.Log.Debug("Closed status stream")

	return err
}
func (cl *gatewayClient) closeUplink() (err error) {
	err = cl.uplink.stream.CloseSend()
	if err != nil {
		cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to close uplink stream")
	}
	cl.Log.Debug("Closed uplink stream")

	return err
}
func (cl *gatewayClient) closeDownlink() (err error) {
	err = cl.downlink.stream.CloseSend()
	if err != nil {
		cl.Log.WithError(errors.FromGRPCError(err)).Warn("Failed to close downlink stream")
	}
	cl.Log.Debug("Closed downlink stream")

	return err
}
<|endoftext|>


<commit_before>// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

package cmpopts

import (
	"fmt"
	"reflect"
	"unicode"
	"unicode/utf8"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/internal/function"
)

// IgnoreFields returns an Option that ignores exported fields of the
// given names on a single struct type.
// The struct type is specified by passing in a value of that type.
//
// The name may be a dot-delimited string (e.g., "Foo.Bar") to ignore a
// specific sub-field that is embedded or nested within the parent struct.
//
// This does not handle unexported fields; use IgnoreUnexported instead.
func IgnoreFields(typ interface{}, names ...string) cmp.Option {
	sf := newStructFilter(typ, names...)
	return cmp.FilterPath(sf.filter, cmp.Ignore())
}

// IgnoreTypes returns an Option that ignores all values assignable to
// certain types, which are specified by passing in a value of each type.
func IgnoreTypes(typs ...interface{}) cmp.Option {
	tf := newTypeFilter(typs...)
	return cmp.FilterPath(tf.filter, cmp.Ignore())
}

type typeFilter []reflect.Type

func newTypeFilter(typs ...interface{}) (tf typeFilter) {
	for _, typ := range typs {
		t := reflect.TypeOf(typ)
		if t == nil {
			// This occurs if someone tries to pass in sync.Locker(nil)
			panic("cannot determine type; consider using IgnoreInterfaces")
		}
		tf = append(tf, t)
	}
	return tf
}
func (tf typeFilter) filter(p cmp.Path) bool {
	if len(p) < 1 {
		return false
	}
	t := p.Last().Type()
	for _, ti := range tf {
		if t.AssignableTo(ti) {
			return true
		}
	}
	return false
}

// IgnoreInterfaces returns an Option that ignores all values or references of
// values assignable to certain interface types. These interfaces are specified
// by passing in an anonymous struct with the interface types embedded in it.
// For example, to ignore sync.Locker, pass in struct{sync.Locker}{}.
func IgnoreInterfaces(ifaces interface{}) cmp.Option {
	tf := newIfaceFilter(ifaces)
	return cmp.FilterPath(tf.filter, cmp.Ignore())
}

type ifaceFilter []reflect.Type

func newIfaceFilter(ifaces interface{}) (tf ifaceFilter) {
	t := reflect.TypeOf(ifaces)
	if ifaces == nil || t.Name() != "" || t.Kind() != reflect.Struct {
		panic("input must be an anonymous struct")
	}
	for i := 0; i < t.NumField(); i++ {
		fi := t.Field(i)
		switch {
		case !fi.Anonymous:
			panic("struct cannot have named fields")
		case fi.Type.Kind() != reflect.Interface:
			panic("embedded field must be an interface type")
		case fi.Type.NumMethod() == 0:
			// This matches everything; why would you ever want this?
			panic("cannot ignore empty interface")
		default:
			tf = append(tf, fi.Type)
		}
	}
	return tf
}
func (tf ifaceFilter) filter(p cmp.Path) bool {
	if len(p) < 1 {
		return false
	}
	t := p.Last().Type()
	for _, ti := range tf {
		if t.AssignableTo(ti) {
			return true
		}
		if t.Kind() != reflect.Ptr && reflect.PtrTo(t).AssignableTo(ti) {
			return true
		}
	}
	return false
}

// IgnoreUnexported returns an Option that only ignores the immediate unexported
// fields of a struct, including anonymous fields of unexported types.
// In particular, unexported fields within the struct's exported fields
// of struct types, including anonymous fields, will not be ignored unless the
// type of the field itself is also passed to IgnoreUnexported.
//
// Avoid ignoring unexported fields of a type which you do not control (i.e. a
// type from another repository), as changes to the implementation of such types
// may change how the comparison behaves. Prefer a custom Comparer instead.
func IgnoreUnexported(typs ...interface{}) cmp.Option {
	ux := newUnexportedFilter(typs...)
	return cmp.FilterPath(ux.filter, cmp.Ignore())
}

type unexportedFilter struct{ m map[reflect.Type]bool }

func newUnexportedFilter(typs ...interface{}) unexportedFilter {
	ux := unexportedFilter{m: make(map[reflect.Type]bool)}
	for _, typ := range typs {
		t := reflect.TypeOf(typ)
		if t == nil || t.Kind() != reflect.Struct {
			panic(fmt.Sprintf("invalid struct type: %T", typ))
		}
		ux.m[t] = true
	}
	return ux
}
func (xf unexportedFilter) filter(p cmp.Path) bool {
	sf, ok := p.Index(-1).(cmp.StructField)
	if !ok {
		return false
	}
	return xf.m[p.Index(-2).Type()] && !isExported(sf.Name())
}

// isExported reports whether the identifier is exported.
func isExported(id string) bool {
	r, _ := utf8.DecodeRuneInString(id)
	return unicode.IsUpper(r)
}

// IgnoreSliceElements returns an Option that ignores elements of []V.
// The discard function must be of the form "func(T) bool" which is used to
// ignore slice elements of type V, where V is assignable to T.
// Elements are ignored if the function reports true.
func IgnoreSliceElements(discardFunc interface{}) cmp.Option {
	vf := reflect.ValueOf(discardFunc)
	if !function.IsType(vf.Type(), function.ValuePredicate) || vf.IsNil() {
		panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
	}
	return cmp.FilterPath(func(p cmp.Path) bool {
		si, ok := p.Index(-1).(cmp.SliceIndex)
		if !ok {
			return false
		}
		if !si.Type().AssignableTo(vf.Type().In(0)) {
			return false
		}
		vx, vy := si.Values()
		if vx.IsValid() && vf.Call([]reflect.Value{vx})[0].Bool() {
			return true
		}
		if vy.IsValid() && vf.Call([]reflect.Value{vy})[0].Bool() {
			return true
		}
		return false
	}, cmp.Ignore())
}

// IgnoreMapEntries returns an Option that ignores entries of map[K]V.
// The discard function must be of the form "func(T, R) bool" which is used to
// ignore map entries of type K and V, where K and V are assignable to T and R.
// Entries are ignored if the function reports true.
func IgnoreMapEntries(discardFunc interface{}) cmp.Option {
	vf := reflect.ValueOf(discardFunc)
	if !function.IsType(vf.Type(), function.KeyValuePredicate) || vf.IsNil() {
		panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
	}
	return cmp.FilterPath(func(p cmp.Path) bool {
		mi, ok := p.Index(-1).(cmp.MapIndex)
		if !ok {
			return false
		}
		if !mi.Key().Type().AssignableTo(vf.Type().In(0)) || !mi.Type().AssignableTo(vf.Type().In(1)) {
			return false
		}
		k := mi.Key()
		vx, vy := mi.Values()
		if vx.IsValid() && vf.Call([]reflect.Value{k, vx})[0].Bool() {
			return true
		}
		if vy.IsValid() && vf.Call([]reflect.Value{k, vy})[0].Bool() {
			return true
		}
		return false
	}, cmp.Ignore())
}
<commit_msg>Fix documentation on IgnoreFields (#220)<commit_after>// Copyright 2017, The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

package cmpopts

import (
	"fmt"
	"reflect"
	"unicode"
	"unicode/utf8"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/internal/function"
)

// IgnoreFields returns an Option that ignores fields of the
// given names on a single struct type. It respects the names of exported fields
// that are forwarded due to struct embedding.
// The struct type is specified by passing in a value of that type.
//
// The name may be a dot-delimited string (e.g., "Foo.Bar") to ignore a
// specific sub-field that is embedded or nested within the parent struct.
func IgnoreFields(typ interface{}, names ...string) cmp.Option {
	sf := newStructFilter(typ, names...)
	return cmp.FilterPath(sf.filter, cmp.Ignore())
}

// IgnoreTypes returns an Option that ignores all values assignable to
// certain types, which are specified by passing in a value of each type.
func IgnoreTypes(typs ...interface{}) cmp.Option {
	tf := newTypeFilter(typs...)
	return cmp.FilterPath(tf.filter, cmp.Ignore())
}

type typeFilter []reflect.Type

func newTypeFilter(typs ...interface{}) (tf typeFilter) {
	for _, typ := range typs {
		t := reflect.TypeOf(typ)
		if t == nil {
			// This occurs if someone tries to pass in sync.Locker(nil)
			panic("cannot determine type; consider using IgnoreInterfaces")
		}
		tf = append(tf, t)
	}
	return tf
}
func (tf typeFilter) filter(p cmp.Path) bool {
	if len(p) < 1 {
		return false
	}
	t := p.Last().Type()
	for _, ti := range tf {
		if t.AssignableTo(ti) {
			return true
		}
	}
	return false
}

// IgnoreInterfaces returns an Option that ignores all values or references of
// values assignable to certain interface types. These interfaces are specified
// by passing in an anonymous struct with the interface types embedded in it.
// For example, to ignore sync.Locker, pass in struct{sync.Locker}{}.
func IgnoreInterfaces(ifaces interface{}) cmp.Option {
	tf := newIfaceFilter(ifaces)
	return cmp.FilterPath(tf.filter, cmp.Ignore())
}

type ifaceFilter []reflect.Type

func newIfaceFilter(ifaces interface{}) (tf ifaceFilter) {
	t := reflect.TypeOf(ifaces)
	if ifaces == nil || t.Name() != "" || t.Kind() != reflect.Struct {
		panic("input must be an anonymous struct")
	}
	for i := 0; i < t.NumField(); i++ {
		fi := t.Field(i)
		switch {
		case !fi.Anonymous:
			panic("struct cannot have named fields")
		case fi.Type.Kind() != reflect.Interface:
			panic("embedded field must be an interface type")
		case fi.Type.NumMethod() == 0:
			// This matches everything; why would you ever want this?
			panic("cannot ignore empty interface")
		default:
			tf = append(tf, fi.Type)
		}
	}
	return tf
}
func (tf ifaceFilter) filter(p cmp.Path) bool {
	if len(p) < 1 {
		return false
	}
	t := p.Last().Type()
	for _, ti := range tf {
		if t.AssignableTo(ti) {
			return true
		}
		if t.Kind() != reflect.Ptr && reflect.PtrTo(t).AssignableTo(ti) {
			return true
		}
	}
	return false
}

// IgnoreUnexported returns an Option that only ignores the immediate unexported
// fields of a struct, including anonymous fields of unexported types.
// In particular, unexported fields within the struct's exported fields
// of struct types, including anonymous fields, will not be ignored unless the
// type of the field itself is also passed to IgnoreUnexported.
//
// Avoid ignoring unexported fields of a type which you do not control (i.e. a
// type from another repository), as changes to the implementation of such types
// may change how the comparison behaves. Prefer a custom Comparer instead.
func IgnoreUnexported(typs ...interface{}) cmp.Option {
	ux := newUnexportedFilter(typs...)
	return cmp.FilterPath(ux.filter, cmp.Ignore())
}

type unexportedFilter struct{ m map[reflect.Type]bool }

func newUnexportedFilter(typs ...interface{}) unexportedFilter {
	ux := unexportedFilter{m: make(map[reflect.Type]bool)}
	for _, typ := range typs {
		t := reflect.TypeOf(typ)
		if t == nil || t.Kind() != reflect.Struct {
			panic(fmt.Sprintf("invalid struct type: %T", typ))
		}
		ux.m[t] = true
	}
	return ux
}
func (xf unexportedFilter) filter(p cmp.Path) bool {
	sf, ok := p.Index(-1).(cmp.StructField)
	if !ok {
		return false
	}
	return xf.m[p.Index(-2).Type()] && !isExported(sf.Name())
}

// isExported reports whether the identifier is exported.
func isExported(id string) bool {
	r, _ := utf8.DecodeRuneInString(id)
	return unicode.IsUpper(r)
}

// IgnoreSliceElements returns an Option that ignores elements of []V.
// The discard function must be of the form "func(T) bool" which is used to
// ignore slice elements of type V, where V is assignable to T.
// Elements are ignored if the function reports true.
func IgnoreSliceElements(discardFunc interface{}) cmp.Option {
	vf := reflect.ValueOf(discardFunc)
	if !function.IsType(vf.Type(), function.ValuePredicate) || vf.IsNil() {
		panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
	}
	return cmp.FilterPath(func(p cmp.Path) bool {
		si, ok := p.Index(-1).(cmp.SliceIndex)
		if !ok {
			return false
		}
		if !si.Type().AssignableTo(vf.Type().In(0)) {
			return false
		}
		vx, vy := si.Values()
		if vx.IsValid() && vf.Call([]reflect.Value{vx})[0].Bool() {
			return true
		}
		if vy.IsValid() && vf.Call([]reflect.Value{vy})[0].Bool() {
			return true
		}
		return false
	}, cmp.Ignore())
}

// IgnoreMapEntries returns an Option that ignores entries of map[K]V.
// The discard function must be of the form "func(T, R) bool" which is used to
// ignore map entries of type K and V, where K and V are assignable to T and R.
// Entries are ignored if the function reports true.
func IgnoreMapEntries(discardFunc interface{}) cmp.Option {
	vf := reflect.ValueOf(discardFunc)
	if !function.IsType(vf.Type(), function.KeyValuePredicate) || vf.IsNil() {
		panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
	}
	return cmp.FilterPath(func(p cmp.Path) bool {
		mi, ok := p.Index(-1).(cmp.MapIndex)
		if !ok {
			return false
		}
		if !mi.Key().Type().AssignableTo(vf.Type().In(0)) || !mi.Type().AssignableTo(vf.Type().In(1)) {
			return false
		}
		k := mi.Key()
		vx, vy := mi.Values()
		if vx.IsValid() && vf.Call([]reflect.Value{k, vx})[0].Bool() {
			return true
		}
		if vy.IsValid() && vf.Call([]reflect.Value{k, vy})[0].Bool() {
			return true
		}
		return false
	}, cmp.Ignore())
}
<|endoftext|>


<commit_before>// Copyright 2015, 2016 Eris Industries (UK) Ltd.
// This file is part of Eris-RT

// Eris-RT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Eris-RT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.

package core

import (
	"encoding/hex"
	"fmt"
	"strconv"

	log "github.com/eris-ltd/eris-logger"

	ptypes "github.com/eris-ltd/eris-db/permission/types"

	"github.com/eris-ltd/eris-db/account"
	"github.com/eris-ltd/eris-db/client"
	"github.com/eris-ltd/eris-db/keys"
	"github.com/eris-ltd/eris-db/txs"
)

var (
	MaxCommitWaitTimeSeconds = 20
)

//------------------------------------------------------------------------------------
// core functions with string args.
// validates strings and forms transaction

func Send(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, toAddr, amtS, nonceS string) (*txs.SendTx, error) {
	pub, amt, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, nonceS)
	if err != nil {
		return nil, err
	}

	if toAddr == "" {
		return nil, fmt.Errorf("destination address must be given with --to flag")
	}

	toAddrBytes, err := hex.DecodeString(toAddr)
	if err != nil {
		return nil, fmt.Errorf("toAddr is bad hex: %v", err)
	}

	tx := txs.NewSendTx()
	tx.AddInputWithNonce(pub, amt, int(nonce))
	tx.AddOutput(toAddrBytes, amt)

	return tx, nil
}

func Call(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, toAddr, amtS, nonceS, gasS, feeS, data string) (*txs.CallTx, error) {
	pub, amt, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, nonceS)
	if err != nil {
		return nil, err
	}

	toAddrBytes, err := hex.DecodeString(toAddr)
	if err != nil {
		return nil, fmt.Errorf("toAddr is bad hex: %v", err)
	}

	fee, err := strconv.ParseInt(feeS, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("fee is misformatted: %v", err)
	}

	gas, err := strconv.ParseInt(gasS, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("gas is misformatted: %v", err)
	}

	dataBytes, err := hex.DecodeString(data)
	if err != nil {
		return nil, fmt.Errorf("data is bad hex: %v", err)
	}

	tx := txs.NewCallTxWithNonce(pub, toAddrBytes, dataBytes, amt, gas, fee, int(nonce))
	return tx, nil
}

func Name(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, amtS, nonceS, feeS, name, data string) (*txs.NameTx, error) {
	pub, amt, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, nonceS)
	if err != nil {
		return nil, err
	}

	fee, err := strconv.ParseInt(feeS, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("fee is misformatted: %v", err)
	}

	tx := txs.NewNameTxWithNonce(pub, name, data, amt, fee, int(nonce))
	return tx, nil
}

type PermFunc struct {
	Name string
	Args string
}

var PermsFuncs = []PermFunc{
	{"set_base", "address, permission flag, value"},
	{"unset_base", "address, permission flag"},
	{"set_global", "permission flag, value"},
	{"add_role", "address, role"},
	{"rm_role", "address, role"},
}

func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addrS, nonceS, permFunc string, argsS []string) (*txs.PermissionsTx, error) {
	pub, _, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addrS, "0", nonceS)
	if err != nil {
		return nil, err
	}
	var args ptypes.PermArgs
	switch permFunc {
	case "set_base":
		addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1])
		if err != nil {
			return nil, err
		}
		if len(argsS) != 3 {
			return nil, fmt.Errorf("set_base also takes a value (true or false)")
		}
		var value bool
		if argsS[2] == "true" {
			value = true
		} else if argsS[2] == "false" {
			value = false
		} else {
			return nil, fmt.Errorf("Unknown value %s", argsS[2])
		}
		args = &ptypes.SetBaseArgs{addr, pF, value}
	case "unset_base":
		addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1])
		if err != nil {
			return nil, err
		}
		args = &ptypes.UnsetBaseArgs{addr, pF}
	case "set_global":
		pF, err := ptypes.PermStringToFlag(argsS[0])
		if err != nil {
			return nil, err
		}
		var value bool
		if argsS[1] == "true" {
			value = true
		} else if argsS[1] == "false" {
			value = false
		} else {
			return nil, fmt.Errorf("Unknown value %s", argsS[1])
		}
		args = &ptypes.SetGlobalArgs{pF, value}
	case "add_role":
		addr, err := hex.DecodeString(argsS[0])
		if err != nil {
			return nil, err
		}
		args = &ptypes.AddRoleArgs{addr, argsS[1]}
	case "rm_role":
		addr, err := hex.DecodeString(argsS[0])
		if err != nil {
			return nil, err
		}
		args = &ptypes.RmRoleArgs{addr, argsS[1]}
	default:
		return nil, fmt.Errorf("Invalid permission function for use in PermissionsTx: %s", permFunc)
	}
	// args := snativeArgs(
	tx := txs.NewPermissionsTxWithNonce(pub, args, int(nonce))
	return tx, nil
}

func Bond(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, unbondAddr, amtS, nonceS string) (*txs.BondTx, error) {
	return nil, fmt.Errorf("Bond Transaction formation to be implemented on 0.12.0")
	// pub, amt, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, "", amtS, nonceS)
	// if err != nil {
	// 	return nil, err
	// }
	// var pubKey crypto.PubKeyEd25519
	// var unbondAddrBytes []byte

	// if unbondAddr == "" {
	// 	pkb, _ := hex.DecodeString(pubkey)
	// 	copy(pubKey[:], pkb)
	// 	unbondAddrBytes = pubKey.Address()
	// } else {
	// 	unbondAddrBytes, err = hex.DecodeString(unbondAddr)
	// 	if err != nil {
	// 		return nil, fmt.Errorf("unbondAddr is bad hex: %v", err)
	// 	}

	// }

	// tx, err := types.NewBondTx(pub)
	// if err != nil {
	// 	return nil, err
	// }
	// tx.AddInputWithNonce(pub, amt, int(nonce))
	// tx.AddOutput(unbondAddrBytes, amt)

	// return tx, nil
}

func Unbond(addrS, heightS string) (*txs.UnbondTx, error) {
	return nil, fmt.Errorf("Unbond Transaction formation to be implemented on 0.12.0")
	// if addrS == "" {
	// 	return nil, fmt.Errorf("Validator address must be given with --addr flag")
	// }

	// addrBytes, err := hex.DecodeString(addrS)
	// if err != nil {
	// 	return nil, fmt.Errorf("addr is bad hex: %v", err)
	// }

	// height, err := strconv.ParseInt(heightS, 10, 32)
	// if err != nil {
	// 	return nil, fmt.Errorf("height is misformatted: %v", err)
	// }

	// return &types.UnbondTx{
	// 	Address: addrBytes,
	// 	Height:  int(height),
	// }, nil
}

func Rebond(addrS, heightS string) (*txs.RebondTx, error) {
	return nil, fmt.Errorf("Rebond Transaction formation to be implemented on 0.12.0")
	// 	if addrS == "" {
	// 		return nil, fmt.Errorf("Validator address must be given with --addr flag")
	// 	}

	// 	addrBytes, err := hex.DecodeString(addrS)
	// 	if err != nil {
	// 		return nil, fmt.Errorf("addr is bad hex: %v", err)
	// 	}

	// 	height, err := strconv.ParseInt(heightS, 10, 32)
	// 	if err != nil {
	// 		return nil, fmt.Errorf("height is misformatted: %v", err)
	// 	}

	// 	return &types.RebondTx{
	// 		Address: addrBytes,
	// 		Height:  int(height),
	// 	}, nil
}

type TxResult struct {
	BlockHash []byte // all txs get in a block
	Hash      []byte // all txs get a hash

	// only CallTx
	Address   []byte // only for new contracts
	Return    []byte
	Exception string

	//TODO: make Broadcast() errors more responsive so we
	// can differentiate mempool errors from other
}

// Preserve
func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) {
	var inputAddr []byte
	if sign {
		inputAddr, tx, err = signTx(keyClient, chainID, tx)
		if err != nil {
			return nil, err
		}
		log.WithFields(log.Fields{
			"transaction": string(account.SignBytes(chainID, tx)),
		}).Debug("Signed transaction")
	}

	if broadcast {
		if wait {
			wsClient, err := nodeClient.DeriveWebsocketClient()
			if err != nil {
				return nil, err
			}
			var confirmationChannel chan client.Confirmation
			confirmationChannel, err = wsClient.WaitForConfirmation(tx, chainID, inputAddr)
			if err != nil {
				return nil, err
			} else {
				defer func() {
					if err != nil {
						// if broadcast threw an error, just return
						return
					}
					log.Debug("Waiting for transaction to be confirmed.")
					confirmation := <-confirmationChannel
					if confirmation.Error != nil {
						log.Errorf("Encountered error waiting for event: %s\n", confirmation.Error)
						err = confirmation.Error
						return
					}
					if confirmation.Exception != nil {
						log.Errorf("Encountered Exception from chain w: %s\n", confirmation.Error)
						err = confirmation.Exception
						return
					}
					txResult.BlockHash = confirmation.BlockHash
					txResult.Exception = ""
					eventDataTx, ok := confirmation.Event.(*txs.EventDataTx)
					if !ok {
						log.Errorf("Received wrong event type.")
						err = fmt.Errorf("Received wrong event type.")
						return
					}
					txResult.Return = eventDataTx.Return
				}()
			}
		}

		var receipt *txs.Receipt
		receipt, err = nodeClient.Broadcast(tx)
		if err != nil {
			return nil, err
		}
		txResult = &TxResult{
			Hash: receipt.TxHash,
		}
		// NOTE: [ben] is this consistent with the Ethereum protocol?  It should seem
		// reasonable to get this returned from the chain directly.  Alternatively,
		// the benefit is that the we don't need to trust the chain node
		if tx_, ok := tx.(*txs.CallTx); ok {
			if len(tx_.Address) == 0 {
				txResult.Address = txs.NewContractAddress(tx_.Input.Address, tx_.Input.Sequence)
			}
		}
	}
	return
}
<commit_msg>client/core: fixes 378 report exception, not error<commit_after>// Copyright 2015, 2016 Eris Industries (UK) Ltd.
// This file is part of Eris-RT

// Eris-RT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Eris-RT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.

package core

import (
	"encoding/hex"
	"fmt"
	"strconv"

	log "github.com/eris-ltd/eris-logger"

	ptypes "github.com/eris-ltd/eris-db/permission/types"

	"github.com/eris-ltd/eris-db/account"
	"github.com/eris-ltd/eris-db/client"
	"github.com/eris-ltd/eris-db/keys"
	"github.com/eris-ltd/eris-db/txs"
)

var (
	MaxCommitWaitTimeSeconds = 20
)

//------------------------------------------------------------------------------------
// core functions with string args.
// validates strings and forms transaction

func Send(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, toAddr, amtS, nonceS string) (*txs.SendTx, error) {
	pub, amt, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, nonceS)
	if err != nil {
		return nil, err
	}

	if toAddr == "" {
		return nil, fmt.Errorf("destination address must be given with --to flag")
	}

	toAddrBytes, err := hex.DecodeString(toAddr)
	if err != nil {
		return nil, fmt.Errorf("toAddr is bad hex: %v", err)
	}

	tx := txs.NewSendTx()
	tx.AddInputWithNonce(pub, amt, int(nonce))
	tx.AddOutput(toAddrBytes, amt)

	return tx, nil
}

func Call(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, toAddr, amtS, nonceS, gasS, feeS, data string) (*txs.CallTx, error) {
	pub, amt, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, nonceS)
	if err != nil {
		return nil, err
	}

	toAddrBytes, err := hex.DecodeString(toAddr)
	if err != nil {
		return nil, fmt.Errorf("toAddr is bad hex: %v", err)
	}

	fee, err := strconv.ParseInt(feeS, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("fee is misformatted: %v", err)
	}

	gas, err := strconv.ParseInt(gasS, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("gas is misformatted: %v", err)
	}

	dataBytes, err := hex.DecodeString(data)
	if err != nil {
		return nil, fmt.Errorf("data is bad hex: %v", err)
	}

	tx := txs.NewCallTxWithNonce(pub, toAddrBytes, dataBytes, amt, gas, fee, int(nonce))
	return tx, nil
}

func Name(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, amtS, nonceS, feeS, name, data string) (*txs.NameTx, error) {
	pub, amt, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addr, amtS, nonceS)
	if err != nil {
		return nil, err
	}

	fee, err := strconv.ParseInt(feeS, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("fee is misformatted: %v", err)
	}

	tx := txs.NewNameTxWithNonce(pub, name, data, amt, fee, int(nonce))
	return tx, nil
}

type PermFunc struct {
	Name string
	Args string
}

var PermsFuncs = []PermFunc{
	{"set_base", "address, permission flag, value"},
	{"unset_base", "address, permission flag"},
	{"set_global", "permission flag, value"},
	{"add_role", "address, role"},
	{"rm_role", "address, role"},
}

func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addrS, nonceS, permFunc string, argsS []string) (*txs.PermissionsTx, error) {
	pub, _, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addrS, "0", nonceS)
	if err != nil {
		return nil, err
	}
	var args ptypes.PermArgs
	switch permFunc {
	case "set_base":
		addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1])
		if err != nil {
			return nil, err
		}
		if len(argsS) != 3 {
			return nil, fmt.Errorf("set_base also takes a value (true or false)")
		}
		var value bool
		if argsS[2] == "true" {
			value = true
		} else if argsS[2] == "false" {
			value = false
		} else {
			return nil, fmt.Errorf("Unknown value %s", argsS[2])
		}
		args = &ptypes.SetBaseArgs{addr, pF, value}
	case "unset_base":
		addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1])
		if err != nil {
			return nil, err
		}
		args = &ptypes.UnsetBaseArgs{addr, pF}
	case "set_global":
		pF, err := ptypes.PermStringToFlag(argsS[0])
		if err != nil {
			return nil, err
		}
		var value bool
		if argsS[1] == "true" {
			value = true
		} else if argsS[1] == "false" {
			value = false
		} else {
			return nil, fmt.Errorf("Unknown value %s", argsS[1])
		}
		args = &ptypes.SetGlobalArgs{pF, value}
	case "add_role":
		addr, err := hex.DecodeString(argsS[0])
		if err != nil {
			return nil, err
		}
		args = &ptypes.AddRoleArgs{addr, argsS[1]}
	case "rm_role":
		addr, err := hex.DecodeString(argsS[0])
		if err != nil {
			return nil, err
		}
		args = &ptypes.RmRoleArgs{addr, argsS[1]}
	default:
		return nil, fmt.Errorf("Invalid permission function for use in PermissionsTx: %s", permFunc)
	}
	// args := snativeArgs(
	tx := txs.NewPermissionsTxWithNonce(pub, args, int(nonce))
	return tx, nil
}

func Bond(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, unbondAddr, amtS, nonceS string) (*txs.BondTx, error) {
	return nil, fmt.Errorf("Bond Transaction formation to be implemented on 0.12.0")
	// pub, amt, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, "", amtS, nonceS)
	// if err != nil {
	// 	return nil, err
	// }
	// var pubKey crypto.PubKeyEd25519
	// var unbondAddrBytes []byte

	// if unbondAddr == "" {
	// 	pkb, _ := hex.DecodeString(pubkey)
	// 	copy(pubKey[:], pkb)
	// 	unbondAddrBytes = pubKey.Address()
	// } else {
	// 	unbondAddrBytes, err = hex.DecodeString(unbondAddr)
	// 	if err != nil {
	// 		return nil, fmt.Errorf("unbondAddr is bad hex: %v", err)
	// 	}

	// }

	// tx, err := types.NewBondTx(pub)
	// if err != nil {
	// 	return nil, err
	// }
	// tx.AddInputWithNonce(pub, amt, int(nonce))
	// tx.AddOutput(unbondAddrBytes, amt)

	// return tx, nil
}

func Unbond(addrS, heightS string) (*txs.UnbondTx, error) {
	return nil, fmt.Errorf("Unbond Transaction formation to be implemented on 0.12.0")
	// if addrS == "" {
	// 	return nil, fmt.Errorf("Validator address must be given with --addr flag")
	// }

	// addrBytes, err := hex.DecodeString(addrS)
	// if err != nil {
	// 	return nil, fmt.Errorf("addr is bad hex: %v", err)
	// }

	// height, err := strconv.ParseInt(heightS, 10, 32)
	// if err != nil {
	// 	return nil, fmt.Errorf("height is misformatted: %v", err)
	// }

	// return &types.UnbondTx{
	// 	Address: addrBytes,
	// 	Height:  int(height),
	// }, nil
}

func Rebond(addrS, heightS string) (*txs.RebondTx, error) {
	return nil, fmt.Errorf("Rebond Transaction formation to be implemented on 0.12.0")
	// 	if addrS == "" {
	// 		return nil, fmt.Errorf("Validator address must be given with --addr flag")
	// 	}

	// 	addrBytes, err := hex.DecodeString(addrS)
	// 	if err != nil {
	// 		return nil, fmt.Errorf("addr is bad hex: %v", err)
	// 	}

	// 	height, err := strconv.ParseInt(heightS, 10, 32)
	// 	if err != nil {
	// 		return nil, fmt.Errorf("height is misformatted: %v", err)
	// 	}

	// 	return &types.RebondTx{
	// 		Address: addrBytes,
	// 		Height:  int(height),
	// 	}, nil
}

type TxResult struct {
	BlockHash []byte // all txs get in a block
	Hash      []byte // all txs get a hash

	// only CallTx
	Address   []byte // only for new contracts
	Return    []byte
	Exception string

	//TODO: make Broadcast() errors more responsive so we
	// can differentiate mempool errors from other
}

// Preserve
func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) {
	var inputAddr []byte
	if sign {
		inputAddr, tx, err = signTx(keyClient, chainID, tx)
		if err != nil {
			return nil, err
		}
		log.WithFields(log.Fields{
			"transaction": string(account.SignBytes(chainID, tx)),
		}).Debug("Signed transaction")
	}

	if broadcast {
		if wait {
			wsClient, err := nodeClient.DeriveWebsocketClient()
			if err != nil {
				return nil, err
			}
			var confirmationChannel chan client.Confirmation
			confirmationChannel, err = wsClient.WaitForConfirmation(tx, chainID, inputAddr)
			if err != nil {
				return nil, err
			} else {
				defer func() {
					if err != nil {
						// if broadcast threw an error, just return
						return
					}
					log.Debug("Waiting for transaction to be confirmed.")
					confirmation := <-confirmationChannel
					if confirmation.Error != nil {
						log.Errorf("Encountered error waiting for event: %s\n", confirmation.Error)
						err = confirmation.Error
						return
					}
					if confirmation.Exception != nil {
						log.Errorf("Encountered Exception from chain: %s\n", confirmation.Exception)
						err = confirmation.Exception
						return
					}
					txResult.BlockHash = confirmation.BlockHash
					txResult.Exception = ""
					eventDataTx, ok := confirmation.Event.(*txs.EventDataTx)
					if !ok {
						log.Errorf("Received wrong event type.")
						err = fmt.Errorf("Received wrong event type.")
						return
					}
					txResult.Return = eventDataTx.Return
				}()
			}
		}

		var receipt *txs.Receipt
		receipt, err = nodeClient.Broadcast(tx)
		if err != nil {
			return nil, err
		}
		txResult = &TxResult{
			Hash: receipt.TxHash,
		}
		// NOTE: [ben] is this consistent with the Ethereum protocol?  It should seem
		// reasonable to get this returned from the chain directly.  Alternatively,
		// the benefit is that the we don't need to trust the chain node
		if tx_, ok := tx.(*txs.CallTx); ok {
			if len(tx_.Address) == 0 {
				txResult.Address = txs.NewContractAddress(tx_.Input.Address, tx_.Input.Sequence)
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package dht

import (
	"log"
	"net"
	"sync"

	"bitbucket.org/anacrolix/go.torrent/util"
)

type peerDiscovery struct {
	*peerStream
	triedAddrs map[string]struct{}
	backlog    map[string]net.Addr
	pending    int
	server     *Server
	infoHash   string
}

const (
	parallelQueries = 100
	backlogMaxLen   = 10000
)

func (me *peerDiscovery) Close() {
	me.peerStream.Close()
}

func (s *Server) GetPeers(infoHash string) (*peerStream, error) {
	s.mu.Lock()
	startAddrs := func() (ret []net.Addr) {
		for _, n := range s.closestGoodNodes(160, infoHash) {
			ret = append(ret, n.addr)
		}
		return
	}()
	s.mu.Unlock()
	if len(startAddrs) == 0 {
		addr, err := bootstrapAddr()
		if err != nil {
			return nil, err
		}
		startAddrs = append(startAddrs, addr)
	}
	disc := &peerDiscovery{
		peerStream: &peerStream{
			Values: make(chan peerStreamValue),
			stop:   make(chan struct{}),
			values: make(chan peerStreamValue),
		},
		triedAddrs: make(map[string]struct{}, 500),
		backlog:    make(map[string]net.Addr, parallelQueries),
		server:     s,
		infoHash:   infoHash,
	}
	// Function ferries from values to Values until discovery is halted.
	go func() {
		defer close(disc.Values)
		for {
			select {
			case psv := <-disc.values:
				select {
				case disc.Values <- psv:
				case <-disc.stop:
					return
				}
			case <-disc.stop:
				return
			}
		}
	}()
	disc.mu.Lock()
	for _, addr := range startAddrs {
		disc.contact(addr)
	}
	disc.mu.Unlock()
	return disc.peerStream, nil
}

func (me *peerDiscovery) gotNodeAddr(addr net.Addr) {
	if util.AddrPort(addr) == 0 {
		// Not a contactable address.
		return
	}
	if me.server.ipBlocked(util.AddrIP(addr)) {
		return
	}
	if _, ok := me.triedAddrs[addr.String()]; ok {
		return
	}
	if _, ok := me.backlog[addr.String()]; ok {
		return
	}
	if me.pending >= parallelQueries {
		if len(me.backlog) < backlogMaxLen {
			me.backlog[addr.String()] = addr
		}
	} else {
		me.contact(addr)
	}
}

func (me *peerDiscovery) contact(addr net.Addr) {
	me.triedAddrs[addr.String()] = struct{}{}
	if err := me.getPeers(addr); err != nil {
		log.Printf("error sending get_peers request to %s: %s", addr, err)
		return
	}
	me.pending++
}

func (me *peerDiscovery) transactionClosed() {
	me.pending--
	for key, addr := range me.backlog {
		if me.pending >= parallelQueries {
			break
		}
		delete(me.backlog, key)
		me.contact(addr)
	}
	if me.pending == 0 {
		me.Close()
		return
	}
}

func (me *peerDiscovery) responseNode(node NodeInfo) {
	me.gotNodeAddr(node.Addr)
}

func (me *peerDiscovery) closingCh() chan struct{} {
	return me.peerStream.stop
}

func (me *peerDiscovery) getPeers(addr net.Addr) error {
	me.server.mu.Lock()
	defer me.server.mu.Unlock()
	t, err := me.server.getPeers(addr, me.infoHash)
	if err != nil {
		return err
	}
	go func() {
		select {
		case m := <-t.Response:
			me.mu.Lock()
			if nodes := m.Nodes(); len(nodes) != 0 {
				for _, n := range nodes {
					me.responseNode(n)
				}
			}
			me.mu.Unlock()
			if vs := extractValues(m); vs != nil {
				nodeInfo := NodeInfo{
					Addr: t.remoteAddr,
				}
				id := func() string {
					defer func() {
						recover()
					}()
					return m["r"].(map[string]interface{})["id"].(string)
				}()
				copy(nodeInfo.ID[:], id)
				select {
				case me.peerStream.values <- peerStreamValue{
					Peers:    vs,
					NodeInfo: nodeInfo,
				}:
				case <-me.peerStream.stop:
				}
			}
		case <-me.closingCh():
		}
		t.Close()
		me.mu.Lock()
		me.transactionClosed()
		me.mu.Unlock()
	}()
	return nil
}

type peerStreamValue struct {
	Peers    []util.CompactPeer // Peers given in get_peers response.
	NodeInfo                    // The node that gave the response.
}

type peerStream struct {
	mu     sync.Mutex
	Values chan peerStreamValue
	// Inner chan is set to nil when on close.
	values chan peerStreamValue
	stop   chan struct{}
}

func (ps *peerStream) Close() {
	ps.mu.Lock()
	defer ps.mu.Unlock()
	select {
	case <-ps.stop:
	default:
		close(ps.stop)
	}
}
<commit_msg>dht: Slow down the start a little, as lots of torrents will hammer out UDP packets<commit_after>package dht

import (
	"log"
	"net"
	"sync"
	"time"

	"bitbucket.org/anacrolix/go.torrent/util"
)

type peerDiscovery struct {
	*peerStream
	triedAddrs map[string]struct{}
	backlog    map[string]net.Addr
	pending    int
	server     *Server
	infoHash   string
}

const (
	parallelQueries = 100
	backlogMaxLen   = 10000
)

func (me *peerDiscovery) Close() {
	me.peerStream.Close()
}

func (s *Server) GetPeers(infoHash string) (*peerStream, error) {
	s.mu.Lock()
	startAddrs := func() (ret []net.Addr) {
		for _, n := range s.closestGoodNodes(160, infoHash) {
			ret = append(ret, n.addr)
		}
		return
	}()
	s.mu.Unlock()
	if len(startAddrs) == 0 {
		addr, err := bootstrapAddr()
		if err != nil {
			return nil, err
		}
		startAddrs = append(startAddrs, addr)
	}
	disc := &peerDiscovery{
		peerStream: &peerStream{
			Values: make(chan peerStreamValue),
			stop:   make(chan struct{}),
			values: make(chan peerStreamValue),
		},
		triedAddrs: make(map[string]struct{}, 500),
		backlog:    make(map[string]net.Addr, parallelQueries),
		server:     s,
		infoHash:   infoHash,
	}
	// Function ferries from values to Values until discovery is halted.
	go func() {
		defer close(disc.Values)
		for {
			select {
			case psv := <-disc.values:
				select {
				case disc.Values <- psv:
				case <-disc.stop:
					return
				}
			case <-disc.stop:
				return
			}
		}
	}()
	disc.mu.Lock()
	for i, addr := range startAddrs {
		if i != 0 {
			time.Sleep(time.Millisecond)
		}
		disc.contact(addr)
	}
	disc.mu.Unlock()
	return disc.peerStream, nil
}

func (me *peerDiscovery) gotNodeAddr(addr net.Addr) {
	if util.AddrPort(addr) == 0 {
		// Not a contactable address.
		return
	}
	if me.server.ipBlocked(util.AddrIP(addr)) {
		return
	}
	if _, ok := me.triedAddrs[addr.String()]; ok {
		return
	}
	if _, ok := me.backlog[addr.String()]; ok {
		return
	}
	if me.pending >= parallelQueries {
		if len(me.backlog) < backlogMaxLen {
			me.backlog[addr.String()] = addr
		}
	} else {
		me.contact(addr)
	}
}

func (me *peerDiscovery) contact(addr net.Addr) {
	me.triedAddrs[addr.String()] = struct{}{}
	if err := me.getPeers(addr); err != nil {
		log.Printf("error sending get_peers request to %s: %s", addr, err)
		return
	}
	me.pending++
}

func (me *peerDiscovery) transactionClosed() {
	me.pending--
	for key, addr := range me.backlog {
		if me.pending >= parallelQueries {
			break
		}
		delete(me.backlog, key)
		me.contact(addr)
	}
	if me.pending == 0 {
		me.Close()
		return
	}
}

func (me *peerDiscovery) responseNode(node NodeInfo) {
	me.gotNodeAddr(node.Addr)
}

func (me *peerDiscovery) closingCh() chan struct{} {
	return me.peerStream.stop
}

func (me *peerDiscovery) getPeers(addr net.Addr) error {
	me.server.mu.Lock()
	defer me.server.mu.Unlock()
	t, err := me.server.getPeers(addr, me.infoHash)
	if err != nil {
		return err
	}
	go func() {
		select {
		case m := <-t.Response:
			me.mu.Lock()
			if nodes := m.Nodes(); len(nodes) != 0 {
				for _, n := range nodes {
					me.responseNode(n)
				}
			}
			me.mu.Unlock()
			if vs := extractValues(m); vs != nil {
				nodeInfo := NodeInfo{
					Addr: t.remoteAddr,
				}
				id := func() string {
					defer func() {
						recover()
					}()
					return m["r"].(map[string]interface{})["id"].(string)
				}()
				copy(nodeInfo.ID[:], id)
				select {
				case me.peerStream.values <- peerStreamValue{
					Peers:    vs,
					NodeInfo: nodeInfo,
				}:
				case <-me.peerStream.stop:
				}
			}
		case <-me.closingCh():
		}
		t.Close()
		me.mu.Lock()
		me.transactionClosed()
		me.mu.Unlock()
	}()
	return nil
}

type peerStreamValue struct {
	Peers    []util.CompactPeer // Peers given in get_peers response.
	NodeInfo                    // The node that gave the response.
}

type peerStream struct {
	mu     sync.Mutex
	Values chan peerStreamValue
	// Inner chan is set to nil when on close.
	values chan peerStreamValue
	stop   chan struct{}
}

func (ps *peerStream) Close() {
	ps.mu.Lock()
	defer ps.mu.Unlock()
	select {
	case <-ps.stop:
	default:
		close(ps.stop)
	}
}
<|endoftext|>


<commit_before>package transfer

import (
	"context"
	"errors"
	"net"
	"sync"

	"github.com/coredns/coredns/plugin"
	clog "github.com/coredns/coredns/plugin/pkg/log"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
)

var log = clog.NewWithPlugin("transfer")

// Transfer is a plugin that handles zone transfers.
type Transfer struct {
	Transferers []Transferer // List of plugins that implement Transferer
	xfrs        []*xfr
	Next        plugin.Handler
}

type xfr struct {
	Zones []string
	to    []string
}

// Transferer may be implemented by plugins to enable zone transfers
type Transferer interface {
	// Transfer returns a channel to which it writes responses to the transfer request.
	// If the plugin is not authoritative for the zone, it should immediately return the
	// transfer.ErrNotAuthoritative error. This is important otherwise the transfer plugin can
	// use plugin X while it should transfer the data from plugin Y.
	//
	// If serial is 0, handle as an AXFR request. Transfer should send all records
	// in the zone to the channel. The SOA should be written to the channel first, followed
	// by all other records, including all NS + glue records. The implemenation is also responsible
	// for sending the last SOA record (to signal end of the transfer). This plugin will just grab
	// these records and send them back to the requester, there is little validation done.
	//
	// If serial is not 0, it will be handled as an IXFR request. If the serial is equal to or greater (newer) than
	// the current serial for the zone, send a single SOA record to the channel and then close it.
	// If the serial is less (older) than the current serial for the zone, perform an AXFR fallback
	// by proceeding as if an AXFR was requested (as above).
	Transfer(zone string, serial uint32) (<-chan []dns.RR, error)
}

var (
	// ErrNotAuthoritative is returned by Transfer() when the plugin is not authoritative for the zone.
	ErrNotAuthoritative = errors.New("not authoritative for zone")
)

// ServeDNS implements the plugin.Handler interface.
func (t *Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
	state := request.Request{W: w, Req: r}
	if state.QType() != dns.TypeAXFR && state.QType() != dns.TypeIXFR {
		return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
	}

	x := longestMatch(t.xfrs, state.QName())
	if x == nil {
		return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
	}

	if !x.allowed(state) {
		// write msg here, so logging will pick it up
		m := new(dns.Msg)
		m.SetRcode(r, dns.RcodeRefused)
		w.WriteMsg(m)
		return 0, nil
	}

	// Get serial from request if this is an IXFR.
	var serial uint32
	if state.QType() == dns.TypeIXFR {
		if len(r.Ns) != 1 {
			return dns.RcodeServerFailure, nil
		}
		soa, ok := r.Ns[0].(*dns.SOA)
		if !ok {
			return dns.RcodeServerFailure, nil
		}
		serial = soa.Serial
	}

	// Get a receiving channel from the first Transferer plugin that returns one.
	var pchan <-chan []dns.RR
	var err error
	for _, p := range t.Transferers {
		pchan, err = p.Transfer(state.QName(), serial)
		if err == ErrNotAuthoritative {
			// plugin was not authoritative for the zone, try next plugin
			continue
		}
		if err != nil {
			return dns.RcodeServerFailure, err
		}
		break
	}

	if pchan == nil {
		return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
	}

	// Send response to client
	ch := make(chan *dns.Envelope)
	tr := new(dns.Transfer)
	wg := new(sync.WaitGroup)
	wg.Add(1)
	go func() {
		tr.Out(w, r, ch)
		wg.Done()
	}()

	rrs := []dns.RR{}
	l := 0
	var soa *dns.SOA
	for records := range pchan {
		if x, ok := records[0].(*dns.SOA); ok && soa == nil {
			soa = x
		}
		rrs = append(rrs, records...)
		if len(rrs) > 500 {
			ch <- &dns.Envelope{RR: rrs}
			l += len(rrs)
			rrs = []dns.RR{}
		}
	}

	// if we are here and we only hold 1 soa (len(rrs) == 1) and soa != nil, and IXFR fallback should
	// be performed. We haven't send anything on ch yet, so that can be closed (and waited for), and we only
	// need to return the SOA back to the client and return.
	if len(rrs) == 1 && soa != nil { // soa should never be nil...
		close(ch)
		wg.Wait()

		m := new(dns.Msg)
		m.SetReply(r)
		m.Answer = []dns.RR{soa}
		w.WriteMsg(m)

		log.Infof("Outgoing incremental transfer for up to date zone %q to %s for %d SOA serial", state.QName(), state.IP(), serial)
		return 0, nil
	}

	// if we are here and we only hold 1 soa (len(rrs) == 1) and soa != nil, and IXFR fallback should
	// be performed. We haven't send anything on ch yet, so that can be closed (and waited for), and we only
	// need to return the SOA back to the client and return.
	if len(rrs) == 1 && soa != nil { // soa should never be nil...
		close(ch)
		wg.Wait()

		m := new(dns.Msg)
		m.SetReply(r)
		m.Answer = []dns.RR{soa}
		w.WriteMsg(m)

		log.Infof("Outgoing noop, incremental transfer for up to date zone %q to %s for %d SOA serial", state.QName(), state.IP(), soa.Serial)
		return 0, nil
	}

	if len(rrs) > 0 {
		ch <- &dns.Envelope{RR: rrs}
		l += len(rrs)
	}

	close(ch) // Even though we close the channel here, we still have
	wg.Wait() // to wait before we can return and close the connection.

	logserial := uint32(0)
	if soa != nil {
		logserial = soa.Serial
	}
	log.Infof("Outgoing transfer of %d records of zone %q to %s for %d SOA serial", l, state.QName(), state.IP(), logserial)
	return 0, nil
}

func (x xfr) allowed(state request.Request) bool {
	for _, h := range x.to {
		if h == "*" {
			return true
		}
		to, _, err := net.SplitHostPort(h)
		if err != nil {
			return false
		}
		// If remote IP matches we accept. TODO(): make this works with ranges
		if to == state.IP() {
			return true
		}
	}
	return false
}

// Find the first transfer instance for which the queried zone is the longest match. When nothing
// is found nil is returned.
func longestMatch(xfrs []*xfr, name string) *xfr {
	// TODO(xxx): optimize and make it a map (or maps)
	var x *xfr
	zone := "" // longest zone match wins
	for _, xfr := range xfrs {
		if z := plugin.Zones(xfr.Zones).Matches(name); z != "" {
			if z > zone {
				zone = z
				x = xfr
			}
		}
	}
	return x
}

// Name implements the Handler interface.
func (Transfer) Name() string { return "transfer" }
<commit_msg>plugin/transfer: remove duplicate code (#4200)<commit_after>package transfer

import (
	"context"
	"errors"
	"net"
	"sync"

	"github.com/coredns/coredns/plugin"
	clog "github.com/coredns/coredns/plugin/pkg/log"
	"github.com/coredns/coredns/request"

	"github.com/miekg/dns"
)

var log = clog.NewWithPlugin("transfer")

// Transfer is a plugin that handles zone transfers.
type Transfer struct {
	Transferers []Transferer // List of plugins that implement Transferer
	xfrs        []*xfr
	Next        plugin.Handler
}

type xfr struct {
	Zones []string
	to    []string
}

// Transferer may be implemented by plugins to enable zone transfers
type Transferer interface {
	// Transfer returns a channel to which it writes responses to the transfer request.
	// If the plugin is not authoritative for the zone, it should immediately return the
	// transfer.ErrNotAuthoritative error. This is important otherwise the transfer plugin can
	// use plugin X while it should transfer the data from plugin Y.
	//
	// If serial is 0, handle as an AXFR request. Transfer should send all records
	// in the zone to the channel. The SOA should be written to the channel first, followed
	// by all other records, including all NS + glue records. The implemenation is also responsible
	// for sending the last SOA record (to signal end of the transfer). This plugin will just grab
	// these records and send them back to the requester, there is little validation done.
	//
	// If serial is not 0, it will be handled as an IXFR request. If the serial is equal to or greater (newer) than
	// the current serial for the zone, send a single SOA record to the channel and then close it.
	// If the serial is less (older) than the current serial for the zone, perform an AXFR fallback
	// by proceeding as if an AXFR was requested (as above).
	Transfer(zone string, serial uint32) (<-chan []dns.RR, error)
}

var (
	// ErrNotAuthoritative is returned by Transfer() when the plugin is not authoritative for the zone.
	ErrNotAuthoritative = errors.New("not authoritative for zone")
)

// ServeDNS implements the plugin.Handler interface.
func (t *Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
	state := request.Request{W: w, Req: r}
	if state.QType() != dns.TypeAXFR && state.QType() != dns.TypeIXFR {
		return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
	}

	x := longestMatch(t.xfrs, state.QName())
	if x == nil {
		return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
	}

	if !x.allowed(state) {
		// write msg here, so logging will pick it up
		m := new(dns.Msg)
		m.SetRcode(r, dns.RcodeRefused)
		w.WriteMsg(m)
		return 0, nil
	}

	// Get serial from request if this is an IXFR.
	var serial uint32
	if state.QType() == dns.TypeIXFR {
		if len(r.Ns) != 1 {
			return dns.RcodeServerFailure, nil
		}
		soa, ok := r.Ns[0].(*dns.SOA)
		if !ok {
			return dns.RcodeServerFailure, nil
		}
		serial = soa.Serial
	}

	// Get a receiving channel from the first Transferer plugin that returns one.
	var pchan <-chan []dns.RR
	var err error
	for _, p := range t.Transferers {
		pchan, err = p.Transfer(state.QName(), serial)
		if err == ErrNotAuthoritative {
			// plugin was not authoritative for the zone, try next plugin
			continue
		}
		if err != nil {
			return dns.RcodeServerFailure, err
		}
		break
	}

	if pchan == nil {
		return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
	}

	// Send response to client
	ch := make(chan *dns.Envelope)
	tr := new(dns.Transfer)
	wg := new(sync.WaitGroup)
	wg.Add(1)
	go func() {
		tr.Out(w, r, ch)
		wg.Done()
	}()

	rrs := []dns.RR{}
	l := 0
	var soa *dns.SOA
	for records := range pchan {
		if x, ok := records[0].(*dns.SOA); ok && soa == nil {
			soa = x
		}
		rrs = append(rrs, records...)
		if len(rrs) > 500 {
			ch <- &dns.Envelope{RR: rrs}
			l += len(rrs)
			rrs = []dns.RR{}
		}
	}

	// if we are here and we only hold 1 soa (len(rrs) == 1) and soa != nil, and IXFR fallback should
	// be performed. We haven't send anything on ch yet, so that can be closed (and waited for), and we only
	// need to return the SOA back to the client and return.
	if len(rrs) == 1 && soa != nil { // soa should never be nil...
		close(ch)
		wg.Wait()

		m := new(dns.Msg)
		m.SetReply(r)
		m.Answer = []dns.RR{soa}
		w.WriteMsg(m)

		log.Infof("Outgoing noop, incremental transfer for up to date zone %q to %s for %d SOA serial", state.QName(), state.IP(), soa.Serial)
		return 0, nil
	}

	if len(rrs) > 0 {
		ch <- &dns.Envelope{RR: rrs}
		l += len(rrs)
	}

	close(ch) // Even though we close the channel here, we still have
	wg.Wait() // to wait before we can return and close the connection.

	logserial := uint32(0)
	if soa != nil {
		logserial = soa.Serial
	}
	log.Infof("Outgoing transfer of %d records of zone %q to %s for %d SOA serial", l, state.QName(), state.IP(), logserial)
	return 0, nil
}

func (x xfr) allowed(state request.Request) bool {
	for _, h := range x.to {
		if h == "*" {
			return true
		}
		to, _, err := net.SplitHostPort(h)
		if err != nil {
			return false
		}
		// If remote IP matches we accept. TODO(): make this works with ranges
		if to == state.IP() {
			return true
		}
	}
	return false
}

// Find the first transfer instance for which the queried zone is the longest match. When nothing
// is found nil is returned.
func longestMatch(xfrs []*xfr, name string) *xfr {
	// TODO(xxx): optimize and make it a map (or maps)
	var x *xfr
	zone := "" // longest zone match wins
	for _, xfr := range xfrs {
		if z := plugin.Zones(xfr.Zones).Matches(name); z != "" {
			if z > zone {
				zone = z
				x = xfr
			}
		}
	}
	return x
}

// Name implements the Handler interface.
func (Transfer) Name() string { return "transfer" }
<|endoftext|>


<commit_before>package server

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// Version of IronFunctions
var Version = "0.1.42"

func handleVersion(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"version": Version})
}
<commit_msg>functions: 0.1.43 release [skip ci]<commit_after>package server

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// Version of IronFunctions
var Version = "0.1.43"

func handleVersion(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"version": Version})
}
<|endoftext|>