<commit_before>package kping

import (
	"context"
	"fmt"
	"net"
	"os"
	"sync"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pfring"
	"golang.org/x/net/ipv4"
)

type ipEvent struct {
	ip           string
	seq          int
	sendDuration time.Duration
	recvRTT      time.Duration
}

// A Statistic is a ping statistic of IP
type Statistic struct {
	RecvNum   int64     // recv packets number
	SentNum   int64     // sent packets number
	LostNum   float64   // lost packets number
	RTTs      []float64 // RTT of each recv packet
	MinRTT    float64   // minimum RTT of RTTs
	MaxRTT    float64   // maxmum RTT of RTTs
	AvgRTT    float64   // average RTT of RTTs
	StdDevRTT float64   // stddev of RTTs

	ipEvents map[int]ipEvent // key: ICMP seq
}

// DefaultRecvMode is the default recv mode
var DefaultRecvMode = "afpacket"

// Options representes send & recv options
type Options interface{}

type sendOptions struct {
	BatchSize   int64
	BufferSize  int64
	Parallel    int64
	Timeout     time.Duration
	WaitTimeout time.Duration
}

var defaultSendOptions = sendOptions{
	BatchSize:   1024,
	BufferSize:  10 * 1024 * 1024,
	Parallel:    30,
	Timeout:     100 * time.Second,
	WaitTimeout: 20 * time.Millisecond,
}

// SendOptions creates batch send options
//   batchSize: batch send ICMP packet number, must <= 1024, default: 1024
//   bufferSize: batch send buffer size, default: 10MB
//   parallel: send goroutine number, default: 30
//   timeout: send timeout, default: 100s
//   waitTimeout: batch send interval, default: 20ms
func SendOptions(batchSize, bufferSize, parallel int64, timeout, waitTimeout time.Duration) (options Options, err error) {
	return sendOptions{
		BatchSize:   batchSize,
		BufferSize:  bufferSize,
		Parallel:    parallel,
		Timeout:     timeout,
		WaitTimeout: waitTimeout,
	}, nil
}

type batchRecvOptions struct {
	BatchSize  int64
	BufferSize int64
	Parallel   int64
	Timeout    time.Duration
}

var defaultBatchRecvOptions = batchRecvOptions{
	BatchSize:  100,
	BufferSize: 10 * 1024 * 1024,
	Parallel:   10,
	Timeout:    100 * time.Millisecond,
}

// BatchRecvOptions creates batch recv options
//   batchSize: batch recv ICMP packet number, must <= 1024, default: 100
//   bufferSize: batch recv buffer size, default: 10MB
//   parallel: recv goroutine number, default: 10
//   timeout: recv timeout, default: 100ms
func BatchRecvOptions(batchSize, bufferSize, parallel int64, timeout time.Duration) (options Options, err error) {
	return batchRecvOptions{
		BatchSize:  batchSize,
		BufferSize: bufferSize,
		Parallel:   parallel,
		Timeout:    timeout,
	}, nil
}

type afpacketRecvOptions struct {
	Parallel   int64
	BlockMB    int64
	Timeout    time.Duration
	Iface      string
	SnapLength int64
}

var defaultAfPacketRecvOptions = afpacketRecvOptions{
	Parallel:   1,
	BlockMB:    128,
	Timeout:    100 * time.Millisecond,
	Iface:      "eth0",
	SnapLength: 128,
}

// AfPacketRecvOptions creates af_packet recv options
//   parallel: recv goroutine number, default: 1
//   blockMB: af_packet: total block size, default: 128MB
//   timeout: af_packet: poll timeout, default: 100ms
//   iface:  recv interface name, default: eth0
//   snapLength: snap byte number, default: 128B
func AfPacketRecvOptions(parallel, blockMB, snapLength int64, iface string, timeout time.Duration) (options Options, err error) {
	return afpacketRecvOptions{
		Parallel:   parallel,
		BlockMB:    blockMB,
		Timeout:    timeout,
		Iface:      iface,
		SnapLength: snapLength,
	}, nil
}

type pfringRecvOptions struct {
	Iface      string
	SnapLength int64
	Parallel   int64
}

var defaultPFRingRecvOptions = pfringRecvOptions{
	Iface:      "eth0",
	SnapLength: 128,
	Parallel:   1,
}

// PFRingRecvOptions creates pf_ring recv options
//   parallel: recv goroutine number， default: 1
//   snapLength: snap byte number, default: 128B
//   iface: recv interface name, default: eth0
func PFRingRecvOptions(parallel, snapLength int64, iface string) (options Options, err error) {
	return pfringRecvOptions{
		Iface:      iface,
		SnapLength: snapLength,
		Parallel:   parallel,
	}, nil
}

// A Pinger provides various methods of kping
type Pinger interface {
	// SetRecvMode set recv mode, oneof: afpacket(default)|batch|pfring
	SetRecvMode(recvMode string) error
	// SetOptions set send or recv options
	SetOptions(options Options) error
	// AddIPs add IP addrs to pinger
	AddIPs(addrs []string) error
	// Run flood ping, then calculate statistic of each IP
	Run() (statistics map[string]*Statistic, err error)
}

// NewPinger returns a new pinger
func NewPinger(sourceIP string, count, size int64, timeout, interval time.Duration) (p Pinger, err error) {
	p = &kping{
		sourceIP:         sourceIP,
		count:            count,
		size:             size,
		timeout:          timeout,
		interval:         interval,
		recvMode:         DefaultRecvMode,
		sendOpts:         defaultSendOptions,
		afpacketRecvOpts: defaultAfPacketRecvOptions,
		batchRecvOpts:    defaultBatchRecvOptions,
		pfringRecvOpts:   defaultPFRingRecvOptions,
		recvReady:        make(chan bool),
		sendDone:         make(chan bool),
		sendLock:         new(sync.Mutex),
	}
	return p, nil
}

type kping struct {
	sourceIP string        // ping source IP
	count    int64         // ping count, must < 55536
	size     int64         // ping ICMP packet payload bytes, must > 8
	timeout  time.Duration // ping total timeout
	interval time.Duration // ping interval
	recvMode string

	sendOpts         sendOptions
	afpacketRecvOpts afpacketRecvOptions
	batchRecvOpts    batchRecvOptions
	pfringRecvOpts   pfringRecvOptions

	rawConn      *rawConn
	ring         *pfring.Ring
	packetSource *gopacket.PacketSource
	stats        map[string]*Statistic // key: ip
	addrs        []*net.IPAddr
	ipCount      int64
	context      context.Context
	cancel       context.CancelFunc
	recvReady    chan bool
	sendDone     chan bool
	sendLock     *sync.Mutex
	ipEventChan  chan *ipEvent
}

func (p *kping) SetRecvMode(mode string) (err error) {
	switch mode {
	case "afpacket", "pfring", "batch":
		p.recvMode = mode
	default:
		return fmt.Errorf("unknown recv mode: %s, should be oneof: afpacket|pfring|batch", p.recvMode)
	}
	return nil
}

func (p *kping) SetOptions(options Options) (err error) {
	switch opts := options.(type) {
	case sendOptions:
		p.sendOpts = opts
	case batchRecvOptions:
		p.batchRecvOpts = opts
	case afpacketRecvOptions:
		p.afpacketRecvOpts = opts
	case pfringRecvOptions:
		p.pfringRecvOpts = opts
	}
	return nil
}

func (p *kping) AddIPs(ipaddrs []string) error {
	p.addrs = make([]*net.IPAddr, 0, len(ipaddrs))
	p.stats = make(map[string]*Statistic, len(ipaddrs))
	for _, ipaddr := range ipaddrs {
		addr, err := net.ResolveIPAddr("ip4", ipaddr)
		if err != nil {
			return fmt.Errorf("invalid IP: %s", ipaddr)
		}
		p.addrs = append(p.addrs, addr)
		p.ipCount++
	}
	return nil
}

type addrBatch struct {
	seq   int
	addrs []*net.IPAddr
}

func (p *kping) Run() (statistics map[string]*Statistic, err error) {
	// used by send & recv, so buffer size is double
	p.ipEventChan = make(chan *ipEvent, p.ipCount*p.count*2)

	// set context
	ctx, cancel := context.WithTimeout(context.TODO(), p.timeout)
	p.context = ctx
	p.cancel = cancel

	// create raw socket connection
	rawConn, err := newRawConn(p.sourceIP, p.batchRecvOpts.BufferSize, p.sendOpts.BufferSize, p.batchRecvOpts.Timeout, p.sendOpts.Timeout)
	if err != nil {
		return nil, err
	}
	defer rawConn.close()

	if p.recvMode == "batch" {
		// filter ICMP Echo & Reply type packet
		filter := ipv4.ICMPFilter{}
		filter.SetAll(false)
		filter.Accept(ipv4.ICMPTypeEchoReply)
		if err := rawConn.setICMPFilter(&filter); err != nil {
			return nil, fmt.Errorf("setICMPFilter failed: %v", err)
		}
	} else if p.recvMode == "pfring" {
		ring, err := pfring.NewRing(p.pfringRecvOpts.Iface, uint32(p.pfringRecvOpts.SnapLength), 0)
		if err != nil {
			return nil, fmt.Errorf("pfring: NewRing failed: %v", err)
		}
		filter := fmt.Sprintf("ip and dst %s and icmp[icmptype] = icmp-echoreply and icmp[4:2] > %d and icmp[6:2] > %d", p.sourceIP, icmpIDSeqInitNum, icmpIDSeqInitNum)
		if err := ring.SetBPFFilter(filter); err != nil {
			return nil, fmt.Errorf("pfring: set bpf filter failed: %v, filter: %s", err, filter)
		}
		if err := ring.SetSocketMode(pfring.ReadOnly); err != nil {
			return nil, fmt.Errorf("pfring: set socket mode failed: %v", err)
		}
		if err := ring.SetDirection(pfring.ReceiveOnly); err != nil {
			return nil, fmt.Errorf("pfring: set direction failed: %v", err)
		}
		if err := ring.Enable(); err != nil {
			return nil, fmt.Errorf("pfring: enable failed: %v", err)
		}
		p.ring = ring
		defer p.ring.Close()

		packetSource := gopacket.NewPacketSource(p.ring, layers.LinkTypeEthernet)
		packetSource.NoCopy = true
		p.packetSource = packetSource
	}

	// receive packets
	go func() {
		defer close(p.ipEventChan)

		fmt.Fprintf(os.Stderr, "kping recv: started, ipCount: %d\n", p.ipCount)
		wg := new(sync.WaitGroup)
		stime := time.Now()

		var recvParallel int64
		var recvFunc func(index int, wg *sync.WaitGroup)
		switch p.recvMode {
		case "afpacket":
			recvParallel = p.afpacketRecvOpts.Parallel
			recvFunc = p.afpacketRecv
		case "pfring":
			recvParallel = p.pfringRecvOpts.Parallel
			recvFunc = p.pfringRecv
		case "batch":
			recvParallel = p.batchRecvOpts.Parallel
			recvFunc = p.batchRecv
		default:
			panic(fmt.Sprintf("ping recv: unknown recvMode: %s", p.recvMode))

		}
		for i := 0; i < int(recvParallel); i++ {
			wg.Add(1)
			index := i
			go recvFunc(index, wg)
		}

		// receive ready
		close(p.recvReady)

		wg.Wait()
		fmt.Fprintf(os.Stderr, "kping recv: all done, ipCount: %d, usedTime: %s\n", p.ipCount, time.Since(stime))
	}()

	// send packets
	go func() {
		defer p.cancel()
		defer close(p.sendDone)

		// wait receive ready
		<-p.recvReady

		stime := time.Now()
		addrBatchChan := make(chan addrBatch)
		fmt.Fprintf(os.Stderr, "kping send: started, ipCount: %d, \n", p.ipCount)

		// cocurenccy send packets
		for i := 0; i < int(p.sendOpts.Parallel); i++ {
			index := i
			go p.send(index, addrBatchChan) // after close addrBatchChan, goroutine return
		}

		// caculate batch number
		var batchNum = 0
		if p.ipCount%p.sendOpts.BatchSize == 0 {
			batchNum = int(p.ipCount / p.sendOpts.BatchSize)
		} else {
			batchNum = int(p.ipCount/p.sendOpts.BatchSize + 1)
		}

		// fill address for each batch
		addrBatchs := make([]addrBatch, batchNum)
		for i := range addrBatchs {
			addrBatchs[i] = addrBatch{
				addrs: make([]*net.IPAddr, 0, p.sendOpts.BatchSize),
			}
		}
		for i, addr := range p.addrs {
			j := i / int(p.sendOpts.BatchSize)
			batch := addrBatchs[j]
			batch.addrs = append(batch.addrs, addr)
			addrBatchs[j] = batch
		}

		// send extra 10 packets
		for n := 0; n < int(p.count+10); n++ {
			stime := time.Now()
			for _, batch := range addrBatchs {
				batch.seq = n
				addrBatchChan <- batch
			}
			time.Sleep(p.interval)
			fmt.Fprintf(os.Stderr, "kping send: seq %d(%d) done, usedTime: %s\n", n, p.count+10, time.Since(stime))
		}

		// sent done, sleep 1s
		close(addrBatchChan)
		fmt.Fprintf(os.Stderr, "kping send: all done, ipCount: %d, usedTime: %s\n", p.ipCount, time.Since(stime))
		time.Sleep(1 * time.Second)
	}()

	p.process()
	statistics = p.statistic()
	return statistics, nil
}
<commit_msg>comment NewPinger<commit_after>package kping

import (
	"context"
	"fmt"
	"net"
	"os"
	"sync"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pfring"
	"golang.org/x/net/ipv4"
)

type ipEvent struct {
	ip           string
	seq          int
	sendDuration time.Duration
	recvRTT      time.Duration
}

// A Statistic is a ping statistic of IP
type Statistic struct {
	RecvNum   int64     // recv packets number
	SentNum   int64     // sent packets number
	LostNum   float64   // lost packets number
	RTTs      []float64 // RTT of each recv packet
	MinRTT    float64   // minimum RTT of RTTs
	MaxRTT    float64   // maxmum RTT of RTTs
	AvgRTT    float64   // average RTT of RTTs
	StdDevRTT float64   // stddev of RTTs

	ipEvents map[int]ipEvent // key: ICMP seq
}

// DefaultRecvMode is the default recv mode
var DefaultRecvMode = "afpacket"

// Options representes send & recv options
type Options interface{}

type sendOptions struct {
	BatchSize   int64
	BufferSize  int64
	Parallel    int64
	Timeout     time.Duration
	WaitTimeout time.Duration
}

var defaultSendOptions = sendOptions{
	BatchSize:   1024,
	BufferSize:  10 * 1024 * 1024,
	Parallel:    30,
	Timeout:     100 * time.Second,
	WaitTimeout: 20 * time.Millisecond,
}

// SendOptions creates batch send options
//   batchSize: batch send ICMP packet number, must <= 1024, default: 1024
//   bufferSize: batch send buffer size, default: 10MB
//   parallel: send goroutine number, default: 30
//   timeout: send timeout, default: 100s
//   waitTimeout: batch send interval, default: 20ms
func SendOptions(batchSize, bufferSize, parallel int64, timeout, waitTimeout time.Duration) (options Options, err error) {
	return sendOptions{
		BatchSize:   batchSize,
		BufferSize:  bufferSize,
		Parallel:    parallel,
		Timeout:     timeout,
		WaitTimeout: waitTimeout,
	}, nil
}

type batchRecvOptions struct {
	BatchSize  int64
	BufferSize int64
	Parallel   int64
	Timeout    time.Duration
}

var defaultBatchRecvOptions = batchRecvOptions{
	BatchSize:  100,
	BufferSize: 10 * 1024 * 1024,
	Parallel:   10,
	Timeout:    100 * time.Millisecond,
}

// BatchRecvOptions creates batch recv options
//   batchSize: batch recv ICMP packet number, must <= 1024, default: 100
//   bufferSize: batch recv buffer size, default: 10MB
//   parallel: recv goroutine number, default: 10
//   timeout: recv timeout, default: 100ms
func BatchRecvOptions(batchSize, bufferSize, parallel int64, timeout time.Duration) (options Options, err error) {
	return batchRecvOptions{
		BatchSize:  batchSize,
		BufferSize: bufferSize,
		Parallel:   parallel,
		Timeout:    timeout,
	}, nil
}

type afpacketRecvOptions struct {
	Parallel   int64
	BlockMB    int64
	Timeout    time.Duration
	Iface      string
	SnapLength int64
}

var defaultAfPacketRecvOptions = afpacketRecvOptions{
	Parallel:   1,
	BlockMB:    128,
	Timeout:    100 * time.Millisecond,
	Iface:      "eth0",
	SnapLength: 128,
}

// AfPacketRecvOptions creates af_packet recv options
//   parallel: recv goroutine number, default: 1
//   blockMB: af_packet: total block size, default: 128MB
//   timeout: af_packet: poll timeout, default: 100ms
//   iface:  recv interface name, default: eth0
//   snapLength: snap byte number, default: 128B
func AfPacketRecvOptions(parallel, blockMB, snapLength int64, iface string, timeout time.Duration) (options Options, err error) {
	return afpacketRecvOptions{
		Parallel:   parallel,
		BlockMB:    blockMB,
		Timeout:    timeout,
		Iface:      iface,
		SnapLength: snapLength,
	}, nil
}

type pfringRecvOptions struct {
	Iface      string
	SnapLength int64
	Parallel   int64
}

var defaultPFRingRecvOptions = pfringRecvOptions{
	Iface:      "eth0",
	SnapLength: 128,
	Parallel:   1,
}

// PFRingRecvOptions creates pf_ring recv options
//   parallel: recv goroutine number， default: 1
//   snapLength: snap byte number, default: 128B
//   iface: recv interface name, default: eth0
func PFRingRecvOptions(parallel, snapLength int64, iface string) (options Options, err error) {
	return pfringRecvOptions{
		Iface:      iface,
		SnapLength: snapLength,
		Parallel:   parallel,
	}, nil
}

// A Pinger provides various methods of kping
type Pinger interface {
	// SetRecvMode set recv mode, oneof: afpacket(default)|batch|pfring
	SetRecvMode(recvMode string) error
	// SetOptions set send or recv options
	SetOptions(options Options) error
	// AddIPs add IP addrs to pinger
	AddIPs(addrs []string) error
	// Run flood ping, then calculate statistic of each IP
	Run() (statistics map[string]*Statistic, err error)
}

// NewPinger returns a new pinger
//   sourceIP: ping source IP
//   count: number of request packets to send to each IP
//   size: ICMP payload size in bytes, must >= 8
//   timeout: the time to wait for all ping done
//   interval: the time between sending a ping packet to each IP
func NewPinger(sourceIP string, count, size int64, timeout, interval time.Duration) (p Pinger, err error) {
	p = &kping{
		sourceIP:         sourceIP,
		count:            count,
		size:             size,
		timeout:          timeout,
		interval:         interval,
		recvMode:         DefaultRecvMode,
		sendOpts:         defaultSendOptions,
		afpacketRecvOpts: defaultAfPacketRecvOptions,
		batchRecvOpts:    defaultBatchRecvOptions,
		pfringRecvOpts:   defaultPFRingRecvOptions,
		recvReady:        make(chan bool),
		sendDone:         make(chan bool),
		sendLock:         new(sync.Mutex),
	}
	return p, nil
}

type kping struct {
	sourceIP string        // ping source IP
	count    int64         // ping count, must < 55536
	size     int64         // ping ICMP packet payload bytes, must > 8
	timeout  time.Duration // ping total timeout
	interval time.Duration // ping interval
	recvMode string

	sendOpts         sendOptions
	afpacketRecvOpts afpacketRecvOptions
	batchRecvOpts    batchRecvOptions
	pfringRecvOpts   pfringRecvOptions

	rawConn      *rawConn
	ring         *pfring.Ring
	packetSource *gopacket.PacketSource
	stats        map[string]*Statistic // key: ip
	addrs        []*net.IPAddr
	ipCount      int64
	context      context.Context
	cancel       context.CancelFunc
	recvReady    chan bool
	sendDone     chan bool
	sendLock     *sync.Mutex
	ipEventChan  chan *ipEvent
}

func (p *kping) SetRecvMode(mode string) (err error) {
	switch mode {
	case "afpacket", "pfring", "batch":
		p.recvMode = mode
	default:
		return fmt.Errorf("unknown recv mode: %s, should be oneof: afpacket|pfring|batch", p.recvMode)
	}
	return nil
}

func (p *kping) SetOptions(options Options) (err error) {
	switch opts := options.(type) {
	case sendOptions:
		p.sendOpts = opts
	case batchRecvOptions:
		p.batchRecvOpts = opts
	case afpacketRecvOptions:
		p.afpacketRecvOpts = opts
	case pfringRecvOptions:
		p.pfringRecvOpts = opts
	}
	return nil
}

func (p *kping) AddIPs(ipaddrs []string) error {
	p.addrs = make([]*net.IPAddr, 0, len(ipaddrs))
	p.stats = make(map[string]*Statistic, len(ipaddrs))
	for _, ipaddr := range ipaddrs {
		addr, err := net.ResolveIPAddr("ip4", ipaddr)
		if err != nil {
			return fmt.Errorf("invalid IP: %s", ipaddr)
		}
		p.addrs = append(p.addrs, addr)
		p.ipCount++
	}
	return nil
}

type addrBatch struct {
	seq   int
	addrs []*net.IPAddr
}

func (p *kping) Run() (statistics map[string]*Statistic, err error) {
	// used by send & recv, so buffer size is double
	p.ipEventChan = make(chan *ipEvent, p.ipCount*p.count*2)

	// set context
	ctx, cancel := context.WithTimeout(context.TODO(), p.timeout)
	p.context = ctx
	p.cancel = cancel

	// create raw socket connection
	rawConn, err := newRawConn(p.sourceIP, p.batchRecvOpts.BufferSize, p.sendOpts.BufferSize, p.batchRecvOpts.Timeout, p.sendOpts.Timeout)
	if err != nil {
		return nil, err
	}
	defer rawConn.close()

	if p.recvMode == "batch" {
		// filter ICMP Echo & Reply type packet
		filter := ipv4.ICMPFilter{}
		filter.SetAll(false)
		filter.Accept(ipv4.ICMPTypeEchoReply)
		if err := rawConn.setICMPFilter(&filter); err != nil {
			return nil, fmt.Errorf("setICMPFilter failed: %v", err)
		}
	} else if p.recvMode == "pfring" {
		ring, err := pfring.NewRing(p.pfringRecvOpts.Iface, uint32(p.pfringRecvOpts.SnapLength), 0)
		if err != nil {
			return nil, fmt.Errorf("pfring: NewRing failed: %v", err)
		}
		filter := fmt.Sprintf("ip and dst %s and icmp[icmptype] = icmp-echoreply and icmp[4:2] > %d and icmp[6:2] > %d", p.sourceIP, icmpIDSeqInitNum, icmpIDSeqInitNum)
		if err := ring.SetBPFFilter(filter); err != nil {
			return nil, fmt.Errorf("pfring: set bpf filter failed: %v, filter: %s", err, filter)
		}
		if err := ring.SetSocketMode(pfring.ReadOnly); err != nil {
			return nil, fmt.Errorf("pfring: set socket mode failed: %v", err)
		}
		if err := ring.SetDirection(pfring.ReceiveOnly); err != nil {
			return nil, fmt.Errorf("pfring: set direction failed: %v", err)
		}
		if err := ring.Enable(); err != nil {
			return nil, fmt.Errorf("pfring: enable failed: %v", err)
		}
		p.ring = ring
		defer p.ring.Close()

		packetSource := gopacket.NewPacketSource(p.ring, layers.LinkTypeEthernet)
		packetSource.NoCopy = true
		p.packetSource = packetSource
	}

	// receive packets
	go func() {
		defer close(p.ipEventChan)

		fmt.Fprintf(os.Stderr, "kping recv: started, ipCount: %d\n", p.ipCount)
		wg := new(sync.WaitGroup)
		stime := time.Now()

		var recvParallel int64
		var recvFunc func(index int, wg *sync.WaitGroup)
		switch p.recvMode {
		case "afpacket":
			recvParallel = p.afpacketRecvOpts.Parallel
			recvFunc = p.afpacketRecv
		case "pfring":
			recvParallel = p.pfringRecvOpts.Parallel
			recvFunc = p.pfringRecv
		case "batch":
			recvParallel = p.batchRecvOpts.Parallel
			recvFunc = p.batchRecv
		default:
			panic(fmt.Sprintf("ping recv: unknown recvMode: %s", p.recvMode))

		}
		for i := 0; i < int(recvParallel); i++ {
			wg.Add(1)
			index := i
			go recvFunc(index, wg)
		}

		// receive ready
		close(p.recvReady)

		wg.Wait()
		fmt.Fprintf(os.Stderr, "kping recv: all done, ipCount: %d, usedTime: %s\n", p.ipCount, time.Since(stime))
	}()

	// send packets
	go func() {
		defer p.cancel()
		defer close(p.sendDone)

		// wait receive ready
		<-p.recvReady

		stime := time.Now()
		addrBatchChan := make(chan addrBatch)
		fmt.Fprintf(os.Stderr, "kping send: started, ipCount: %d, \n", p.ipCount)

		// cocurenccy send packets
		for i := 0; i < int(p.sendOpts.Parallel); i++ {
			index := i
			go p.send(index, addrBatchChan) // after close addrBatchChan, goroutine return
		}

		// caculate batch number
		var batchNum = 0
		if p.ipCount%p.sendOpts.BatchSize == 0 {
			batchNum = int(p.ipCount / p.sendOpts.BatchSize)
		} else {
			batchNum = int(p.ipCount/p.sendOpts.BatchSize + 1)
		}

		// fill address for each batch
		addrBatchs := make([]addrBatch, batchNum)
		for i := range addrBatchs {
			addrBatchs[i] = addrBatch{
				addrs: make([]*net.IPAddr, 0, p.sendOpts.BatchSize),
			}
		}
		for i, addr := range p.addrs {
			j := i / int(p.sendOpts.BatchSize)
			batch := addrBatchs[j]
			batch.addrs = append(batch.addrs, addr)
			addrBatchs[j] = batch
		}

		// send extra 10 packets
		for n := 0; n < int(p.count+10); n++ {
			stime := time.Now()
			for _, batch := range addrBatchs {
				batch.seq = n
				addrBatchChan <- batch
			}
			time.Sleep(p.interval)
			fmt.Fprintf(os.Stderr, "kping send: seq %d(%d) done, usedTime: %s\n", n, p.count+10, time.Since(stime))
		}

		// sent done, sleep 1s
		close(addrBatchChan)
		fmt.Fprintf(os.Stderr, "kping send: all done, ipCount: %d, usedTime: %s\n", p.ipCount, time.Since(stime))
		time.Sleep(1 * time.Second)
	}()

	p.process()
	statistics = p.statistic()
	return statistics, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Tigera, Inc. All rights reserved.

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

package errors

import (
	"fmt"
)

// Error indicating a problem connecting to the backend.
type ErrorDatastoreError struct {
	Err        error
	Identifier interface{}
}

func (e ErrorDatastoreError) Error() string {
	return e.Err.Error()
}

// Error indicating a resource does not exist.  Used when attempting to delete or
// update a non-existent resource.
type ErrorResourceDoesNotExist struct {
	Err        error
	Identifier interface{}
}

func (e ErrorResourceDoesNotExist) Error() string {
	return fmt.Sprintf("resource does not exist: %v with error: %v", e.Identifier, e.Err)
}

// Error indicating an operation is not supported.
type ErrorOperationNotSupported struct {
	Operation  string
	Identifier interface{}
	Reason     string
}

func (e ErrorOperationNotSupported) Error() string {
	if e.Reason == "" {
		return fmt.Sprintf("operation %s is not supported on %v", e.Operation, e.Identifier)
	} else {
		return fmt.Sprintf("operation %s is not supported on %v: %s", e.Operation, e.Identifier, e.Reason)
	}
}

// Error indicating a resource already exists.  Used when attempting to create a
// resource that already exists.
type ErrorResourceAlreadyExists struct {
	Err        error
	Identifier interface{}
}

func (e ErrorResourceAlreadyExists) Error() string {
	return fmt.Sprintf("resource already exists: %v", e.Identifier)
}

// Error indicating a problem connecting to the backend.
type ErrorConnectionUnauthorized struct {
	Err error
}

func (e ErrorConnectionUnauthorized) Error() string {
	return fmt.Sprintf("connection is unauthorized: %v", e.Err)
}

// Validation error containing the fields that are failed validation.
type ErrorValidation struct {
	ErroredFields []ErroredField
}

type ErroredField struct {
	Name   string
	Value  interface{}
	Reason string
}

func (e ErroredField) String() string {
	var fieldString string
	if e.Value == nil {
		fieldString = e.Name
	} else {
		fieldString = fmt.Sprintf("%s = '%v'", e.Name, e.Value)
	}
	if e.Reason != "" {
		fieldString = fmt.Sprintf("%s (%s)", fieldString, e.Reason)
	}
	return fieldString
}

func (e ErrorValidation) Error() string {
	if len(e.ErroredFields) == 0 {
		return fmt.Sprintf("unknown validation error: %v", e)
	} else if len(e.ErroredFields) == 1 {
		f := e.ErroredFields[0]
		return fmt.Sprintf("error with field %s", f)
	} else {
		s := "error with the following fields:\n"
		for _, f := range e.ErroredFields {
			s = s + fmt.Sprintf("-  %s\n", f)
		}
		return s
	}
}

// Error indicating insufficient identifiers have been supplied on a resource
// management request (create, apply, update, get, delete).
type ErrorInsufficientIdentifiers struct {
	Name string
}

func (e ErrorInsufficientIdentifiers) Error() string {
	return fmt.Sprintf("insufficient identifiers, missing '%s'", e.Name)
}

// Error indicating an atomic update attempt that failed due to a update conflict.
type ErrorResourceUpdateConflict struct {
	Err        error
	Identifier interface{}
}

func (e ErrorResourceUpdateConflict) Error() string {
	return fmt.Sprintf("update conflict: %v", e.Identifier)
}

// Error indicating that the operation may have partially succeeded, then
// failed, without rolling back. A common example is when a function failed
// in an acceptable way after it successfully wrote some data to the datastore.
type ErrorPartialFailure struct {
	Err error
}

func (e ErrorPartialFailure) Error() string {
	return fmt.Sprintf("operation partially failed: %v", e.Err)
}

// UpdateErrorIdentifier modifies the supplied error to use the new resource
// identifier.
func UpdateErrorIdentifier(err error, id interface{}) error {
	if err == nil {
		return nil
	}

	switch e := err.(type) {
	case ErrorDatastoreError:
		e.Identifier = id
		err = e
	case ErrorResourceDoesNotExist:
		e.Identifier = id
		err = e
	case ErrorOperationNotSupported:
		e.Identifier = id
		err = e
	case ErrorResourceAlreadyExists:
		e.Identifier = id
		err = e
	case ErrorResourceUpdateConflict:
		e.Identifier = id
		err = e
	}
	return err
}

// Error indicating the watcher has been terminated.
type ErrorWatchTerminated struct {
	Err            error
	ClosedByRemote bool
}

func (e ErrorWatchTerminated) Error() string {
	return fmt.Sprintf("watch terminated (closedByRemote:%v): %v", e.ClosedByRemote, e.Err)
}

// Error indicating the datastore has failed to parse an entry.
type ErrorParsingDatastoreEntry struct {
	RawKey   string
	RawValue string
	Err      error
}

func (e ErrorParsingDatastoreEntry) Error() string {
	return fmt.Sprintf("failed to parse datastore entry key=%s; value=%s: %v", e.RawKey, e.RawValue, e.Err)
}
<commit_msg>Fix recrusive Error() call for ErrorValidation struct<commit_after>// Copyright (c) 2020 Tigera, Inc. All rights reserved.

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

package errors

import (
	"fmt"
)

// Error indicating a problem connecting to the backend.
type ErrorDatastoreError struct {
	Err        error
	Identifier interface{}
}

func (e ErrorDatastoreError) Error() string {
	return e.Err.Error()
}

// Error indicating a resource does not exist.  Used when attempting to delete or
// update a non-existent resource.
type ErrorResourceDoesNotExist struct {
	Err        error
	Identifier interface{}
}

func (e ErrorResourceDoesNotExist) Error() string {
	return fmt.Sprintf("resource does not exist: %v with error: %v", e.Identifier, e.Err)
}

// Error indicating an operation is not supported.
type ErrorOperationNotSupported struct {
	Operation  string
	Identifier interface{}
	Reason     string
}

func (e ErrorOperationNotSupported) Error() string {
	if e.Reason == "" {
		return fmt.Sprintf("operation %s is not supported on %v", e.Operation, e.Identifier)
	} else {
		return fmt.Sprintf("operation %s is not supported on %v: %s", e.Operation, e.Identifier, e.Reason)
	}
}

// Error indicating a resource already exists.  Used when attempting to create a
// resource that already exists.
type ErrorResourceAlreadyExists struct {
	Err        error
	Identifier interface{}
}

func (e ErrorResourceAlreadyExists) Error() string {
	return fmt.Sprintf("resource already exists: %v", e.Identifier)
}

// Error indicating a problem connecting to the backend.
type ErrorConnectionUnauthorized struct {
	Err error
}

func (e ErrorConnectionUnauthorized) Error() string {
	return fmt.Sprintf("connection is unauthorized: %v", e.Err)
}

// Validation error containing the fields that are failed validation.
type ErrorValidation struct {
	ErroredFields []ErroredField
}

type ErroredField struct {
	Name   string
	Value  interface{}
	Reason string
}

func (e ErroredField) String() string {
	var fieldString string
	if e.Value == nil {
		fieldString = e.Name
	} else {
		fieldString = fmt.Sprintf("%s = '%v'", e.Name, e.Value)
	}
	if e.Reason != "" {
		fieldString = fmt.Sprintf("%s (%s)", fieldString, e.Reason)
	}
	return fieldString
}

func (e ErrorValidation) Error() string {
	if len(e.ErroredFields) == 0 {
		return "unknown validation error"
	} else if len(e.ErroredFields) == 1 {
		f := e.ErroredFields[0]
		return fmt.Sprintf("error with field %s", f)
	} else {
		s := "error with the following fields:\n"
		for _, f := range e.ErroredFields {
			s = s + fmt.Sprintf("-  %s\n", f)
		}
		return s
	}
}

// Error indicating insufficient identifiers have been supplied on a resource
// management request (create, apply, update, get, delete).
type ErrorInsufficientIdentifiers struct {
	Name string
}

func (e ErrorInsufficientIdentifiers) Error() string {
	return fmt.Sprintf("insufficient identifiers, missing '%s'", e.Name)
}

// Error indicating an atomic update attempt that failed due to a update conflict.
type ErrorResourceUpdateConflict struct {
	Err        error
	Identifier interface{}
}

func (e ErrorResourceUpdateConflict) Error() string {
	return fmt.Sprintf("update conflict: %v", e.Identifier)
}

// Error indicating that the operation may have partially succeeded, then
// failed, without rolling back. A common example is when a function failed
// in an acceptable way after it successfully wrote some data to the datastore.
type ErrorPartialFailure struct {
	Err error
}

func (e ErrorPartialFailure) Error() string {
	return fmt.Sprintf("operation partially failed: %v", e.Err)
}

// UpdateErrorIdentifier modifies the supplied error to use the new resource
// identifier.
func UpdateErrorIdentifier(err error, id interface{}) error {
	if err == nil {
		return nil
	}

	switch e := err.(type) {
	case ErrorDatastoreError:
		e.Identifier = id
		err = e
	case ErrorResourceDoesNotExist:
		e.Identifier = id
		err = e
	case ErrorOperationNotSupported:
		e.Identifier = id
		err = e
	case ErrorResourceAlreadyExists:
		e.Identifier = id
		err = e
	case ErrorResourceUpdateConflict:
		e.Identifier = id
		err = e
	}
	return err
}

// Error indicating the watcher has been terminated.
type ErrorWatchTerminated struct {
	Err            error
	ClosedByRemote bool
}

func (e ErrorWatchTerminated) Error() string {
	return fmt.Sprintf("watch terminated (closedByRemote:%v): %v", e.ClosedByRemote, e.Err)
}

// Error indicating the datastore has failed to parse an entry.
type ErrorParsingDatastoreEntry struct {
	RawKey   string
	RawValue string
	Err      error
}

func (e ErrorParsingDatastoreEntry) Error() string {
	return fmt.Sprintf("failed to parse datastore entry key=%s; value=%s: %v", e.RawKey, e.RawValue, e.Err)
}
<|endoftext|>


<commit_before>package errors

import (
	"errors"
	"fmt"
	"reflect"
)

// New returns an error that formats as the given text.
func New(text string) error {
	return errors.New(text)
}

// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
func Errorf(format string, a ...interface{}) error {
	return fmt.Errorf(format, a...)
}

// WalkFunc is the signature of the Walk callback function. The function gets the
// current error in the chain and should return true if the chain processing
// should be aborted.
type WalkFunc func(error) bool

// Walk invokes the given function for each error in the chain. If the
// provided functions returns true or no further cause can be found, the process
// is stopped and no further calls will be made.
//
// The next error in the chain is determined by the following rules:
// - If the current error has a `Cause() error` method (github.com/pkg/errors),
//   the return value of this method is used.
// - If the current error has a `Unwrap() error` method (golang.org/x/xerrors),
//   the return value of this method is used.
// - Common errors in the Go runtime that contain an Err field will use this value.
func Walk(err error, f WalkFunc) {
	for prev := err; err != nil; prev = err {
		if f(err) {
			return
		}

		switch e := err.(type) {
		case causer:
			err = e.Cause()
		case wrapper:
			err = e.Unwrap()
		default:
			// Unpack any struct or *struct with a field of name Err which satisfies
			// the error interface. This includes *url.Error, *net.OpError,
			// *os.SyscallError and many others in the stdlib.
			errType := reflect.TypeOf(err)
			errValue := reflect.ValueOf(err)
			if errValue.IsValid() && errType.Kind() == reflect.Ptr {
				errType = errType.Elem()
				errValue = errValue.Elem()
			}
			if errValue.IsValid() && errType.Kind() == reflect.Struct {
				if errField := errValue.FieldByName("Err"); errField.IsValid() {
					errFieldValue := errField.Interface()
					if newErr, ok := errFieldValue.(error); ok {
						err = newErr
					}
				}
			}
		}
		if err == prev {
			break
		}
	}
}

type causer interface {
	Cause() error
}
type wrapper interface {
	Unwrap() error
}
<commit_msg>lib/errors: don't panic on uncomparable errors #3123<commit_after>package errors

import (
	"errors"
	"fmt"
	"reflect"
)

// New returns an error that formats as the given text.
func New(text string) error {
	return errors.New(text)
}

// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
func Errorf(format string, a ...interface{}) error {
	return fmt.Errorf(format, a...)
}

// WalkFunc is the signature of the Walk callback function. The function gets the
// current error in the chain and should return true if the chain processing
// should be aborted.
type WalkFunc func(error) bool

// Walk invokes the given function for each error in the chain. If the
// provided functions returns true or no further cause can be found, the process
// is stopped and no further calls will be made.
//
// The next error in the chain is determined by the following rules:
// - If the current error has a `Cause() error` method (github.com/pkg/errors),
//   the return value of this method is used.
// - If the current error has a `Unwrap() error` method (golang.org/x/xerrors),
//   the return value of this method is used.
// - Common errors in the Go runtime that contain an Err field will use this value.
func Walk(err error, f WalkFunc) {
	for prev := err; err != nil; prev = err {
		if f(err) {
			return
		}

		switch e := err.(type) {
		case causer:
			err = e.Cause()
		case wrapper:
			err = e.Unwrap()
		default:
			// Unpack any struct or *struct with a field of name Err which satisfies
			// the error interface. This includes *url.Error, *net.OpError,
			// *os.SyscallError and many others in the stdlib.
			errType := reflect.TypeOf(err)
			errValue := reflect.ValueOf(err)
			if errValue.IsValid() && errType.Kind() == reflect.Ptr {
				errType = errType.Elem()
				errValue = errValue.Elem()
			}
			if errValue.IsValid() && errType.Kind() == reflect.Struct {
				if errField := errValue.FieldByName("Err"); errField.IsValid() {
					errFieldValue := errField.Interface()
					if newErr, ok := errFieldValue.(error); ok {
						err = newErr
					}
				}
			}
		}
		if reflect.DeepEqual(err, prev) {
			break
		}
	}
}

type causer interface {
	Cause() error
}
type wrapper interface {
	Unwrap() error
}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"encoding/json"
	"github.com/stretchr/testify/assert"
	"gopkg.in/h2non/gock.v1"
	"strings"
	"testing"
	"io/ioutil"
)

func TestGettingResource(t *testing.T) {

	alias := "alias1"
	resourceType := "datasource"
	environment := "environment"
	application := "application"
	zone := "zone"

	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias).
		MatchParam("type", resourceType).
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse.json")

	resource, err := fasit.getScopedResource(ResourceRequest{alias, resourceType}, environment, application, zone)

	assert.NoError(t, err)
	assert.Equal(t, alias, resource.name)
	assert.Equal(t, resourceType, resource.resourceType)
	assert.Equal(t, "jdbc:oracle:thin:@//a01dbfl030.adeo.no:1521/basta", resource.properties["url"])
	assert.Equal(t, "basta", resource.properties["username"])
}

func TestGetLoadBalancerConfig(t *testing.T) {

	environment := "environment"
	application := "application"

	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/resources").
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("type", "LoadBalancerConfig").
		Reply(200).File("testdata/fasitLbConfigResponse.json")

	resource, err := fasit.getLoadBalancerConfig("application","environment")

	assert.NoError(t, err)
	assert.Equal(t,len(resource.ingresses),2)
}

func TestResourceError(t *testing.T) {
	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		Reply(404).BodyString("not found")

	resource, err := fetchFasitResources("https://fasit.local", NaisDeploymentRequest{Application: "app", Environment: "env", Version: "123"}, NaisAppConfig{FasitResources: FasitResources{Used: []UsedResource{{Alias: "resourcealias", ResourceType: "baseurl"}}}})

	assert.Error(t, err)
	assert.Empty(t, resource)
	assert.True(t, strings.Contains(err.Error(), "not found (404)"))
}

func TestGettingListOfResources(t *testing.T) {

	alias := "alias1"
	alias2 := "alias2"
	alias3 := "alias3"
	alias4 := "alias4"

	resourceType := "datasource"
	environment := "environment"
	application := "application"
	zone := "zone"

	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias).
		MatchParam("type", resourceType).
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse.json")

	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias2).
		MatchParam("type", resourceType).
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse2.json")

	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias3).
		MatchParam("type", resourceType).
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse3.json")

	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias4).
		MatchParam("type", "applicationproperties").
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse4.json")

	resources := []ResourceRequest{}
	resources = append(resources, ResourceRequest{alias, resourceType})
	resources = append(resources, ResourceRequest{alias2, resourceType})
	resources = append(resources, ResourceRequest{alias3, resourceType})
	resources = append(resources, ResourceRequest{alias4, "applicationproperties"})

	resourcesReplies, err := fasit.GetScopedResources(resources, environment, application, zone)

	assert.NoError(t, err)
	assert.Equal(t, 4, len(resourcesReplies))
	assert.Equal(t, alias, resourcesReplies[0].name)
	assert.Equal(t, alias2, resourcesReplies[1].name)
	assert.Equal(t, alias3, resourcesReplies[2].name)
	assert.Equal(t, alias4, resourcesReplies[3].name)
	assert.Equal(t, 2, len(resourcesReplies[3].properties))
	assert.Equal(t, "value1", resourcesReplies[3].properties["key1"])
	assert.Equal(t, "dc=preprod,dc=local", resourcesReplies[3].properties["key2"])
}

func TestResourceWithArbitraryPropertyKeys(t *testing.T) {
	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", "alias").
		Reply(200).File("testdata/fasitResponse-arbitrary-keys.json")

	resource, err := fasit.getScopedResource(ResourceRequest{"alias", "DataSource"}, "dev", "app", "zone")

	assert.NoError(t, err)

	assert.Equal(t, "1", resource.properties["a"])
	assert.Equal(t, "2", resource.properties["b"])
	assert.Equal(t, "3", resource.properties["c"])
}

func TestResolvingSecret(t *testing.T) {
	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", "aliaset").
		Reply(200).File("testdata/response-with-secret.json")

	gock.New("https://fasit.adeo.no").
		Get("/api/v2/secrets/696969").
		HeaderPresent("Authorization").
		Reply(200).BodyString("hemmelig")

	resource, err := fasit.getScopedResource(ResourceRequest{"aliaset", "DataSource"}, "dev", "app", "zone")

	assert.NoError(t, err)

	assert.Equal(t, "1", resource.properties["a"])
	assert.Equal(t, "hemmelig", resource.secret["password"])
}

func TestResolveCertifcates(t *testing.T) {
	fasit := FasitClient{"https://fasit.local", "", ""}

	t.Run("Fetch certificate file for resources of type certificate", func(t *testing.T) {

		defer gock.Off()
		gock.New("https://fasit.local").
			Get("/api/v2/scopedresource").
			MatchParam("alias", "alias").
			Reply(200).File("testdata/fasitCertResponse.json")
		gock.New("https://fasit.adeo.no").
			Get("/api/v2/resources/3024713/file/keystore").
			Reply(200).Body(bytes.NewReader([]byte("Some binary format")))

		resource, err := fasit.getScopedResource(ResourceRequest{"alias", "Certificate"}, "dev", "app", "zone")

		assert.NoError(t, err)

		assert.Equal(t, "Some binary format", string(resource.certificates["srvvarseloppgave_cert_keystore"]))

	})

	t.Run("Ignore non certificate resources with files ", func(t *testing.T) {

		defer gock.Off()
		gock.New("https://fasit.local").
			Get("/api/v2/scopedresource").
			MatchParam("alias", "alias").
			Reply(200).File("testdata/fasitFilesNoCertifcateResponse.json").
			Done()

		resource, err := fasit.getScopedResource(ResourceRequest{"alias", "Certificate"}, "dev", "app", "zone")

		assert.NoError(t, err)

		assert.Equal(t, 0, len(resource.certificates))

	})

}

func TestParseLoadBalancerConfig(t *testing.T) {
	t.Run("Parse array of load balancer config correctly", func(t *testing.T) {
		b,_ := ioutil.ReadFile("testdata/fasitLbConfigResponse.json")
		result, err := parseLoadBalancerConfig(b)
		assert.NoError(t,err)
		assert.Equal(t,2, len(result))
		assert.Equal(t,result["url.with.root"],"root")
		assert.Equal(t,result["url.without.root"],"")

	})

	t.Run("Err if no loadbalancer config is found", func(t *testing.T) {
		_, err := parseLoadBalancerConfig([]byte(`["json1","json2"]`))
		assert.Error(t,err)
	})
}

func TestParseFilesObject(t *testing.T) {

	t.Run("Parse filename and fileurl correctly", func(t *testing.T) {
		var jsonMap map[string]interface{}
		json.Unmarshal([]byte(`{
			"keystore": {
				"filename": "keystore",
				"ref": "https://file.url"
			}}`), &jsonMap)
		fileName, fileUrl, err := parseFilesObject(jsonMap)

		assert.NoError(t, err)
		assert.Equal(t, "keystore", fileName)
		assert.Equal(t, "https://file.url", fileUrl)

	})

	t.Run("Err if filename not found ", func(t *testing.T) {
		var jsonMap map[string]interface{}
		json.Unmarshal([]byte(`{
			"keystore": {
				"ref": "https://file.url"
			}}`), &jsonMap)
		_, _, err := parseFilesObject(jsonMap)

		assert.Error(t, err)
	})

	t.Run("Err if fileurl not found ", func(t *testing.T) {
		var jsonMap map[string]interface{}
		json.Unmarshal([]byte(`{
			"keystore": {
				"filename": "keystore",
			}}`), &jsonMap)
		_, _, err := parseFilesObject(jsonMap)

		assert.Error(t, err)
	})

}
<commit_msg>Fix wrong expectation/actual order.<commit_after>package api

import (
	"bytes"
	"encoding/json"
	"github.com/stretchr/testify/assert"
	"gopkg.in/h2non/gock.v1"
	"strings"
	"testing"
	"io/ioutil"
)

func TestGettingResource(t *testing.T) {

	alias := "alias1"
	resourceType := "datasource"
	environment := "environment"
	application := "application"
	zone := "zone"

	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias).
		MatchParam("type", resourceType).
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse.json")

	resource, err := fasit.getScopedResource(ResourceRequest{alias, resourceType}, environment, application, zone)

	assert.NoError(t, err)
	assert.Equal(t, alias, resource.name)
	assert.Equal(t, resourceType, resource.resourceType)
	assert.Equal(t, "jdbc:oracle:thin:@//a01dbfl030.adeo.no:1521/basta", resource.properties["url"])
	assert.Equal(t, "basta", resource.properties["username"])
}

func TestGetLoadBalancerConfig(t *testing.T) {

	environment := "environment"
	application := "application"

	fasit := FasitClient{"https://fasit.local", "", ""}

	t.Run("Get load balancer config happy path", func(t *testing.T) {

		defer gock.Off()
		gock.New("https://fasit.local").
			Get("/api/v2/resources").
			MatchParam("environment", environment).
			MatchParam("application", application).
			MatchParam("type", "LoadBalancerConfig").
			Reply(200).File("testdata/fasitLbConfigResponse.json")

		resource, err := fasit.getLoadBalancerConfig("application", "environment")

		assert.NoError(t, err)
		assert.Equal(t, 2, len(resource.ingresses))
		assert.Equal(t, "LoadBalancerConfig", resource.resourceType)
	})

}

func TestResourceError(t *testing.T) {
	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		Reply(404).BodyString("not found")

	resource, err := fetchFasitResources("https://fasit.local", NaisDeploymentRequest{Application: "app", Environment: "env", Version: "123"}, NaisAppConfig{FasitResources: FasitResources{Used: []UsedResource{{Alias: "resourcealias", ResourceType: "baseurl"}}}})

	assert.Error(t, err)
	assert.Empty(t, resource)
	assert.True(t, strings.Contains(err.Error(), "not found (404)"))
}

func TestGettingListOfResources(t *testing.T) {

	alias := "alias1"
	alias2 := "alias2"
	alias3 := "alias3"
	alias4 := "alias4"

	resourceType := "datasource"
	environment := "environment"
	application := "application"
	zone := "zone"

	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias).
		MatchParam("type", resourceType).
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse.json")

	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias2).
		MatchParam("type", resourceType).
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse2.json")

	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias3).
		MatchParam("type", resourceType).
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse3.json")

	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", alias4).
		MatchParam("type", "applicationproperties").
		MatchParam("environment", environment).
		MatchParam("application", application).
		MatchParam("zone", zone).
		Reply(200).File("testdata/fasitResponse4.json")

	resources := []ResourceRequest{}
	resources = append(resources, ResourceRequest{alias, resourceType})
	resources = append(resources, ResourceRequest{alias2, resourceType})
	resources = append(resources, ResourceRequest{alias3, resourceType})
	resources = append(resources, ResourceRequest{alias4, "applicationproperties"})

	resourcesReplies, err := fasit.GetScopedResources(resources, environment, application, zone)

	assert.NoError(t, err)
	assert.Equal(t, 4, len(resourcesReplies))
	assert.Equal(t, alias, resourcesReplies[0].name)
	assert.Equal(t, alias2, resourcesReplies[1].name)
	assert.Equal(t, alias3, resourcesReplies[2].name)
	assert.Equal(t, alias4, resourcesReplies[3].name)
	assert.Equal(t, 2, len(resourcesReplies[3].properties))
	assert.Equal(t, "value1", resourcesReplies[3].properties["key1"])
	assert.Equal(t, "dc=preprod,dc=local", resourcesReplies[3].properties["key2"])
}

func TestResourceWithArbitraryPropertyKeys(t *testing.T) {
	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", "alias").
		Reply(200).File("testdata/fasitResponse-arbitrary-keys.json")

	resource, err := fasit.getScopedResource(ResourceRequest{"alias", "DataSource"}, "dev", "app", "zone")

	assert.NoError(t, err)

	assert.Equal(t, "1", resource.properties["a"])
	assert.Equal(t, "2", resource.properties["b"])
	assert.Equal(t, "3", resource.properties["c"])
}

func TestResolvingSecret(t *testing.T) {
	fasit := FasitClient{"https://fasit.local", "", ""}

	defer gock.Off()
	gock.New("https://fasit.local").
		Get("/api/v2/scopedresource").
		MatchParam("alias", "aliaset").
		Reply(200).File("testdata/response-with-secret.json")

	gock.New("https://fasit.adeo.no").
		Get("/api/v2/secrets/696969").
		HeaderPresent("Authorization").
		Reply(200).BodyString("hemmelig")

	resource, err := fasit.getScopedResource(ResourceRequest{"aliaset", "DataSource"}, "dev", "app", "zone")

	assert.NoError(t, err)

	assert.Equal(t, "1", resource.properties["a"])
	assert.Equal(t, "hemmelig", resource.secret["password"])
}

func TestResolveCertifcates(t *testing.T) {
	fasit := FasitClient{"https://fasit.local", "", ""}

	t.Run("Fetch certificate file for resources of type certificate", func(t *testing.T) {

		defer gock.Off()
		gock.New("https://fasit.local").
			Get("/api/v2/scopedresource").
			MatchParam("alias", "alias").
			Reply(200).File("testdata/fasitCertResponse.json")
		gock.New("https://fasit.adeo.no").
			Get("/api/v2/resources/3024713/file/keystore").
			Reply(200).Body(bytes.NewReader([]byte("Some binary format")))

		resource, err := fasit.getScopedResource(ResourceRequest{"alias", "Certificate"}, "dev", "app", "zone")

		assert.NoError(t, err)

		assert.Equal(t, "Some binary format", string(resource.certificates["srvvarseloppgave_cert_keystore"]))

	})

	t.Run("Ignore non certificate resources with files ", func(t *testing.T) {

		defer gock.Off()
		gock.New("https://fasit.local").
			Get("/api/v2/scopedresource").
			MatchParam("alias", "alias").
			Reply(200).File("testdata/fasitFilesNoCertifcateResponse.json").
			Done()

		resource, err := fasit.getScopedResource(ResourceRequest{"alias", "Certificate"}, "dev", "app", "zone")

		assert.NoError(t, err)

		assert.Equal(t, 0, len(resource.certificates))

	})

}

func TestParseLoadBalancerConfig(t *testing.T) {
	t.Run("Parse array of load balancer config correctly", func(t *testing.T) {
		b, _ := ioutil.ReadFile("testdata/fasitLbConfigResponse.json")
		result, err := parseLoadBalancerConfig(b)
		assert.NoError(t, err)
		assert.Equal(t, 2, len(result))
		assert.Equal(t, "root", result["url.with.root"])
		assert.Equal(t, "", result["url.without.root"])

	})

	t.Run("Err if no loadbalancer config is found", func(t *testing.T) {
		_, err := parseLoadBalancerConfig([]byte(`["json1","json2"]`))
		assert.Error(t, err)
	})
}

func TestParseFilesObject(t *testing.T) {

	t.Run("Parse filename and fileurl correctly", func(t *testing.T) {
		var jsonMap map[string]interface{}
		json.Unmarshal([]byte(`{
			"keystore": {
				"filename": "keystore",
				"ref": "https://file.url"
			}}`), &jsonMap)
		fileName, fileUrl, err := parseFilesObject(jsonMap)

		assert.NoError(t, err)
		assert.Equal(t, "keystore", fileName)
		assert.Equal(t, "https://file.url", fileUrl)

	})

	t.Run("Err if filename not found ", func(t *testing.T) {
		var jsonMap map[string]interface{}
		json.Unmarshal([]byte(`{
			"keystore": {
				"ref": "https://file.url"
			}}`), &jsonMap)
		_, _, err := parseFilesObject(jsonMap)

		assert.Error(t, err)
	})

	t.Run("Err if fileurl not found ", func(t *testing.T) {
		var jsonMap map[string]interface{}
		json.Unmarshal([]byte(`{
			"keystore": {
				"filename": "keystore",
			}}`), &jsonMap)
		_, _, err := parseFilesObject(jsonMap)

		assert.Error(t, err)
	})

}
<|endoftext|>


<commit_before>package watch

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/masahide/fsnotify"
	"golang.org/x/net/context"
)

// InotifyFileWatcher uses inotify to monitor file changes.
type InotifyFileWatcher struct {
	Filename string
	dw       *fsnotify.Watcher
	w        *fsnotify.Watcher
	delay    time.Duration
}

func NewInotifyFileWatcher(filename string, dw, w *fsnotify.Watcher, delay time.Duration) *InotifyFileWatcher {
	fw := &InotifyFileWatcher{
		Filename: filename,
		dw:       dw,
		w:        w,
		delay:    delay,
	}
	return fw
}

func (fw *InotifyFileWatcher) BlockUntilExists(ctx context.Context) error {
	var err error
	// Do a real check now as the file might have been created before
	// calling `WatchFlags` above.
	if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) {
		// file exists, or stat returned an error.
		return err
	}

	fwFilename, err := filepath.Abs(fw.Filename)
	if err != nil {
		return err
	}
	for {
		select {
		case evt, ok := <-fw.dw.Event:
			if !ok {
				return fmt.Errorf("fsnotify watcher has been closed")
			}
			evtName, err := filepath.Abs(evt.Name)
			if err != nil {
				return err
			}
			if evtName == fwFilename {
				return nil
			}
		case <-ctx.Done():
			return ctx.Err()
		}
	}
	panic("unreachable")
}

func (fw *InotifyFileWatcher) ChangeEvents(ctx context.Context, fi os.FileInfo) *FileChanges {
	changes := NewFileChanges()

	err := fw.w.Watch(fw.Filename)
	if err != nil {
		log.Fatalf("Error watching %v: %v", fw.Filename, err)
	}

	go func() {
		defer fw.w.RemoveWatch(fw.Filename)
		defer changes.Close()
		var inCreate bool
		var CreateTimer <-chan time.Time
		fwFilename, err := filepath.Abs(fw.Filename)
		if err != nil {
			return
		}

		for {
			var evt *fsnotify.FileEvent
			var ok bool

			select {
			case evt, ok = <-fw.dw.Event: // ディレクトリ監視イベント
				if !ok {
					return
				}
				if !evt.IsCreate() { // IN_CREATE 以外は無視
					continue
				}
				evtName, err := filepath.Abs(evt.Name)
				if err != nil {
					return
				}
				if evtName == fwFilename {
					inCreate = true
					log.Printf("Received IN_CREATE: %s", fwFilename)
					CreateTimer = time.After(fw.delay)
					continue
				}

			case evt, ok = <-fw.w.Event: // ファイル監視イベント
				if !ok {
					return
				}
			case <-CreateTimer:
				log.Printf("IN_CREATE timeout: %s", fwFilename)
				changes.NotifyRotated() //IN_CREATEからタイムアウトしたら強制rotate
				return
			case <-ctx.Done():
				return
			}

			switch {
			case evt.IsCloseWrite():
				if inCreate {
					log.Printf("Received IN_CREATE & IN_CLOSE_WRITE: %s", fwFilename)
					changes.NotifyRotated()
					return
				}
			case evt.IsModify():
				changes.NotifyModified()
			}
		}
	}()

	return changes
}
<commit_msg>コメントを修正<commit_after>package watch

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/masahide/fsnotify"
	"golang.org/x/net/context"
)

// InotifyFileWatcher uses inotify to monitor file changes.
type InotifyFileWatcher struct {
	Filename string
	dw       *fsnotify.Watcher
	w        *fsnotify.Watcher
	delay    time.Duration
}

func NewInotifyFileWatcher(filename string, dw, w *fsnotify.Watcher, delay time.Duration) *InotifyFileWatcher {
	fw := &InotifyFileWatcher{
		Filename: filename,
		dw:       dw,
		w:        w,
		delay:    delay,
	}
	return fw
}

func (fw *InotifyFileWatcher) BlockUntilExists(ctx context.Context) error {
	var err error
	// Do a real check now as the file might have been created before
	// calling `WatchFlags` above.
	if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) {
		// file exists, or stat returned an error.
		return err
	}

	fwFilename, err := filepath.Abs(fw.Filename)
	if err != nil {
		return err
	}
	for {
		select {
		case evt, ok := <-fw.dw.Event:
			if !ok {
				return fmt.Errorf("fsnotify watcher has been closed")
			}
			evtName, err := filepath.Abs(evt.Name)
			if err != nil {
				return err
			}
			if evtName == fwFilename {
				return nil
			}
		case <-ctx.Done():
			return ctx.Err()
		}
	}
	panic("unreachable")
}

func (fw *InotifyFileWatcher) ChangeEvents(ctx context.Context, fi os.FileInfo) *FileChanges {
	changes := NewFileChanges()

	err := fw.w.Watch(fw.Filename)
	if err != nil {
		log.Fatalf("Error watching %v: %v", fw.Filename, err)
	}

	go func() {
		defer fw.w.RemoveWatch(fw.Filename)
		defer changes.Close()
		var inCreate bool
		var CreateTimer <-chan time.Time
		fwFilename, err := filepath.Abs(fw.Filename)
		if err != nil {
			return
		}

		for {
			var evt *fsnotify.FileEvent
			var ok bool

			select {
			case evt, ok = <-fw.dw.Event: // ディレクトリ監視イベント
				if !ok {
					return
				}
				if !evt.IsCreate() { // IsCreate 以外は無視
					continue
				}
				evtName, err := filepath.Abs(evt.Name)
				if err != nil {
					return
				}
				if evtName == fwFilename {
					inCreate = true
					log.Printf("Received IsCreate: %s", fwFilename)
					CreateTimer = time.After(fw.delay)
					continue
				}

			case evt, ok = <-fw.w.Event: // ファイル監視イベント
				if !ok {
					return
				}
			case <-CreateTimer:
				log.Printf("IsCreate timeout: %s", fwFilename)
				changes.NotifyRotated() //IsCreateからタイムアウトしたら強制rotate
				return
			case <-ctx.Done():
				return
			}

			switch {
			case evt.IsCloseWrite():
				if inCreate {
					log.Printf("Received IsCreate & IsCloseWrite: %s", fwFilename)
					changes.NotifyRotated()
					return
				}
			case evt.IsModify():
				changes.NotifyModified()
			}
		}
	}()

	return changes
}
<|endoftext|>


<commit_before>// (C) Copyright 2019 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceEthernetNetwork() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceEthernetNetworkRead,

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"vlan_id": {
				Type:     schema.TypeInt,
				Computed: true,
			},
			"purpose": {
				Type:     schema.TypeString,
				Computed: true,
				Default:  "General",
			},
			"private_network": {
				Type:     schema.TypeBool,
				Computed: true,
				Default:  false,
			},
			"smart_link": {
				Type:     schema.TypeBool,
				Computed: true,
				Default:  false,
			},
			"ethernet_network_type": {
				Type:     schema.TypeString,
				Computed: true,
				Default:  "Tagged",
			},
			"type": {
				Type:     schema.TypeString,
				Computed: true,
				Default:  "ethernet-networkV3",
			},
			"connection_template_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"modified": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"category": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"fabric_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"eTag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"scopesUri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"initial_scope_uris": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
		},
	}
}

func dataSourceEthernetNetworkRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	name := d.Get("name").(string)
	eNet, err := config.ovClient.GetEthernetNetworkByName(name)
	if err != nil || eNet.URI.IsNil() {
		d.SetId("")
		return nil
	}
	d.Set("name", eNet.Name)
	d.Set("vlan_id", eNet.VlanId)
	d.Set("purpose", eNet.Purpose)
	d.Set("smart_link", eNet.SmartLink)
	d.Set("private_network", eNet.PrivateNetwork)
	d.Set("ethernet_network_type", eNet.EthernetNetworkType)
	d.Set("type", eNet.Type)
	d.Set("created", eNet.Created)
	d.Set("modified", eNet.Modified)
	d.Set("uri", eNet.URI.String())
	d.Set("connection_template_uri", eNet.ConnectionTemplateUri.String())
	d.Set("status", eNet.Status)
	d.Set("category", eNet.Category)
	d.Set("state", eNet.State)
	d.Set("fabric_uri", eNet.FabricUri.String())
	d.Set("eTag", eNet.ETAG)
	d.Set("scopesUri", eNet.ScopesUri.String())
	d.Set("initial_scope_uris", eNet.InitialScopeUris)
	d.SetId(name)
	return nil
}
<commit_msg>unit testing error resloved<commit_after>// (C) Copyright 2019 Hewlett Packard Enterprise Development LP
//
// 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 oneview

import (
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceEthernetNetwork() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceEthernetNetworkRead,

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"vlan_id": {
				Type:     schema.TypeInt,
				Computed: true,
			},
			"purpose": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"private_network": {
				Type:     schema.TypeBool,
				Computed: true,
			},
			"smart_link": {
				Type:     schema.TypeBool,
				Computed: true,
			},
			"ethernet_network_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"connection_template_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"modified": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"category": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"fabric_uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"eTag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"scopesUri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"initial_scope_uris": {
				Computed: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Set: schema.HashString,
			},
		},
	}
}

func dataSourceEthernetNetworkRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	name := d.Get("name").(string)
	eNet, err := config.ovClient.GetEthernetNetworkByName(name)
	if err != nil || eNet.URI.IsNil() {
		d.SetId("")
		return nil
	}
	d.Set("name", eNet.Name)
	d.Set("vlan_id", eNet.VlanId)
	d.Set("purpose", eNet.Purpose)
	d.Set("smart_link", eNet.SmartLink)
	d.Set("private_network", eNet.PrivateNetwork)
	d.Set("ethernet_network_type", eNet.EthernetNetworkType)
	d.Set("type", eNet.Type)
	d.Set("created", eNet.Created)
	d.Set("modified", eNet.Modified)
	d.Set("uri", eNet.URI.String())
	d.Set("connection_template_uri", eNet.ConnectionTemplateUri.String())
	d.Set("status", eNet.Status)
	d.Set("category", eNet.Category)
	d.Set("state", eNet.State)
	d.Set("fabric_uri", eNet.FabricUri.String())
	d.Set("eTag", eNet.ETAG)
	d.Set("scopesUri", eNet.ScopesUri.String())
	d.Set("initial_scope_uris", eNet.InitialScopeUris)
	d.SetId(name)
	return nil
}
<|endoftext|>


<commit_before>package mantle

import (
        "github.com/vireshas/mantle/backends/redis"
)

type Mantle interface {
        Get(key string) string
        Set(key string, value interface{}) bool
        //MGet(key ...interface{}) map[interface{}]interface{}
        //MSet(k_v_map map[interface{}]interface{}) bool
        //Expire(keys ...interface{}) bool
}

type Orm struct {
        Driver string
        Host string
        Port string
}

func (o *Orm) Get() Mantle {
        if o.Driver == "memcache" {
                return Mantle(&mantle.Redis{Host : "", Port : ""})
        }else{
		redis := &mantle.Redis{Host : "", Port : ""}
		redis.Configure()
                return Mantle(redis)
        }
}
<commit_msg>pool capacity is configurable<commit_after>package mantle

import (
        "github.com/vireshas/mantle/backends/redis"
)

type Mantle interface {
        Get(key string) string
        Set(key string, value interface{}) bool
        MGet(key ...interface{}) []string
        //MSet(k_v_map map[string]interface{}) bool
        //Expire(keys ...interface{}) bool
}

type Orm struct {
        Driver string
        Host string
        Port string
        Capacity int
}

func (o *Orm) Get() Mantle {
        if o.Driver == "memcache" {
                return Mantle(&mantle.Redis{Host:o.Host, Port:o.Port, Capacity:o.Capacity})
        }else{
		redis := &mantle.Redis{Host:o.Host, Port:o.Port, Capacity:o.Capacity}
		redis.Configure()
                return Mantle(redis)
        }
}
<|endoftext|>


<commit_before>// Package kubernetes implements kubernetes micro runtime
package kubernetes

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/micro/go-micro/runtime"
	"github.com/micro/go-micro/runtime/kubernetes/client"
	"github.com/micro/go-micro/util/log"
)

// action to take on runtime service
type action int

const (
	start action = iota
	update
	stop
)

// task is queued into runtime queue
type task struct {
	action  action
	service *service
}

type kubernetes struct {
	sync.RWMutex
	// options configure runtime
	options runtime.Options
	// indicates if we're running
	running bool
	// task queue for kubernetes services
	queue chan *task
	// used to stop the runtime
	closed chan bool
	// client is kubernetes client
	client client.Kubernetes
}

// getService queries kubernetes for micro service
// NOTE: this function is not thread-safe
func (k *kubernetes) getService(labels map[string]string) ([]*runtime.Service, error) {
	// get the service status
	serviceList := new(client.ServiceList)
	r := &client.Resource{
		Kind:  "service",
		Value: serviceList,
	}

	// get the service from k8s
	if err := k.client.Get(r, labels); err != nil {
		return nil, err
	}

	// get the deployment status
	depList := new(client.DeploymentList)
	d := &client.Resource{
		Kind:  "deployment",
		Value: depList,
	}

	// get the deployment from k8s
	if err := k.client.Get(d, labels); err != nil {
		return nil, err
	}

	// service map
	svcMap := make(map[string]*runtime.Service)

	// collect info from kubernetes service
	for _, kservice := range serviceList.Items {
		// name of the service
		name := kservice.Metadata.Labels["name"]
		// version of the service
		version := kservice.Metadata.Labels["version"]

		// save as service
		svcMap[name+version] = &runtime.Service{
			Name:     name,
			Version:  version,
			Metadata: make(map[string]string),
		}

		// copy annotations metadata into service metadata
		for k, v := range kservice.Metadata.Annotations {
			svcMap[name+version].Metadata[k] = v
		}
	}

	// collect additional info from kubernetes deployment
	for _, kdep := range depList.Items {
		// name of the service
		name := kdep.Metadata.Labels["name"]
		// versio of the service
		version := kdep.Metadata.Labels["version"]

		// access existing service map based on name + version
		if svc, ok := svcMap[name+version]; ok {
			// we're expecting our own service name in metadata
			if _, ok := kdep.Metadata.Annotations["name"]; !ok {
				continue
			}

			// set the service name, version and source
			// based on existing annotations we stored
			svc.Name = kdep.Metadata.Annotations["name"]
			svc.Version = kdep.Metadata.Annotations["version"]
			svc.Source = kdep.Metadata.Annotations["source"]

			// delete from metadata
			delete(kdep.Metadata.Annotations, "name")
			delete(kdep.Metadata.Annotations, "version")
			delete(kdep.Metadata.Annotations, "source")

			// copy all annotations metadata into service metadata
			for k, v := range kdep.Metadata.Annotations {
				svc.Metadata[k] = v
			}

			// parse out deployment status and inject into service metadata
			if len(kdep.Status.Conditions) > 0 {
				status := kdep.Status.Conditions[0].Type
				// pick the last known condition type and mark the service status with it
				log.Debugf("Runtime setting %s service deployment status: %v", name, status)
				svc.Metadata["status"] = status
			}

			// parse out deployment build
			if build, ok := kdep.Metadata.Annotations["build"]; ok {
				buildTime, err := time.Parse(time.RFC3339, build)
				if err != nil {
					log.Debugf("Runtime failed parsing build time for %s: %v", name, err)
					continue
				}
				svc.Metadata["build"] = fmt.Sprintf("%d", buildTime.Unix())
				continue
			}
			// if no build annotation is found, set it to current time
			svc.Metadata["build"] = fmt.Sprintf("%d", time.Now().Unix())
		}
	}

	// collect all the services and return
	services := make([]*runtime.Service, 0, len(serviceList.Items))

	for _, service := range svcMap {
		services = append(services, service)
	}

	return services, nil
}

// run runs the runtime management loop
func (k *kubernetes) run(events <-chan runtime.Event) {
	t := time.NewTicker(time.Second * 10)
	defer t.Stop()

	for {
		select {
		case <-t.C:
			// TODO: figure out what to do here
			// - do we even need the ticker for k8s services?
		case task := <-k.queue:
			// The task queue is used to take actions e.g (CRUD - R)
			switch task.action {
			case start:
				log.Debugf("Runtime starting new service: %s", task.service.Name)
				if err := task.service.Start(k.client); err != nil {
					log.Debugf("Runtime failed to start service %s: %v", task.service.Name, err)
					continue
				}
			case stop:
				log.Debugf("Runtime stopping service: %s", task.service.Name)
				if err := task.service.Stop(k.client); err != nil {
					log.Debugf("Runtime failed to stop service %s: %v", task.service.Name, err)
					continue
				}
			case update:
				log.Debugf("Runtime updating service: %s", task.service.Name)
				if err := task.service.Update(k.client); err != nil {
					log.Debugf("Runtime failed to update service %s: %v", task.service.Name, err)
					continue
				}
			default:
				log.Debugf("Runtime received unknown action for service: %s", task.service.Name)
			}
		case event := <-events:
			// NOTE: we only handle Update events for now
			log.Debugf("Runtime received notification event: %v", event)
			switch event.Type {
			case runtime.Update:
				// only process if there's an actual service
				// we do not update all the things individually
				if len(event.Service) == 0 {
					continue
				}

				// set the default labels
				labels := map[string]string{
					"micro": "service",
					"name":  event.Service,
				}

				if len(event.Version) > 0 {
					labels["version"] = event.Version
				}

				// get the deployment status
				deployed := new(client.DeploymentList)

				// get the existing service rather than creating a new one
				err := k.client.Get(&client.Resource{
					Kind:  "deployment",
					Value: deployed,
				}, labels)

				if err != nil {
					log.Debugf("Runtime update failed to get service %s: %v", event.Service, err)
					continue
				}

				// technically we should not receive multiple versions but hey ho
				for _, service := range deployed.Items {
					// update build time annotation
					service.Spec.Template.Metadata.Annotations["build"] = event.Timestamp.Format(time.RFC3339)

					log.Debugf("Runtime updating service: %s", event.Service)
					if err := k.client.Update(deploymentResource(&service)); err != nil {
						log.Debugf("Runtime failed to update service %s: %v", event.Service, err)
						continue
					}
				}
			}
		case <-k.closed:
			log.Debugf("Runtime stopped")
			return
		}
	}
}

// Init initializes runtime options
func (k *kubernetes) Init(opts ...runtime.Option) error {
	k.Lock()
	defer k.Unlock()

	for _, o := range opts {
		o(&k.options)
	}

	return nil
}

// Creates a service
func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) error {
	k.Lock()
	defer k.Unlock()

	var options runtime.CreateOptions
	for _, o := range opts {
		o(&options)
	}

	// quickly prevalidate the name and version
	name := s.Name
	if len(s.Version) > 0 {
		name = name + "-" + s.Version
	}

	// format as we'll format in the deployment
	name = client.Format(name)

	// create new kubernetes micro service
	service := newService(s, options)

	log.Debugf("Runtime queueing service %s version %s for start action", service.Name, service.Version)

	// push into start queue
	k.queue <- &task{
		action:  start,
		service: service,
	}

	return nil
}

// Read returns all instances of given service
func (k *kubernetes) Read(name string, opts ...runtime.ReadOption) ([]*runtime.Service, error) {
	k.Lock()
	defer k.Unlock()

	// if no name has been passed in, return error
	if len(name) == 0 {
		return nil, errors.New("missing service name")
	}

	// format the name
	name = client.Format(name)

	// set the default labels
	labels := map[string]string{
		"micro": "service",
		"name":  name,
	}

	var options runtime.ReadOptions
	for _, o := range opts {
		o(&options)
	}

	// add version to labels if a version has been supplied
	if len(options.Version) > 0 {
		labels["version"] = options.Version
	}

	log.Debugf("Runtime querying service %s", name)

	return k.getService(labels)
}

// List the managed services
func (k *kubernetes) List() ([]*runtime.Service, error) {
	k.Lock()
	defer k.Unlock()

	labels := map[string]string{
		"micro": "service",
	}

	log.Debugf("Runtime listing all micro services")

	return k.getService(labels)
}

// Update the service in place
func (k *kubernetes) Update(s *runtime.Service) error {
	// create new kubernetes micro service
	service := newService(s, runtime.CreateOptions{})

	// update build time annotation
	service.kdeploy.Spec.Template.Metadata.Annotations["build"] = time.Now().Format(time.RFC3339)

	log.Debugf("Runtime queueing service %s for update action", service.Name)

	// queue service for removal
	k.queue <- &task{
		action:  update,
		service: service,
	}

	return nil
}

// Delete removes a service
func (k *kubernetes) Delete(s *runtime.Service) error {
	k.Lock()
	defer k.Unlock()

	// create new kubernetes micro service
	service := newService(s, runtime.CreateOptions{})

	log.Debugf("Runtime queueing service %s for delete action", service.Name)

	// queue service for removal
	k.queue <- &task{
		action:  stop,
		service: service,
	}

	return nil
}

// Start starts the runtime
func (k *kubernetes) Start() error {
	k.Lock()
	defer k.Unlock()

	// already running
	if k.running {
		return nil
	}

	// set running
	k.running = true
	k.closed = make(chan bool)

	var events <-chan runtime.Event
	if k.options.Notifier != nil {
		var err error
		events, err = k.options.Notifier.Notify()
		if err != nil {
			// TODO: should we bail here?
			log.Debugf("Runtime failed to start update notifier")
		}
	}

	go k.run(events)

	return nil
}

// Stop shuts down the runtime
func (k *kubernetes) Stop() error {
	k.Lock()
	defer k.Unlock()

	if !k.running {
		return nil
	}

	select {
	case <-k.closed:
		return nil
	default:
		close(k.closed)
		// set not running
		k.running = false
		// stop the notifier too
		if k.options.Notifier != nil {
			return k.options.Notifier.Close()
		}
	}

	return nil
}

// String implements stringer interface
func (k *kubernetes) String() string {
	return "kubernetes"
}

// NewRuntime creates new kubernetes runtime
func NewRuntime(opts ...runtime.Option) runtime.Runtime {
	// get default options
	options := runtime.Options{}

	// apply requested options
	for _, o := range opts {
		o(&options)
	}

	// kubernetes client
	client := client.NewClientInCluster()

	return &kubernetes{
		options: options,
		closed:  make(chan bool),
		queue:   make(chan *task, 128),
		client:  client,
	}
}
<commit_msg>fix entry into nil map<commit_after>// Package kubernetes implements kubernetes micro runtime
package kubernetes

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/micro/go-micro/runtime"
	"github.com/micro/go-micro/runtime/kubernetes/client"
	"github.com/micro/go-micro/util/log"
)

// action to take on runtime service
type action int

const (
	start action = iota
	update
	stop
)

// task is queued into runtime queue
type task struct {
	action  action
	service *service
}

type kubernetes struct {
	sync.RWMutex
	// options configure runtime
	options runtime.Options
	// indicates if we're running
	running bool
	// task queue for kubernetes services
	queue chan *task
	// used to stop the runtime
	closed chan bool
	// client is kubernetes client
	client client.Kubernetes
}

// getService queries kubernetes for micro service
// NOTE: this function is not thread-safe
func (k *kubernetes) getService(labels map[string]string) ([]*runtime.Service, error) {
	// get the service status
	serviceList := new(client.ServiceList)
	r := &client.Resource{
		Kind:  "service",
		Value: serviceList,
	}

	// get the service from k8s
	if err := k.client.Get(r, labels); err != nil {
		return nil, err
	}

	// get the deployment status
	depList := new(client.DeploymentList)
	d := &client.Resource{
		Kind:  "deployment",
		Value: depList,
	}

	// get the deployment from k8s
	if err := k.client.Get(d, labels); err != nil {
		return nil, err
	}

	// service map
	svcMap := make(map[string]*runtime.Service)

	// collect info from kubernetes service
	for _, kservice := range serviceList.Items {
		// name of the service
		name := kservice.Metadata.Labels["name"]
		// version of the service
		version := kservice.Metadata.Labels["version"]

		// save as service
		svcMap[name+version] = &runtime.Service{
			Name:     name,
			Version:  version,
			Metadata: make(map[string]string),
		}

		// copy annotations metadata into service metadata
		for k, v := range kservice.Metadata.Annotations {
			svcMap[name+version].Metadata[k] = v
		}
	}

	// collect additional info from kubernetes deployment
	for _, kdep := range depList.Items {
		// name of the service
		name := kdep.Metadata.Labels["name"]
		// versio of the service
		version := kdep.Metadata.Labels["version"]

		// access existing service map based on name + version
		if svc, ok := svcMap[name+version]; ok {
			// we're expecting our own service name in metadata
			if _, ok := kdep.Metadata.Annotations["name"]; !ok {
				continue
			}

			// set the service name, version and source
			// based on existing annotations we stored
			svc.Name = kdep.Metadata.Annotations["name"]
			svc.Version = kdep.Metadata.Annotations["version"]
			svc.Source = kdep.Metadata.Annotations["source"]

			// delete from metadata
			delete(kdep.Metadata.Annotations, "name")
			delete(kdep.Metadata.Annotations, "version")
			delete(kdep.Metadata.Annotations, "source")

			// copy all annotations metadata into service metadata
			for k, v := range kdep.Metadata.Annotations {
				svc.Metadata[k] = v
			}

			// parse out deployment status and inject into service metadata
			if len(kdep.Status.Conditions) > 0 {
				status := kdep.Status.Conditions[0].Type
				// pick the last known condition type and mark the service status with it
				log.Debugf("Runtime setting %s service deployment status: %v", name, status)
				svc.Metadata["status"] = status
			}

			// parse out deployment build
			if build, ok := kdep.Metadata.Annotations["build"]; ok {
				buildTime, err := time.Parse(time.RFC3339, build)
				if err != nil {
					log.Debugf("Runtime failed parsing build time for %s: %v", name, err)
					continue
				}
				svc.Metadata["build"] = fmt.Sprintf("%d", buildTime.Unix())
				continue
			}
			// if no build annotation is found, set it to current time
			svc.Metadata["build"] = fmt.Sprintf("%d", time.Now().Unix())
		}
	}

	// collect all the services and return
	services := make([]*runtime.Service, 0, len(serviceList.Items))

	for _, service := range svcMap {
		services = append(services, service)
	}

	return services, nil
}

// run runs the runtime management loop
func (k *kubernetes) run(events <-chan runtime.Event) {
	t := time.NewTicker(time.Second * 10)
	defer t.Stop()

	for {
		select {
		case <-t.C:
			// TODO: figure out what to do here
			// - do we even need the ticker for k8s services?
		case task := <-k.queue:
			// The task queue is used to take actions e.g (CRUD - R)
			switch task.action {
			case start:
				log.Debugf("Runtime starting new service: %s", task.service.Name)
				if err := task.service.Start(k.client); err != nil {
					log.Debugf("Runtime failed to start service %s: %v", task.service.Name, err)
					continue
				}
			case stop:
				log.Debugf("Runtime stopping service: %s", task.service.Name)
				if err := task.service.Stop(k.client); err != nil {
					log.Debugf("Runtime failed to stop service %s: %v", task.service.Name, err)
					continue
				}
			case update:
				log.Debugf("Runtime updating service: %s", task.service.Name)
				if err := task.service.Update(k.client); err != nil {
					log.Debugf("Runtime failed to update service %s: %v", task.service.Name, err)
					continue
				}
			default:
				log.Debugf("Runtime received unknown action for service: %s", task.service.Name)
			}
		case event := <-events:
			// NOTE: we only handle Update events for now
			log.Debugf("Runtime received notification event: %v", event)
			switch event.Type {
			case runtime.Update:
				// only process if there's an actual service
				// we do not update all the things individually
				if len(event.Service) == 0 {
					continue
				}

				// set the default labels
				labels := map[string]string{
					"micro": "service",
					"name":  event.Service,
				}

				if len(event.Version) > 0 {
					labels["version"] = event.Version
				}

				// get the deployment status
				deployed := new(client.DeploymentList)

				// get the existing service rather than creating a new one
				err := k.client.Get(&client.Resource{
					Kind:  "deployment",
					Value: deployed,
				}, labels)

				if err != nil {
					log.Debugf("Runtime update failed to get service %s: %v", event.Service, err)
					continue
				}

				// technically we should not receive multiple versions but hey ho
				for _, service := range deployed.Items {
					// update build time annotation
					if service.Spec.Template.Metadata.Annotations == nil {
						service.Spec.Template.Metadata.Annotations = make(map[string]string)
					}
					service.Spec.Template.Metadata.Annotations["build"] = event.Timestamp.Format(time.RFC3339)

					log.Debugf("Runtime updating service: %s", event.Service)
					if err := k.client.Update(deploymentResource(&service)); err != nil {
						log.Debugf("Runtime failed to update service %s: %v", event.Service, err)
						continue
					}
				}
			}
		case <-k.closed:
			log.Debugf("Runtime stopped")
			return
		}
	}
}

// Init initializes runtime options
func (k *kubernetes) Init(opts ...runtime.Option) error {
	k.Lock()
	defer k.Unlock()

	for _, o := range opts {
		o(&k.options)
	}

	return nil
}

// Creates a service
func (k *kubernetes) Create(s *runtime.Service, opts ...runtime.CreateOption) error {
	k.Lock()
	defer k.Unlock()

	var options runtime.CreateOptions
	for _, o := range opts {
		o(&options)
	}

	// quickly prevalidate the name and version
	name := s.Name
	if len(s.Version) > 0 {
		name = name + "-" + s.Version
	}

	// format as we'll format in the deployment
	name = client.Format(name)

	// create new kubernetes micro service
	service := newService(s, options)

	log.Debugf("Runtime queueing service %s version %s for start action", service.Name, service.Version)

	// push into start queue
	k.queue <- &task{
		action:  start,
		service: service,
	}

	return nil
}

// Read returns all instances of given service
func (k *kubernetes) Read(name string, opts ...runtime.ReadOption) ([]*runtime.Service, error) {
	k.Lock()
	defer k.Unlock()

	// if no name has been passed in, return error
	if len(name) == 0 {
		return nil, errors.New("missing service name")
	}

	// format the name
	name = client.Format(name)

	// set the default labels
	labels := map[string]string{
		"micro": "service",
		"name":  name,
	}

	var options runtime.ReadOptions
	for _, o := range opts {
		o(&options)
	}

	// add version to labels if a version has been supplied
	if len(options.Version) > 0 {
		labels["version"] = options.Version
	}

	log.Debugf("Runtime querying service %s", name)

	return k.getService(labels)
}

// List the managed services
func (k *kubernetes) List() ([]*runtime.Service, error) {
	k.Lock()
	defer k.Unlock()

	labels := map[string]string{
		"micro": "service",
	}

	log.Debugf("Runtime listing all micro services")

	return k.getService(labels)
}

// Update the service in place
func (k *kubernetes) Update(s *runtime.Service) error {
	// create new kubernetes micro service
	service := newService(s, runtime.CreateOptions{})

	// update build time annotation
	service.kdeploy.Spec.Template.Metadata.Annotations["build"] = time.Now().Format(time.RFC3339)

	log.Debugf("Runtime queueing service %s for update action", service.Name)

	// queue service for removal
	k.queue <- &task{
		action:  update,
		service: service,
	}

	return nil
}

// Delete removes a service
func (k *kubernetes) Delete(s *runtime.Service) error {
	k.Lock()
	defer k.Unlock()

	// create new kubernetes micro service
	service := newService(s, runtime.CreateOptions{})

	log.Debugf("Runtime queueing service %s for delete action", service.Name)

	// queue service for removal
	k.queue <- &task{
		action:  stop,
		service: service,
	}

	return nil
}

// Start starts the runtime
func (k *kubernetes) Start() error {
	k.Lock()
	defer k.Unlock()

	// already running
	if k.running {
		return nil
	}

	// set running
	k.running = true
	k.closed = make(chan bool)

	var events <-chan runtime.Event
	if k.options.Notifier != nil {
		var err error
		events, err = k.options.Notifier.Notify()
		if err != nil {
			// TODO: should we bail here?
			log.Debugf("Runtime failed to start update notifier")
		}
	}

	go k.run(events)

	return nil
}

// Stop shuts down the runtime
func (k *kubernetes) Stop() error {
	k.Lock()
	defer k.Unlock()

	if !k.running {
		return nil
	}

	select {
	case <-k.closed:
		return nil
	default:
		close(k.closed)
		// set not running
		k.running = false
		// stop the notifier too
		if k.options.Notifier != nil {
			return k.options.Notifier.Close()
		}
	}

	return nil
}

// String implements stringer interface
func (k *kubernetes) String() string {
	return "kubernetes"
}

// NewRuntime creates new kubernetes runtime
func NewRuntime(opts ...runtime.Option) runtime.Runtime {
	// get default options
	options := runtime.Options{}

	// apply requested options
	for _, o := range opts {
		o(&options)
	}

	// kubernetes client
	client := client.NewClientInCluster()

	return &kubernetes{
		options: options,
		closed:  make(chan bool),
		queue:   make(chan *task, 128),
		client:  client,
	}
}
<|endoftext|>


<commit_before>/*
Package "matrix" provides types and operations for matrix manipulation.
*/
package matrix

type Matrix interface {
	// Return the shape of matrix, which consists of the "rows" and the "columns".
	Shape() (rows, columns int)

	// Return the "rows" of matrix.
	Rows() (rows int)

	// Return the "columns" of matrix.
	Columns() (columns int)

	// Return a row of matrix speficied with index "row".
	Row(row int) Row

	// Return a column of matrix speficied with index "column".
	Column(column int) Column

	// Get an element of matrix speficied with "row" and "column".
	Get(row, column int) (element float64)

	// Update the element of matrix speficied with "row" and "column".
	Update(row, column int, element float64) Matrix
}

type Row interface {
}

type Column interface {
}
<commit_msg>"matrix.IsSquare()" checks whether a matrix "m" is square or not.<commit_after>/*
Package "matrix" provides types and operations for matrix manipulation.
*/
package matrix

type Matrix interface {
	// Return the shape of matrix, which consists of the "rows" and the "columns".
	Shape() (rows, columns int)

	// Return the "rows" of matrix.
	Rows() (rows int)

	// Return the "columns" of matrix.
	Columns() (columns int)

	// Return a row of matrix speficied with index "row".
	Row(row int) Row

	// Return a column of matrix speficied with index "column".
	Column(column int) Column

	// Get an element of matrix speficied with "row" and "column".
	Get(row, column int) (element float64)

	// Update the element of matrix speficied with "row" and "column".
	Update(row, column int, element float64) Matrix
}

type Row interface {
}

type Column interface {
}

// Check whether a matrix "m" is square or not.
func IsSquare(m Matrix) bool {
	return m.Rows() == m.Columns()
}
<|endoftext|>