<commit_before>/*
Copyright 2016 Mirantis

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.

Parts of this file are copied from https://github.com/google/netboot/blob/8e5c0d07937f8c1dea6e5f218b64f6b95c32ada3/pixiecore/dhcp.go

*/

package dhcp

import (
	"bytes"
	"errors"
	"fmt"
	"net"

	"github.com/golang/glog"
	"go.universe.tf/netboot/dhcp4"
)

const (
	serverPort = 67
)

var (
	defaultDNS = []byte{8, 8, 8, 8}
)

type Server struct {
	config   *Config
	listener *dhcp4.Conn
}

func NewServer(config *Config) *Server {
	return &Server{config: config}
}

func (s *Server) SetupListener(laddr string) error {
	if listener, err := dhcp4.NewConn(fmt.Sprintf("%s:%d", laddr, serverPort)); err != nil {
		return err
	} else {
		s.listener = listener
	}
	return nil
}

func (s *Server) Close() error {
	return s.listener.Close()
}

func (s *Server) Serve() error {
	for {
		pkt, intf, err := s.listener.RecvDHCP()
		if err != nil {
			return fmt.Errorf("receiving DHCP packet: %v", err)
		}
		if intf == nil {
			return fmt.Errorf("received DHCP packet with no interface information - please fill a bug to https://github.com/google/netboot")
		}
		glog.V(2).Infof("Received dhcp packet from: %s", pkt.HardwareAddr.String())

		serverIP, err := interfaceIP(intf)
		if err != nil {
			glog.Warningf("Want to respond to %s on %s, but couldn't get a source address: %s", pkt.HardwareAddr.String(), intf.Name, err)
			continue
		}

		var resp *dhcp4.Packet
		switch pkt.Type {
		case dhcp4.MsgDiscover:
			resp, err = s.offerDHCP(pkt, serverIP)
			if err != nil {
				glog.Warningf("Failed to construct DHCP offer for %s: %s", pkt.HardwareAddr.String(), err)
				continue
			}
		case dhcp4.MsgRequest:
			resp, err = s.ackDHCP(pkt, serverIP)
			if err != nil {
				glog.Warningf("Failed to construct DHCP ACK for %s: %s", pkt.HardwareAddr.String(), err)
				continue
			}
		default:
			glog.Warningf("Ignoring packet from %s: packet is %s", pkt.HardwareAddr.String(), pkt.Type.String())
			continue
		}

		if resp != nil {
			glog.V(2).Infof("Sending %s packet to %s", resp.Type.String(), pkt.HardwareAddr.String())
			glog.V(3).Info(resp.DebugString())
			if err = s.listener.SendDHCP(resp, intf); err != nil {
				glog.Warningf("Failed to send DHCP offer for %s: %s", pkt.HardwareAddr.String(), err)
			}
		}
	}
}

func interfaceIP(intf *net.Interface) (net.IP, error) {
	addrs, err := intf.Addrs()
	if err != nil {
		return nil, err
	}

	// Try to find an IPv4 address to use, in the following order:
	// global unicast (includes rfc1918), link-local unicast,
	// loopback.
	fs := [](func(net.IP) bool){
		net.IP.IsGlobalUnicast,
		net.IP.IsLinkLocalUnicast,
		net.IP.IsLoopback,
	}
	for _, f := range fs {
		for _, a := range addrs {
			ipaddr, ok := a.(*net.IPNet)
			if !ok {
				continue
			}
			ip := ipaddr.IP.To4()
			if ip == nil {
				continue
			}
			if f(ip) {
				return ip, nil
			}
		}
	}

	return nil, errors.New("no usable unicast address configured on interface")
}

func (s *Server) prepareResponse(pkt *dhcp4.Packet, serverIP net.IP, mt dhcp4.MessageType) (*dhcp4.Packet, error) {
	if !bytes.Equal(pkt.HardwareAddr, s.config.PeerHardwareAddress) {
		return nil, fmt.Errorf("unexpected packet from %v", pkt.HardwareAddr)
	}

	p := &dhcp4.Packet{
		Type:          mt,
		TransactionID: pkt.TransactionID,
		Broadcast:     true,
		HardwareAddr:  pkt.HardwareAddr,
		RelayAddr:     pkt.RelayAddr,
		ServerAddr:    serverIP,
		Options:       make(dhcp4.Options),
	}
	p.Options[dhcp4.OptServerIdentifier] = serverIP

	// if guid was sent, copy it
	if pkt.Options[97] != nil {
		p.Options[97] = pkt.Options[97]
	}

	p.YourAddr = s.config.CNIResult.IP4.IP.IP
	p.Options[dhcp4.OptSubnetMask] = s.config.CNIResult.IP4.IP.Mask

	if s.config.CNIResult.IP4.Gateway != nil {
		p.Options[dhcp4.OptRouters] = []byte(s.config.CNIResult.IP4.Gateway)
	}
	// option 121 is for static routes as defined in rfc3442
	if data, err := s.getStaticRoutes(); err != nil {
		glog.Warningf("Can not transform static routes for mac %v: %v", pkt.HardwareAddr, err)
	} else if data != nil {
		p.Options[121] = data
	}

	// 86400 - full 24h
	p.Options[dhcp4.OptLeaseTime] = []byte{0, 1, 81, 128}

	// 43200 - 12h
	p.Options[dhcp4.OptRenewalTime] = []byte{0, 0, 168, 192}

	// 64800 - 18h
	p.Options[dhcp4.OptRebindingTime] = []byte{0, 0, 253, 32}

	// TODO: include more dns options
	if len(s.config.CNIResult.DNS.Nameservers) == 0 {
		p.Options[dhcp4.OptDNSServers] = defaultDNS
	} else {
		var b bytes.Buffer
		for _, ns := range s.config.CNIResult.DNS.Nameservers {
			ip := net.ParseIP(ns).To4()
			if len(ip) != 4 {
				glog.Warningf("failed to parse nameserver ip %q", ip)
			} else {
				b.Write(ip)
			}
		}
		if b.Len() > 0 {
			p.Options[dhcp4.OptDNSServers] = b.Bytes()
		} else {
			p.Options[dhcp4.OptDNSServers] = defaultDNS
		}
	}

	return p, nil
}

func (s *Server) offerDHCP(pkt *dhcp4.Packet, serverIP net.IP) (*dhcp4.Packet, error) {
	return s.prepareResponse(pkt, serverIP, dhcp4.MsgOffer)
}

func (s *Server) ackDHCP(pkt *dhcp4.Packet, serverIP net.IP) (*dhcp4.Packet, error) {
	return s.prepareResponse(pkt, serverIP, dhcp4.MsgAck)
}

func (s *Server) getStaticRoutes() ([]byte, error) {
	if len(s.config.CNIResult.IP4.Routes) == 0 {
		return nil, nil
	}

	var b bytes.Buffer
	for _, route := range s.config.CNIResult.IP4.Routes {
		b.Write(toDestinationDescriptor(route.Dst))
		b.Write(route.GW)
	}

	return b.Bytes(), nil
}

// toDestinationDescriptor returns calculated destination descriptor according to rfc3442 (page 3)
// warning: there is no check if ipnet is in required ipv4 type
func toDestinationDescriptor(network net.IPNet) []byte {
	s, _ := network.Mask.Size()
	ipAsBytes := []byte(network.IP)
	return append(
		[]byte{byte(s)},
		ipAsBytes[:widthOfMaskToSignificantOctets(s)]...,
	)
}

func widthOfMaskToSignificantOctets(mask int) int {
	return (mask + 7) / 8
}
<commit_msg>Fix handling of null route gw in dhcp server<commit_after>/*
Copyright 2016 Mirantis

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.

Parts of this file are copied from https://github.com/google/netboot/blob/8e5c0d07937f8c1dea6e5f218b64f6b95c32ada3/pixiecore/dhcp.go

*/

package dhcp

import (
	"bytes"
	"errors"
	"fmt"
	"net"

	"github.com/golang/glog"
	"go.universe.tf/netboot/dhcp4"
)

const (
	serverPort = 67
)

var (
	defaultDNS = []byte{8, 8, 8, 8}
)

type Server struct {
	config   *Config
	listener *dhcp4.Conn
}

func NewServer(config *Config) *Server {
	return &Server{config: config}
}

func (s *Server) SetupListener(laddr string) error {
	if listener, err := dhcp4.NewConn(fmt.Sprintf("%s:%d", laddr, serverPort)); err != nil {
		return err
	} else {
		s.listener = listener
	}
	return nil
}

func (s *Server) Close() error {
	return s.listener.Close()
}

func (s *Server) Serve() error {
	for {
		pkt, intf, err := s.listener.RecvDHCP()
		if err != nil {
			return fmt.Errorf("receiving DHCP packet: %v", err)
		}
		if intf == nil {
			return fmt.Errorf("received DHCP packet with no interface information - please fill a bug to https://github.com/google/netboot")
		}
		glog.V(2).Infof("Received dhcp packet from: %s", pkt.HardwareAddr.String())

		serverIP, err := interfaceIP(intf)
		if err != nil {
			glog.Warningf("Want to respond to %s on %s, but couldn't get a source address: %s", pkt.HardwareAddr.String(), intf.Name, err)
			continue
		}

		var resp *dhcp4.Packet
		switch pkt.Type {
		case dhcp4.MsgDiscover:
			resp, err = s.offerDHCP(pkt, serverIP)
			if err != nil {
				glog.Warningf("Failed to construct DHCP offer for %s: %s", pkt.HardwareAddr.String(), err)
				continue
			}
		case dhcp4.MsgRequest:
			resp, err = s.ackDHCP(pkt, serverIP)
			if err != nil {
				glog.Warningf("Failed to construct DHCP ACK for %s: %s", pkt.HardwareAddr.String(), err)
				continue
			}
		default:
			glog.Warningf("Ignoring packet from %s: packet is %s", pkt.HardwareAddr.String(), pkt.Type.String())
			continue
		}

		if resp != nil {
			glog.V(2).Infof("Sending %s packet to %s", resp.Type.String(), pkt.HardwareAddr.String())
			glog.V(3).Info(resp.DebugString())
			if err = s.listener.SendDHCP(resp, intf); err != nil {
				glog.Warningf("Failed to send DHCP offer for %s: %s", pkt.HardwareAddr.String(), err)
			}
		}
	}
}

func interfaceIP(intf *net.Interface) (net.IP, error) {
	addrs, err := intf.Addrs()
	if err != nil {
		return nil, err
	}

	// Try to find an IPv4 address to use, in the following order:
	// global unicast (includes rfc1918), link-local unicast,
	// loopback.
	fs := [](func(net.IP) bool){
		net.IP.IsGlobalUnicast,
		net.IP.IsLinkLocalUnicast,
		net.IP.IsLoopback,
	}
	for _, f := range fs {
		for _, a := range addrs {
			ipaddr, ok := a.(*net.IPNet)
			if !ok {
				continue
			}
			ip := ipaddr.IP.To4()
			if ip == nil {
				continue
			}
			if f(ip) {
				return ip, nil
			}
		}
	}

	return nil, errors.New("no usable unicast address configured on interface")
}

func (s *Server) prepareResponse(pkt *dhcp4.Packet, serverIP net.IP, mt dhcp4.MessageType) (*dhcp4.Packet, error) {
	if !bytes.Equal(pkt.HardwareAddr, s.config.PeerHardwareAddress) {
		return nil, fmt.Errorf("unexpected packet from %v", pkt.HardwareAddr)
	}

	p := &dhcp4.Packet{
		Type:          mt,
		TransactionID: pkt.TransactionID,
		Broadcast:     true,
		HardwareAddr:  pkt.HardwareAddr,
		RelayAddr:     pkt.RelayAddr,
		ServerAddr:    serverIP,
		Options:       make(dhcp4.Options),
	}
	p.Options[dhcp4.OptServerIdentifier] = serverIP

	// if guid was sent, copy it
	if pkt.Options[97] != nil {
		p.Options[97] = pkt.Options[97]
	}

	p.YourAddr = s.config.CNIResult.IP4.IP.IP
	p.Options[dhcp4.OptSubnetMask] = s.config.CNIResult.IP4.IP.Mask

	if s.config.CNIResult.IP4.Gateway != nil {
		p.Options[dhcp4.OptRouters] = []byte(s.config.CNIResult.IP4.Gateway)
	}
	// option 121 is for static routes as defined in rfc3442
	if data, err := s.getStaticRoutes(); err != nil {
		glog.Warningf("Can not transform static routes for mac %v: %v", pkt.HardwareAddr, err)
	} else if data != nil {
		p.Options[121] = data
	}

	// 86400 - full 24h
	p.Options[dhcp4.OptLeaseTime] = []byte{0, 1, 81, 128}

	// 43200 - 12h
	p.Options[dhcp4.OptRenewalTime] = []byte{0, 0, 168, 192}

	// 64800 - 18h
	p.Options[dhcp4.OptRebindingTime] = []byte{0, 0, 253, 32}

	// TODO: include more dns options
	if len(s.config.CNIResult.DNS.Nameservers) == 0 {
		p.Options[dhcp4.OptDNSServers] = defaultDNS
	} else {
		var b bytes.Buffer
		for _, ns := range s.config.CNIResult.DNS.Nameservers {
			ip := net.ParseIP(ns).To4()
			if len(ip) != 4 {
				glog.Warningf("failed to parse nameserver ip %q", ip)
			} else {
				b.Write(ip)
			}
		}
		if b.Len() > 0 {
			p.Options[dhcp4.OptDNSServers] = b.Bytes()
		} else {
			p.Options[dhcp4.OptDNSServers] = defaultDNS
		}
	}

	return p, nil
}

func (s *Server) offerDHCP(pkt *dhcp4.Packet, serverIP net.IP) (*dhcp4.Packet, error) {
	return s.prepareResponse(pkt, serverIP, dhcp4.MsgOffer)
}

func (s *Server) ackDHCP(pkt *dhcp4.Packet, serverIP net.IP) (*dhcp4.Packet, error) {
	return s.prepareResponse(pkt, serverIP, dhcp4.MsgAck)
}

func (s *Server) getStaticRoutes() ([]byte, error) {
	if len(s.config.CNIResult.IP4.Routes) == 0 {
		return nil, nil
	}

	var b bytes.Buffer
	for _, route := range s.config.CNIResult.IP4.Routes {
		b.Write(toDestinationDescriptor(route.Dst))
		if route.GW != nil {
			b.Write(route.GW)
		} else {
			b.Write([]byte{0, 0, 0, 0})
		}
	}

	return b.Bytes(), nil
}

// toDestinationDescriptor returns calculated destination descriptor according to rfc3442 (page 3)
// warning: there is no check if ipnet is in required ipv4 type
func toDestinationDescriptor(network net.IPNet) []byte {
	s, _ := network.Mask.Size()
	ipAsBytes := []byte(network.IP)
	return append(
		[]byte{byte(s)},
		ipAsBytes[:widthOfMaskToSignificantOctets(s)]...,
	)
}

func widthOfMaskToSignificantOctets(mask int) int {
	return (mask + 7) / 8
}
<|endoftext|>


<commit_before>/*
Copyright 2013 Google Inc.

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

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

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

// Package types provides various common types.
package types

import (
	"bytes"
	"encoding/json"
	"expvar"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"regexp"
	"runtime"
	"strings"
	"time"
)

var (
	goVersion  = runtime.Version()
	dotNumbers = regexp.MustCompile(`\.\d+`)
	null_b     = []byte("null")
)

// NopCloser is an io.Closer that does nothing.
var NopCloser io.Closer = ioutil.NopCloser(nil)

// Time3339 is a time.Time which encodes to and from JSON
// as an RFC 3339 time in UTC.
type Time3339 time.Time

var (
	_ json.Marshaler   = Time3339{}
	_ json.Unmarshaler = (*Time3339)(nil)
)

func (t Time3339) String() string {
	return time.Time(t).UTC().Format(time.RFC3339Nano)
}

func (t Time3339) MarshalJSON() ([]byte, error) {
	if t.Time().IsZero() {
		return null_b, nil
	}
	return json.Marshal(t.String())
}

func (t *Time3339) UnmarshalJSON(b []byte) error {
	if bytes.Equal(b, null_b) {
		*t = Time3339{}
		return nil
	}
	if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
		return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time")
	}
	s := string(b[1 : len(b)-1])
	if s == "" {
		*t = Time3339{}
		return nil
	}
	tm, err := time.Parse(time.RFC3339Nano, s)
	if err != nil {
		if strings.HasPrefix(s, "0000-00-00T00:00:00") {
			*t = Time3339{}
			return nil
		}
		return err
	}
	*t = Time3339(tm)
	return nil
}

// ParseTime3339OrZero parses a string in RFC3339 format. If it's invalid,
// the zero time value is returned instead.
func ParseTime3339OrZero(v string) Time3339 {
	t, err := time.Parse(time.RFC3339Nano, v)
	if err != nil {
		return Time3339{}
	}
	return Time3339(t)
}

func ParseTime3339OrNil(v string) *Time3339 {
	t, err := time.Parse(time.RFC3339Nano, v)
	if err != nil {
		return nil
	}
	tm := Time3339(t)
	return &tm
}

// Time returns the time as a time.Time with slightly less stutter
// than a manual conversion.
func (t Time3339) Time() time.Time {
	return time.Time(t)
}

// IsZero returns whether the time is Go zero or Unix zero.
func (t *Time3339) IsZero() bool {
	return t == nil || time.Time(*t).IsZero() || time.Time(*t).Unix() == 0
}

// ByTime sorts times.
type ByTime []time.Time

func (s ByTime) Len() int           { return len(s) }
func (s ByTime) Less(i, j int) bool { return s[i].Before(s[j]) }
func (s ByTime) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

// A ReadSeekCloser can Read, Seek, and Close.
type ReadSeekCloser interface {
	io.Reader
	io.Seeker
	io.Closer
}

type ReaderAtCloser interface {
	io.ReaderAt
	io.Closer
}

type SizeReaderAt interface {
	io.ReaderAt
	Size() int64
}

// TODO(wathiede): make sure all the stat readers work with code that
// type asserts ReadFrom/WriteTo.

type varStatReader struct {
	*expvar.Int
	r io.Reader
}

// NewReaderStats returns an io.Reader that will have the number of bytes
// read from r added to v.
func NewStatsReader(v *expvar.Int, r io.Reader) io.Reader {
	return &varStatReader{v, r}
}

func (v *varStatReader) Read(p []byte) (int, error) {
	n, err := v.r.Read(p)
	v.Int.Add(int64(n))
	return n, err
}

type varStatReadSeeker struct {
	*expvar.Int
	rs io.ReadSeeker
}

// NewReaderStats returns an io.ReadSeeker that will have the number of bytes
// read from rs added to v.
func NewStatsReadSeeker(v *expvar.Int, r io.ReadSeeker) io.ReadSeeker {
	return &varStatReadSeeker{v, r}
}

func (v *varStatReadSeeker) Read(p []byte) (int, error) {
	n, err := v.rs.Read(p)
	v.Int.Add(int64(n))
	return n, err
}

func (v *varStatReadSeeker) Seek(offset int64, whence int) (int64, error) {
	return v.rs.Seek(offset, whence)
}

// InvertedBool is a bool that marshals to and from JSON with the opposite of its in-memory value.
type InvertedBool bool

func (ib InvertedBool) MarshalJSON() ([]byte, error) {
	return json.Marshal(!bool(ib))
}

func (ib *InvertedBool) UnmarshalJSON(b []byte) error {
	var bo bool
	if err := json.Unmarshal(b, &bo); err != nil {
		return err
	}
	*ib = InvertedBool(!bo)
	return nil
}

// Get returns the logical value of ib.
func (ib InvertedBool) Get() bool {
	return !bool(ib)
}

// U32 converts n to an uint32, or panics if n is out of range
func U32(n int64) uint32 {
	if n < 0 || n > math.MaxUint32 {
		panic("bad size " + fmt.Sprint(n))
	}
	return uint32(n)
}

// TB is a copy of Go 1.2's testing.TB.
type TB interface {
	Error(args ...interface{})
	Errorf(format string, args ...interface{})
	Fail()
	FailNow()
	Failed() bool
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})
	Log(args ...interface{})
	Logf(format string, args ...interface{})
	Skip(args ...interface{})
	SkipNow()
	Skipf(format string, args ...interface{})
	Skipped() bool
}
<commit_msg>types: add EmptyBody<commit_after>/*
Copyright 2013 Google Inc.

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

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

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

// Package types provides various common types.
package types

import (
	"bytes"
	"encoding/json"
	"expvar"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"regexp"
	"runtime"
	"strings"
	"time"
)

var (
	goVersion  = runtime.Version()
	dotNumbers = regexp.MustCompile(`\.\d+`)
	null_b     = []byte("null")
)

// NopCloser is an io.Closer that does nothing.
var NopCloser io.Closer = ioutil.NopCloser(nil)

// EmptyBody is a ReadCloser that returns EOF on Read and does nothing
// on Close.
var EmptyBody io.ReadCloser = ioutil.NopCloser(strings.NewReader(""))

// Time3339 is a time.Time which encodes to and from JSON
// as an RFC 3339 time in UTC.
type Time3339 time.Time

var (
	_ json.Marshaler   = Time3339{}
	_ json.Unmarshaler = (*Time3339)(nil)
)

func (t Time3339) String() string {
	return time.Time(t).UTC().Format(time.RFC3339Nano)
}

func (t Time3339) MarshalJSON() ([]byte, error) {
	if t.Time().IsZero() {
		return null_b, nil
	}
	return json.Marshal(t.String())
}

func (t *Time3339) UnmarshalJSON(b []byte) error {
	if bytes.Equal(b, null_b) {
		*t = Time3339{}
		return nil
	}
	if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
		return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time")
	}
	s := string(b[1 : len(b)-1])
	if s == "" {
		*t = Time3339{}
		return nil
	}
	tm, err := time.Parse(time.RFC3339Nano, s)
	if err != nil {
		if strings.HasPrefix(s, "0000-00-00T00:00:00") {
			*t = Time3339{}
			return nil
		}
		return err
	}
	*t = Time3339(tm)
	return nil
}

// ParseTime3339OrZero parses a string in RFC3339 format. If it's invalid,
// the zero time value is returned instead.
func ParseTime3339OrZero(v string) Time3339 {
	t, err := time.Parse(time.RFC3339Nano, v)
	if err != nil {
		return Time3339{}
	}
	return Time3339(t)
}

func ParseTime3339OrNil(v string) *Time3339 {
	t, err := time.Parse(time.RFC3339Nano, v)
	if err != nil {
		return nil
	}
	tm := Time3339(t)
	return &tm
}

// Time returns the time as a time.Time with slightly less stutter
// than a manual conversion.
func (t Time3339) Time() time.Time {
	return time.Time(t)
}

// IsZero returns whether the time is Go zero or Unix zero.
func (t *Time3339) IsZero() bool {
	return t == nil || time.Time(*t).IsZero() || time.Time(*t).Unix() == 0
}

// ByTime sorts times.
type ByTime []time.Time

func (s ByTime) Len() int           { return len(s) }
func (s ByTime) Less(i, j int) bool { return s[i].Before(s[j]) }
func (s ByTime) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

// A ReadSeekCloser can Read, Seek, and Close.
type ReadSeekCloser interface {
	io.Reader
	io.Seeker
	io.Closer
}

type ReaderAtCloser interface {
	io.ReaderAt
	io.Closer
}

type SizeReaderAt interface {
	io.ReaderAt
	Size() int64
}

// TODO(wathiede): make sure all the stat readers work with code that
// type asserts ReadFrom/WriteTo.

type varStatReader struct {
	*expvar.Int
	r io.Reader
}

// NewReaderStats returns an io.Reader that will have the number of bytes
// read from r added to v.
func NewStatsReader(v *expvar.Int, r io.Reader) io.Reader {
	return &varStatReader{v, r}
}

func (v *varStatReader) Read(p []byte) (int, error) {
	n, err := v.r.Read(p)
	v.Int.Add(int64(n))
	return n, err
}

type varStatReadSeeker struct {
	*expvar.Int
	rs io.ReadSeeker
}

// NewReaderStats returns an io.ReadSeeker that will have the number of bytes
// read from rs added to v.
func NewStatsReadSeeker(v *expvar.Int, r io.ReadSeeker) io.ReadSeeker {
	return &varStatReadSeeker{v, r}
}

func (v *varStatReadSeeker) Read(p []byte) (int, error) {
	n, err := v.rs.Read(p)
	v.Int.Add(int64(n))
	return n, err
}

func (v *varStatReadSeeker) Seek(offset int64, whence int) (int64, error) {
	return v.rs.Seek(offset, whence)
}

// InvertedBool is a bool that marshals to and from JSON with the opposite of its in-memory value.
type InvertedBool bool

func (ib InvertedBool) MarshalJSON() ([]byte, error) {
	return json.Marshal(!bool(ib))
}

func (ib *InvertedBool) UnmarshalJSON(b []byte) error {
	var bo bool
	if err := json.Unmarshal(b, &bo); err != nil {
		return err
	}
	*ib = InvertedBool(!bo)
	return nil
}

// Get returns the logical value of ib.
func (ib InvertedBool) Get() bool {
	return !bool(ib)
}

// U32 converts n to an uint32, or panics if n is out of range
func U32(n int64) uint32 {
	if n < 0 || n > math.MaxUint32 {
		panic("bad size " + fmt.Sprint(n))
	}
	return uint32(n)
}

// TB is a copy of Go 1.2's testing.TB.
type TB interface {
	Error(args ...interface{})
	Errorf(format string, args ...interface{})
	Fail()
	FailNow()
	Failed() bool
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})
	Log(args ...interface{})
	Logf(format string, args ...interface{})
	Skip(args ...interface{})
	SkipNow()
	Skipf(format string, args ...interface{})
	Skipped() bool
}
<|endoftext|>


<commit_before>package cfutil

import (
	"errors"
	"fmt"
	consul "github.com/hashicorp/consul/api"
	"net/url"
	"os"
	"strconv"
	"strings"
)

// Services() returns the list of services available from the
// Consul cluster
func Services() ([]string, error) {
	client, err := NewConsulClient()
	if err != nil {
		return nil, err
	}
	catalogServices, _, err := client.Catalog().Services(nil)
	if err != nil {
		return []string{}, err
	}
	var services []string
	for k := range catalogServices {
		services = append(services, k)
	}
	return services, nil
}

func DiscoverServiceURL(serviceName, tags string) (string, error) {
	client, err := NewConsulClient()
	if err != nil {
		return "", err
	}
	services, _, err := client.Catalog().Service(serviceName, tags, nil)
	if err != nil {
		return "", fmt.Errorf("Service `%s` not found: %s", serviceName, err)
	}
	if len(services) > 0 {
		return CreateURLFromServiceCatalog(services[0])
	}
	return "", fmt.Errorf("Service `%s` not found", serviceName)

}

func CreateURLFromServiceCatalog(catalog *consul.CatalogService) (string, error) {
	var serviceURL url.URL
	if catalog.ServicePort == 443 {
		serviceURL.Scheme = "https"
		serviceURL.Host = catalog.ServiceAddress
	} else {
		serviceURL.Scheme = "http"
		serviceURL.Host = fmt.Sprintf("%s:%d", catalog.ServiceAddress, catalog.ServicePort)
	}
	return serviceURL.String(), nil
}

// Use ServiceRegister() to register your app in the Consul cluster
// Optionally you can provide a health endpoint on your URL and
// a number of tags to make your service more discoverable
func ServiceRegister(name string, path string, tags ...string) error {
	appEnv, _ := Current()
	client, err := NewConsulClient()
	if err != nil {
		return err
	}
	schema, port := schemaAndPortForServices()

	appURL, _ := url.Parse(schema + "://" + appEnv.ApplicationURIs[0])
	splitted := strings.Split(appURL.Host, ":")
	hostWithoutPort := splitted[0]
	if hostWithoutPort == "" {
		hostWithoutPort = "localhost"
	}
	if len(splitted) > 1 {
		addedPort, err := strconv.Atoi(splitted[1])
		if err == nil && addedPort != port {
			port = addedPort
		}
	}

	err = client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		Name:    name,
		Address: hostWithoutPort,
		Port:    port,
		Tags:    tags,
		Check: &consul.AgentServiceCheck{
			HTTP:     fmt.Sprintf(schema + "://" + appURL.Host + path),
			Interval: "60s",
		},
	})
	if err != nil {
		return err
	}
	return nil
}

// NewConsulClient() returns a new consul client which you can use to
// access the Consul cluster HTTP API. It uses `CONSUL_MASTER` and
// `CONSUL_TOKEN` environment variables to set up the HTTP API connection.
func NewConsulClient() (*consul.Client, error) {
	dialScheme, dialHost, err := consulDialstring("consul")
	if err != nil {
		return nil, err
	}
	client, consulErr := consul.NewClient(&consul.Config{
		Address: dialHost,
		Scheme:  dialScheme,
		Token:   os.Getenv("CONSUL_TOKEN"),
	})
	if consulErr != nil {
		return nil, consulErr
	}
	return client, nil
}

func GetConsulKey(mooncoreKey string) (string, error) {
	ns := ConsulNamespace()
	key := "mooncore/" + ns + "/" + mooncoreKey
	client, err := NewConsulClient()
	if err != nil {
		return "", err
	}
	kvPair, _, err := client.KV().Get(key, nil)
	if err != nil {
		return "", err
	}
	if kvPair == nil || kvPair.Value == nil {
		return "", fmt.Errorf("Key not found")
	}
	return string(kvPair.Value), nil
}

func ConsulNamespace() string {
	return os.Getenv("CONSUL_NAMESPACE")
}

func consulDialstring(serviceName string) (string, string, error) {
	consulMaster := ""
	if consulMaster = os.Getenv("CONSUL_MASTER"); consulMaster != "" {
		parsed, err := url.Parse(consulMaster)
		if err == nil {
			return parsed.Scheme, parsed.Host, nil
		}
	}
	return "", "", errors.New("CONSUL_MASTER not found or invalid url")
}

func schemaAndPortForServices() (string, int) {
	if ForceHTTP() {
		return "http", 80
	}
	return "https", 443
}
<commit_msg>Find out in which datacenter we are<commit_after>package cfutil

import (
	"errors"
	"fmt"
	consul "github.com/hashicorp/consul/api"
	"net/url"
	"os"
	"strconv"
	"strings"
)

// Services() returns the list of services available from the
// Consul cluster
func Services() ([]string, error) {
	client, err := NewConsulClient()
	if err != nil {
		return nil, err
	}
	catalogServices, _, err := client.Catalog().Services(nil)
	if err != nil {
		return []string{}, err
	}
	var services []string
	for k := range catalogServices {
		services = append(services, k)
	}
	return services, nil
}

func DiscoverServiceURL(serviceName, tags string) (string, error) {
	client, err := NewConsulClient()
	if err != nil {
		return "", err
	}
	services, _, err := client.Catalog().Service(serviceName, tags, nil)
	if err != nil {
		return "", fmt.Errorf("Service `%s` not found: %s", serviceName, err)
	}
	if len(services) > 0 {
		return CreateURLFromServiceCatalog(services[0])
	}
	return "", fmt.Errorf("Service `%s` not found", serviceName)

}

func CreateURLFromServiceCatalog(catalog *consul.CatalogService) (string, error) {
	var serviceURL url.URL
	if catalog.ServicePort == 443 {
		serviceURL.Scheme = "https"
		serviceURL.Host = catalog.ServiceAddress
	} else {
		serviceURL.Scheme = "http"
		serviceURL.Host = fmt.Sprintf("%s:%d", catalog.ServiceAddress, catalog.ServicePort)
	}
	return serviceURL.String(), nil
}

// Use ServiceRegister() to register your app in the Consul cluster
// Optionally you can provide a health endpoint on your URL and
// a number of tags to make your service more discoverable
func ServiceRegister(name string, path string, tags ...string) error {
	appEnv, _ := Current()
	client, err := NewConsulClient()
	if err != nil {
		return err
	}
	schema, port := schemaAndPortForServices()

	appURL, _ := url.Parse(schema + "://" + appEnv.ApplicationURIs[0])
	splitted := strings.Split(appURL.Host, ":")
	hostWithoutPort := splitted[0]
	if hostWithoutPort == "" {
		hostWithoutPort = "localhost"
	}
	if len(splitted) > 1 {
		addedPort, err := strconv.Atoi(splitted[1])
		if err == nil && addedPort != port {
			port = addedPort
		}
	}

	err = client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		Name:    name,
		Address: hostWithoutPort,
		Port:    port,
		Tags:    tags,
		Check: &consul.AgentServiceCheck{
			HTTP:     fmt.Sprintf(schema + "://" + appURL.Host + path),
			Interval: "60s",
		},
	})
	if err != nil {
		return err
	}
	return nil
}

// NewConsulClient() returns a new consul client which you can use to
// access the Consul cluster HTTP API. It uses `CONSUL_MASTER` and
// `CONSUL_TOKEN` environment variables to set up the HTTP API connection.
func NewConsulClient() (*consul.Client, error) {
	dialScheme, dialHost, err := consulDialstring("consul")
	if err != nil {
		return nil, err
	}
	client, consulErr := consul.NewClient(&consul.Config{
		Address: dialHost,
		Scheme:  dialScheme,
		Token:   os.Getenv("CONSUL_TOKEN"),
	})
	if consulErr != nil {
		return nil, consulErr
	}
	return client, nil
}

func GetConsulKey(mooncoreKey string) (string, error) {
	ns := ConsulNamespace()
	key := "mooncore/" + ns + "/" + mooncoreKey
	client, err := NewConsulClient()
	if err != nil {
		return "", err
	}
	kvPair, _, err := client.KV().Get(key, nil)
	if err != nil {
		return "", err
	}
	if kvPair == nil || kvPair.Value == nil {
		return "", fmt.Errorf("Key not found")
	}
	return string(kvPair.Value), nil
}

func ConsulDatacenter() (string, error) {
	client, err := NewConsulClient()
	if err != nil {
		return "", err
	}
	self, err := client.Agent().Self()
	if err != nil {
		return "", err
	}
	dc, ok := self["Config"]["Datacenter"].(string)
	if !ok {
		return "", fmt.Errorf("Invalid lookup for Datacenter")
	}
	return dc, nil
}

func ConsulNamespace() string {
	return os.Getenv("CONSUL_NAMESPACE")
}

func consulDialstring(serviceName string) (string, string, error) {
	consulMaster := ""
	if consulMaster = os.Getenv("CONSUL_MASTER"); consulMaster != "" {
		parsed, err := url.Parse(consulMaster)
		if err == nil {
			return parsed.Scheme, parsed.Host, nil
		}
	}
	return "", "", errors.New("CONSUL_MASTER not found or invalid url")
}

func schemaAndPortForServices() (string, int) {
	if ForceHTTP() {
		return "http", 80
	}
	return "https", 443
}
<|endoftext|>


<commit_before>// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package utils

import (
	"net"
	"strings"
	"time"

	"github.com/garyburd/redigo/redis"

	"github.com/wandoulabs/codis/pkg/utils/errors"
)

func DialTo(addr string, passwd string) (redis.Conn, error) {
	c, err := redis.DialTimeout("tcp", addr, time.Second, time.Second, time.Second)
	if err != nil {
		return nil, errors.Trace(err)
	}
	if passwd != "" {
		if _, err := c.Do("AUTH", passwd); err != nil {
			c.Close()
			return nil, errors.Trace(err)
		}
	}
	return c, nil
}

func SlotsInfo(addr, passwd string, fromSlot, toSlot int) (map[int]int, error) {
	c, err := DialTo(addr, passwd)
	if err != nil {
		return nil, err
	}
	defer c.Close()

	infos, err := redis.Values(c.Do("SLOTSINFO", fromSlot, toSlot-fromSlot+1))
	if err != nil {
		return nil, errors.Trace(err)
	}

	slots := make(map[int]int)
	if infos != nil {
		for i := 0; i < len(infos); i++ {
			info, err := redis.Values(infos[i], nil)
			if err != nil {
				return nil, errors.Trace(err)
			}
			var slotid, slotsize int
			if _, err := redis.Scan(info, &slotid, &slotsize); err != nil {
				return nil, errors.Trace(err)
			} else {
				slots[slotid] = slotsize
			}
		}
	}
	return slots, nil
}

var (
	ErrInvalidAddr       = errors.New("invalid addr")
	ErrStopMigrateByUser = errors.New("migration stopped by user")
)

func SlotsMgrtTagSlot(c redis.Conn, slotId int, toAddr string) (int, int, error) {
	addrParts := strings.Split(toAddr, ":")
	if len(addrParts) != 2 {
		return -1, -1, errors.Trace(ErrInvalidAddr)
	}

	reply, err := redis.Values(c.Do("SLOTSMGRTTAGSLOT", addrParts[0], addrParts[1], 30000, slotId))
	if err != nil {
		return -1, -1, errors.Trace(err)
	}

	var succ, remain int
	if _, err := redis.Scan(reply, &succ, &remain); err != nil {
		return -1, -1, errors.Trace(err)
	} else {
		return succ, remain, nil
	}
}

func GetRedisStat(addr, passwd string) (map[string]string, error) {
	c, err := DialTo(addr, passwd)
	if err != nil {
		return nil, err
	}
	defer c.Close()

	ret, err := redis.String(c.Do("INFO"))
	if err != nil {
		return nil, errors.Trace(err)
	}
	m := make(map[string]string)
	lines := strings.Split(ret, "\n")
	for _, line := range lines {
		kv := strings.SplitN(line, ":", 2)
		if len(kv) == 2 {
			k, v := strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])
			m[k] = v
		}
	}

	reply, err := redis.Strings(c.Do("config", "get", "maxmemory"))
	if err != nil {
		return nil, errors.Trace(err)
	}
	// we got result
	if len(reply) == 2 {
		if reply[1] != "0" {
			m["maxmemory"] = reply[1]
		} else {
			m["maxmemory"] = "∞"
		}
	}
	return m, nil
}

func GetRedisConfig(addr, passwd string, configName string) (string, error) {
	c, err := DialTo(addr, passwd)
	if err != nil {
		return "", err
	}
	defer c.Close()

	ret, err := redis.Strings(c.Do("config", "get", configName))
	if err != nil {
		return "", errors.Trace(err)
	}
	if len(ret) == 2 {
		return ret[1], nil
	}
	return "", nil
}

func SlaveOf(slave, passwd string, master string) error {
	if master == slave {
		return errors.Errorf("can not slave of itself")
	}

	c, err := DialTo(slave, passwd)
	if err != nil {
		return err
	}
	defer c.Close()

	host, port, err := net.SplitHostPort(master)
	if err != nil {
		return errors.Trace(err)
	}

	if _, err := c.Do("SLAVEOF", host, port); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func SlaveNoOne(addr, passwd string) error {
	c, err := DialTo(addr, passwd)
	if err != nil {
		return err
	}
	defer c.Close()

	if _, err = c.Do("SLAVEOF", "NO", "ONE"); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<commit_msg>change slaveof timeout to 15 min<commit_after>// Copyright 2014 Wandoujia Inc. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package utils

import (
	"net"
	"strings"
	"time"

	"github.com/garyburd/redigo/redis"

	"github.com/wandoulabs/codis/pkg/utils/errors"
)

func DialToTimeout(addr string, passwd string, readTimeout, writeTimeout time.Duration) (redis.Conn, error) {
	c, err := redis.DialTimeout("tcp", addr, time.Second, readTimeout, writeTimeout)
	if err != nil {
		return nil, errors.Trace(err)
	}
	if passwd != "" {
		if _, err := c.Do("AUTH", passwd); err != nil {
			c.Close()
			return nil, errors.Trace(err)
		}
	}
	return c, nil
}

func DialTo(addr string, passwd string) (redis.Conn, error) {
	return DialToTimeout(addr, passwd, time.Second*5, time.Second*5)
}

func SlotsInfo(addr, passwd string, fromSlot, toSlot int) (map[int]int, error) {
	c, err := DialTo(addr, passwd)
	if err != nil {
		return nil, err
	}
	defer c.Close()

	infos, err := redis.Values(c.Do("SLOTSINFO", fromSlot, toSlot-fromSlot+1))
	if err != nil {
		return nil, errors.Trace(err)
	}

	slots := make(map[int]int)
	if infos != nil {
		for i := 0; i < len(infos); i++ {
			info, err := redis.Values(infos[i], nil)
			if err != nil {
				return nil, errors.Trace(err)
			}
			var slotid, slotsize int
			if _, err := redis.Scan(info, &slotid, &slotsize); err != nil {
				return nil, errors.Trace(err)
			} else {
				slots[slotid] = slotsize
			}
		}
	}
	return slots, nil
}

var (
	ErrInvalidAddr       = errors.New("invalid addr")
	ErrStopMigrateByUser = errors.New("migration stopped by user")
)

func SlotsMgrtTagSlot(c redis.Conn, slotId int, toAddr string) (int, int, error) {
	addrParts := strings.Split(toAddr, ":")
	if len(addrParts) != 2 {
		return -1, -1, errors.Trace(ErrInvalidAddr)
	}

	reply, err := redis.Values(c.Do("SLOTSMGRTTAGSLOT", addrParts[0], addrParts[1], 30000, slotId))
	if err != nil {
		return -1, -1, errors.Trace(err)
	}

	var succ, remain int
	if _, err := redis.Scan(reply, &succ, &remain); err != nil {
		return -1, -1, errors.Trace(err)
	} else {
		return succ, remain, nil
	}
}

func GetRedisStat(addr, passwd string) (map[string]string, error) {
	c, err := DialTo(addr, passwd)
	if err != nil {
		return nil, err
	}
	defer c.Close()

	ret, err := redis.String(c.Do("INFO"))
	if err != nil {
		return nil, errors.Trace(err)
	}
	m := make(map[string]string)
	lines := strings.Split(ret, "\n")
	for _, line := range lines {
		kv := strings.SplitN(line, ":", 2)
		if len(kv) == 2 {
			k, v := strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])
			m[k] = v
		}
	}

	reply, err := redis.Strings(c.Do("config", "get", "maxmemory"))
	if err != nil {
		return nil, errors.Trace(err)
	}
	// we got result
	if len(reply) == 2 {
		if reply[1] != "0" {
			m["maxmemory"] = reply[1]
		} else {
			m["maxmemory"] = "∞"
		}
	}
	return m, nil
}

func GetRedisConfig(addr, passwd string, configName string) (string, error) {
	c, err := DialTo(addr, passwd)
	if err != nil {
		return "", err
	}
	defer c.Close()

	ret, err := redis.Strings(c.Do("config", "get", configName))
	if err != nil {
		return "", errors.Trace(err)
	}
	if len(ret) == 2 {
		return ret[1], nil
	}
	return "", nil
}

func SlaveOf(slave, passwd string, master string) error {
	if master == slave {
		return errors.Errorf("can not slave of itself")
	}

	c, err := DialToTimeout(slave, passwd, time.Minute*15, time.Second*5)
	if err != nil {
		return err
	}
	defer c.Close()

	host, port, err := net.SplitHostPort(master)
	if err != nil {
		return errors.Trace(err)
	}

	if _, err := c.Do("SLAVEOF", host, port); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func SlaveNoOne(addr, passwd string) error {
	c, err := DialTo(addr, passwd)
	if err != nil {
		return err
	}
	defer c.Close()

	if _, err = c.Do("SLAVEOF", "NO", "ONE"); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<|endoftext|>


<commit_before>package aci

import (
	"bytes"
	"fmt"
)

func rnBridgeDomain(bd string) string {
	return "BD-" + bd
}

func dnBridgeDomain(tenant, bd string) string {
	return rnTenant(tenant) + "/" + rnBridgeDomain(bd)
}

func rnSubnet(subnet string) string {
	return "subnet-[" + subnet + "]"
}

func dnSubnet(tenant, bd, subnet string) string {
	return dnBridgeDomain(tenant, bd) + "/" + rnSubnet(subnet)
}

// BridgeDomainAdd creates a new bridge domain in a tenant.
func (c *Client) BridgeDomainAdd(tenant, bd, descr string) error {

	me := "BridgeDomainAdd"

	rn := rnBridgeDomain(bd)

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvBD":{"attributes":{"dn":"uni/%s","name":"%s","descr":"%s","rn":"%s","status":"created"}}}`,
		dn, bd, descr, rn)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainDel deletes an existing bridge domain from a tenant.
func (c *Client) BridgeDomainDel(tenant, bd string) error {

	me := "BridgeDomainDel"

	rnT := rnTenant(tenant)

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + rnT + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvTenant":{"attributes":{"dn":"uni/%s","status":"modified"},"children":[{"fvBD":{"attributes":{"dn":"uni/%s","status":"deleted"}}}]}}`,
		rnT, dn)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainList retrieves the list of bridge domains from a tenant.
func (c *Client) BridgeDomainList(tenant string) ([]map[string]interface{}, error) {

	me := "BridgeDomainList"

	key := "fvBD"

	t := rnTenant(tenant)

	api := "/api/node/mo/uni/" + t + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainVrfSet defines the VRF for a bridge domain.
func (c *Client) BridgeDomainVrfSet(tenant, bd, vrf string) error {

	me := "BridgeDomainVrfSet"

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + "/rsctx.json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvRsCtx":{"attributes":{"tnFvCtxName":"%s"}}}`,
		vrf)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainVrfGet retrieves the VRF for a bridge domain.
func (c *Client) BridgeDomainVrfGet(tenant, bd string) (string, error) {

	me := "BridgeDomainVrfGet"

	key := "fvRsCtx"

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return "", fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	attrs, errAttr := jsonImdataAttributes(c, body, key, me)
	if errAttr != nil {
		return "", fmt.Errorf("%s: %v", me, errAttr)
	}

	if len(attrs) < 1 {
		return "", fmt.Errorf("%s: empty list of VRFs", me)
	}

	attr := attrs[0]
	v, found := attr["tnFvCtxName"]
	if !found {
		return "", fmt.Errorf("%s: VRF not found", me)
	}

	vrf, isStr := v.(string)
	if !isStr {
		return "", fmt.Errorf("%s: VRF is not a string", me)
	}

	if vrf == "" {
		return "", fmt.Errorf("%s: empty VRF name", me)
	}

	return vrf, nil
}

// BridgeDomainSubnetAdd creates a new subnet in a bridge domain.
func (c *Client) BridgeDomainSubnetAdd(tenant, bd, subnet, descr string) error {

	me := "BridgeDomainSubnetAdd"

	rnSN := rnSubnet(subnet)

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvSubnet":{"attributes":{"dn":"uni/%s","ip":"%s","descr":"%s","rn":"%s","status":"created"}}}`,
		dnSN, subnet, descr, rnSN)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetDel deletes an existing subnet from a bridge domain.
func (c *Client) BridgeDomainSubnetDel(tenant, bd, subnet string) error {

	me := "BridgeDomainSubnetDel"

	dnBD := dnBridgeDomain(tenant, bd)

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnBD + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvBD":{"attributes":{"dn":"uni/%s","status":"modified"},"children":[{"fvSubnet":{"attributes":{"dn":"uni/%s","status":"deleted"}}}]}}`,
		dnBD, dnSN)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetList retrieves the list of subnets from a bridge domain.
func (c *Client) BridgeDomainSubnetList(tenant, bd string) ([]map[string]interface{}, error) {

	me := "BridgeDomainSubnetList"

	key := "fvSubnet"

	dnBD := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dnBD + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainSubnetGet retrieves specific subnet from a bridge domain.
func (c *Client) BridgeDomainSubnetGet(tenant, bd, subnet string) ([]map[string]interface{}, error) {

	me := "BridgeDomainSubnetGet"

	key := "fvSubnet"

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainSubnetScopeSet defines the scope for a bridge domain subnet.
func (c *Client) BridgeDomainSubnetScopeSet(tenant, bd, subnet, scope string) error {

	me := "BridgeDomainSubnetScopeSet"

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvSubnet":{"attributes":{"dn":"uni/%s","scope":"%s"}}}`,
		dnSN, scope)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetScopeGet retrieves the scope from a bridge domain subnet.
func (c *Client) BridgeDomainSubnetScopeGet(tenant, bd, subnet string) (string, error) {

	me := "BridgeDomainSubnetScopeGet"

	list, errSubnet := c.BridgeDomainSubnetGet(tenant, bd, subnet)
	if errSubnet != nil {
		return "", fmt.Errorf("%s: %v", me, errSubnet)
	}

	if len(list) < 1 {
		return "", fmt.Errorf("%s: empty list of subnets", me)
	}

	attrs := list[0]
	s := attrs["scope"]

	scope, isStr := s.(string)
	if !isStr {
		return "", fmt.Errorf("%s: scope is not a string", me)
	}

	return scope, nil
}
<commit_msg>Check scope attribute.<commit_after>package aci

import (
	"bytes"
	"fmt"
)

func rnBridgeDomain(bd string) string {
	return "BD-" + bd
}

func dnBridgeDomain(tenant, bd string) string {
	return rnTenant(tenant) + "/" + rnBridgeDomain(bd)
}

func rnSubnet(subnet string) string {
	return "subnet-[" + subnet + "]"
}

func dnSubnet(tenant, bd, subnet string) string {
	return dnBridgeDomain(tenant, bd) + "/" + rnSubnet(subnet)
}

// BridgeDomainAdd creates a new bridge domain in a tenant.
func (c *Client) BridgeDomainAdd(tenant, bd, descr string) error {

	me := "BridgeDomainAdd"

	rn := rnBridgeDomain(bd)

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvBD":{"attributes":{"dn":"uni/%s","name":"%s","descr":"%s","rn":"%s","status":"created"}}}`,
		dn, bd, descr, rn)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainDel deletes an existing bridge domain from a tenant.
func (c *Client) BridgeDomainDel(tenant, bd string) error {

	me := "BridgeDomainDel"

	rnT := rnTenant(tenant)

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + rnT + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvTenant":{"attributes":{"dn":"uni/%s","status":"modified"},"children":[{"fvBD":{"attributes":{"dn":"uni/%s","status":"deleted"}}}]}}`,
		rnT, dn)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainList retrieves the list of bridge domains from a tenant.
func (c *Client) BridgeDomainList(tenant string) ([]map[string]interface{}, error) {

	me := "BridgeDomainList"

	key := "fvBD"

	t := rnTenant(tenant)

	api := "/api/node/mo/uni/" + t + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainVrfSet defines the VRF for a bridge domain.
func (c *Client) BridgeDomainVrfSet(tenant, bd, vrf string) error {

	me := "BridgeDomainVrfSet"

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + "/rsctx.json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvRsCtx":{"attributes":{"tnFvCtxName":"%s"}}}`,
		vrf)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainVrfGet retrieves the VRF for a bridge domain.
func (c *Client) BridgeDomainVrfGet(tenant, bd string) (string, error) {

	me := "BridgeDomainVrfGet"

	key := "fvRsCtx"

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return "", fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	attrs, errAttr := jsonImdataAttributes(c, body, key, me)
	if errAttr != nil {
		return "", fmt.Errorf("%s: %v", me, errAttr)
	}

	if len(attrs) < 1 {
		return "", fmt.Errorf("%s: empty list of VRFs", me)
	}

	attr := attrs[0]
	v, found := attr["tnFvCtxName"]
	if !found {
		return "", fmt.Errorf("%s: VRF not found", me)
	}

	vrf, isStr := v.(string)
	if !isStr {
		return "", fmt.Errorf("%s: VRF is not a string", me)
	}

	if vrf == "" {
		return "", fmt.Errorf("%s: empty VRF name", me)
	}

	return vrf, nil
}

// BridgeDomainSubnetAdd creates a new subnet in a bridge domain.
func (c *Client) BridgeDomainSubnetAdd(tenant, bd, subnet, descr string) error {

	me := "BridgeDomainSubnetAdd"

	rnSN := rnSubnet(subnet)

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvSubnet":{"attributes":{"dn":"uni/%s","ip":"%s","descr":"%s","rn":"%s","status":"created"}}}`,
		dnSN, subnet, descr, rnSN)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetDel deletes an existing subnet from a bridge domain.
func (c *Client) BridgeDomainSubnetDel(tenant, bd, subnet string) error {

	me := "BridgeDomainSubnetDel"

	dnBD := dnBridgeDomain(tenant, bd)

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnBD + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvBD":{"attributes":{"dn":"uni/%s","status":"modified"},"children":[{"fvSubnet":{"attributes":{"dn":"uni/%s","status":"deleted"}}}]}}`,
		dnBD, dnSN)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetList retrieves the list of subnets from a bridge domain.
func (c *Client) BridgeDomainSubnetList(tenant, bd string) ([]map[string]interface{}, error) {

	me := "BridgeDomainSubnetList"

	key := "fvSubnet"

	dnBD := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dnBD + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainSubnetGet retrieves specific subnet from a bridge domain.
func (c *Client) BridgeDomainSubnetGet(tenant, bd, subnet string) ([]map[string]interface{}, error) {

	me := "BridgeDomainSubnetGet"

	key := "fvSubnet"

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainSubnetScopeSet defines the scope for a bridge domain subnet.
func (c *Client) BridgeDomainSubnetScopeSet(tenant, bd, subnet, scope string) error {

	me := "BridgeDomainSubnetScopeSet"

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvSubnet":{"attributes":{"dn":"uni/%s","scope":"%s"}}}`,
		dnSN, scope)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetScopeGet retrieves the scope from a bridge domain subnet.
func (c *Client) BridgeDomainSubnetScopeGet(tenant, bd, subnet string) (string, error) {

	me := "BridgeDomainSubnetScopeGet"

	list, errSubnet := c.BridgeDomainSubnetGet(tenant, bd, subnet)
	if errSubnet != nil {
		return "", fmt.Errorf("%s: %v", me, errSubnet)
	}

	if len(list) < 1 {
		return "", fmt.Errorf("%s: empty list of subnets", me)
	}

	attrs := list[0]
	s, found := attrs["scope"]
	if !found {
		return "", fmt.Errorf("%s: scope not found", me)
	}

	scope, isStr := s.(string)
	if !isStr {
		return "", fmt.Errorf("%s: scope is not a string", me)
	}

	return scope, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Michael Schenk. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package project

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"
	"time"

	"github.com/git-time-metric/gtm/scm"
	isatty "github.com/mattn/go-isatty"
)

var (
	// ErrNotInitialized is raised when a git repo not initialized for time tracking
	ErrNotInitialized = errors.New("Git Time Metric is not initialized")
	// ErrFileNotFound is raised when record an event for a file that does not exist
	ErrFileNotFound = errors.New("File does not exist")
)

var (
	// GitHooks is map of hooks to apply to the git repo
	GitHooks = map[string]string{
		"post-commit": "gtm commit --yes"}
	// GitConfig is map of git configuration settings
	GitConfig = map[string]string{
		"alias.pushgtm":    "push origin refs/notes/gtm-data",
		"alias.fetchgtm":   "fetch origin refs/notes/gtm-data:refs/notes/gtm-data",
		"notes.rewriteref": "refs/notes/gtm-data"}
	// GitIgnore is file ignore to apply to git repo
	GitIgnore = "/.gtm/"
)

const (
	// NoteNameSpace is the gtm git note namespace
	NoteNameSpace = "gtm-data"
	// GTMDir is the subdir for gtm within the git repo root directory
	GTMDir = ".gtm"
)

const initMsgTpl string = `
{{print "Git Time Metric initialized for " (.ProjectPath) | printf (.HeaderFormat) }}

{{ range $hook, $command := .GitHooks -}}
	{{- $hook | printf "%16s" }}: {{ $command }}
{{ end -}}
{{ range $key, $val := .GitConfig -}}
	{{- $key | printf "%16s" }}: {{ $val }}
{{end -}}
{{ print "terminal:" | printf "%17s" }} {{ .Terminal }}
{{ print ".gitignore:" | printf "%17s" }} {{ .GitIgnore }}
{{ print "tags:" | printf "%17s" }} {{.Tags }}
`
const removeMsgTpl string = `
{{print "Git Time Metric uninitialized for " (.ProjectPath) | printf (.HeaderFormat) }}

The following items have been removed.

{{ range $hook, $command := .GitHooks -}}
	{{- $hook | printf "%16s" }}: {{ $command }}
{{ end -}}
{{ range $key, $val := .GitConfig -}}
	{{- $key | printf "%16s" }}: {{ $val }}
{{end -}}
{{ print ".gitignore:" | printf "%17s" }} {{ .GitIgnore }}
`

// Now is the func used for system time within gtm
// This allows for manipulating system time during testing
var Now = func() time.Time { return time.Now() }

// Initialize initializes a git repo for time tracking
func Initialize(terminal bool, tags []string, clearTags bool) (string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	projRoot, err := scm.RootPath(wd)
	if err != nil {
		return "", fmt.Errorf(
			"Unable to intialize Git Time Metric, Git repository not found in %s", projRoot)
	}

	gitPath := filepath.Join(projRoot, ".git")
	if _, err := os.Stat(gitPath); os.IsNotExist(err) {
		return "", fmt.Errorf(
			"Unable to intialize Git Time Metric, Git repository not found in %s", gitPath)
	}

	gtmPath := filepath.Join(projRoot, GTMDir)
	if _, err := os.Stat(gtmPath); os.IsNotExist(err) {
		if err := os.MkdirAll(gtmPath, 0700); err != nil {
			return "", err
		}
	}

	if clearTags {
		err = removeTags(gtmPath)
		if err != nil {
			return "", err
		}
	}
	err = saveTags(tags, gtmPath)
	if err != nil {
		return "", err
	}
	tags, err = LoadTags(gtmPath)
	if err != nil {
		return "", err
	}

	if terminal {
		if err := ioutil.WriteFile(filepath.Join(gtmPath, "terminal.app"), []byte(""), 0644); err != nil {
			return "", err
		}
	} else {
		// file may not exist, ignore error
		os.Remove(filepath.Join(gtmPath, "terminal.app"))
	}

	if err := scm.SetHooks(GitHooks, projRoot); err != nil {
		return "", err
	}

	if err := scm.ConfigSet(GitConfig, projRoot); err != nil {
		return "", err
	}

	if err := scm.IgnoreSet(GitIgnore, projRoot); err != nil {
		return "", err
	}

	headerFormat := "%s"
	if isatty.IsTerminal(os.Stdout.Fd()) && runtime.GOOS != "windows" {
		headerFormat = "\x1b[1m%s\x1b[0m"
	}

	b := new(bytes.Buffer)
	t := template.Must(template.New("msg").Parse(initMsgTpl))
	err = t.Execute(b,
		struct {
			Tags         string
			HeaderFormat string
			ProjectPath  string
			GitHooks     map[string]string
			GitConfig    map[string]string
			GitIgnore    string
			Terminal     bool
		}{
			strings.Join(tags, " "),
			headerFormat,
			projRoot,
			GitHooks,
			GitConfig,
			GitIgnore,
			terminal,
		})

	if err != nil {
		return "", err
	}

	index, err := NewIndex()
	if err != nil {
		return "", err
	}

	index.add(projRoot)
	err = index.save()
	if err != nil {
		return "", err
	}

	return b.String(), nil
}

//Uninitialize remove GTM tracking from the project in the current working directory
func Uninitialize() (string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	projRoot, err := scm.RootPath(wd)
	if err != nil {
		return "", fmt.Errorf(
			"Unable to unintialize Git Time Metric, Git repository not found in %s", projRoot)
	}

	gtmPath := filepath.Join(projRoot, GTMDir)
	if _, err := os.Stat(gtmPath); os.IsNotExist(err) {
		return "", fmt.Errorf(
			"Unable to uninitialize Git Time Metric, %s directory not found", gtmPath)
	}
	if err := scm.RemoveHooks(GitHooks, projRoot); err != nil {
		return "", err
	}
	if err := scm.ConfigRemove(GitConfig, projRoot); err != nil {
		return "", err
	}
	if err := scm.IgnoreRemove(GitIgnore, projRoot); err != nil {
		return "", err
	}
	if err := os.RemoveAll(gtmPath); err != nil {
		return "", err
	}

	headerFormat := "%s"
	if isatty.IsTerminal(os.Stdout.Fd()) && runtime.GOOS != "windows" {
		headerFormat = "\x1b[1m%s\x1b[0m"
	}
	b := new(bytes.Buffer)
	t := template.Must(template.New("msg").Parse(removeMsgTpl))
	err = t.Execute(b,
		struct {
			HeaderFormat string
			ProjectPath  string
			GitHooks     map[string]string
			GitConfig    map[string]string
			GitIgnore    string
		}{
			headerFormat,
			projRoot,
			GitHooks,
			GitConfig,
			GitIgnore})

	if err != nil {
		return "", err
	}

	index, err := NewIndex()
	if err != nil {
		return "", err
	}

	index.remove(projRoot)
	err = index.save()
	if err != nil {
		return "", err
	}

	return b.String(), nil
}

//Clean removes any event or metrics files from project in the current working directory
func Clean() (string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	projRoot, err := scm.RootPath(wd)
	if err != nil {
		return "", fmt.Errorf(
			"Unable to clean, Git repository not found in %s", projRoot)
	}

	gtmPath := filepath.Join(projRoot, GTMDir)
	if _, err := os.Stat(gtmPath); os.IsNotExist(err) {
		return "", fmt.Errorf(
			"Unable to clean GTM data, %s directory not found", gtmPath)
	}

	files, err := ioutil.ReadDir(gtmPath)
	if err != nil {
		return "", err
	}
	for _, f := range files {
		if !strings.HasSuffix(f.Name(), ".event") &&
			!strings.HasSuffix(f.Name(), ".metric") {
			continue
		}
		if err := os.Remove(filepath.Join(gtmPath, f.Name())); err != nil {
			return "", err
		}
	}

	return "", nil
}

// Paths returns the root git repo and gtm paths
func Paths(wd ...string) (string, string, error) {
	var (
		repoPath string
		err      error
	)
	if len(wd) > 0 {
		repoPath, err = scm.RootPath(wd[0])
	} else {
		repoPath, err = scm.RootPath()
	}
	if err != nil {
		return "", "", ErrNotInitialized
	}

	gtmPath := filepath.Join(repoPath, GTMDir)
	if _, err := os.Stat(gtmPath); os.IsNotExist(err) {
		return "", "", ErrNotInitialized
	}
	return repoPath, gtmPath, nil
}

// Log logs to a gtm log in the GTMDir
func Log(v ...interface{}) error {
	_, gtmPath, err := Paths()
	if err != nil {
		return err
	}
	f, err := os.OpenFile(filepath.Join(gtmPath, "gtm.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return fmt.Errorf("error opening log file: %v", err)
	}
	defer func() { _ = f.Close() }()
	log.SetOutput(f)

	log.Println(v)
	return nil
}

func removeTags(gtmPath string) error {
	files, err := ioutil.ReadDir(gtmPath)
	if err != nil {
		return err
	}
	for i := range files {
		if strings.HasSuffix(files[i].Name(), ".tag") {
			tagFile := filepath.Join(gtmPath, files[i].Name())
			if err := os.Remove(tagFile); err != nil {
				return err
			}
		}
	}
	return nil
}

func LoadTags(gtmPath string) ([]string, error) {
	tags := []string{}
	files, err := ioutil.ReadDir(gtmPath)
	if err != nil {
		return []string{}, err
	}
	for i := range files {
		if strings.HasSuffix(files[i].Name(), ".tag") {
			tags = append(tags, strings.TrimSuffix(files[i].Name(), filepath.Ext(files[i].Name())))
		}
	}
	return tags, nil
}

func saveTags(tags []string, gtmPath string) error {
	if len(tags) > 0 {
		for _, t := range tags {
			if err := ioutil.WriteFile(filepath.Join(gtmPath, fmt.Sprintf("%s.tag", t)), []byte(""), 0644); err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>Fix gtm init creates empty .tag file<commit_after>// Copyright 2016 Michael Schenk. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package project

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"
	"time"

	"github.com/git-time-metric/gtm/scm"
	isatty "github.com/mattn/go-isatty"
)

var (
	// ErrNotInitialized is raised when a git repo not initialized for time tracking
	ErrNotInitialized = errors.New("Git Time Metric is not initialized")
	// ErrFileNotFound is raised when record an event for a file that does not exist
	ErrFileNotFound = errors.New("File does not exist")
)

var (
	// GitHooks is map of hooks to apply to the git repo
	GitHooks = map[string]string{
		"post-commit": "gtm commit --yes"}
	// GitConfig is map of git configuration settings
	GitConfig = map[string]string{
		"alias.pushgtm":    "push origin refs/notes/gtm-data",
		"alias.fetchgtm":   "fetch origin refs/notes/gtm-data:refs/notes/gtm-data",
		"notes.rewriteref": "refs/notes/gtm-data"}
	// GitIgnore is file ignore to apply to git repo
	GitIgnore = "/.gtm/"
)

const (
	// NoteNameSpace is the gtm git note namespace
	NoteNameSpace = "gtm-data"
	// GTMDir is the subdir for gtm within the git repo root directory
	GTMDir = ".gtm"
)

const initMsgTpl string = `
{{print "Git Time Metric initialized for " (.ProjectPath) | printf (.HeaderFormat) }}

{{ range $hook, $command := .GitHooks -}}
	{{- $hook | printf "%16s" }}: {{ $command }}
{{ end -}}
{{ range $key, $val := .GitConfig -}}
	{{- $key | printf "%16s" }}: {{ $val }}
{{end -}}
{{ print "terminal:" | printf "%17s" }} {{ .Terminal }}
{{ print ".gitignore:" | printf "%17s" }} {{ .GitIgnore }}
{{ print "tags:" | printf "%17s" }} {{.Tags }}
`
const removeMsgTpl string = `
{{print "Git Time Metric uninitialized for " (.ProjectPath) | printf (.HeaderFormat) }}

The following items have been removed.

{{ range $hook, $command := .GitHooks -}}
	{{- $hook | printf "%16s" }}: {{ $command }}
{{ end -}}
{{ range $key, $val := .GitConfig -}}
	{{- $key | printf "%16s" }}: {{ $val }}
{{end -}}
{{ print ".gitignore:" | printf "%17s" }} {{ .GitIgnore }}
`

// Now is the func used for system time within gtm
// This allows for manipulating system time during testing
var Now = func() time.Time { return time.Now() }

// Initialize initializes a git repo for time tracking
func Initialize(terminal bool, tags []string, clearTags bool) (string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	projRoot, err := scm.RootPath(wd)
	if err != nil {
		return "", fmt.Errorf(
			"Unable to intialize Git Time Metric, Git repository not found in %s", projRoot)
	}

	gitPath := filepath.Join(projRoot, ".git")
	if _, err := os.Stat(gitPath); os.IsNotExist(err) {
		return "", fmt.Errorf(
			"Unable to intialize Git Time Metric, Git repository not found in %s", gitPath)
	}

	gtmPath := filepath.Join(projRoot, GTMDir)
	if _, err := os.Stat(gtmPath); os.IsNotExist(err) {
		if err := os.MkdirAll(gtmPath, 0700); err != nil {
			return "", err
		}
	}

	if clearTags {
		err = removeTags(gtmPath)
		if err != nil {
			return "", err
		}
	}
	err = saveTags(tags, gtmPath)
	if err != nil {
		return "", err
	}
	tags, err = LoadTags(gtmPath)
	if err != nil {
		return "", err
	}

	if terminal {
		if err := ioutil.WriteFile(filepath.Join(gtmPath, "terminal.app"), []byte(""), 0644); err != nil {
			return "", err
		}
	} else {
		// file may not exist, ignore error
		os.Remove(filepath.Join(gtmPath, "terminal.app"))
	}

	if err := scm.SetHooks(GitHooks, projRoot); err != nil {
		return "", err
	}

	if err := scm.ConfigSet(GitConfig, projRoot); err != nil {
		return "", err
	}

	if err := scm.IgnoreSet(GitIgnore, projRoot); err != nil {
		return "", err
	}

	headerFormat := "%s"
	if isatty.IsTerminal(os.Stdout.Fd()) && runtime.GOOS != "windows" {
		headerFormat = "\x1b[1m%s\x1b[0m"
	}

	b := new(bytes.Buffer)
	t := template.Must(template.New("msg").Parse(initMsgTpl))
	err = t.Execute(b,
		struct {
			Tags         string
			HeaderFormat string
			ProjectPath  string
			GitHooks     map[string]string
			GitConfig    map[string]string
			GitIgnore    string
			Terminal     bool
		}{
			strings.Join(tags, " "),
			headerFormat,
			projRoot,
			GitHooks,
			GitConfig,
			GitIgnore,
			terminal,
		})

	if err != nil {
		return "", err
	}

	index, err := NewIndex()
	if err != nil {
		return "", err
	}

	index.add(projRoot)
	err = index.save()
	if err != nil {
		return "", err
	}

	return b.String(), nil
}

//Uninitialize remove GTM tracking from the project in the current working directory
func Uninitialize() (string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	projRoot, err := scm.RootPath(wd)
	if err != nil {
		return "", fmt.Errorf(
			"Unable to unintialize Git Time Metric, Git repository not found in %s", projRoot)
	}

	gtmPath := filepath.Join(projRoot, GTMDir)
	if _, err := os.Stat(gtmPath); os.IsNotExist(err) {
		return "", fmt.Errorf(
			"Unable to uninitialize Git Time Metric, %s directory not found", gtmPath)
	}
	if err := scm.RemoveHooks(GitHooks, projRoot); err != nil {
		return "", err
	}
	if err := scm.ConfigRemove(GitConfig, projRoot); err != nil {
		return "", err
	}
	if err := scm.IgnoreRemove(GitIgnore, projRoot); err != nil {
		return "", err
	}
	if err := os.RemoveAll(gtmPath); err != nil {
		return "", err
	}

	headerFormat := "%s"
	if isatty.IsTerminal(os.Stdout.Fd()) && runtime.GOOS != "windows" {
		headerFormat = "\x1b[1m%s\x1b[0m"
	}
	b := new(bytes.Buffer)
	t := template.Must(template.New("msg").Parse(removeMsgTpl))
	err = t.Execute(b,
		struct {
			HeaderFormat string
			ProjectPath  string
			GitHooks     map[string]string
			GitConfig    map[string]string
			GitIgnore    string
		}{
			headerFormat,
			projRoot,
			GitHooks,
			GitConfig,
			GitIgnore})

	if err != nil {
		return "", err
	}

	index, err := NewIndex()
	if err != nil {
		return "", err
	}

	index.remove(projRoot)
	err = index.save()
	if err != nil {
		return "", err
	}

	return b.String(), nil
}

//Clean removes any event or metrics files from project in the current working directory
func Clean() (string, error) {
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	projRoot, err := scm.RootPath(wd)
	if err != nil {
		return "", fmt.Errorf(
			"Unable to clean, Git repository not found in %s", projRoot)
	}

	gtmPath := filepath.Join(projRoot, GTMDir)
	if _, err := os.Stat(gtmPath); os.IsNotExist(err) {
		return "", fmt.Errorf(
			"Unable to clean GTM data, %s directory not found", gtmPath)
	}

	files, err := ioutil.ReadDir(gtmPath)
	if err != nil {
		return "", err
	}
	for _, f := range files {
		if !strings.HasSuffix(f.Name(), ".event") &&
			!strings.HasSuffix(f.Name(), ".metric") {
			continue
		}
		if err := os.Remove(filepath.Join(gtmPath, f.Name())); err != nil {
			return "", err
		}
	}

	return "", nil
}

// Paths returns the root git repo and gtm paths
func Paths(wd ...string) (string, string, error) {
	var (
		repoPath string
		err      error
	)
	if len(wd) > 0 {
		repoPath, err = scm.RootPath(wd[0])
	} else {
		repoPath, err = scm.RootPath()
	}
	if err != nil {
		return "", "", ErrNotInitialized
	}

	gtmPath := filepath.Join(repoPath, GTMDir)
	if _, err := os.Stat(gtmPath); os.IsNotExist(err) {
		return "", "", ErrNotInitialized
	}
	return repoPath, gtmPath, nil
}

// Log logs to a gtm log in the GTMDir
func Log(v ...interface{}) error {
	_, gtmPath, err := Paths()
	if err != nil {
		return err
	}
	f, err := os.OpenFile(filepath.Join(gtmPath, "gtm.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return fmt.Errorf("error opening log file: %v", err)
	}
	defer func() { _ = f.Close() }()
	log.SetOutput(f)

	log.Println(v)
	return nil
}

func removeTags(gtmPath string) error {
	files, err := ioutil.ReadDir(gtmPath)
	if err != nil {
		return err
	}
	for i := range files {
		if strings.HasSuffix(files[i].Name(), ".tag") {
			tagFile := filepath.Join(gtmPath, files[i].Name())
			if err := os.Remove(tagFile); err != nil {
				return err
			}
		}
	}
	return nil
}

func LoadTags(gtmPath string) ([]string, error) {
	tags := []string{}
	files, err := ioutil.ReadDir(gtmPath)
	if err != nil {
		return []string{}, err
	}
	for i := range files {
		if strings.HasSuffix(files[i].Name(), ".tag") {
			tags = append(tags, strings.TrimSuffix(files[i].Name(), filepath.Ext(files[i].Name())))
		}
	}
	return tags, nil
}

func saveTags(tags []string, gtmPath string) error {
	if len(tags) > 0 {
		for _, t := range tags {
			if strings.TrimSpace(t) == "" {
				continue
			}
			if err := ioutil.WriteFile(filepath.Join(gtmPath, fmt.Sprintf("%s.tag", t)), []byte(""), 0644); err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package project

import (
	"crypto/md5"
	"fmt"

	"github.com/hashicorp/hcl"
	"github.com/juhovuori/builder/fetcher"
)

// Project represents a single managed project
type Project interface {
	Manager
	ProjectConfig
}

// Manager represents the manager of a single project
type Manager interface {
	URL() string
	ID() string
	Err() error
}

// ProjectConfig represents the configuration of a single project
type ProjectConfig interface {
	Name() string
	Description() string
	Script() string
}

// defaultProject is the main implementation of Project
type defaultProject struct {
	Purl         string `json:"url"`
	Pmd5         string `json:"id"`
	Perr         error  `json:"error"`
	Pname        string `hcl:"name"`
	Pdescription string `hcl:"description"`
	Pscript      string `hcl:"script"`
}

func (p *defaultProject) URL() string {
	return p.Purl
}

func (p *defaultProject) ID() string {
	return p.Pmd5
}

func (p *defaultProject) Err() error {
	return p.Perr
}

func (p *defaultProject) Name() string {
	return p.Pname
}

func (p *defaultProject) Description() string {
	return p.Pdescription
}

func (p *defaultProject) Script() string {
	return p.Pscript
}

func fetchConfig(filename string) (string, error) {
	bytes, err := fetcher.Fetch(filename)
	return string(bytes), err
}

func newProject(config string, URL string, err error) (*defaultProject, error) {
	var p defaultProject
	if err == nil {
		err = hcl.Decode(&p, config)
	}
	MD5 := md5.Sum([]byte(URL))
	p.Purl = URL
	p.Pmd5 = fmt.Sprintf("%x", MD5)
	p.Perr = err
	return &p, err
}

// NewProject creates a new project
func NewProject(URL string) (Project, error) {
	config, err := fetchConfig(URL)
	return newProject(config, URL, err)
}

// NewFromString creates a new project from configuration string
func NewFromString(config string) (Project, error) {
	return newProject(config, "", nil)
}
<commit_msg>ProjectConfig => Attributes<commit_after>package project

import (
	"crypto/md5"
	"fmt"

	"github.com/hashicorp/hcl"
	"github.com/juhovuori/builder/fetcher"
)

// Project represents a single managed project
type Project interface {
	Manager
	Attributes
}

// Manager represents the manager of a single project
type Manager interface {
	URL() string
	ID() string
	Err() error
}

// Attributes represents the configuration of a single project
type Attributes interface {
	Name() string
	Description() string
	Script() string
}

// defaultProject is the main implementation of Project
type defaultProject struct {
	Purl         string `json:"url"`
	Pmd5         string `json:"id"`
	Perr         error  `json:"error"`
	Pname        string `hcl:"name"`
	Pdescription string `hcl:"description"`
	Pscript      string `hcl:"script"`
}

func (p *defaultProject) URL() string {
	return p.Purl
}

func (p *defaultProject) ID() string {
	return p.Pmd5
}

func (p *defaultProject) Err() error {
	return p.Perr
}

func (p *defaultProject) Name() string {
	return p.Pname
}

func (p *defaultProject) Description() string {
	return p.Pdescription
}

func (p *defaultProject) Script() string {
	return p.Pscript
}

func fetchConfig(filename string) (string, error) {
	bytes, err := fetcher.Fetch(filename)
	return string(bytes), err
}

func newProject(config string, URL string, err error) (*defaultProject, error) {
	var p defaultProject
	if err == nil {
		err = hcl.Decode(&p, config)
	}
	MD5 := md5.Sum([]byte(URL))
	p.Purl = URL
	p.Pmd5 = fmt.Sprintf("%x", MD5)
	p.Perr = err
	return &p, err
}

// NewProject creates a new project
func NewProject(URL string) (Project, error) {
	config, err := fetchConfig(URL)
	return newProject(config, URL, err)
}

// NewFromString creates a new project from configuration string
func NewFromString(config string) (Project, error) {
	return newProject(config, "", nil)
}
<|endoftext|>


<commit_before>package awsat

import (
	"testing"

	"github.com/aws/aws-sdk-go/service/elbv2"
)

func TestTargetgroup(t *testing.T) {
	t.Run("create", func(t *testing.T) {
		Template("create targetgroup name=new-tg port=80 protocol=HTTP vpc=any-vpc-id healthcheckinterval=2 healthcheckpath=/health healthcheckport=80 healthcheckprotocol=HTTP healthchecktimeout=180 healthythreshold=30 unhealthythreshold=10 matcher=OK").Mock(&elbv2Mock{
			CreateTargetGroupFunc: func(input *elbv2.CreateTargetGroupInput) (*elbv2.CreateTargetGroupOutput, error) {
				return &elbv2.CreateTargetGroupOutput{
					TargetGroups: []*elbv2.TargetGroup{{TargetGroupArn: String("new-tg-arn")}},
				}, nil
			}}).ExpectInput("CreateTargetGroup", &elbv2.CreateTargetGroupInput{
			Name:     String("new-tg"),
			Port:     Int64(80),
			Protocol: String("HTTP"),
			VpcId:    String("any-vpc-id"),
			HealthCheckIntervalSeconds: Int64(2),
			HealthCheckPath:            String("/health"),
			HealthCheckPort:            String("80"),
			HealthCheckProtocol:        String("HTTP"),
			HealthCheckTimeoutSeconds:  Int64(180),
			HealthyThresholdCount:      Int64(30),
			UnhealthyThresholdCount:    Int64(10),
			Matcher: &elbv2.Matcher{
				HttpCode: String("OK"),
			},
		},
		).ExpectCommandResult("new-tg-arn").ExpectCalls("CreateTargetGroup").Run(t)
	})

	t.Run("update", func(t *testing.T) {
		Template("update targetgroup id=any-tg stickiness=ouech stickinessduration=ouechdur deregistrationdelay=yeap healthcheckinterval=2 healthcheckpath=/health healthcheckport=80 healthcheckprotocol=HTTP healthchecktimeout=180 healthythreshold=30 unhealthythreshold=10 matcher=OK").Mock(&elbv2Mock{
			ModifyTargetGroupAttributesFunc: func(input *elbv2.ModifyTargetGroupAttributesInput) (*elbv2.ModifyTargetGroupAttributesOutput, error) {
				return &elbv2.ModifyTargetGroupAttributesOutput{
					Attributes: []*elbv2.TargetGroupAttribute{},
				}, nil
			},
			ModifyTargetGroupFunc: func(input *elbv2.ModifyTargetGroupInput) (*elbv2.ModifyTargetGroupOutput, error) {
				return &elbv2.ModifyTargetGroupOutput{
					TargetGroups: []*elbv2.TargetGroup{{TargetGroupArn: String("any-tg")}},
				}, nil
			}}).ExpectInput("ModifyTargetGroupAttributes", &elbv2.ModifyTargetGroupAttributesInput{
			TargetGroupArn: String("any-tg"),
			Attributes: []*elbv2.TargetGroupAttribute{
				{Key: String("stickiness.enabled"), Value: String("ouech")},
				{Key: String("stickiness.lb_cookie.duration_seconds"), Value: String("ouechdur")},
				{Key: String("deregistration_delay.timeout_seconds"), Value: String("yeap")},
			}}).ExpectInput("ModifyTargetGroup", &elbv2.ModifyTargetGroupInput{
			TargetGroupArn:             String("any-tg"),
			HealthCheckIntervalSeconds: Int64(2),
			HealthCheckPath:            String("/health"),
			HealthCheckPort:            String("80"),
			HealthCheckProtocol:        String("HTTP"),
			HealthCheckTimeoutSeconds:  Int64(180),
			HealthyThresholdCount:      Int64(30),
			UnhealthyThresholdCount:    Int64(10),
			Matcher: &elbv2.Matcher{
				HttpCode: String("OK"),
			},
		}).ExpectCommandResult("any-tg").ExpectCalls("ModifyTargetGroupAttributes", "ModifyTargetGroup").Run(t)
	})

	t.Run("delete", func(t *testing.T) {
		Template("delete targetgroup id=any-tg-arn").Mock(&elbv2Mock{
			DeleteTargetGroupFunc: func(input *elbv2.DeleteTargetGroupInput) (*elbv2.DeleteTargetGroupOutput, error) {
				return &elbv2.DeleteTargetGroupOutput{}, nil
			}}).ExpectInput("DeleteTargetGroup", &elbv2.DeleteTargetGroupInput{
			TargetGroupArn: String("any-tg-arn"),
		}).ExpectCalls("DeleteTargetGroup").Run(t)
	})
}
<commit_msg>Update security group do not return a result<commit_after>package awsat

import (
	"testing"

	"github.com/aws/aws-sdk-go/service/elbv2"
)

func TestTargetgroup(t *testing.T) {
	t.Run("create", func(t *testing.T) {
		Template("create targetgroup name=new-tg port=80 protocol=HTTP vpc=any-vpc-id healthcheckinterval=2 healthcheckpath=/health healthcheckport=80 healthcheckprotocol=HTTP healthchecktimeout=180 healthythreshold=30 unhealthythreshold=10 matcher=OK").Mock(&elbv2Mock{
			CreateTargetGroupFunc: func(input *elbv2.CreateTargetGroupInput) (*elbv2.CreateTargetGroupOutput, error) {
				return &elbv2.CreateTargetGroupOutput{
					TargetGroups: []*elbv2.TargetGroup{{TargetGroupArn: String("new-tg-arn")}},
				}, nil
			}}).ExpectInput("CreateTargetGroup", &elbv2.CreateTargetGroupInput{
			Name:     String("new-tg"),
			Port:     Int64(80),
			Protocol: String("HTTP"),
			VpcId:    String("any-vpc-id"),
			HealthCheckIntervalSeconds: Int64(2),
			HealthCheckPath:            String("/health"),
			HealthCheckPort:            String("80"),
			HealthCheckProtocol:        String("HTTP"),
			HealthCheckTimeoutSeconds:  Int64(180),
			HealthyThresholdCount:      Int64(30),
			UnhealthyThresholdCount:    Int64(10),
			Matcher: &elbv2.Matcher{
				HttpCode: String("OK"),
			},
		},
		).ExpectCommandResult("new-tg-arn").ExpectCalls("CreateTargetGroup").Run(t)
	})

	t.Run("update", func(t *testing.T) {
		Template("update targetgroup id=any-tg stickiness=ouech stickinessduration=ouechdur deregistrationdelay=yeap healthcheckinterval=2 healthcheckpath=/health healthcheckport=80 healthcheckprotocol=HTTP healthchecktimeout=180 healthythreshold=30 unhealthythreshold=10 matcher=OK").Mock(&elbv2Mock{
			ModifyTargetGroupAttributesFunc: func(input *elbv2.ModifyTargetGroupAttributesInput) (*elbv2.ModifyTargetGroupAttributesOutput, error) {
				return &elbv2.ModifyTargetGroupAttributesOutput{
					Attributes: []*elbv2.TargetGroupAttribute{},
				}, nil
			},
			ModifyTargetGroupFunc: func(input *elbv2.ModifyTargetGroupInput) (*elbv2.ModifyTargetGroupOutput, error) { return nil, nil }}).ExpectInput("ModifyTargetGroupAttributes", &elbv2.ModifyTargetGroupAttributesInput{
			TargetGroupArn: String("any-tg"),
			Attributes: []*elbv2.TargetGroupAttribute{
				{Key: String("stickiness.enabled"), Value: String("ouech")},
				{Key: String("stickiness.lb_cookie.duration_seconds"), Value: String("ouechdur")},
				{Key: String("deregistration_delay.timeout_seconds"), Value: String("yeap")},
			}}).ExpectInput("ModifyTargetGroup", &elbv2.ModifyTargetGroupInput{
			TargetGroupArn:             String("any-tg"),
			HealthCheckIntervalSeconds: Int64(2),
			HealthCheckPath:            String("/health"),
			HealthCheckPort:            String("80"),
			HealthCheckProtocol:        String("HTTP"),
			HealthCheckTimeoutSeconds:  Int64(180),
			HealthyThresholdCount:      Int64(30),
			UnhealthyThresholdCount:    Int64(10),
			Matcher: &elbv2.Matcher{
				HttpCode: String("OK"),
			},
		}).ExpectCalls("ModifyTargetGroupAttributes", "ModifyTargetGroup").Run(t)
	})

	t.Run("delete", func(t *testing.T) {
		Template("delete targetgroup id=any-tg-arn").Mock(&elbv2Mock{
			DeleteTargetGroupFunc: func(input *elbv2.DeleteTargetGroupInput) (*elbv2.DeleteTargetGroupOutput, error) {
				return &elbv2.DeleteTargetGroupOutput{}, nil
			}}).ExpectInput("DeleteTargetGroup", &elbv2.DeleteTargetGroupInput{
			TargetGroupArn: String("any-tg-arn"),
		}).ExpectCalls("DeleteTargetGroup").Run(t)
	})
}
<|endoftext|>


<commit_before>package cni

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/romana/core/agent/iptsave"
	"github.com/romana/rlog"
)

func enablePodPolicy(ifaceName string) error {
	return manageDivertRules(MakeDivertRules(ifaceName, iptsave.RenderAppendRule))
}

func disablePodPolicy(ifaceName string) error {
	return manageDivertRules(MakeDivertRules(ifaceName, iptsave.RenderDeleteRule))
}

func manageDivertRules(divertRules []*iptsave.IPchain) error {
	IptablesBin, err := exec.LookPath("iptables")
	if err != nil {
		return err
	}

	var rules string
	for _, chain := range divertRules {
		rules += chain.RenderFooter()
	}

	makeArgs := func(a []string, b ...string) []string {
		var result []string
		result = append(b, a...)
		return result
	}

	for _, rule := range strings.Split(rules, "\n") {
		if rule == "" {
			continue
		}
		rlog.Debugf("EXEC %s", makeArgs(strings.Split(rule, " ")), IptablesBin, "-t", "filter")
		data, err := exec.Command(IptablesBin, makeArgs(strings.Split(rule, " "), "-t", "filter")...).CombinedOutput()
		if err != nil {
			return fmt.Errorf("%s, err=%s", data, err)
		}
	}

	return nil
}
<commit_msg>cni: add missing license header to policy.go file.<commit_after>// Copyright (c) 2017 Pani Networks
// 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 cni

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/romana/core/agent/iptsave"
	"github.com/romana/rlog"
)

func enablePodPolicy(ifaceName string) error {
	return manageDivertRules(MakeDivertRules(ifaceName, iptsave.RenderAppendRule))
}

func disablePodPolicy(ifaceName string) error {
	return manageDivertRules(MakeDivertRules(ifaceName, iptsave.RenderDeleteRule))
}

func manageDivertRules(divertRules []*iptsave.IPchain) error {
	IptablesBin, err := exec.LookPath("iptables")
	if err != nil {
		return err
	}

	var rules string
	for _, chain := range divertRules {
		rules += chain.RenderFooter()
	}

	makeArgs := func(a []string, b ...string) []string {
		var result []string
		result = append(b, a...)
		return result
	}

	for _, rule := range strings.Split(rules, "\n") {
		if rule == "" {
			continue
		}
		rlog.Debugf("EXEC %s", makeArgs(strings.Split(rule, " ")), IptablesBin, "-t", "filter")
		data, err := exec.Command(IptablesBin, makeArgs(strings.Split(rule, " "), "-t", "filter")...).CombinedOutput()
		if err != nil {
			return fmt.Errorf("%s, err=%s", data, err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package postgres

import (
	"net/http"
	"testing"

	"github.com/nuveo/prest/api"
	. "github.com/smartystreets/goconvey/convey"
)

func TestWhereByRequest(t *testing.T) {
	Convey("Where by request without paginate", t, func() {
		r, err := http.NewRequest("GET", "/databases?dbname=prest&test=cool", nil)
		So(err, ShouldBeNil)

		where, values, err := WhereByRequest(r, 1)
		So(err, ShouldBeNil)
		So(where, ShouldContainSubstring, "dbname=$")
		So(where, ShouldContainSubstring, "test=$")
		So(where, ShouldContainSubstring, " AND ")
		So(values, ShouldContain, "prest")
		So(values, ShouldContain, "cool")
	})

	Convey("Where by request with jsonb field", t, func() {
		r, err := http.NewRequest("GET", "/prest/public/test?name=nuveo&data->>description:jsonb=bla", nil)
		So(err, ShouldBeNil)

		where, values, err := WhereByRequest(r, 1)
		So(err, ShouldBeNil)
		So(where, ShouldContainSubstring, "name=$")
		So(where, ShouldContainSubstring, "data->>'description'=$")
		So(where, ShouldContainSubstring, " AND ")
		So(values, ShouldContain, "nuveo")
		So(values, ShouldContain, "bla")
	})
}

func TestConnection(t *testing.T) {
	Convey("Verify database connection", t, func() {
		sqlx := Conn()
		So(sqlx, ShouldNotBeNil)
		err := sqlx.Ping()
		So(err, ShouldBeNil)
	})
}

func TestQuery(t *testing.T) {
	Convey("Query execution", t, func() {
		sql := "SELECT schema_name FROM information_schema.schemata ORDER BY schema_name ASC"
		json, err := Query(sql)
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})

	Convey("Query execution with params", t, func() {
		sql := "SELECT schema_name FROM information_schema.schemata WHERE schema_name = $1 ORDER BY schema_name ASC"
		json, err := Query(sql, "public")
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})
}

func TestPaginateIfPossible(t *testing.T) {
	Convey("Paginate if possible", t, func() {
		r, err := http.NewRequest("GET", "/databases?dbname=prest&test=cool&_page=1&_page_size=20", nil)
		So(err, ShouldBeNil)
		where, err := PaginateIfPossible(r)
		So(err, ShouldBeNil)
		So(where, ShouldContainSubstring, "LIMIT 20 OFFSET(1 - 1) * 20")
	})
}

func TestInsert(t *testing.T) {
	Convey("Insert data into a table", t, func() {
		r := api.Request{
			Data: map[string]string{
				"name": "prest",
			},
		}
		json, err := Insert("prest", "public", "test", r)
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})
}

func TestDelete(t *testing.T) {
	Convey("Delete data from table", t, func() {
		json, err := Delete("prest", "public", "test", "name=$1", []interface{}{"nuveo"})
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})
}

func TestUpdate(t *testing.T) {
	Convey("Update data into a table", t, func() {
		r := api.Request{
			Data: map[string]string{
				"name": "prest",
			},
		}
		json, err := Update("prest", "public", "test", "name=$1", []interface{}{"prest"}, r)
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})
}
<commit_msg>test chkInvaidIdentifier<commit_after>package postgres

import (
	"net/http"
	"testing"

	"github.com/nuveo/prest/api"
	. "github.com/smartystreets/goconvey/convey"
)

func TestWhereByRequest(t *testing.T) {
	Convey("Where by request without paginate", t, func() {
		r, err := http.NewRequest("GET", "/databases?dbname=prest&test=cool", nil)
		So(err, ShouldBeNil)

		where, values, err := WhereByRequest(r, 1)
		So(err, ShouldBeNil)
		So(where, ShouldContainSubstring, "dbname=$")
		So(where, ShouldContainSubstring, "test=$")
		So(where, ShouldContainSubstring, " AND ")
		So(values, ShouldContain, "prest")
		So(values, ShouldContain, "cool")
	})

	Convey("Where by request with jsonb field", t, func() {
		r, err := http.NewRequest("GET", "/prest/public/test?name=nuveo&data->>description:jsonb=bla", nil)
		So(err, ShouldBeNil)

		where, values, err := WhereByRequest(r, 1)
		So(err, ShouldBeNil)
		So(where, ShouldContainSubstring, "name=$")
		So(where, ShouldContainSubstring, "data->>'description'=$")
		So(where, ShouldContainSubstring, " AND ")
		So(values, ShouldContain, "nuveo")
		So(values, ShouldContain, "bla")
	})
}

func TestConnection(t *testing.T) {
	Convey("Verify database connection", t, func() {
		sqlx := Conn()
		So(sqlx, ShouldNotBeNil)
		err := sqlx.Ping()
		So(err, ShouldBeNil)
	})
}

func TestQuery(t *testing.T) {
	Convey("Query execution", t, func() {
		sql := "SELECT schema_name FROM information_schema.schemata ORDER BY schema_name ASC"
		json, err := Query(sql)
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})

	Convey("Query execution with params", t, func() {
		sql := "SELECT schema_name FROM information_schema.schemata WHERE schema_name = $1 ORDER BY schema_name ASC"
		json, err := Query(sql, "public")
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})
}

func TestPaginateIfPossible(t *testing.T) {
	Convey("Paginate if possible", t, func() {
		r, err := http.NewRequest("GET", "/databases?dbname=prest&test=cool&_page=1&_page_size=20", nil)
		So(err, ShouldBeNil)
		where, err := PaginateIfPossible(r)
		So(err, ShouldBeNil)
		So(where, ShouldContainSubstring, "LIMIT 20 OFFSET(1 - 1) * 20")
	})
}

func TestInsert(t *testing.T) {
	Convey("Insert data into a table", t, func() {
		r := api.Request{
			Data: map[string]string{
				"name": "prest",
			},
		}
		json, err := Insert("prest", "public", "test", r)
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})
}

func TestDelete(t *testing.T) {
	Convey("Delete data from table", t, func() {
		json, err := Delete("prest", "public", "test", "name=$1", []interface{}{"nuveo"})
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})
}

func TestUpdate(t *testing.T) {
	Convey("Update data into a table", t, func() {
		r := api.Request{
			Data: map[string]string{
				"name": "prest",
			},
		}
		json, err := Update("prest", "public", "test", "name=$1", []interface{}{"prest"}, r)
		So(err, ShouldBeNil)
		So(len(json), ShouldBeGreaterThan, 0)
	})
}

func TestChkInvaidIdentifier(t *testing.T) {
	Convey("Check invalid character on identifier", t, func() {
		chk := chkInvaidIdentifier("fildName")
		So(chk, ShouldBeFalse)
		chk = chkInvaidIdentifier("_9fildName")
		So(chk, ShouldBeFalse)
		chk = chkInvaidIdentifier("_fild.Name")
		So(chk, ShouldBeFalse)

		chk = chkInvaidIdentifier("0fildName")
		So(chk, ShouldBeTrue)
		chk = chkInvaidIdentifier("fild'Name")
		So(chk, ShouldBeTrue)
		chk = chkInvaidIdentifier("fild\"Name")
		So(chk, ShouldBeTrue)
		chk = chkInvaidIdentifier("fild;Name")
		So(chk, ShouldBeTrue)
		chk = chkInvaidIdentifier("_123456789_123456789_123456789_123456789_123456789_123456789_12345")
		So(chk, ShouldBeTrue)

	})
}
<|endoftext|>