<commit_before>package route

import (
	"encoding/binary"
	"fmt"
	"strconv"
	"strings"

	"github.com/btcsuite/btcd/btcec"
	sphinx "github.com/lightningnetwork/lightning-onion"
	"github.com/lightningnetwork/lnd/lnwire"
)

// ErrNoRouteHopsProvided is returned when a caller attempts to construct a new
// sphinx packet, but provides an empty set of hops for each route.
var ErrNoRouteHopsProvided = fmt.Errorf("empty route hops provided")

// Vertex is a simple alias for the serialization of a compressed Bitcoin
// public key.
type Vertex [33]byte

// NewVertex returns a new Vertex given a public key.
func NewVertex(pub *btcec.PublicKey) Vertex {
	var v Vertex
	copy(v[:], pub.SerializeCompressed())
	return v
}

// String returns a human readable version of the Vertex which is the
// hex-encoding of the serialized compressed public key.
func (v Vertex) String() string {
	return fmt.Sprintf("%x", v[:])
}

// Hop represents an intermediate or final node of the route. This naming
// is in line with the definition given in BOLT #4: Onion Routing Protocol.
// The struct houses the channel along which this hop can be reached and
// the values necessary to create the HTLC that needs to be sent to the
// next hop. It is also used to encode the per-hop payload included within
// the Sphinx packet.
type Hop struct {
	// PubKeyBytes is the raw bytes of the public key of the target node.
	PubKeyBytes Vertex

	// ChannelID is the unique channel ID for the channel. The first 3
	// bytes are the block height, the next 3 the index within the block,
	// and the last 2 bytes are the output index for the channel.
	ChannelID uint64

	// OutgoingTimeLock is the timelock value that should be used when
	// crafting the _outgoing_ HTLC from this hop.
	OutgoingTimeLock uint32

	// AmtToForward is the amount that this hop will forward to the next
	// hop. This value is less than the value that the incoming HTLC
	// carries as a fee will be subtracted by the hop.
	AmtToForward lnwire.MilliSatoshi
}

// Route represents a path through the channel graph which runs over one or
// more channels in succession. This struct carries all the information
// required to craft the Sphinx onion packet, and send the payment along the
// first hop in the path. A route is only selected as valid if all the channels
// have sufficient capacity to carry the initial payment amount after fees are
// accounted for.
type Route struct {
	// TotalTimeLock is the cumulative (final) time lock across the entire
	// route. This is the CLTV value that should be extended to the first
	// hop in the route. All other hops will decrement the time-lock as
	// advertised, leaving enough time for all hops to wait for or present
	// the payment preimage to complete the payment.
	TotalTimeLock uint32

	// TotalAmount is the total amount of funds required to complete a
	// payment over this route. This value includes the cumulative fees at
	// each hop. As a result, the HTLC extended to the first-hop in the
	// route will need to have at least this many satoshis, otherwise the
	// route will fail at an intermediate node due to an insufficient
	// amount of fees.
	TotalAmount lnwire.MilliSatoshi

	// SourcePubKey is the pubkey of the node where this route originates
	// from.
	SourcePubKey Vertex

	// Hops contains details concerning the specific forwarding details at
	// each hop.
	Hops []*Hop
}

// HopFee returns the fee charged by the route hop indicated by hopIndex.
func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
	var incomingAmt lnwire.MilliSatoshi
	if hopIndex == 0 {
		incomingAmt = r.TotalAmount
	} else {
		incomingAmt = r.Hops[hopIndex-1].AmtToForward
	}

	// Fee is calculated as difference between incoming and outgoing amount.
	return incomingAmt - r.Hops[hopIndex].AmtToForward
}

// TotalFees is the sum of the fees paid at each hop within the final route. In
// the case of a one-hop payment, this value will be zero as we don't need to
// pay a fee to ourself.
func (r *Route) TotalFees() lnwire.MilliSatoshi {
	if len(r.Hops) == 0 {
		return 0
	}

	return r.TotalAmount - r.Hops[len(r.Hops)-1].AmtToForward
}

// NewRouteFromHops creates a new Route structure from the minimally required
// information to perform the payment. It infers fee amounts and populates the
// node, chan and prev/next hop maps.
func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
	sourceVertex Vertex, hops []*Hop) (*Route, error) {

	if len(hops) == 0 {
		return nil, ErrNoRouteHopsProvided
	}

	// First, we'll create a route struct and populate it with the fields
	// for which the values are provided as arguments of this function.
	// TotalFees is determined based on the difference between the amount
	// that is send from the source and the final amount that is received
	// by the destination.
	route := &Route{
		SourcePubKey:  sourceVertex,
		Hops:          hops,
		TotalTimeLock: timeLock,
		TotalAmount:   amtToSend,
	}

	return route, nil
}

// ToSphinxPath converts a complete route into a sphinx PaymentPath that
// contains the per-hop paylods used to encoding the HTLC routing data for each
// hop in the route.
func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
	var path sphinx.PaymentPath

	// For each hop encoded within the route, we'll convert the hop struct
	// to an OnionHop with matching per-hop payload within the path as used
	// by the sphinx package.
	for i, hop := range r.Hops {
		pub, err := btcec.ParsePubKey(
			hop.PubKeyBytes[:], btcec.S256(),
		)
		if err != nil {
			return nil, err
		}

		path[i] = sphinx.OnionHop{
			NodePub: *pub,
			HopData: sphinx.HopData{
				// TODO(roasbeef): properly set realm, make
				// sphinx type an enum actually?
				Realm:         [1]byte{0},
				ForwardAmount: uint64(hop.AmtToForward),
				OutgoingCltv:  hop.OutgoingTimeLock,
			},
		}

		// As a base case, the next hop is set to all zeroes in order
		// to indicate that the "last hop" as no further hops after it.
		nextHop := uint64(0)

		// If we aren't on the last hop, then we set the "next address"
		// field to be the channel that directly follows it.
		if i != len(r.Hops)-1 {
			nextHop = r.Hops[i+1].ChannelID
		}

		binary.BigEndian.PutUint64(
			path[i].HopData.NextAddress[:], nextHop,
		)
	}

	return &path, nil
}

// String returns a human readable representation of the route.
func (r *Route) String() string {
	var b strings.Builder

	for i, hop := range r.Hops {
		if i > 0 {
			b.WriteString(",")
		}
		b.WriteString(strconv.FormatUint(hop.ChannelID, 10))
	}

	return fmt.Sprintf("amt=%v, fees=%v, tl=%v, chans=%v",
		r.TotalAmount-r.TotalFees(), r.TotalFees(), r.TotalTimeLock,
		b.String(),
	)
}
<commit_msg>routing/route: add vertex constructor from bytes<commit_after>package route

import (
	"encoding/binary"
	"fmt"
	"strconv"
	"strings"

	"github.com/btcsuite/btcd/btcec"
	sphinx "github.com/lightningnetwork/lightning-onion"
	"github.com/lightningnetwork/lnd/lnwire"
)

// VertexSize is the size of the array to store a vertex.
const VertexSize = 33

// ErrNoRouteHopsProvided is returned when a caller attempts to construct a new
// sphinx packet, but provides an empty set of hops for each route.
var ErrNoRouteHopsProvided = fmt.Errorf("empty route hops provided")

// Vertex is a simple alias for the serialization of a compressed Bitcoin
// public key.
type Vertex [VertexSize]byte

// NewVertex returns a new Vertex given a public key.
func NewVertex(pub *btcec.PublicKey) Vertex {
	var v Vertex
	copy(v[:], pub.SerializeCompressed())
	return v
}

// NewVertexFromBytes returns a new Vertex based on a serialized pubkey in a
// byte slice.
func NewVertexFromBytes(b []byte) (Vertex, error) {
	vertexLen := len(b)
	if vertexLen != VertexSize {
		return Vertex{}, fmt.Errorf("invalid vertex length of %v, "+
			"want %v", vertexLen, VertexSize)
	}

	var v Vertex
	copy(v[:], b)
	return v, nil
}

// String returns a human readable version of the Vertex which is the
// hex-encoding of the serialized compressed public key.
func (v Vertex) String() string {
	return fmt.Sprintf("%x", v[:])
}

// Hop represents an intermediate or final node of the route. This naming
// is in line with the definition given in BOLT #4: Onion Routing Protocol.
// The struct houses the channel along which this hop can be reached and
// the values necessary to create the HTLC that needs to be sent to the
// next hop. It is also used to encode the per-hop payload included within
// the Sphinx packet.
type Hop struct {
	// PubKeyBytes is the raw bytes of the public key of the target node.
	PubKeyBytes Vertex

	// ChannelID is the unique channel ID for the channel. The first 3
	// bytes are the block height, the next 3 the index within the block,
	// and the last 2 bytes are the output index for the channel.
	ChannelID uint64

	// OutgoingTimeLock is the timelock value that should be used when
	// crafting the _outgoing_ HTLC from this hop.
	OutgoingTimeLock uint32

	// AmtToForward is the amount that this hop will forward to the next
	// hop. This value is less than the value that the incoming HTLC
	// carries as a fee will be subtracted by the hop.
	AmtToForward lnwire.MilliSatoshi
}

// Route represents a path through the channel graph which runs over one or
// more channels in succession. This struct carries all the information
// required to craft the Sphinx onion packet, and send the payment along the
// first hop in the path. A route is only selected as valid if all the channels
// have sufficient capacity to carry the initial payment amount after fees are
// accounted for.
type Route struct {
	// TotalTimeLock is the cumulative (final) time lock across the entire
	// route. This is the CLTV value that should be extended to the first
	// hop in the route. All other hops will decrement the time-lock as
	// advertised, leaving enough time for all hops to wait for or present
	// the payment preimage to complete the payment.
	TotalTimeLock uint32

	// TotalAmount is the total amount of funds required to complete a
	// payment over this route. This value includes the cumulative fees at
	// each hop. As a result, the HTLC extended to the first-hop in the
	// route will need to have at least this many satoshis, otherwise the
	// route will fail at an intermediate node due to an insufficient
	// amount of fees.
	TotalAmount lnwire.MilliSatoshi

	// SourcePubKey is the pubkey of the node where this route originates
	// from.
	SourcePubKey Vertex

	// Hops contains details concerning the specific forwarding details at
	// each hop.
	Hops []*Hop
}

// HopFee returns the fee charged by the route hop indicated by hopIndex.
func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
	var incomingAmt lnwire.MilliSatoshi
	if hopIndex == 0 {
		incomingAmt = r.TotalAmount
	} else {
		incomingAmt = r.Hops[hopIndex-1].AmtToForward
	}

	// Fee is calculated as difference between incoming and outgoing amount.
	return incomingAmt - r.Hops[hopIndex].AmtToForward
}

// TotalFees is the sum of the fees paid at each hop within the final route. In
// the case of a one-hop payment, this value will be zero as we don't need to
// pay a fee to ourself.
func (r *Route) TotalFees() lnwire.MilliSatoshi {
	if len(r.Hops) == 0 {
		return 0
	}

	return r.TotalAmount - r.Hops[len(r.Hops)-1].AmtToForward
}

// NewRouteFromHops creates a new Route structure from the minimally required
// information to perform the payment. It infers fee amounts and populates the
// node, chan and prev/next hop maps.
func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
	sourceVertex Vertex, hops []*Hop) (*Route, error) {

	if len(hops) == 0 {
		return nil, ErrNoRouteHopsProvided
	}

	// First, we'll create a route struct and populate it with the fields
	// for which the values are provided as arguments of this function.
	// TotalFees is determined based on the difference between the amount
	// that is send from the source and the final amount that is received
	// by the destination.
	route := &Route{
		SourcePubKey:  sourceVertex,
		Hops:          hops,
		TotalTimeLock: timeLock,
		TotalAmount:   amtToSend,
	}

	return route, nil
}

// ToSphinxPath converts a complete route into a sphinx PaymentPath that
// contains the per-hop paylods used to encoding the HTLC routing data for each
// hop in the route.
func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
	var path sphinx.PaymentPath

	// For each hop encoded within the route, we'll convert the hop struct
	// to an OnionHop with matching per-hop payload within the path as used
	// by the sphinx package.
	for i, hop := range r.Hops {
		pub, err := btcec.ParsePubKey(
			hop.PubKeyBytes[:], btcec.S256(),
		)
		if err != nil {
			return nil, err
		}

		path[i] = sphinx.OnionHop{
			NodePub: *pub,
			HopData: sphinx.HopData{
				// TODO(roasbeef): properly set realm, make
				// sphinx type an enum actually?
				Realm:         [1]byte{0},
				ForwardAmount: uint64(hop.AmtToForward),
				OutgoingCltv:  hop.OutgoingTimeLock,
			},
		}

		// As a base case, the next hop is set to all zeroes in order
		// to indicate that the "last hop" as no further hops after it.
		nextHop := uint64(0)

		// If we aren't on the last hop, then we set the "next address"
		// field to be the channel that directly follows it.
		if i != len(r.Hops)-1 {
			nextHop = r.Hops[i+1].ChannelID
		}

		binary.BigEndian.PutUint64(
			path[i].HopData.NextAddress[:], nextHop,
		)
	}

	return &path, nil
}

// String returns a human readable representation of the route.
func (r *Route) String() string {
	var b strings.Builder

	for i, hop := range r.Hops {
		if i > 0 {
			b.WriteString(",")
		}
		b.WriteString(strconv.FormatUint(hop.ChannelID, 10))
	}

	return fmt.Sprintf("amt=%v, fees=%v, tl=%v, chans=%v",
		r.TotalAmount-r.TotalFees(), r.TotalFees(), r.TotalTimeLock,
		b.String(),
	)
}
<|endoftext|>


<commit_before>package adoc

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// This part contains apis for the containers listed in
// https://docs.docker.com/reference/api/docker_remote_api_v1.17/#21-containers

type Port struct {
	IP          string
	PrivatePort int
	PublicPort  int
	Type        string
}

// Container defines basic container information for ListContainers
type Container struct {
	Command    string
	Created    int64
	Id         string
	Image      string
	Labels     map[string]string
	Names      []string
	Ports      []Port
	SizeRootFs int64
	SizeRw     int64
	Status     string
}

// ContainerConfig defines basic container creation data stucture
type ContainerConfig struct {
	AttachStderr    bool
	AttachStdin     bool
	AttachStdout    bool
	Cmd             []string
	CpuShares       int
	Cpuset          string
	Domainname      string
	Entrypoint      []string
	Env             []string
	ExposedPorts    map[string]struct{}
	Hostname        string
	Image           string
	Labels          map[string]string
	MacAddress      string
	Memory          int64
	MemorySwap      int64
	NetworkDisabled bool
	OnBuild         []string
	OpenStdin       bool
	PortSpecs       []string
	StdinOnce       bool
	Tty             bool
	User            string
	VolumeDriver    string
	Volumes         map[string]struct{}
	WorkingDir      string
}

type Device struct {
	PathOnHost        string
	PathInContainer   string
	CgroupPermissions string
}

type RestartPolicy struct {
	MaximumRetryCount int
	Name              string
}

type Ulimit struct {
	Name string
	Soft int64
	Hard int64
}

type LogConfig struct {
	Type   string
	Config map[string]string
}

// HostConfig defines basic host configuration for container to run
type HostConfig struct {
	Binds           []string
	CapAdd          []string
	CapDrop         []string
	CgroupParent    string
	ContainerIDFile string
	CpuShares       int
	CpusetCpus      string
	Devices         []Device
	Dns             []string
	DnsSearch       []string
	ExtraHosts      []string
	IpcMode         string
	Links           []string
	LxcConf         []map[string]string
	Memory          int64
	MemorySwap      int64
	NetworkMode     string
	PidMode         string
	PortBindings    map[string][]PortBinding
	Privileged      bool
	PublishAllPorts bool
	ReadonlyRootfs  bool
	RestartPolicy   RestartPolicy
	SecurityOpt     []string
	VolumesFrom     []string
	Ulimits         []Ulimit  // 1.18
	LogConfig       LogConfig // 1.18
}

type PortBinding struct {
	HostIp   string
	HostPort string
}

type NetworkSettings struct {
	Bridge                 string
	Gateway                string
	GlobalIPv6Address      string
	GlobalIPv6PrefixLen    int
	IPAddress              string
	IPPrefixLen            int
	IPv6Gateway            string
	LinkLocalIPv6Address   string
	LinkLocalIPv6PrefixLen int
	MacAddress             string
	Ports                  map[string][]PortBinding
}

// ContainerState defines container running state from inspection
type ContainerState struct {
	Dead       bool
	Error      string
	ExitCode   int
	FinishedAt time.Time
	OOMKilled  bool
	Paused     bool
	Pid        int64
	Restarting bool
	Running    bool
	StartedAt  time.Time
}

// SwarmNode defines the swarm api data for container running node
type SwarmNode struct {
	Name   string
	ID     string
	Addr   string
	IP     string
	Cpus   int
	Memory int64
	Labels map[string]string
}

// ContainerDetail defines the detail data of the container from inspection, including the swarm node infor
type ContainerDetail struct {
	AppArmorProfile string
	Args            []string
	Config          ContainerConfig
	Created         time.Time
	Driver          string
	ExecDriver      string
	ExecIDs         []string
	HostConfig      HostConfig
	HostnamePath    string
	HostsPath       string
	Id              string
	Image           string
	LogPath         string
	MountLabel      string
	Name            string
	NetworkSettings NetworkSettings
	Path            string
	ProcessLabel    string
	ResolvConfPath  string
	RestartCount    int
	State           ContainerState
	Volumes         map[string]string
	VolumesRW       map[string]bool
	Node            SwarmNode // swarm api
}

type NetworkOptions struct {
	Container      string
	EndpointConfig EndpointConfig
	Force          bool
}

type EndpointConfig struct {
	IPAMConfig IPAMConfig
}

type IPAMConfig struct {
	IPv4Address string
	IPv6Address string
}

// ListContainers returns containers data, showAll flag defines if you want to show all the containers including the stopped ones
func (client *DockerClient) ListContainers(showAll, showSize bool, filters ...string) ([]Container, error) {
	v := url.Values{}
	v.Set("all", formatBoolToIntString(showAll))
	v.Set("size", formatBoolToIntString(showSize))
	if len(filters) > 0 && filters[0] != "" {
		v.Set("filters", filters[0])
	}
	uri := fmt.Sprintf("containers/json?%s", v.Encode())
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var ret []Container
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

// InspectContainer returns container detail data with container id
func (client *DockerClient) InspectContainer(id string) (ContainerDetail, error) {
	uri := fmt.Sprintf("containers/%s/json", id)
	var ret ContainerDetail
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return ret, err
	} else {
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

func (client *DockerClient) CreateContainer(containerConf ContainerConfig, hostConf HostConfig, name ...string) (string, error) {
	var config struct {
		ContainerConfig
		HostConfig HostConfig
	}
	config.ContainerConfig = containerConf
	config.HostConfig = hostConf

	if body, err := json.Marshal(config); err != nil {
		return "", err
	} else {
		uri := "containers/create"
		if len(name) > 0 && name[0] != "" {
			v := url.Values{}
			v.Set("name", name[0])
			uri += "?" + v.Encode()
		}
		if data, err := client.sendRequest("POST", uri, body, nil, true); err != nil {
			return "", err
		} else {
			var resp struct {
				Id       string
				Warnings []string
			}
			err := json.Unmarshal(data, &resp)
			if len(resp.Warnings) > 0 {
				logger.Warnf("Create container returns warning from docker daemon: %+v", resp.Warnings)
			}
			return resp.Id, err
		}
	}
}

func (client *DockerClient) ConnectContainer(networkName string, id string, ipAddr string) error {
	nc := NetworkOptions
	nc.Container = id
	nc.EndpointConfig.IPAMConfig.IPv4Address = ipAddr
	if body, err := json.Marshal(nc); err != nil {
		return err
	} else {
		url := fmt.Sprintf("networks/%s/connect", networkName)
		_, err := client.sendRequest("POST", uri, body, nil)
		return err
	}
}

func (client *DockerClient) DisconncetContainer(networkName string, id string, force bool) error {
	nc := NetworkOptions
	nc.Container = id
	nc.Force = force
	if body, err := json.Marshal(nc); err != nil {
		return err
	} else {
		url := fmt.Sprintf("networks/%s/disconnect", networkName)
		_, err := client.sendRequest("POST", uri, nil, nil)
		return err
	}
}

func (client *DockerClient) StartContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/start", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) StopContainer(id string, timeout ...int) error {
	uri := fmt.Sprintf("containers/%s/stop", id)
	if len(timeout) > 0 && timeout[0] >= 0 {
		v := url.Values{}
		v.Set("t", fmt.Sprintf("%d", timeout[0]))
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) RestartContainer(id string, timeout ...int) error {
	uri := fmt.Sprintf("containers/%s/restart", id)
	if len(timeout) > 0 && timeout[0] >= 0 {
		v := url.Values{}
		v.Set("t", fmt.Sprintf("%d", timeout[0]))
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) KillContainer(id string, signal ...string) error {
	uri := fmt.Sprintf("containers/%s/kill", id)
	if len(signal) > 0 && signal[0] != "" {
		v := url.Values{}
		v.Set("signal", signal[0])
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) PauseContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/pause", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) UnpauseContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/unpause", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
	v := url.Values{}
	v.Set("force", formatBoolToIntString(force))
	v.Set("v", formatBoolToIntString(volumes))
	uri := fmt.Sprintf("containers/%s?%s", id, v.Encode())
	_, err := client.sendRequest("DELETE", uri, nil, nil)
	return err
}

func (client *DockerClient) RenameContainer(id string, name string) error {
	v := url.Values{}
	v.Set("name", name)
	uri := fmt.Sprintf("containers/%s/rename?%s", id, v.Encode())
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

// This will block the call routine until the container is stopped
func (client *DockerClient) WaitContainer(id string) (int, error) {
	uri := fmt.Sprintf("containers/%s/wait", id)
	if data, err := client.sendRequest("POST", uri, nil, nil, true); err != nil {
		return 0, err
	} else {
		var ret map[string]int
		if err := json.Unmarshal(data, &ret); err != nil {
			return 0, err
		}
		if code, ok := ret["StatusCode"]; ok {
			return code, nil
		} else {
			logger.Warnf("There is no StatusCode key inside results map, the API maybe changed, ret=%+v", ret)
			return 0, fmt.Errorf("Cannot get StatusCode from return data, %+v", ret)
		}
	}
}

func (client *DockerClient) ContainerLogs(id string, stdout, stderr, timestamps bool, tail ...int) ([]LogEntry, error) {
	// no following mode
	v := url.Values{}
	v.Set("stdout", formatBoolToIntString(stdout))
	v.Set("stderr", formatBoolToIntString(stderr))
	v.Set("timestamps", formatBoolToIntString(timestamps))
	if len(tail) > 0 && tail[0] >= 0 {
		v.Set("tail", fmt.Sprintf("%d", tail[0]))
	}
	uri := fmt.Sprintf("containers/%s/logs?%s", id, v.Encode())

	var entries []LogEntry
	err := client.sendRequestCallback("GET", uri, nil, nil, func(resp *http.Response) error {
		var cbErr error
		entries, cbErr = ReadAllDockerLogs(resp.Body)
		return cbErr
	})
	return entries, err
}

type Processes struct {
	Titles    []string
	Processes [][]string
}

func (client *DockerClient) ContainerProcesses(id string, psArgs ...string) (Processes, error) {
	var procs Processes
	v := url.Values{}
	if len(psArgs) > 0 && psArgs[0] != "" {
		v.Set("ps_args", psArgs[0])
	}
	uri := fmt.Sprintf("containers/%s/top", id)
	if len(v) > 0 {
		uri += "?" + v.Encode()
	}
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return procs, err
	} else {
		err := json.Unmarshal(data, &procs)
		return procs, err
	}
}

type FsChange struct {
	Path string
	Kind int
}

func (client *DockerClient) ContainerChanges(id string) ([]FsChange, error) {
	uri := fmt.Sprintf("containers/%s/changes", id)
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var changes []FsChange
		err := json.Unmarshal(data, &changes)
		return changes, err
	}
}

// Missing apis for
// containers/(id)/copy
// containers/(id)/attach
// containers/(id)/export
// containers/(id)/resize?h=<height>&w=<width>
// containers/(id)/attach/ws
<commit_msg>bug fix<commit_after>package adoc

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// This part contains apis for the containers listed in
// https://docs.docker.com/reference/api/docker_remote_api_v1.17/#21-containers

type Port struct {
	IP          string
	PrivatePort int
	PublicPort  int
	Type        string
}

// Container defines basic container information for ListContainers
type Container struct {
	Command    string
	Created    int64
	Id         string
	Image      string
	Labels     map[string]string
	Names      []string
	Ports      []Port
	SizeRootFs int64
	SizeRw     int64
	Status     string
}

// ContainerConfig defines basic container creation data stucture
type ContainerConfig struct {
	AttachStderr    bool
	AttachStdin     bool
	AttachStdout    bool
	Cmd             []string
	CpuShares       int
	Cpuset          string
	Domainname      string
	Entrypoint      []string
	Env             []string
	ExposedPorts    map[string]struct{}
	Hostname        string
	Image           string
	Labels          map[string]string
	MacAddress      string
	Memory          int64
	MemorySwap      int64
	NetworkDisabled bool
	OnBuild         []string
	OpenStdin       bool
	PortSpecs       []string
	StdinOnce       bool
	Tty             bool
	User            string
	VolumeDriver    string
	Volumes         map[string]struct{}
	WorkingDir      string
}

type Device struct {
	PathOnHost        string
	PathInContainer   string
	CgroupPermissions string
}

type RestartPolicy struct {
	MaximumRetryCount int
	Name              string
}

type Ulimit struct {
	Name string
	Soft int64
	Hard int64
}

type LogConfig struct {
	Type   string
	Config map[string]string
}

// HostConfig defines basic host configuration for container to run
type HostConfig struct {
	Binds           []string
	CapAdd          []string
	CapDrop         []string
	CgroupParent    string
	ContainerIDFile string
	CpuShares       int
	CpusetCpus      string
	Devices         []Device
	Dns             []string
	DnsSearch       []string
	ExtraHosts      []string
	IpcMode         string
	Links           []string
	LxcConf         []map[string]string
	Memory          int64
	MemorySwap      int64
	NetworkMode     string
	PidMode         string
	PortBindings    map[string][]PortBinding
	Privileged      bool
	PublishAllPorts bool
	ReadonlyRootfs  bool
	RestartPolicy   RestartPolicy
	SecurityOpt     []string
	VolumesFrom     []string
	Ulimits         []Ulimit  // 1.18
	LogConfig       LogConfig // 1.18
}

type PortBinding struct {
	HostIp   string
	HostPort string
}

type NetworkSettings struct {
	Bridge                 string
	Gateway                string
	GlobalIPv6Address      string
	GlobalIPv6PrefixLen    int
	IPAddress              string
	IPPrefixLen            int
	IPv6Gateway            string
	LinkLocalIPv6Address   string
	LinkLocalIPv6PrefixLen int
	MacAddress             string
	Ports                  map[string][]PortBinding
}

// ContainerState defines container running state from inspection
type ContainerState struct {
	Dead       bool
	Error      string
	ExitCode   int
	FinishedAt time.Time
	OOMKilled  bool
	Paused     bool
	Pid        int64
	Restarting bool
	Running    bool
	StartedAt  time.Time
}

// SwarmNode defines the swarm api data for container running node
type SwarmNode struct {
	Name   string
	ID     string
	Addr   string
	IP     string
	Cpus   int
	Memory int64
	Labels map[string]string
}

// ContainerDetail defines the detail data of the container from inspection, including the swarm node infor
type ContainerDetail struct {
	AppArmorProfile string
	Args            []string
	Config          ContainerConfig
	Created         time.Time
	Driver          string
	ExecDriver      string
	ExecIDs         []string
	HostConfig      HostConfig
	HostnamePath    string
	HostsPath       string
	Id              string
	Image           string
	LogPath         string
	MountLabel      string
	Name            string
	NetworkSettings NetworkSettings
	Path            string
	ProcessLabel    string
	ResolvConfPath  string
	RestartCount    int
	State           ContainerState
	Volumes         map[string]string
	VolumesRW       map[string]bool
	Node            SwarmNode // swarm api
}

type NetworkOptions struct {
	Container      string
	EndpointConfig EndpointConfig
	Force          bool
}

type EndpointConfig struct {
	IPAMConfig IPAMConfig
}

type IPAMConfig struct {
	IPv4Address string
	IPv6Address string
}

// ListContainers returns containers data, showAll flag defines if you want to show all the containers including the stopped ones
func (client *DockerClient) ListContainers(showAll, showSize bool, filters ...string) ([]Container, error) {
	v := url.Values{}
	v.Set("all", formatBoolToIntString(showAll))
	v.Set("size", formatBoolToIntString(showSize))
	if len(filters) > 0 && filters[0] != "" {
		v.Set("filters", filters[0])
	}
	uri := fmt.Sprintf("containers/json?%s", v.Encode())
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var ret []Container
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

// InspectContainer returns container detail data with container id
func (client *DockerClient) InspectContainer(id string) (ContainerDetail, error) {
	uri := fmt.Sprintf("containers/%s/json", id)
	var ret ContainerDetail
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return ret, err
	} else {
		err := json.Unmarshal(data, &ret)
		return ret, err
	}
}

func (client *DockerClient) CreateContainer(containerConf ContainerConfig, hostConf HostConfig, name ...string) (string, error) {
	var config struct {
		ContainerConfig
		HostConfig HostConfig
	}
	config.ContainerConfig = containerConf
	config.HostConfig = hostConf

	if body, err := json.Marshal(config); err != nil {
		return "", err
	} else {
		uri := "containers/create"
		if len(name) > 0 && name[0] != "" {
			v := url.Values{}
			v.Set("name", name[0])
			uri += "?" + v.Encode()
		}
		if data, err := client.sendRequest("POST", uri, body, nil, true); err != nil {
			return "", err
		} else {
			var resp struct {
				Id       string
				Warnings []string
			}
			err := json.Unmarshal(data, &resp)
			if len(resp.Warnings) > 0 {
				logger.Warnf("Create container returns warning from docker daemon: %+v", resp.Warnings)
			}
			return resp.Id, err
		}
	}
}

func (client *DockerClient) ConnectContainer(networkName string, id string, ipAddr string) error {
	var nc NetworkOptions
	nc.Container = id
	nc.EndpointConfig.IPAMConfig.IPv4Address = ipAddr
	if body, err := json.Marshal(nc); err != nil {
		return err
	} else {
		url := fmt.Sprintf("networks/%s/connect", networkName)
		_, err := client.sendRequest("POST", uri, body, nil)
		return err
	}
}

func (client *DockerClient) DisconncetContainer(networkName string, id string, force bool) error {
	var nc NetworkOptions
	nc.Container = id
	nc.Force = force
	if body, err := json.Marshal(nc); err != nil {
		return err
	} else {
		url := fmt.Sprintf("networks/%s/disconnect", networkName)
		_, err := client.sendRequest("POST", uri, nil, nil)
		return err
	}
}

func (client *DockerClient) StartContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/start", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) StopContainer(id string, timeout ...int) error {
	uri := fmt.Sprintf("containers/%s/stop", id)
	if len(timeout) > 0 && timeout[0] >= 0 {
		v := url.Values{}
		v.Set("t", fmt.Sprintf("%d", timeout[0]))
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) RestartContainer(id string, timeout ...int) error {
	uri := fmt.Sprintf("containers/%s/restart", id)
	if len(timeout) > 0 && timeout[0] >= 0 {
		v := url.Values{}
		v.Set("t", fmt.Sprintf("%d", timeout[0]))
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) KillContainer(id string, signal ...string) error {
	uri := fmt.Sprintf("containers/%s/kill", id)
	if len(signal) > 0 && signal[0] != "" {
		v := url.Values{}
		v.Set("signal", signal[0])
		uri += "?" + v.Encode()
	}
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) PauseContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/pause", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) UnpauseContainer(id string) error {
	uri := fmt.Sprintf("containers/%s/unpause", id)
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
	v := url.Values{}
	v.Set("force", formatBoolToIntString(force))
	v.Set("v", formatBoolToIntString(volumes))
	uri := fmt.Sprintf("containers/%s?%s", id, v.Encode())
	_, err := client.sendRequest("DELETE", uri, nil, nil)
	return err
}

func (client *DockerClient) RenameContainer(id string, name string) error {
	v := url.Values{}
	v.Set("name", name)
	uri := fmt.Sprintf("containers/%s/rename?%s", id, v.Encode())
	_, err := client.sendRequest("POST", uri, nil, nil)
	return err
}

// This will block the call routine until the container is stopped
func (client *DockerClient) WaitContainer(id string) (int, error) {
	uri := fmt.Sprintf("containers/%s/wait", id)
	if data, err := client.sendRequest("POST", uri, nil, nil, true); err != nil {
		return 0, err
	} else {
		var ret map[string]int
		if err := json.Unmarshal(data, &ret); err != nil {
			return 0, err
		}
		if code, ok := ret["StatusCode"]; ok {
			return code, nil
		} else {
			logger.Warnf("There is no StatusCode key inside results map, the API maybe changed, ret=%+v", ret)
			return 0, fmt.Errorf("Cannot get StatusCode from return data, %+v", ret)
		}
	}
}

func (client *DockerClient) ContainerLogs(id string, stdout, stderr, timestamps bool, tail ...int) ([]LogEntry, error) {
	// no following mode
	v := url.Values{}
	v.Set("stdout", formatBoolToIntString(stdout))
	v.Set("stderr", formatBoolToIntString(stderr))
	v.Set("timestamps", formatBoolToIntString(timestamps))
	if len(tail) > 0 && tail[0] >= 0 {
		v.Set("tail", fmt.Sprintf("%d", tail[0]))
	}
	uri := fmt.Sprintf("containers/%s/logs?%s", id, v.Encode())

	var entries []LogEntry
	err := client.sendRequestCallback("GET", uri, nil, nil, func(resp *http.Response) error {
		var cbErr error
		entries, cbErr = ReadAllDockerLogs(resp.Body)
		return cbErr
	})
	return entries, err
}

type Processes struct {
	Titles    []string
	Processes [][]string
}

func (client *DockerClient) ContainerProcesses(id string, psArgs ...string) (Processes, error) {
	var procs Processes
	v := url.Values{}
	if len(psArgs) > 0 && psArgs[0] != "" {
		v.Set("ps_args", psArgs[0])
	}
	uri := fmt.Sprintf("containers/%s/top", id)
	if len(v) > 0 {
		uri += "?" + v.Encode()
	}
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return procs, err
	} else {
		err := json.Unmarshal(data, &procs)
		return procs, err
	}
}

type FsChange struct {
	Path string
	Kind int
}

func (client *DockerClient) ContainerChanges(id string) ([]FsChange, error) {
	uri := fmt.Sprintf("containers/%s/changes", id)
	if data, err := client.sendRequest("GET", uri, nil, nil); err != nil {
		return nil, err
	} else {
		var changes []FsChange
		err := json.Unmarshal(data, &changes)
		return changes, err
	}
}

// Missing apis for
// containers/(id)/copy
// containers/(id)/attach
// containers/(id)/export
// containers/(id)/resize?h=<height>&w=<width>
// containers/(id)/attach/ws
<|endoftext|>


<commit_before>// package kodingkey provides functions for generating koding.key
// and heplers for conversions.
package kodingkey

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
)

// Number of characters in a koding key whebn converted to string.
// Must be multiple of 8.
const StringLength = 64

// Length of the koding key when converted to []byte.
// We are using base64 conversion and it uses 6/8 of the ASCII table,
// that's the reason of this calculation.
const BytesLength = StringLength * 6 / 8

// KodingKey is a byte representation of a koding.key file
// generated by "kd register" command.
type KodingKey []byte

func NewKodingKey() (KodingKey, error) {
	key := make(KodingKey, BytesLength, BytesLength)

	if _, err := io.ReadFull(rand.Reader, key); err != nil {
		return nil, err
	}

	return key, nil
}

func FromString(s string) (KodingKey, error) {
	s = strings.TrimSpace(s)

	if len(s) != 64 {
		return nil, fmt.Errorf("Length of the koding key must be %s", StringLength)
	}

	return base64.URLEncoding.DecodeString(s)
}

func FromFile(path string) (KodingKey, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	return FromString(string(data))
}

func (k KodingKey) String() string {
	return base64.URLEncoding.EncodeToString(k)
}
<commit_msg>add Bytes32 method to KodingKey<commit_after>// package kodingkey provides functions for generating koding.key
// and heplers for conversions.
package kodingkey

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"io"
	"io/ioutil"
	"strings"
)

// Length of the koding key bytes.
// Must be multiple of 6 to not create padding in string representation.
const BytesLength = 48

// Number of characters in a koding key when converted to string.
// We are using base64 conversion and it uses 6/8 of the ASCII table,
// that's the reason of this calculation.
const StringLength = BytesLength * 8 / 6

var Encoding *base64.Encoding

func init() {
	Encoding = base64.URLEncoding
}

// KodingKey is a byte representation of a koding.key file
// generated by "kd register" command.
type KodingKey []byte

// NewKodingKey returns a new random generated KodingKey of length "BytesLength".
func NewKodingKey() (KodingKey, error) {
	key := make(KodingKey, BytesLength, BytesLength)

	if _, err := io.ReadFull(rand.Reader, key); err != nil {
		return nil, err
	}

	return key, nil
}

func FromString(s string) (KodingKey, error) {
	s = strings.TrimSpace(s)

	if len(s) == 0 {
		return nil, errors.New("Zero length Koding Key")
	}

	return Encoding.DecodeString(s)
}

func FromFile(path string) (KodingKey, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	return FromString(string(data))
}

func (k KodingKey) String() string {
	return Encoding.EncodeToString(k)
}

func (k KodingKey) Bytes32() []byte {
	h := sha256.New()
	h.Write(k)
	return h.Sum(nil)
}
<|endoftext|>


<commit_before>package rx

import "time"

//jig:template <Foo>Observer
//jig:needs <Foo>ObserveFuncMethods

// FooObserver is the interface used with CreateFoo when implementing a custom
// observable.
type FooObserver interface {
	// Next emits the next foo value.
	Next(foo)
	// Error signals an error condition.
	Error(error)
	// Complete signals that no more data is to be expected.
	Complete()
	// Closed returns true when the subscription has been canceled.
	Closed() bool
}

//jig:template Create<Foo>
//jig:needs Observable<Foo>, <Foo>Observer

// CreateFoo creates an Observable from scratch by calling observer methods
// programmatically.
func CreateFoo(f func(FooObserver)) ObservableFoo {
	observable := func(observe FooObserveFunc, scheduler Scheduler, subscriber Subscriber) {
		scheduler.Schedule(func() {
			if subscriber.Closed() {
				return
			}
			observer := func(next foo, err error, done bool) {
				if !subscriber.Closed() {
					observe(next, err, done)
				}
			}
			type ObserverSubscriber struct {
				FooObserveFunc
				Subscriber
			}
			f(&ObserverSubscriber{observer, subscriber})
		})
	}
	return observable
}

//jig:template Defer<Foo>
//jig:needs Observable<Foo>

// DeferFoo does not create the ObservableFoo until the observer subscribes,
// and creates a fresh ObservableFoo for each observer.
func DeferFoo(factory func() ObservableFoo) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		factory()(observe, subscribeOn, subscriber)
	}
	return observable
}

//jig:template Empty<Foo>
//jig:needs Observable<Foo>

// EmptyFoo creates an Observable that emits no items but terminates normally.
func EmptyFoo() ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.Schedule(func() {
			if !subscriber.Canceled() {
				observe(zeroFoo, nil, true)
			}
		})
	}
	return observable
}

//jig:template Error<Foo>
//jig:needs Observable<Foo>

// ErrorFoo creates an Observable that emits no items and terminates with an
// error.
func ErrorFoo(err error) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.Schedule(func() {
			if !subscriber.Canceled() {
				observe(zeroFoo, err, true)
			}
		})
	}
	return observable
}

//jig:template FromChan<Foo>
//jig:needs Observable<Foo>

// FromChanFoo creates an ObservableFoo from a Go channel of foo values.
// It's not possible for the code feeding into the channel to send an error.
// The feeding code can send zero or more foo items and then closing the
// channel will be seen as completion.
func FromChanFoo(ch <-chan foo) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.ScheduleRecursive(func(self func()) {
			if subscriber.Canceled() {
				return
			}
			next, ok := <-ch;
			if subscriber.Canceled() {
				return
			}
			if ok {
				observe(next, nil, false)
				if !subscriber.Canceled() {
					self()
				}
			} else {
				observe(zeroFoo, nil, true)
			}
		})
	}
	return observable
}

//jig:template FromChan
//jig:needs Observable

// FromChan creates an Observable from a Go channel of interface{}
// values. This allows the code feeding into the channel to send either an error
// or the next value. The feeding code can send zero or more items and then
// closing the channel will be seen as completion. When the feeding code sends
// an error into the channel, it should close the channel immediately to
// indicate termination with error.
func FromChan(ch <-chan interface{}) Observable {
	observable := func(observe ObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.ScheduleRecursive(func(self func()) {
			if subscriber.Canceled() {
				return
			}
			next, ok := <-ch
			if subscriber.Canceled() {
				return
			}
			if ok {
				err, ok := next.(error)
				if !ok {
					observe(next, nil, false)
					if !subscriber.Canceled() {
						self()
					}
				} else {
					observe(zero, err, true)
				}
			} else {
				observe(zero, nil, true)
			}
		})
	}
	return observable
}

//jig:template From<Foo>
//jig:needs FromSlice<Foo>

// FromFoo creates an ObservableFoo from multiple foo values passed in.
func FromFoo(slice ...foo) ObservableFoo {
	return FromSliceFoo(slice)
}

//jig:template From<Foo>s
//jig:needs FromSlice<Foo>

// FromFoos creates an ObservableFoo from multiple foo values passed in.
func FromFoos(slice ...foo) ObservableFoo {
	return FromSliceFoo(slice)
}

//jig:template FromSlice<Foo>
//jig:needs Observable<Foo>

// FromSliceFoo creates an ObservableFoo from a slice of foo values passed in.
func FromSliceFoo(slice []foo) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		i := 0
		subscribeOn.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if i < len(slice) {
					observe(slice[i], nil, false)
					if !subscriber.Canceled() {
						i++
						self()
					}
				} else {
					observe(zeroFoo, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Interval
//jig:needs ObservableInt

// Interval creates an ObservableInt that emits a sequence of integers spaced
// by a particular time interval.
func Interval(interval time.Duration) ObservableInt {
	observable := func(observe IntObserveFunc, scheduler Scheduler, subscriber Subscriber) {
		i := 0
		scheduler.ScheduleRecursive(func(self func()) {
			if subscriber.Canceled() {
				return
			}
			time.Sleep(interval)
			if subscriber.Canceled() {
				return
			}
			observe(i, nil, false)
			if subscriber.Canceled() {
				return
			}
			i++
			self()
		})
	}
	return observable
}

//jig:template Just<Foo>
//jig:needs Observable<Foo>

// JustFoo creates an ObservableFoo that emits a particular item.
func JustFoo(element foo) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		done := false
		subscribeOn.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if !done {
					observe(element, nil, false)
					if !subscriber.Canceled() {
						done = true
						self()
					}
				} else {
					observe(zeroFoo, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Never<Foo>
//jig:needs Observable<Foo>

// NeverFoo creates an ObservableFoo that emits no items and does't terminate.
func NeverFoo() ObservableFoo {
	observable := func(observe FooObserveFunc, scheduler Scheduler, subscriber Subscriber) {
	}
	return observable
}

//jig:template Range
//jig:needs ObservableInt

// Range creates an ObservableInt that emits a range of sequential integers.
func Range(start, count int) ObservableInt {
	end := start + count
	observable := func(observe IntObserveFunc, scheduler Scheduler, subscriber Subscriber) {
		i := start
		scheduler.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if i < end {
					observe(i, nil, false)
					if !subscriber.Canceled() {
						i++
						self()
					}
				} else {
					observe(zeroInt, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Observable Repeat

// Repeat creates an Observable that emits a sequence of items repeatedly.
func (o Observable) Repeat(count int) Observable {
	if count == 0 {
		return Empty()
	}
	observable := func(observe ObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		var repeated int
		var observer ObserveFunc
		observer = func(next interface{}, err error, done bool) {
			if !done || err != nil {
				observe(next, err, done)
			} else {
				repeated++
				if repeated < count {
					o(observer, subscribeOn, subscriber)
				} else {
					observe(nil, nil, true)
				}
			}
		}
		o(observer, subscribeOn, subscriber)
	}
	return observable
}

//jig:template Observable<Foo> Repeat
//jig:needs Observable Repeat

// Repeat creates an ObservableFoo that emits a sequence of items repeatedly.
func (o ObservableFoo) Repeat(count int) ObservableFoo {
	return o.AsObservable().Repeat(count).AsObservableFoo()
}

//jig:template Repeat<Foo>
//jig:needs Observable<Foo>

// RepeatFoo creates an ObservableFoo that emits a particular item or sequence
// of items repeatedly.
func RepeatFoo(value foo, count int) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		i := 0
		subscribeOn.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if i < count {
					observe(value, nil, false)
					if !subscriber.Canceled() {
						i++
						self()
					}
				} else {
					observe(zeroFoo, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Start<Foo>
//jig:needs Observable<Foo>

// StartFoo creates an ObservableFoo that emits the return value of a function.
// It is designed to be used with a function that returns a (foo, error) tuple.
// If the error is non-nil the returned ObservableFoo will be that error,
// otherwise it will be a single-value stream of foo.
func StartFoo(f func() (foo, error)) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		done := false
		subscribeOn.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if !done {
					if next, err := f(); err == nil {
						observe(next, nil, false)
						if !subscriber.Canceled() {
							done = true
							self()
						}
					} else {
						observe(zeroFoo, err, true)
					}
				} else {
					observe(zeroFoo, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Throw<Foo>
//jig:needs Observable<Foo>

// ThrowFoo creates an Observable that emits no items and terminates with an
// error.
func ThrowFoo(err error) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.Schedule(func() {
			if !subscriber.Canceled() {
				observe(zeroFoo, err, true)
			}
		})
	}
	return observable
}
<commit_msg>Cleanup FromChanFoo<commit_after>package rx

import "time"

//jig:template <Foo>Observer
//jig:needs <Foo>ObserveFuncMethods

// FooObserver is the interface used with CreateFoo when implementing a custom
// observable.
type FooObserver interface {
	// Next emits the next foo value.
	Next(foo)
	// Error signals an error condition.
	Error(error)
	// Complete signals that no more data is to be expected.
	Complete()
	// Closed returns true when the subscription has been canceled.
	Closed() bool
}

//jig:template Create<Foo>
//jig:needs Observable<Foo>, <Foo>Observer

// CreateFoo creates an Observable from scratch by calling observer methods
// programmatically.
func CreateFoo(f func(FooObserver)) ObservableFoo {
	observable := func(observe FooObserveFunc, scheduler Scheduler, subscriber Subscriber) {
		scheduler.Schedule(func() {
			if subscriber.Closed() {
				return
			}
			observer := func(next foo, err error, done bool) {
				if !subscriber.Closed() {
					observe(next, err, done)
				}
			}
			type ObserverSubscriber struct {
				FooObserveFunc
				Subscriber
			}
			f(&ObserverSubscriber{observer, subscriber})
		})
	}
	return observable
}

//jig:template Defer<Foo>
//jig:needs Observable<Foo>

// DeferFoo does not create the ObservableFoo until the observer subscribes,
// and creates a fresh ObservableFoo for each observer.
func DeferFoo(factory func() ObservableFoo) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		factory()(observe, subscribeOn, subscriber)
	}
	return observable
}

//jig:template Empty<Foo>
//jig:needs Observable<Foo>

// EmptyFoo creates an Observable that emits no items but terminates normally.
func EmptyFoo() ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.Schedule(func() {
			if !subscriber.Canceled() {
				observe(zeroFoo, nil, true)
			}
		})
	}
	return observable
}

//jig:template Error<Foo>
//jig:needs Observable<Foo>

// ErrorFoo creates an Observable that emits no items and terminates with an
// error.
func ErrorFoo(err error) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.Schedule(func() {
			if !subscriber.Canceled() {
				observe(zeroFoo, err, true)
			}
		})
	}
	return observable
}

//jig:template FromChan<Foo>
//jig:needs Observable<Foo>

// FromChanFoo creates an ObservableFoo from a Go channel of foo values.
// It's not possible for the code feeding into the channel to send an error.
// The feeding code can send zero or more foo items and then closing the
// channel will be seen as completion.
func FromChanFoo(ch <-chan foo) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.ScheduleRecursive(func(self func()) {
			if subscriber.Canceled() {
				return
			}
			next, ok := <-ch
			if subscriber.Canceled() {
				return
			}
			if ok {
				observe(next, nil, false)
				if !subscriber.Canceled() {
					self()
				}
			} else {
				observe(zeroFoo, nil, true)
			}
		})
	}
	return observable
}

//jig:template FromChan
//jig:needs Observable

// FromChan creates an Observable from a Go channel of interface{}
// values. This allows the code feeding into the channel to send either an error
// or the next value. The feeding code can send zero or more items and then
// closing the channel will be seen as completion. When the feeding code sends
// an error into the channel, it should close the channel immediately to
// indicate termination with error.
func FromChan(ch <-chan interface{}) Observable {
	observable := func(observe ObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.ScheduleRecursive(func(self func()) {
			if subscriber.Canceled() {
				return
			}
			next, ok := <-ch
			if subscriber.Canceled() {
				return
			}
			if ok {
				err, ok := next.(error)
				if !ok {
					observe(next, nil, false)
					if !subscriber.Canceled() {
						self()
					}
				} else {
					observe(zero, err, true)
				}
			} else {
				observe(zero, nil, true)
			}
		})
	}
	return observable
}

//jig:template From<Foo>
//jig:needs FromSlice<Foo>

// FromFoo creates an ObservableFoo from multiple foo values passed in.
func FromFoo(slice ...foo) ObservableFoo {
	return FromSliceFoo(slice)
}

//jig:template From<Foo>s
//jig:needs FromSlice<Foo>

// FromFoos creates an ObservableFoo from multiple foo values passed in.
func FromFoos(slice ...foo) ObservableFoo {
	return FromSliceFoo(slice)
}

//jig:template FromSlice<Foo>
//jig:needs Observable<Foo>

// FromSliceFoo creates an ObservableFoo from a slice of foo values passed in.
func FromSliceFoo(slice []foo) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		i := 0
		subscribeOn.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if i < len(slice) {
					observe(slice[i], nil, false)
					if !subscriber.Canceled() {
						i++
						self()
					}
				} else {
					observe(zeroFoo, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Interval
//jig:needs ObservableInt

// Interval creates an ObservableInt that emits a sequence of integers spaced
// by a particular time interval.
func Interval(interval time.Duration) ObservableInt {
	observable := func(observe IntObserveFunc, scheduler Scheduler, subscriber Subscriber) {
		i := 0
		scheduler.ScheduleRecursive(func(self func()) {
			if subscriber.Canceled() {
				return
			}
			time.Sleep(interval)
			if subscriber.Canceled() {
				return
			}
			observe(i, nil, false)
			if subscriber.Canceled() {
				return
			}
			i++
			self()
		})
	}
	return observable
}

//jig:template Just<Foo>
//jig:needs Observable<Foo>

// JustFoo creates an ObservableFoo that emits a particular item.
func JustFoo(element foo) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		done := false
		subscribeOn.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if !done {
					observe(element, nil, false)
					if !subscriber.Canceled() {
						done = true
						self()
					}
				} else {
					observe(zeroFoo, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Never<Foo>
//jig:needs Observable<Foo>

// NeverFoo creates an ObservableFoo that emits no items and does't terminate.
func NeverFoo() ObservableFoo {
	observable := func(observe FooObserveFunc, scheduler Scheduler, subscriber Subscriber) {
	}
	return observable
}

//jig:template Range
//jig:needs ObservableInt

// Range creates an ObservableInt that emits a range of sequential integers.
func Range(start, count int) ObservableInt {
	end := start + count
	observable := func(observe IntObserveFunc, scheduler Scheduler, subscriber Subscriber) {
		i := start
		scheduler.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if i < end {
					observe(i, nil, false)
					if !subscriber.Canceled() {
						i++
						self()
					}
				} else {
					observe(zeroInt, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Observable Repeat

// Repeat creates an Observable that emits a sequence of items repeatedly.
func (o Observable) Repeat(count int) Observable {
	if count == 0 {
		return Empty()
	}
	observable := func(observe ObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		var repeated int
		var observer ObserveFunc
		observer = func(next interface{}, err error, done bool) {
			if !done || err != nil {
				observe(next, err, done)
			} else {
				repeated++
				if repeated < count {
					o(observer, subscribeOn, subscriber)
				} else {
					observe(nil, nil, true)
				}
			}
		}
		o(observer, subscribeOn, subscriber)
	}
	return observable
}

//jig:template Observable<Foo> Repeat
//jig:needs Observable Repeat

// Repeat creates an ObservableFoo that emits a sequence of items repeatedly.
func (o ObservableFoo) Repeat(count int) ObservableFoo {
	return o.AsObservable().Repeat(count).AsObservableFoo()
}

//jig:template Repeat<Foo>
//jig:needs Observable<Foo>

// RepeatFoo creates an ObservableFoo that emits a particular item or sequence
// of items repeatedly.
func RepeatFoo(value foo, count int) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		i := 0
		subscribeOn.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if i < count {
					observe(value, nil, false)
					if !subscriber.Canceled() {
						i++
						self()
					}
				} else {
					observe(zeroFoo, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Start<Foo>
//jig:needs Observable<Foo>

// StartFoo creates an ObservableFoo that emits the return value of a function.
// It is designed to be used with a function that returns a (foo, error) tuple.
// If the error is non-nil the returned ObservableFoo will be that error,
// otherwise it will be a single-value stream of foo.
func StartFoo(f func() (foo, error)) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		done := false
		subscribeOn.ScheduleRecursive(func(self func()) {
			if !subscriber.Canceled() {
				if !done {
					if next, err := f(); err == nil {
						observe(next, nil, false)
						if !subscriber.Canceled() {
							done = true
							self()
						}
					} else {
						observe(zeroFoo, err, true)
					}
				} else {
					observe(zeroFoo, nil, true)
				}
			}
		})
	}
	return observable
}

//jig:template Throw<Foo>
//jig:needs Observable<Foo>

// ThrowFoo creates an Observable that emits no items and terminates with an
// error.
func ThrowFoo(err error) ObservableFoo {
	observable := func(observe FooObserveFunc, subscribeOn Scheduler, subscriber Subscriber) {
		subscribeOn.Schedule(func() {
			if !subscriber.Canceled() {
				observe(zeroFoo, err, true)
			}
		})
	}
	return observable
}
<|endoftext|>


<commit_before>package models

import (
  "appengine"
  "appengine/datastore"
  "bytes"
  "html/template"
  "time"

  "github.com/russross/blackfriday"

  mycontext "github.com/tgreiser/victr/context"
)

func NewContentKey(wc mycontext.Context) *datastore.Key {
  return datastore.NewIncompleteKey(wc.Aec, "Content", nil)
}

func NewContent(wc mycontext.Context, page_key *datastore.Key) *Content {
  content := &Content {
    Theme: wc.Ctx.FormValue("theme"),
    Title: wc.Ctx.FormValue("title"),
    Markdown: wc.Ctx.FormValue("content"),
    PageKey: page_key,
    Version: 1,
  }

  return content
}

func FindContent(wc mycontext.Context, k *datastore.Key, c *Content) error {
  if err := datastore.Get(wc.Aec, k, c); err != nil {
    if err != datastore.ErrNoSuchEntity {
      wc.Aec.Errorf("datastore error with FindContent: %v %v", k, err)
    }
    return err
  }
  c.Key = k
  wc.Aec.Infof("Loaded content: %v", c)
  return nil
}

func FetchContentByPage(wc mycontext.Context, page_key *datastore.Key, sel *datastore.Key) ([]*Content, error) {
  lmt := 50
  wc.Aec.Infof("Page_key filter %v", page_key)
  q := datastore.NewQuery("Content").Filter("PageKey=", page_key).Order("-Version").Limit(lmt)
  versions := make([]*Content, 0, lmt)
  keys, err := q.GetAll(wc.Aec, &versions)
  if _, ok := err.(*datastore.ErrFieldMismatch); ok {
    wc.Aec.Infof("datastore missing field, ignoring: %v", err)
    err = nil
  } else if err != nil {
    wc.Aec.Errorf("got error instead of contents: %v", err)
    return nil, err
  } else if len(keys) > 0 {
    for i, k := range keys {
      versions[i].Key = k
      wc.Aec.Infof("Comparing %v and %v", k, sel)
      if versions[i].Key.String() == sel.String() {
        wc.Aec.Infof("Selected version: %v", versions[i].Version)
        versions[i].Selected = true
      }
    }
    return versions, nil
  } else {
    wc.Aec.Infof("Returning nil content - err no such entity")
    err = datastore.ErrNoSuchEntity
  }
  return nil, err

}

type Content struct {
  Key *datastore.Key `datastore:"-"`
  PageKey *datastore.Key
  Theme string
  Title string
  Draft bool
  // version is achieved with CreatedAt, or maybe not?
  Markdown string `datastore:",noindex"`
  CreatedAt time.Time
  Version int

  // non datastore fields
  Selected bool `datastore:"-"`
}

func (c *Content) Validate() map[string]string {
  ret := map[string]string{}

  if c.Theme == "" { ret["theme"] = "Please select a theme to use" }
  if c.Title == "" { ret["title"] = "Please enter a title for your page" }
  if c.Markdown == "" { ret["markdown"] = "Please enter some content" }
  if c.PageKey == nil { ret["site_id"] = "Please select a page" }
  return ret
}

func (c *Content) Save(wc mycontext.Context, key *datastore.Key) error {
  err := datastore.RunInTransaction(wc.Aec, func(aec appengine.Context) error {
    if c.Key == nil { c.CreatedAt = time.Now() }
    key, e := datastore.Put(aec, key, c)
    if e != nil {
      return e
    }
    c.Key = key
    return nil
  }, nil)
  if err != nil {
    wc.Aec.Errorf("datastore write failed: %v", err)
  }
  return err
}

func (c *Content) Build(wc mycontext.Context) bytes.Buffer {
  wc.Aec.Infof("Got form vals: %v", c)
  draft := template.Must(template.ParseFiles(c.Theme))
  var output bytes.Buffer
  path := "http://"
  if wc.Ctx.HttpRequest().TLS != nil { path = "https://" }
  path = path + appengine.DefaultVersionHostname(wc.Aec) + "/themes/" + c.Theme + "/"
  data := struct {
    Content template.HTML
    Title string
    ThemePath string
  }{
    template.HTML(blackfriday.MarkdownBasic([]byte(c.Markdown))),
    c.Title,
    path,
  }
  draft.Execute(&output, data )
  return output
}
<commit_msg>content - theme path bugfix<commit_after>package models

import (
  "appengine"
  "appengine/datastore"
  "bytes"
  "html/template"
  "path"
  "time"

  "github.com/russross/blackfriday"

  mycontext "github.com/tgreiser/victr/context"
)

func NewContentKey(wc mycontext.Context) *datastore.Key {
  return datastore.NewIncompleteKey(wc.Aec, "Content", nil)
}

func NewContent(wc mycontext.Context, page_key *datastore.Key) *Content {
  content := &Content {
    Theme: wc.Ctx.FormValue("theme"),
    Title: wc.Ctx.FormValue("title"),
    Markdown: wc.Ctx.FormValue("content"),
    PageKey: page_key,
    Version: 1,
  }

  return content
}

func FindContent(wc mycontext.Context, k *datastore.Key, c *Content) error {
  if err := datastore.Get(wc.Aec, k, c); err != nil {
    if err != datastore.ErrNoSuchEntity {
      wc.Aec.Errorf("datastore error with FindContent: %v %v", k, err)
    }
    return err
  }
  c.Key = k
  wc.Aec.Infof("Loaded content: %v", c)
  return nil
}

func FetchContentByPage(wc mycontext.Context, page_key *datastore.Key, sel *datastore.Key) ([]*Content, error) {
  lmt := 50
  wc.Aec.Infof("Page_key filter %v", page_key)
  q := datastore.NewQuery("Content").Filter("PageKey=", page_key).Order("-Version").Limit(lmt)
  versions := make([]*Content, 0, lmt)
  keys, err := q.GetAll(wc.Aec, &versions)
  if _, ok := err.(*datastore.ErrFieldMismatch); ok {
    wc.Aec.Infof("datastore missing field, ignoring: %v", err)
    err = nil
  } else if err != nil {
    wc.Aec.Errorf("got error instead of contents: %v", err)
    return nil, err
  } else if len(keys) > 0 {
    for i, k := range keys {
      versions[i].Key = k
      wc.Aec.Infof("Comparing %v and %v", k, sel)
      if versions[i].Key.String() == sel.String() {
        wc.Aec.Infof("Selected version: %v", versions[i].Version)
        versions[i].Selected = true
      }
    }
    return versions, nil
  } else {
    wc.Aec.Infof("Returning nil content - err no such entity")
    err = datastore.ErrNoSuchEntity
  }
  return nil, err

}

type Content struct {
  Key *datastore.Key `datastore:"-"`
  PageKey *datastore.Key
  Theme string
  Title string
  Draft bool
  // version is achieved with CreatedAt, or maybe not?
  Markdown string `datastore:",noindex"`
  CreatedAt time.Time
  Version int

  // non datastore fields
  Selected bool `datastore:"-"`
}

func (c *Content) Validate() map[string]string {
  ret := map[string]string{}

  if c.Theme == "" { ret["theme"] = "Please select a theme to use" }
  if c.Title == "" { ret["title"] = "Please enter a title for your page" }
  if c.Markdown == "" { ret["markdown"] = "Please enter some content" }
  if c.PageKey == nil { ret["site_id"] = "Please select a page" }
  return ret
}

func (c *Content) Save(wc mycontext.Context, key *datastore.Key) error {
  err := datastore.RunInTransaction(wc.Aec, func(aec appengine.Context) error {
    if c.Key == nil { c.CreatedAt = time.Now() }
    key, e := datastore.Put(aec, key, c)
    if e != nil {
      return e
    }
    c.Key = key
    return nil
  }, nil)
  if err != nil {
    wc.Aec.Errorf("datastore write failed: %v", err)
  }
  return err
}

func (c *Content) Build(wc mycontext.Context) bytes.Buffer {
  wc.Aec.Infof("Got form vals: %v", c)
  draft := template.Must(template.ParseFiles(c.Theme))
  var output bytes.Buffer
  url := "http://"
  if wc.Ctx.HttpRequest().TLS != nil { url = "https://" }
  url = url + appengine.DefaultVersionHostname(wc.Aec) + "/" + path.Dir(c.Theme) + "/"
  data := struct {
    Content template.HTML
    Title string
    ThemePath string
  }{
    template.HTML(blackfriday.MarkdownBasic([]byte(c.Markdown))),
    c.Title,
    url,
  }
  draft.Execute(&output, data )
  return output
}
<|endoftext|>


<commit_before>// Copyright 2021 Google Inc.
//
// 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 localbgp_test

import (
	"testing"
	"time"

	"github.com/openconfig/featureprofiles/internal/attrs"
	"github.com/openconfig/featureprofiles/internal/confirm"
	"github.com/openconfig/featureprofiles/internal/deviations"
	"github.com/openconfig/featureprofiles/internal/fptest"
	"github.com/openconfig/ondatra"
	"github.com/openconfig/ondatra/telemetry"
	"github.com/openconfig/ygot/ygot"
)

func TestMain(m *testing.M) {
	fptest.RunTests(m)
}

var (
	dutAttrs = attrs.Attributes{
		Desc:    "To ATE",
		IPv4:    "192.0.2.1",
		IPv4Len: 30,
	}
	ateAttrs = attrs.Attributes{
		Desc:    "To DUT",
		IPv4:    "192.0.2.2",
		IPv4Len: 30,
	}
)

const (
	dutAS = 64500
	ateAS = 64501

	keepAlive = 50
	holdTime  = keepAlive * 3 // Should be 3x keepAlive, see RFC 4271 - A Border Gateway Protocol 4, Sec. 10
)

func bgpWithNbr(as uint32, routerID string, nbr *telemetry.NetworkInstance_Protocol_Bgp_Neighbor) *telemetry.NetworkInstance_Protocol_Bgp {
	bgp := &telemetry.NetworkInstance_Protocol_Bgp{}
	bgp.GetOrCreateGlobal().As = ygot.Uint32(as)
	if routerID != "" {
		bgp.Global.RouterId = ygot.String(routerID)
	}
	bgp.AppendNeighbor(nbr)
	return bgp
}

func TestEstablish(t *testing.T) {
	// Configure interfaces
	dut := ondatra.DUT(t, "dut1")
	dutPortName := dut.Port(t, "port1").Name()
	intf1 := dutAttrs.NewInterface(dutPortName)
	dut.Config().Interface(intf1.GetName()).Replace(t, intf1)
	ate := ondatra.DUT(t, "dut2")
	atePortName := ate.Port(t, "port1").Name()
	intf2 := ateAttrs.NewInterface(atePortName)
	ate.Config().Interface(intf2.GetName()).Replace(t, intf2)
	// Get BGP paths
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	nbrPath := statePath.Neighbor(ateAttrs.IPv4)
	// Remove any existing BGP config
	dutConfPath.Replace(t, nil)
	ateConfPath.Replace(t, nil)
	startTime := uint64(time.Now().Unix())
	// Start a new session
	dutConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(ateAttrs.IPv4),
	})
	ateConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(dutAttrs.IPv4),
	})
	dutConfPath.Replace(t, dutConf)
	ateConfPath.Replace(t, ateConf)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)

	dutState := statePath.Get(t)
	confirm.State(t, dutConf, dutState)
	nbr := dutState.GetNeighbor(ateAttrs.IPv4)

	if !nbr.GetEnabled() {
		t.Errorf("Expected neighbor %v to be enabled", ateAttrs.IPv4)
	}
	if got, want := nbr.GetLastEstablished(), startTime*1000000000; got < want {
		t.Errorf("Bad last-established timestamp: got %v, want >= %v", got, want)
	}
	if got := nbr.GetEstablishedTransitions(); got != 1 {
		t.Errorf("Wrong established-transitions: got %v, want 1", got)
	}

	capabilities := map[telemetry.E_BgpTypes_BGP_CAPABILITY]bool{
		telemetry.BgpTypes_BGP_CAPABILITY_ROUTE_REFRESH: false,
		telemetry.BgpTypes_BGP_CAPABILITY_ASN32:         false,
		telemetry.BgpTypes_BGP_CAPABILITY_MPBGP:         false,
	}
	for _, cap := range nbrPath.SupportedCapabilities().Get(t) {
		capabilities[cap] = true
	}
	for cap, present := range capabilities {
		if !present {
			t.Errorf("Capability not reported: %v", cap)
		}
	}
}

func TestDisconnect(t *testing.T) {
	dut := ondatra.DUT(t, "dut1")
	ate := ondatra.DUT(t, "dut2")
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateIP := ateAttrs.IPv4
	dutIP := dutAttrs.IPv4
	nbrPath := statePath.Neighbor(ateIP)

	// Clear any existing config
	dutConfPath.Replace(t, nil)
	ateConfPath.Replace(t, nil)

	// Apply simple config
	dutConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(ateIP),
	})
	ateConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(dutIP),
	})
	dutConfPath.Replace(t, dutConf)
	ateConfPath.Replace(t, ateConf)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)
	// ateConfPath.Replace(t, nil)
	ateConfPath.Neighbor(dutIP).Enabled().Replace(t, false)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ACTIVE)
	code := nbrPath.Messages().Received().LastNotificationErrorCode().Get(t)
	if code != telemetry.BgpTypes_BGP_ERROR_CODE_CEASE {
		t.Errorf("On disconnect: expected error code %v, got %v", telemetry.BgpTypes_BGP_ERROR_CODE_CEASE, code)
	}
}

func TestParameters(t *testing.T) {
	ateIP := ateAttrs.IPv4
	dutIP := dutAttrs.IPv4
	dut := ondatra.DUT(t, "dut1")
	ate := ondatra.DUT(t, "dut2")
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	nbrPath := statePath.Neighbor(ateIP)

	cases := []struct {
		name      string
		dutConf   *telemetry.NetworkInstance_Protocol_Bgp
		ateConf   *telemetry.NetworkInstance_Protocol_Bgp
		wantState *telemetry.NetworkInstance_Protocol_Bgp
		skipMsg   string
	}{
		{
			name: "basic internal",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "basic external",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "explicit AS",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				LocalAs:         ygot.Uint32(100),
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(100),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "explicit router id",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "password",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				AuthPassword:    ygot.String("password"),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
				AuthPassword:    ygot.String("password"),
			}),
		},
		{
			name: "hold-time",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(holdTime),
					KeepaliveInterval: ygot.Uint16(keepAlive),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "connect-retry",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					ConnectRetry: ygot.Uint16(100),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
			skipMsg: "Not currently supported by EOS (see b/186141921)",
		},
		{
			name: "hold time negotiated",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(holdTime),
					KeepaliveInterval: ygot.Uint16(keepAlive),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(135),
					KeepaliveInterval: ygot.Uint16(45),
				},
			}),
			wantState: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:           ygot.Uint16(holdTime),
					NegotiatedHoldTime: ygot.Uint16(135),
					KeepaliveInterval:  ygot.Uint16(keepAlive),
				},
			}),
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if len(tc.skipMsg) > 0 {
				t.Skip(tc.skipMsg)
			}
			// Disable BGP
			dutConfPath.Replace(t, nil)
			ateConfPath.Replace(t, nil)
			// Renable and wait to establish
			dutConfPath.Replace(t, tc.dutConf)
			ateConfPath.Replace(t, tc.ateConf)
			nbrPath.SessionState().Await(t, time.Second*30, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)
			stateDut := statePath.Get(t)
			wantState := tc.wantState
			if wantState == nil {
				// if you don't specify a wantState, we just validate that the state corresponding to each config value is what it should be.
				wantState = tc.dutConf
			}
			confirm.State(t, wantState, stateDut)
		})
	}
}
<commit_msg>replaced Replace(t, nil) to Delete(t) (#647)<commit_after>// Copyright 2021 Google Inc.
//
// 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 localbgp_test

import (
	"testing"
	"time"

	"github.com/openconfig/featureprofiles/internal/attrs"
	"github.com/openconfig/featureprofiles/internal/confirm"
	"github.com/openconfig/featureprofiles/internal/deviations"
	"github.com/openconfig/featureprofiles/internal/fptest"
	"github.com/openconfig/ondatra"
	"github.com/openconfig/ondatra/telemetry"
	"github.com/openconfig/ygot/ygot"
)

func TestMain(m *testing.M) {
	fptest.RunTests(m)
}

var (
	dutAttrs = attrs.Attributes{
		Desc:    "To ATE",
		IPv4:    "192.0.2.1",
		IPv4Len: 30,
	}
	ateAttrs = attrs.Attributes{
		Desc:    "To DUT",
		IPv4:    "192.0.2.2",
		IPv4Len: 30,
	}
)

const (
	dutAS = 64500
	ateAS = 64501

	keepAlive = 50
	holdTime  = keepAlive * 3 // Should be 3x keepAlive, see RFC 4271 - A Border Gateway Protocol 4, Sec. 10
)

func bgpWithNbr(as uint32, routerID string, nbr *telemetry.NetworkInstance_Protocol_Bgp_Neighbor) *telemetry.NetworkInstance_Protocol_Bgp {
	bgp := &telemetry.NetworkInstance_Protocol_Bgp{}
	bgp.GetOrCreateGlobal().As = ygot.Uint32(as)
	if routerID != "" {
		bgp.Global.RouterId = ygot.String(routerID)
	}
	bgp.AppendNeighbor(nbr)
	return bgp
}

func TestEstablish(t *testing.T) {
	// Configure interfaces
	dut := ondatra.DUT(t, "dut1")
	dutPortName := dut.Port(t, "port1").Name()
	intf1 := dutAttrs.NewInterface(dutPortName)
	dut.Config().Interface(intf1.GetName()).Replace(t, intf1)
	ate := ondatra.DUT(t, "dut2")
	atePortName := ate.Port(t, "port1").Name()
	intf2 := ateAttrs.NewInterface(atePortName)
	ate.Config().Interface(intf2.GetName()).Replace(t, intf2)
	// Get BGP paths
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	nbrPath := statePath.Neighbor(ateAttrs.IPv4)
	// Remove any existing BGP config
	dutConfPath.Delete(t)
	ateConfPath.Delete(t)
	startTime := uint64(time.Now().Unix())
	// Start a new session
	dutConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(ateAttrs.IPv4),
	})
	ateConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(dutAttrs.IPv4),
	})
	dutConfPath.Replace(t, dutConf)
	ateConfPath.Replace(t, ateConf)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)

	dutState := statePath.Get(t)
	confirm.State(t, dutConf, dutState)
	nbr := dutState.GetNeighbor(ateAttrs.IPv4)

	if !nbr.GetEnabled() {
		t.Errorf("Expected neighbor %v to be enabled", ateAttrs.IPv4)
	}
	if got, want := nbr.GetLastEstablished(), startTime*1000000000; got < want {
		t.Errorf("Bad last-established timestamp: got %v, want >= %v", got, want)
	}
	if got := nbr.GetEstablishedTransitions(); got != 1 {
		t.Errorf("Wrong established-transitions: got %v, want 1", got)
	}

	capabilities := map[telemetry.E_BgpTypes_BGP_CAPABILITY]bool{
		telemetry.BgpTypes_BGP_CAPABILITY_ROUTE_REFRESH: false,
		telemetry.BgpTypes_BGP_CAPABILITY_ASN32:         false,
		telemetry.BgpTypes_BGP_CAPABILITY_MPBGP:         false,
	}
	for _, cap := range nbrPath.SupportedCapabilities().Get(t) {
		capabilities[cap] = true
	}
	for cap, present := range capabilities {
		if !present {
			t.Errorf("Capability not reported: %v", cap)
		}
	}
}

func TestDisconnect(t *testing.T) {
	dut := ondatra.DUT(t, "dut1")
	ate := ondatra.DUT(t, "dut2")
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateIP := ateAttrs.IPv4
	dutIP := dutAttrs.IPv4
	nbrPath := statePath.Neighbor(ateIP)

	// Clear any existing config
	dutConfPath.Delete(t)
	ateConfPath.Delete(t)

	// Apply simple config
	dutConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(ateIP),
	})
	ateConf := bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
		PeerAs:          ygot.Uint32(dutAS),
		NeighborAddress: ygot.String(dutIP),
	})
	dutConfPath.Replace(t, dutConf)
	ateConfPath.Replace(t, ateConf)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)
	// ateConfPath.Delete(t)
	ateConfPath.Neighbor(dutIP).Enabled().Replace(t, false)
	nbrPath.SessionState().Await(t, time.Second*15, telemetry.Bgp_Neighbor_SessionState_ACTIVE)
	code := nbrPath.Messages().Received().LastNotificationErrorCode().Get(t)
	if code != telemetry.BgpTypes_BGP_ERROR_CODE_CEASE {
		t.Errorf("On disconnect: expected error code %v, got %v", telemetry.BgpTypes_BGP_ERROR_CODE_CEASE, code)
	}
}

func TestParameters(t *testing.T) {
	ateIP := ateAttrs.IPv4
	dutIP := dutAttrs.IPv4
	dut := ondatra.DUT(t, "dut1")
	ate := ondatra.DUT(t, "dut2")
	dutConfPath := dut.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	ateConfPath := ate.Config().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	statePath := dut.Telemetry().NetworkInstance(*deviations.DefaultNetworkInstance).Protocol(telemetry.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp()
	nbrPath := statePath.Neighbor(ateIP)

	cases := []struct {
		name      string
		dutConf   *telemetry.NetworkInstance_Protocol_Bgp
		ateConf   *telemetry.NetworkInstance_Protocol_Bgp
		wantState *telemetry.NetworkInstance_Protocol_Bgp
		skipMsg   string
	}{
		{
			name: "basic internal",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "basic external",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "explicit AS",
			dutConf: bgpWithNbr(dutAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				LocalAs:         ygot.Uint32(100),
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(100),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "explicit router id",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "password",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				AuthPassword:    ygot.String("password"),
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
				AuthPassword:    ygot.String("password"),
			}),
		},
		{
			name: "hold-time",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(holdTime),
					KeepaliveInterval: ygot.Uint16(keepAlive),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
		},
		{
			name: "connect-retry",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					ConnectRetry: ygot.Uint16(100),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
			}),
			skipMsg: "Not currently supported by EOS (see b/186141921)",
		},
		{
			name: "hold time negotiated",
			dutConf: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(holdTime),
					KeepaliveInterval: ygot.Uint16(keepAlive),
				},
			}),
			ateConf: bgpWithNbr(ateAS, "", &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(dutAS),
				NeighborAddress: ygot.String(dutIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:          ygot.Uint16(135),
					KeepaliveInterval: ygot.Uint16(45),
				},
			}),
			wantState: bgpWithNbr(dutAS, dutIP, &telemetry.NetworkInstance_Protocol_Bgp_Neighbor{
				PeerAs:          ygot.Uint32(ateAS),
				NeighborAddress: ygot.String(ateIP),
				Timers: &telemetry.NetworkInstance_Protocol_Bgp_Neighbor_Timers{
					HoldTime:           ygot.Uint16(holdTime),
					NegotiatedHoldTime: ygot.Uint16(135),
					KeepaliveInterval:  ygot.Uint16(keepAlive),
				},
			}),
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if len(tc.skipMsg) > 0 {
				t.Skip(tc.skipMsg)
			}
			// Disable BGP
			dutConfPath.Delete(t)
			ateConfPath.Delete(t)
			// Renable and wait to establish
			dutConfPath.Replace(t, tc.dutConf)
			ateConfPath.Replace(t, tc.ateConf)
			nbrPath.SessionState().Await(t, time.Second*30, telemetry.Bgp_Neighbor_SessionState_ESTABLISHED)
			stateDut := statePath.Get(t)
			wantState := tc.wantState
			if wantState == nil {
				// if you don't specify a wantState, we just validate that the state corresponding to each config value is what it should be.
				wantState = tc.dutConf
			}
			confirm.State(t, wantState, stateDut)
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"regexp"
	"strings"

	. "github.com/bytbox/cashew/irc"
)

var server = "irc.freenode.net:6667"
var channels = []string{
	//"#go-nuts",
	"#go-bots",
}

func main() {
	flag.Parse()

	log.Printf("Connecting to %s", server)
	conn, err := Connect(server, "cashew", "The Go Nut")
	if err != nil {
		log.Fatal("ERR: ", err)
	}

	conn.JoinChannels(channels)

	messages := conn.Listen()

	// catch signals
	go func() {
		for s := range signal.Incoming {
			log.Print("Got signal: ", s.String())
			// TODO part from all channels
			conn.Quit("Going nuts!")
			os.Exit(1)
		}
	}()

	// TODO recover from a panic
	for m := range messages {
		switch m.Kind {
		case MSG_NOTICE:
			log.Printf("NOTICE\t%s", m.Text)
		case MSG_PRIVMSG:
			handlePriv(conn, getNick(m.From), m.To, m.Text)
		default:
			log.Print("WARNING: unhandled message kind")
		}
	}
}

func getNick(s string) string {
	a := strings.SplitN(s, "!", 2)
	return a[0]
}

func handlePriv(c *Client, from, to, text string) {
	replyTo := to
	if to[0] != '#' { // not sent to a channel - reply directly to user
		replyTo = from
	} else {
		// exit if this message isn't for us. TODO match ^! ?
		if r, _ := regexp.MatchString("cashew[,:] ", text); !r {
			return
		} else {
			_, text, _ = nextField(text)
		}
	}
	r, e := getReply(text)
	if r != "" {
		if e {
			r = fmt.Sprintf("%s, %s", from, r)
		}
		c.PrivMsg(replyTo, r)
	}
}

type Command func(string) (string, bool)

var commands = map[string]Command{
	"learn": func(text string) (string, bool) {
		_, _, g := nextField(text)
		if !g {
			return "I need more than that!", true
		}
		return "I would have learned if I knew how...", true
	},
}

func getReply(text string) (string, bool) {
	f, rest, _ := nextField(text)
	if len(f) < 2 {
		return "", true
	}
	switch f[0] {
	case '@':
		r, e := getReply(rest)
		if !e {
			return f[1:] + ", " + r, false
		}
		return r, true
	case '!':
		f = f[1:]
	}
	// look for f in command index
	c, ok := commands[f]
	if ok {
		return c(rest)
	}
	return "I don't understand "+f, true
}
<commit_msg>Changing username<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"regexp"
	"strings"

	. "github.com/bytbox/cashew/irc"
)

var server = "irc.freenode.net:6667"
var channels = []string{
	//"#go-nuts",
	"#go-bots",
}

func main() {
	flag.Parse()

	log.Printf("Connecting to %s", server)
	conn, err := Connect(server, "cashu", "The Go Nut")
	if err != nil {
		log.Fatal("ERR: ", err)
	}

	conn.JoinChannels(channels)

	messages := conn.Listen()

	// catch signals
	go func() {
		for s := range signal.Incoming {
			log.Print("Got signal: ", s.String())
			// TODO part from all channels
			conn.Quit("Going nuts!")
			os.Exit(1)
		}
	}()

	// TODO recover from a panic
	for m := range messages {
		switch m.Kind {
		case MSG_NOTICE:
			log.Printf("NOTICE\t%s", m.Text)
		case MSG_PRIVMSG:
			handlePriv(conn, getNick(m.From), m.To, m.Text)
		default:
			log.Print("WARNING: unhandled message kind")
		}
	}
}

func getNick(s string) string {
	a := strings.SplitN(s, "!", 2)
	return a[0]
}

func handlePriv(c *Client, from, to, text string) {
	replyTo := to
	if to[0] != '#' { // not sent to a channel - reply directly to user
		replyTo = from
	} else {
		// exit if this message isn't for us. TODO match ^! ?
		if r, _ := regexp.MatchString("cashew[,:] ", text); !r {
			return
		} else {
			_, text, _ = nextField(text)
		}
	}
	r, e := getReply(text)
	if r != "" {
		if e {
			r = fmt.Sprintf("%s, %s", from, r)
		}
		c.PrivMsg(replyTo, r)
	}
}

type Command func(string) (string, bool)

var commands = map[string]Command{
	"learn": func(text string) (string, bool) {
		_, _, g := nextField(text)
		if !g {
			return "I need more than that!", true
		}
		return "I would have learned if I knew how...", true
	},
}

func getReply(text string) (string, bool) {
	f, rest, _ := nextField(text)
	if len(f) < 2 {
		return "", true
	}
	switch f[0] {
	case '@':
		r, e := getReply(rest)
		if !e {
			return f[1:] + ", " + r, false
		}
		return r, true
	case '!':
		f = f[1:]
	}
	// look for f in command index
	c, ok := commands[f]
	if ok {
		return c(rest)
	}
	return "I don't understand "+f, true
}
<|endoftext|>


<commit_before>package geos

/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>
#include <stdint.h>

extern void IndexQueryCallback(void *, void *);
extern void goIndexSendQueryResult(size_t, void *);
extern uint32_t *IndexQuery(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, uint32_t *);
extern void IndexAdd(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, size_t);

*/
import "C"
import (
	"sync"
	"unsafe"
)

// IndexGeom is a struct for indexed geometries used by Index
// and returned by IndexQuery. Access to Prepared requires acquiring .Lock()
type IndexGeom struct {
	*sync.Mutex // Mutex for Prepared
	Geom        *Geom
	Prepared    *PreparedGeom
}
type Index struct {
	v     *C.GEOSSTRtree
	mu    *sync.Mutex
	geoms []IndexGeom
}

func (this *Geos) CreateIndex() *Index {
	tree := C.GEOSSTRtree_create_r(this.v, 10)
	if tree == nil {
		panic("unable to create tree")
	}
	return &Index{tree, &sync.Mutex{}, []IndexGeom{}}
}

// IndexQuery adds a geom to the index with the id.
func (this *Geos) IndexAdd(index *Index, geom *Geom) {
	index.mu.Lock()
	defer index.mu.Unlock()
	id := len(index.geoms)
	C.IndexAdd(this.v, index.v, geom.v, C.size_t(id))
	prep := this.Prepare(geom)
	index.geoms = append(index.geoms, IndexGeom{&sync.Mutex{}, geom, prep})
}

// IndexQuery queries the index for intersections with geom.
func (this *Geos) IndexQuery(index *Index, geom *Geom) []IndexGeom {
	index.mu.Lock()
	defer index.mu.Unlock()
	var num C.uint32_t
	r := C.IndexQuery(this.v, index.v, geom.v, &num)
	if r == nil {
		return nil
	}
	hits := (*[2 << 16]C.uint32_t)(unsafe.Pointer(r))[:num]
	defer C.free(unsafe.Pointer(r))

	var geoms []IndexGeom
	for idx := range hits {
		geoms = append(geoms, index.geoms[idx])
	}
	return geoms
}
<commit_msg>collect the right geometries for IndexQuery result<commit_after>package geos

/*
#cgo LDFLAGS: -lgeos_c
#include "geos_c.h"
#include <stdlib.h>
#include <stdint.h>

extern void IndexQueryCallback(void *, void *);
extern void goIndexSendQueryResult(size_t, void *);
extern uint32_t *IndexQuery(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, uint32_t *);
extern void IndexAdd(GEOSContextHandle_t, GEOSSTRtree *, const GEOSGeometry *, size_t);

*/
import "C"
import (
	"sync"
	"unsafe"
)

// IndexGeom is a struct for indexed geometries used by Index
// and returned by IndexQuery. Access to Prepared requires acquiring .Lock()
type IndexGeom struct {
	*sync.Mutex // Mutex for Prepared
	Geom        *Geom
	Prepared    *PreparedGeom
}
type Index struct {
	v     *C.GEOSSTRtree
	mu    *sync.Mutex
	geoms []IndexGeom
}

func (this *Geos) CreateIndex() *Index {
	tree := C.GEOSSTRtree_create_r(this.v, 10)
	if tree == nil {
		panic("unable to create tree")
	}
	return &Index{tree, &sync.Mutex{}, []IndexGeom{}}
}

// IndexQuery adds a geom to the index with the id.
func (this *Geos) IndexAdd(index *Index, geom *Geom) {
	index.mu.Lock()
	defer index.mu.Unlock()
	id := len(index.geoms)
	C.IndexAdd(this.v, index.v, geom.v, C.size_t(id))
	prep := this.Prepare(geom)
	index.geoms = append(index.geoms, IndexGeom{&sync.Mutex{}, geom, prep})
}

// IndexQuery queries the index for intersections with geom.
func (this *Geos) IndexQuery(index *Index, geom *Geom) []IndexGeom {
	index.mu.Lock()
	defer index.mu.Unlock()
	var num C.uint32_t
	r := C.IndexQuery(this.v, index.v, geom.v, &num)
	if r == nil {
		return nil
	}
	hits := (*[2 << 16]C.uint32_t)(unsafe.Pointer(r))[:num]
	defer C.free(unsafe.Pointer(r))

	var geoms []IndexGeom
	for _, idx := range hits {
		geoms = append(geoms, index.geoms[idx])
	}
	return geoms
}
<|endoftext|>