<commit_before>package tunnel

import (
	"fmt"
	"io"
	"net"

	"github.com/mmatczuk/go-http-tunnel/log"
	"github.com/mmatczuk/go-http-tunnel/proto"
)

// TCPProxy forwards TCP streams.
type TCPProxy struct {
	// localAddr specifies default TCP address of the local server.
	localAddr string
	// localAddrMap specifies mapping from ControlMessage ForwardedBy to
	// local server address, keys may contain host and port, only host or
	// only port. The order of precedence is the following
	// * host and port
	// * port
	// * host
	localAddrMap map[string]string
	// logger is the proxy logger.
	logger log.Logger
}

// NewTCPProxy creates new direct TCPProxy, everything will be proxied to
// localAddr.
func NewTCPProxy(localAddr string, logger log.Logger) *TCPProxy {
	if localAddr == "" {
		panic("missing localAddr")
	}

	if logger == nil {
		logger = log.NewNopLogger()
	}

	return &TCPProxy{
		localAddr: localAddr,
		logger:    logger,
	}
}

// NewMultiTCPProxy creates a new dispatching TCPProxy, connections may go to
// different backends based on localAddrMap.
func NewMultiTCPProxy(localAddrMap map[string]string, logger log.Logger) *TCPProxy {
	if localAddrMap == nil {
		panic("missing localAddrMap")
	}

	if logger == nil {
		logger = log.NewNopLogger()
	}

	return &TCPProxy{
		localAddrMap: localAddrMap,
		logger:       logger,
	}
}

// Proxy is a ProxyFunc.
func (p *TCPProxy) Proxy(w io.Writer, r io.ReadCloser, msg *proto.ControlMessage) {
	switch msg.Protocol {
	case proto.TCP, proto.TCP4, proto.TCP6:
		// ok
	default:
		p.logger.Log(
			"level", 0,
			"msg", "unsupported protocol",
			"ctrlMsg", msg,
		)
		return
	}

	target := p.localAddrFor(msg.ForwardedBy)
	if target == "" {
		p.logger.Log(
			"level", 1,
			"msg", "no target",
			"ctrlMsg", msg,
		)
		return
	}

	local, err := net.DialTimeout("tcp", target, DefaultTimeout)
	if err != nil {
		p.logger.Log(
			"level", 0,
			"msg", "dial failed",
			"target", target,
			"ctrlMsg", msg,
			"err", err,
		)
		return
	}

	done := make(chan struct{})
	go func() {
		transfer(flushWriter{w}, local, log.NewContext(p.logger).With(
			"dst", msg.ForwardedBy,
			"src", target,
		))
		close(done)
	}()
	transfer(local, r, log.NewContext(p.logger).With(
		"dst", target,
		"src", msg.ForwardedBy,
	))
	<-done
}

func (p *TCPProxy) localAddrFor(hostPort string) string {
	if p.localAddrMap == nil {
		return p.localAddr
	}

	// try hostPort
	if addr := p.localAddrMap[hostPort]; addr != "" {
		return addr
	}

	// try port
	host, port, _ := net.SplitHostPort(hostPort)
	if addr := p.localAddrMap[port]; addr != "" {
		return addr
	}

	// try 0.0.0.0:port
	if addr := p.localAddrMap[fmt.Sprintf("0.0.0.0:%s", port)]; addr != "" {
		return addr
	}

	// try host
	if addr := p.localAddrMap[host]; addr != "" {
		return addr
	}

	return p.localAddr
}
<commit_msg>proxy: whitespaces<commit_after>package tunnel

import (
	"fmt"
	"io"
	"net"

	"github.com/mmatczuk/go-http-tunnel/log"
	"github.com/mmatczuk/go-http-tunnel/proto"
)

// TCPProxy forwards TCP streams.
type TCPProxy struct {
	// localAddr specifies default TCP address of the local server.
	localAddr string
	// localAddrMap specifies mapping from ControlMessage ForwardedBy to
	// local server address, keys may contain host and port, only host or
	// only port. The order of precedence is the following
	// * host and port
	// * port
	// * host
	localAddrMap map[string]string
	// logger is the proxy logger.
	logger log.Logger
}

// NewTCPProxy creates new direct TCPProxy, everything will be proxied to
// localAddr.
func NewTCPProxy(localAddr string, logger log.Logger) *TCPProxy {
	if localAddr == "" {
		panic("missing localAddr")
	}

	if logger == nil {
		logger = log.NewNopLogger()
	}

	return &TCPProxy{
		localAddr: localAddr,
		logger:    logger,
	}
}

// NewMultiTCPProxy creates a new dispatching TCPProxy, connections may go to
// different backends based on localAddrMap.
func NewMultiTCPProxy(localAddrMap map[string]string, logger log.Logger) *TCPProxy {
	if localAddrMap == nil {
		panic("missing localAddrMap")
	}

	if logger == nil {
		logger = log.NewNopLogger()
	}

	return &TCPProxy{
		localAddrMap: localAddrMap,
		logger:       logger,
	}
}

// Proxy is a ProxyFunc.
func (p *TCPProxy) Proxy(w io.Writer, r io.ReadCloser, msg *proto.ControlMessage) {
	switch msg.Protocol {
	case proto.TCP, proto.TCP4, proto.TCP6:
		// ok
	default:
		p.logger.Log(
			"level", 0,
			"msg", "unsupported protocol",
			"ctrlMsg", msg,
		)
		return
	}

	target := p.localAddrFor(msg.ForwardedBy)
	if target == "" {
		p.logger.Log(
			"level", 1,
			"msg", "no target",
			"ctrlMsg", msg,
		)
		return
	}

	local, err := net.DialTimeout("tcp", target, DefaultTimeout)
	if err != nil {
		p.logger.Log(
			"level", 0,
			"msg", "dial failed",
			"target", target,
			"ctrlMsg", msg,
			"err", err,
		)
		return
	}

	done := make(chan struct{})
	go func() {
		transfer(flushWriter{w}, local, log.NewContext(p.logger).With(
			"dst", msg.ForwardedBy,
			"src", target,
		))
		close(done)
	}()

	transfer(local, r, log.NewContext(p.logger).With(
		"dst", target,
		"src", msg.ForwardedBy,
	))

	<-done
}

func (p *TCPProxy) localAddrFor(hostPort string) string {
	if p.localAddrMap == nil {
		return p.localAddr
	}

	// try hostPort
	if addr := p.localAddrMap[hostPort]; addr != "" {
		return addr
	}

	// try port
	host, port, _ := net.SplitHostPort(hostPort)
	if addr := p.localAddrMap[port]; addr != "" {
		return addr
	}

	// try 0.0.0.0:port
	if addr := p.localAddrMap[fmt.Sprintf("0.0.0.0:%s", port)]; addr != "" {
		return addr
	}

	// try host
	if addr := p.localAddrMap[host]; addr != "" {
		return addr
	}

	return p.localAddr
}
<|endoftext|>


<commit_before>package vmware

import (
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"time"
)

type stepPrepareOutputDir struct {
	dir OutputDir
}

func (s *stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*config)
	ui := state.Get("ui").(packer.Ui)

	dir := s.outputDir(state)
	dir.SetOutputDir(config.OutputDir)

	exists, err := dir.DirExists()
	if err != nil {
		state.Put("error", err)
		return multistep.ActionHalt
	}

	if exists {
		if config.PackerForce {
			ui.Say("Deleting previous output directory...")
			dir.RemoveAll()
		} else {
			state.Put("error", fmt.Errorf("Output directory '%s' already exists.", config.OutputDir))
			return multistep.ActionHalt
		}
	}

	if err := dir.MkdirAll(); err != nil {
		state.Put("error", err)
		return multistep.ActionHalt
	}

	s.dir = dir

	state.Put("dir", dir)

	return multistep.ActionContinue
}

func (s *stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
	_, cancelled := state.GetOk(multistep.StateCancelled)
	_, halted := state.GetOk(multistep.StateHalted)

	if cancelled || halted {
		ui := state.Get("ui").(packer.Ui)

		if s.dir != nil {
			ui.Say("Deleting output directory...")
			for i := 0; i < 5; i++ {
				err := s.dir.RemoveAll()
				if err == nil {
					break
				}

				log.Printf("Error removing output dir: %s", err)
				time.Sleep(2 * time.Second)
			}
		}
	}
}

func (s *stepPrepareOutputDir) outputDir(state multistep.StateBag) (dir OutputDir) {
	driver := state.Get("driver").(Driver)

	switch d := driver.(type) {
	case OutputDir:
		log.Printf("Using driver as the OutputDir implementation")
		dir = d
	default:
		log.Printf("Using localOutputDir implementation")
		dir = new(localOutputDir)
	}

	return
}
<commit_msg>builder/vmware: style<commit_after>package vmware

import (
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"time"
)

type stepPrepareOutputDir struct {
	dir OutputDir
}

func (s *stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
	config := state.Get("config").(*config)
	ui := state.Get("ui").(packer.Ui)

	dir := s.outputDir(state)
	dir.SetOutputDir(config.OutputDir)

	exists, err := dir.DirExists()
	if err != nil {
		state.Put("error", err)
		return multistep.ActionHalt
	}

	if exists {
		if config.PackerForce {
			ui.Say("Deleting previous output directory...")
			dir.RemoveAll()
		} else {
			state.Put("error", fmt.Errorf(
				"Output directory '%s' already exists.", config.OutputDir))
			return multistep.ActionHalt
		}
	}

	if err := dir.MkdirAll(); err != nil {
		state.Put("error", err)
		return multistep.ActionHalt
	}

	s.dir = dir
	state.Put("dir", dir)
	return multistep.ActionContinue
}

func (s *stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
	_, cancelled := state.GetOk(multistep.StateCancelled)
	_, halted := state.GetOk(multistep.StateHalted)

	if cancelled || halted {
		ui := state.Get("ui").(packer.Ui)

		if s.dir != nil {
			ui.Say("Deleting output directory...")
			for i := 0; i < 5; i++ {
				err := s.dir.RemoveAll()
				if err == nil {
					break
				}

				log.Printf("Error removing output dir: %s", err)
				time.Sleep(2 * time.Second)
			}
		}
	}
}

func (s *stepPrepareOutputDir) outputDir(state multistep.StateBag) (dir OutputDir) {
	driver := state.Get("driver").(Driver)

	switch d := driver.(type) {
	case OutputDir:
		log.Printf("Using driver as the OutputDir implementation")
		dir = d
	default:
		log.Printf("Using localOutputDir implementation")
		dir = new(localOutputDir)
	}

	return
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsEip() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsEipCreate,
		Read:   resourceAwsEipRead,
		Update: resourceAwsEipUpdate,
		Delete: resourceAwsEipDelete,

		Schema: map[string]*schema.Schema{
			"vpc": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},

			"instance": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"network_interface": &schema.Schema{
				Type:          schema.TypeString,
				Optional:      true,
				ConflictsWith: []string{"instance"},
			},

			"allocation_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"association_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"public_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"private_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error {
	ec2conn := meta.(*AWSClient).ec2conn

	// By default, we're not in a VPC
	domainOpt := ""
	if v := d.Get("vpc"); v != nil && v.(bool) {
		domainOpt = "vpc"
	}

	allocOpts := &ec2.AllocateAddressInput{
		Domain: aws.String(domainOpt),
	}

	log.Printf("[DEBUG] EIP create configuration: %#v", allocOpts)
	allocResp, err := ec2conn.AllocateAddress(allocOpts)
	if err != nil {
		return fmt.Errorf("Error creating EIP: %s", err)
	}

	// The domain tells us if we're in a VPC or not
	d.Set("domain", allocResp.Domain)

	// Assign the eips (unique) allocation id for use later
	// the EIP api has a conditional unique ID (really), so
	// if we're in a VPC we need to save the ID as such, otherwise
	// it defaults to using the public IP
	log.Printf("[DEBUG] EIP Allocate: %#v", allocResp)
	if d.Get("domain").(string) == "vpc" {
		d.SetId(*allocResp.AllocationID)
	} else {
		d.SetId(*allocResp.PublicIP)
	}

	log.Printf("[INFO] EIP ID: %s (domain: %v)", d.Id(), *allocResp.Domain)
	return resourceAwsEipUpdate(d, meta)
}

func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error {
	ec2conn := meta.(*AWSClient).ec2conn

	domain := resourceAwsEipDomain(d)
	id := d.Id()

	var assocIds []*string
	var publicIps []*string
	if domain == "vpc" {
		assocIds = []*string{aws.String(id)}
	} else {
		publicIps = []*string{aws.String(id)}
	}

	log.Printf(
		"[DEBUG] EIP describe configuration: %#v, %#v (domain: %s)",
		assocIds, publicIps, domain)

	req := &ec2.DescribeAddressesInput{}

	if domain == "vpc" {
		req.AllocationIDs = []*string{aws.String(id)}
	} else {
		req.PublicIPs = []*string{aws.String(id)}
	}

	describeAddresses, err := ec2conn.DescribeAddresses(req)
	if err != nil {
		if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidAllocationID.NotFound" {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving EIP: %s", err)
	}

	// Verify AWS returned our EIP
	if len(describeAddresses.Addresses) != 1 ||
		(domain == "vpc" && *describeAddresses.Addresses[0].AllocationID != id) ||
		*describeAddresses.Addresses[0].PublicIP != id {
		if err != nil {
			return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses)
		}
	}

	address := describeAddresses.Addresses[0]

	d.Set("association_id", address.AssociationID)
	d.Set("instance", address.InstanceID)
	d.Set("network_interface", address.NetworkInterfaceID)
	d.Set("private_ip", address.PrivateIPAddress)
	d.Set("public_ip", address.PublicIP)

	return nil
}

func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error {
	ec2conn := meta.(*AWSClient).ec2conn

	domain := resourceAwsEipDomain(d)

	// Associate to instance or interface if specified
	v_instance, ok_instance := d.GetOk("instance")
	v_interface, ok_interface := d.GetOk("network_interface")

	if ok_instance || ok_interface {
		instanceId := v_instance.(string)
		networkInterfaceId := v_interface.(string)

		assocOpts := &ec2.AssociateAddressInput{
			InstanceID: aws.String(instanceId),
			PublicIP:   aws.String(d.Id()),
		}

		// more unique ID conditionals
		if domain == "vpc" {
			assocOpts = &ec2.AssociateAddressInput{
				NetworkInterfaceID: aws.String(networkInterfaceId),
				InstanceID:         aws.String(instanceId),
				AllocationID:       aws.String(d.Id()),
			}
		}

		log.Printf("[DEBUG] EIP associate configuration: %#v (domain: %v)", assocOpts, domain)
		_, err := ec2conn.AssociateAddress(assocOpts)
		if err != nil {
			return fmt.Errorf("Failure associating EIP: %s", err)
		}
	}

	return resourceAwsEipRead(d, meta)
}

func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error {
	ec2conn := meta.(*AWSClient).ec2conn

	if err := resourceAwsEipRead(d, meta); err != nil {
		return err
	}
	if d.Id() == "" {
		// This might happen from the read
		return nil
	}

	// If we are attached to an instance or interface, detach first.
	if d.Get("instance").(string) != "" || d.Get("association_id").(string) != "" {
		log.Printf("[DEBUG] Disassociating EIP: %s", d.Id())
		var err error
		switch resourceAwsEipDomain(d) {
		case "vpc":
			_, err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressInput{
				AssociationID: aws.String(d.Get("association_id").(string)),
			})
		case "standard":
			_, err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressInput{
				PublicIP: aws.String(d.Get("public_ip").(string)),
			})
		}
		if err != nil {
			return err
		}
	}

	domain := resourceAwsEipDomain(d)
	return resource.Retry(3*time.Minute, func() error {
		var err error
		switch domain {
		case "vpc":
			log.Printf(
				"[DEBUG] EIP release (destroy) address allocation: %v",
				d.Id())
			_, err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressInput{
				AllocationID: aws.String(d.Id()),
			})
		case "standard":
			log.Printf("[DEBUG] EIP release (destroy) address: %v", d.Id())
			_, err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressInput{
				PublicIP: aws.String(d.Id()),
			})
		}

		if err == nil {
			return nil
		}
		if _, ok := err.(aws.APIError); !ok {
			return resource.RetryError{Err: err}
		}

		return err
	})
}

func resourceAwsEipDomain(d *schema.ResourceData) string {
	if v, ok := d.GetOk("domain"); ok {
		return v.(string)
	} else if strings.Contains(d.Id(), "eipalloc") {
		// We have to do this for backwards compatibility since TF 0.1
		// didn't have the "domain" computed attribute.
		return "vpc"
	}

	return "standard"
}
<commit_msg>Remove some domain checking duplication<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsEip() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsEipCreate,
		Read:   resourceAwsEipRead,
		Update: resourceAwsEipUpdate,
		Delete: resourceAwsEipDelete,

		Schema: map[string]*schema.Schema{
			"vpc": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},

			"instance": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"network_interface": &schema.Schema{
				Type:          schema.TypeString,
				Optional:      true,
				ConflictsWith: []string{"instance"},
			},

			"allocation_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"association_id": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"public_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"private_ip": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error {
	ec2conn := meta.(*AWSClient).ec2conn

	// By default, we're not in a VPC
	domainOpt := ""
	if v := d.Get("vpc"); v != nil && v.(bool) {
		domainOpt = "vpc"
	}

	allocOpts := &ec2.AllocateAddressInput{
		Domain: aws.String(domainOpt),
	}

	log.Printf("[DEBUG] EIP create configuration: %#v", allocOpts)
	allocResp, err := ec2conn.AllocateAddress(allocOpts)
	if err != nil {
		return fmt.Errorf("Error creating EIP: %s", err)
	}

	// The domain tells us if we're in a VPC or not
	d.Set("domain", allocResp.Domain)

	// Assign the eips (unique) allocation id for use later
	// the EIP api has a conditional unique ID (really), so
	// if we're in a VPC we need to save the ID as such, otherwise
	// it defaults to using the public IP
	log.Printf("[DEBUG] EIP Allocate: %#v", allocResp)
	if d.Get("domain").(string) == "vpc" {
		d.SetId(*allocResp.AllocationID)
	} else {
		d.SetId(*allocResp.PublicIP)
	}

	log.Printf("[INFO] EIP ID: %s (domain: %v)", d.Id(), *allocResp.Domain)
	return resourceAwsEipUpdate(d, meta)
}

func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error {
	ec2conn := meta.(*AWSClient).ec2conn

	domain := resourceAwsEipDomain(d)
	id := d.Id()

	req := &ec2.DescribeAddressesInput{}

	if domain == "vpc" {
		req.AllocationIDs = []*string{aws.String(id)}
	} else {
		req.PublicIPs = []*string{aws.String(id)}
	}

	log.Printf(
		"[DEBUG] EIP describe configuration: %#v (domain: %s)",
		req, domain)

	describeAddresses, err := ec2conn.DescribeAddresses(req)
	if err != nil {
		if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidAllocationID.NotFound" {
			log.Printf("[DEBUG] EIP describe configuration: %#v", req)
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving EIP: %s", err)
	}

	// Verify AWS returned our EIP
	if len(describeAddresses.Addresses) != 1 ||
		(domain == "vpc" && *describeAddresses.Addresses[0].AllocationID != id) ||
		*describeAddresses.Addresses[0].PublicIP != id {
		if err != nil {
			return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses)
		}
	}

	address := describeAddresses.Addresses[0]

	d.Set("association_id", address.AssociationID)
	d.Set("instance", address.InstanceID)
	d.Set("network_interface", address.NetworkInterfaceID)
	d.Set("private_ip", address.PrivateIPAddress)
	d.Set("public_ip", address.PublicIP)

	return nil
}

func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error {
	ec2conn := meta.(*AWSClient).ec2conn

	domain := resourceAwsEipDomain(d)

	// Associate to instance or interface if specified
	v_instance, ok_instance := d.GetOk("instance")
	v_interface, ok_interface := d.GetOk("network_interface")

	if ok_instance || ok_interface {
		instanceId := v_instance.(string)
		networkInterfaceId := v_interface.(string)

		assocOpts := &ec2.AssociateAddressInput{
			InstanceID: aws.String(instanceId),
			PublicIP:   aws.String(d.Id()),
		}

		// more unique ID conditionals
		if domain == "vpc" {
			assocOpts = &ec2.AssociateAddressInput{
				NetworkInterfaceID: aws.String(networkInterfaceId),
				InstanceID:         aws.String(instanceId),
				AllocationID:       aws.String(d.Id()),
			}
		}

		log.Printf("[DEBUG] EIP associate configuration: %#v (domain: %v)", assocOpts, domain)
		_, err := ec2conn.AssociateAddress(assocOpts)
		if err != nil {
			return fmt.Errorf("Failure associating EIP: %s", err)
		}
	}

	return resourceAwsEipRead(d, meta)
}

func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error {
	ec2conn := meta.(*AWSClient).ec2conn

	if err := resourceAwsEipRead(d, meta); err != nil {
		return err
	}
	if d.Id() == "" {
		// This might happen from the read
		return nil
	}

	// If we are attached to an instance or interface, detach first.
	if d.Get("instance").(string) != "" || d.Get("association_id").(string) != "" {
		log.Printf("[DEBUG] Disassociating EIP: %s", d.Id())
		var err error
		switch resourceAwsEipDomain(d) {
		case "vpc":
			_, err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressInput{
				AssociationID: aws.String(d.Get("association_id").(string)),
			})
		case "standard":
			_, err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressInput{
				PublicIP: aws.String(d.Get("public_ip").(string)),
			})
		}
		if err != nil {
			return err
		}
	}

	domain := resourceAwsEipDomain(d)
	return resource.Retry(3*time.Minute, func() error {
		var err error
		switch domain {
		case "vpc":
			log.Printf(
				"[DEBUG] EIP release (destroy) address allocation: %v",
				d.Id())
			_, err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressInput{
				AllocationID: aws.String(d.Id()),
			})
		case "standard":
			log.Printf("[DEBUG] EIP release (destroy) address: %v", d.Id())
			_, err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressInput{
				PublicIP: aws.String(d.Id()),
			})
		}

		if err == nil {
			return nil
		}
		if _, ok := err.(aws.APIError); !ok {
			return resource.RetryError{Err: err}
		}

		return err
	})
}

func resourceAwsEipDomain(d *schema.ResourceData) string {
	if v, ok := d.GetOk("domain"); ok {
		return v.(string)
	} else if strings.Contains(d.Id(), "eipalloc") {
		// We have to do this for backwards compatibility since TF 0.1
		// didn't have the "domain" computed attribute.
		return "vpc"
	}

	return "standard"
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"math/rand"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/go-telegram-bot-api/telegram-bot-api"
	bt "github.com/ikool-cn/gobeanstalk-connection-pool"
)

var (
	telegramBot *TelegramBot
)

// TelegramBot ...
type TelegramBot struct {
	Name          string
	SelfChatID    int64
	ChannelChatID int64
	ComicPath     string
	DeleteDelay   time.Duration
	Client        *tgbotapi.BotAPI
	Queue         *bt.Pool
	Tube          string
}

// NewTelegramBot ...
func NewTelegramBot(cfg *TelegramConfig, btdAddr string) (t *TelegramBot) {
	bot, err := tgbotapi.NewBotAPI(cfg.Token)
	if err != nil {
		logger.Panicf("tg bot init failed: %+v", err)
	}
	delay, err := time.ParseDuration(cfg.DeleteDelay)
	if err != nil {
		logger.Panicf("delete delay error: %+v", err)
	}

	t = &TelegramBot{
		Name:          bot.Self.UserName,
		SelfChatID:    cfg.SelfChatID,
		ChannelChatID: cfg.ChannelChatID,
		ComicPath:     cfg.ComicPath,
		DeleteDelay:   delay,
		Client:        bot,
		Tube:          "tg",
	}
	t.Queue = &bt.Pool{
		Dial: func() (*bt.Conn, error) {
			return bt.Dial(btdAddr)
		},
		MaxIdle:     10,
		MaxActive:   100,
		IdleTimeout: 60 * time.Second,
		MaxLifetime: 180 * time.Second,
		Wait:        true,
	}
	return
}

func (t *TelegramBot) putQueue(msg []byte) {
	conn, err := t.Queue.Get()
	if err != nil {
		logger.Errorf("%+v: %s", err, string(msg))
		return
	}
	conn.Use(t.Tube)
	_, err = conn.Put(msg, 1, t.DeleteDelay, time.Minute)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
}

func (t *TelegramBot) send(chat int64, msg string) (tgbotapi.Message, error) {
	logger.Debugf("[%d]%s", chat, msg)
	return t.Client.Send(tgbotapi.NewMessage(chat, msg))
}

func (t *TelegramBot) delMessage() {
	for {
		conn, err := t.Queue.Get()
		if err != nil {
			logger.Errorf("%+v", err)
			time.Sleep(3 * time.Second)
			continue
		}
		conn.Watch(t.Tube)
		job, err := conn.Reserve()
		if err != nil {
			logger.Warningf("%+v", err)
			time.Sleep(3 * time.Second)
			continue
		}
		msg := &tgbotapi.Message{}
		err = json.Unmarshal(job.Body, msg)
		if err != nil {
			logger.Errorf("%+v", err)
			err = conn.Bury(job.ID, 0)
			if err != nil {
				logger.Errorf("%+v", err)
			}
			time.Sleep(3 * time.Second)
			continue
		}
		delMsg := tgbotapi.DeleteMessageConfig{
			ChatID:    msg.Chat.ID,
			MessageID: msg.MessageID,
		}
		logger.Infof(":[%s]{%s}", getMsgTitle(msg), strconv.Quote(msg.Text))

		_, err = t.Client.DeleteMessage(delMsg)
		if err != nil {
			logger.Errorf("%+v", err)
			err = conn.Bury(job.ID, 0)
			if err != nil {
				logger.Errorf("%+v", err)
			}
			time.Sleep(3 * time.Second)
			continue
		}
		err = conn.Delete(job.ID)
		if err != nil {
			logger.Errorf("%+v", err)
			time.Sleep(3 * time.Second)
		}
		t.Queue.Release(conn, false)
	}
}

func (t *TelegramBot) tgBot() {
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60
	for {
		updates, err := t.Client.GetUpdatesChan(u)
		if err != nil {
			logger.Errorf("%+v", err)
			time.Sleep(3 * time.Second)
			continue
		}
		var message *tgbotapi.Message
		for update := range updates {
			if update.Message != nil {
				message = update.Message
			} else if update.EditedMessage != nil {
				message = update.EditedMessage
			} else if update.CallbackQuery != nil {
				logger.Infof(
					"recv:(%s)[%s]{%s}",
					update.CallbackQuery.ChatInstance,
					update.CallbackQuery.From.String(),
					update.CallbackQuery.Data,
				)
				_type := strings.SplitN(update.CallbackQuery.Data, ":", 1)[0]
				switch _type {
				case "comic":
				case "pic":
				default:
				}

				continue
			} else {
				continue
			}
			if message.Chat.IsGroup() {
				logger.Infof(
					"recv:(%s)[%s]{%s}",
					message.Chat.Title,
					message.From.String(),
					strconv.Quote(message.Text))
			} else {
				logger.Infof(
					"recv:[%s]{%s}",
					message.From.String(),
					strconv.Quote(message.Text),
				)
			}

			if message.IsCommand() {
				switch message.Command() {
				case "start":
					go onStart(t, message)
				case "comic":
					go onComic(t, message)
				case "pic":
					go onPic(t, message)
				default:
					logger.Infof("ignore unkown cmd: %+v", message.Command())
					continue
				}
			} else {
				if message.Text == "" {
					continue
				}
				checkRepeat(t, message)
			}
		}
		logger.Warning("tg bot restarted.")
		time.Sleep(3 * time.Second)
	}
}

func checkRepeat(t *TelegramBot, message *tgbotapi.Message) {
	key := "tg_" + getMsgTitle(message) + "_last"
	flattendMsg := strings.TrimSpace(message.Text)
	defer redisClient.LTrim(key, 0, 10)
	defer redisClient.LPush(key, flattendMsg)

	lastMsgs, err := redisClient.LRange(key, 0, 6).Result()
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	i := 0
	for _, s := range lastMsgs {
		if s == flattendMsg {
			i++
		}
	}
	if i > 1 {
		redisClient.Del(key)
		logger.Infof("repeat: %s", strconv.Quote(message.Text))
		msg := tgbotapi.NewMessage(message.Chat.ID, message.Text)
		t.Client.Send(msg)
	}
}

func onStart(t *TelegramBot, message *tgbotapi.Message) {
	msg := tgbotapi.NewMessage(message.Chat.ID, "呀呀呀")
	msg.ReplyToMessageID = message.MessageID
	t.Client.Send(msg)
}

func onComic(t *TelegramBot, message *tgbotapi.Message) {
	files, err := filepath.Glob(t.ComicPath)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	rand.Seed(time.Now().UnixNano())
	file := files[rand.Intn(len(files))]
	number := strings.Split(strings.Split(file, "@")[1], ".")[0]
	msg := tgbotapi.NewMessage(message.Chat.ID, "🔞 https://nhentai.net/g/"+number)

	msg.ReplyMarkup = buildInlineKeyboardMarkup("comic", number)

	logger.Infof("send:[%s]{%s}", getMsgTitle(message), strconv.Quote(file))
	msgSent, err := t.Client.Send(msg)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	data, err := json.Marshal(msgSent)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	t.putQueue(data)
}

func onPic(t *TelegramBot, message *tgbotapi.Message) {
	files, err := filepath.Glob(filepath.Join(twitterBot.ImgPath, "*"))
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	if files == nil {
		logger.Error("find no pics")
	}
	rand.Seed(time.Now().UnixNano())
	file := files[rand.Intn(len(files))]

	logger.Infof("send:[%s]{%s}", getMsgTitle(message), strconv.Quote(file))

	msg := tgbotapi.NewDocumentUpload(message.Chat.ID, file)
	msg.ReplyMarkup = buildInlineKeyboardMarkup("pic", filepath.Base(file))

	_, err = t.Client.Send(msg)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	// data, err := json.Marshal(msgSent)
	// if err != nil {
	// logger.Errorf("%+v", err)
	// return
	// }
	// t.putQueue(data)
}

func getMsgTitle(m *tgbotapi.Message) string {
	if m.Chat.IsGroup() {
		return m.Chat.Title
	}
	return m.From.String()
}

func buildInlineKeyboardMarkup(_type, id string) tgbotapi.InlineKeyboardMarkup {
	row := tgbotapi.NewInlineKeyboardRow(
		tgbotapi.NewInlineKeyboardButtonData("❤", _type+":"+id+":like"),
		tgbotapi.NewInlineKeyboardButtonData("💔", _type+":"+id+":diss"),
	)
	return tgbotapi.NewInlineKeyboardMarkup(row)
}
<commit_msg>record reaction to redis<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/go-telegram-bot-api/telegram-bot-api"
	bt "github.com/ikool-cn/gobeanstalk-connection-pool"
)

var (
	telegramBot *TelegramBot
)

// TelegramBot ...
type TelegramBot struct {
	Name          string
	SelfChatID    int64
	ChannelChatID int64
	ComicPath     string
	DeleteDelay   time.Duration
	Client        *tgbotapi.BotAPI
	Queue         *bt.Pool
	Tube          string
}

// NewTelegramBot ...
func NewTelegramBot(cfg *TelegramConfig, btdAddr string) (t *TelegramBot) {
	bot, err := tgbotapi.NewBotAPI(cfg.Token)
	if err != nil {
		logger.Panicf("tg bot init failed: %+v", err)
	}
	delay, err := time.ParseDuration(cfg.DeleteDelay)
	if err != nil {
		logger.Panicf("delete delay error: %+v", err)
	}

	t = &TelegramBot{
		Name:          bot.Self.UserName,
		SelfChatID:    cfg.SelfChatID,
		ChannelChatID: cfg.ChannelChatID,
		ComicPath:     cfg.ComicPath,
		DeleteDelay:   delay,
		Client:        bot,
		Tube:          "tg",
	}
	t.Queue = &bt.Pool{
		Dial: func() (*bt.Conn, error) {
			return bt.Dial(btdAddr)
		},
		MaxIdle:     10,
		MaxActive:   100,
		IdleTimeout: 60 * time.Second,
		MaxLifetime: 180 * time.Second,
		Wait:        true,
	}
	return
}

func (t *TelegramBot) putQueue(msg []byte) {
	conn, err := t.Queue.Get()
	if err != nil {
		logger.Errorf("%+v: %s", err, string(msg))
		return
	}
	conn.Use(t.Tube)
	_, err = conn.Put(msg, 1, t.DeleteDelay, time.Minute)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
}

func (t *TelegramBot) send(chat int64, msg string) (tgbotapi.Message, error) {
	logger.Debugf("[%d]%s", chat, msg)
	return t.Client.Send(tgbotapi.NewMessage(chat, msg))
}

func (t *TelegramBot) delMessage() {
	for {
		conn, err := t.Queue.Get()
		if err != nil {
			logger.Errorf("%+v", err)
			time.Sleep(3 * time.Second)
			continue
		}
		conn.Watch(t.Tube)
		job, err := conn.Reserve()
		if err != nil {
			logger.Warningf("%+v", err)
			time.Sleep(3 * time.Second)
			continue
		}
		msg := &tgbotapi.Message{}
		err = json.Unmarshal(job.Body, msg)
		if err != nil {
			logger.Errorf("%+v", err)
			err = conn.Bury(job.ID, 0)
			if err != nil {
				logger.Errorf("%+v", err)
			}
			time.Sleep(3 * time.Second)
			continue
		}
		delMsg := tgbotapi.DeleteMessageConfig{
			ChatID:    msg.Chat.ID,
			MessageID: msg.MessageID,
		}
		logger.Infof(":[%s]{%s}", getMsgTitle(msg), strconv.Quote(msg.Text))

		_, err = t.Client.DeleteMessage(delMsg)
		if err != nil {
			logger.Errorf("%+v", err)
			err = conn.Bury(job.ID, 0)
			if err != nil {
				logger.Errorf("%+v", err)
			}
			time.Sleep(3 * time.Second)
			continue
		}
		err = conn.Delete(job.ID)
		if err != nil {
			logger.Errorf("%+v", err)
			time.Sleep(3 * time.Second)
		}
		t.Queue.Release(conn, false)
	}
}

func (t *TelegramBot) tgBot() {
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60
	for {
		updates, err := t.Client.GetUpdatesChan(u)
		if err != nil {
			logger.Errorf("%+v", err)
			time.Sleep(3 * time.Second)
			continue
		}
		var message *tgbotapi.Message
		for update := range updates {
			if update.Message != nil {
				message = update.Message
			} else if update.EditedMessage != nil {
				message = update.EditedMessage
			} else if update.CallbackQuery != nil {
				logger.Infof(
					"recv:(%s)[%s]{%s}",
					update.CallbackQuery.ChatInstance,
					update.CallbackQuery.From.String(),
					update.CallbackQuery.Data,
				)
				data := strings.SplitN(update.CallbackQuery.Data, ":", 2)
				switch data[0] {
				case "comic", "pic":
					go onReaction(t, update.CallbackQuery)
				}
				continue
			} else {
				continue
			}
			if message.Chat.IsGroup() {
				logger.Infof(
					"recv:(%s)[%s]{%s}",
					message.Chat.Title,
					message.From.String(),
					strconv.Quote(message.Text))
			} else {
				logger.Infof(
					"recv:[%s]{%s}",
					message.From.String(),
					strconv.Quote(message.Text),
				)
			}

			if message.IsCommand() {
				switch message.Command() {
				case "start":
					go onStart(t, message)
				case "comic":
					go onComic(t, message)
				case "pic":
					go onPic(t, message)
				default:
					logger.Infof("ignore unkown cmd: %+v", message.Command())
					continue
				}
			} else {
				if message.Text == "" {
					continue
				}
				checkRepeat(t, message)
			}
		}
		logger.Warning("tg bot restarted.")
		time.Sleep(3 * time.Second)
	}
}

func checkRepeat(t *TelegramBot, message *tgbotapi.Message) {
	key := "tg_" + getMsgTitle(message) + "_last"
	flattendMsg := strings.TrimSpace(message.Text)
	defer redisClient.LTrim(key, 0, 10)
	defer redisClient.LPush(key, flattendMsg)

	lastMsgs, err := redisClient.LRange(key, 0, 6).Result()
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	i := 0
	for _, s := range lastMsgs {
		if s == flattendMsg {
			i++
		}
	}
	if i > 1 {
		redisClient.Del(key)
		logger.Infof("repeat: %s", strconv.Quote(message.Text))
		msg := tgbotapi.NewMessage(message.Chat.ID, message.Text)
		t.Client.Send(msg)
	}
}

func onStart(t *TelegramBot, message *tgbotapi.Message) {
	msg := tgbotapi.NewMessage(message.Chat.ID, "呀呀呀")
	msg.ReplyToMessageID = message.MessageID
	t.Client.Send(msg)
}

func onComic(t *TelegramBot, message *tgbotapi.Message) {
	files, err := filepath.Glob(t.ComicPath)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	rand.Seed(time.Now().UnixNano())
	file := files[rand.Intn(len(files))]
	number := strings.Split(strings.Split(file, "@")[1], ".")[0]
	msg := tgbotapi.NewMessage(message.Chat.ID, "🔞 https://nhentai.net/g/"+number)

	msg.ReplyMarkup = buildInlineKeyboardMarkup("comic", number)

	logger.Infof("send:[%s]{%s}", getMsgTitle(message), strconv.Quote(file))
	msgSent, err := t.Client.Send(msg)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	data, err := json.Marshal(msgSent)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	t.putQueue(data)
}

func onPic(t *TelegramBot, message *tgbotapi.Message) {
	files, err := filepath.Glob(filepath.Join(twitterBot.ImgPath, "*"))
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	if files == nil {
		logger.Error("find no pics")
	}
	rand.Seed(time.Now().UnixNano())
	file := files[rand.Intn(len(files))]

	logger.Infof("send:[%s]{%s}", getMsgTitle(message), strconv.Quote(file))

	msg := tgbotapi.NewDocumentUpload(message.Chat.ID, file)
	msg.ReplyMarkup = buildInlineKeyboardMarkup("pic", filepath.Base(file))

	_, err = t.Client.Send(msg)
	if err != nil {
		logger.Errorf("%+v", err)
	}
}

func onReaction(t *TelegramBot, callbackQuery *tgbotapi.CallbackQuery) {
	_type, _id, err := saveReaction(callbackQuery.Data, callbackQuery.From.ID)
	if err != nil {
		logger.Errorf("%+v", err)
		return
	}
	msg := tgbotapi.NewEditMessageReplyMarkup(
		callbackQuery.Message.Chat.ID,
		callbackQuery.Message.MessageID,
		buildInlineKeyboardMarkup(_type, _id),
	)
	_, err = t.Client.Send(msg)
	if err != nil {
		logger.Errorf("%+v", err)
	}
}

func getMsgTitle(m *tgbotapi.Message) string {
	if m.Chat.IsGroup() {
		return m.Chat.Title
	}
	return m.From.String()
}

func buildInlineKeyboardMarkup(_type, _id string) tgbotapi.InlineKeyboardMarkup {

	likeKey := _type + ":" + _id + ":like"
	dissKey := _type + ":" + _id + ":diss"

	likeCount, _ := redisClient.SCard("tg_reaction_" + likeKey).Result()
	dissCount, _ := redisClient.SCard("tg_reaction_" + dissKey).Result()

	likeText := "❤"
	if likeCount > 0 {
		likeText = likeText + " " + strconv.FormatInt(likeCount, 10)
	}
	dissText := "💔"
	if dissCount > 0 {
		dissText = dissText + " " + strconv.FormatInt(dissCount, 10)
	}

	row := tgbotapi.NewInlineKeyboardRow(
		tgbotapi.NewInlineKeyboardButtonData(likeText, likeKey),
		tgbotapi.NewInlineKeyboardButtonData(dissText, dissKey),
	)
	return tgbotapi.NewInlineKeyboardMarkup(row)
}

func saveReaction(key string, user int) (_type, _id string, err error) {
	token := strings.Split(key, ":")
	if len(token) != 3 {
		err = fmt.Errorf("react data error: %s", key)
		return
	}
	_type = token[0]
	_id = token[1]

	likeKey := "tg_reaction_" + _type + ":" + _id + ":" + ":like"
	dissKey := "tg_reaction_" + _type + ":" + _id + ":" + ":diss"

	pipe := redisClient.Pipeline()
	switch token[2] {
	case "like":
		pipe.SAdd(likeKey, strconv.Itoa(user))
		pipe.SRem(dissKey, strconv.Itoa(user))
		_, err = pipe.Exec()
	case "diss":
		pipe.SAdd(likeKey, strconv.Itoa(user))
		pipe.SRem(dissKey, strconv.Itoa(user))
		_, err = pipe.Exec()
	default:
		err = fmt.Errorf("react type error: %s", key)
	}
	return
}
<|endoftext|>


<commit_before>package liquid

import (
	"fmt"
	"regexp"
	"strings"
)

// Combines template.rb, document.rb and block_body.rb

var (
	fullTokenRegexp         = regexp.MustCompile(fmt.Sprintf(`(?m)\A%v\s*(\w+)\s*(.*)?%v\z`, tagStartRegexp, tagEndRegexp)) // om
	contentOfVariableRegexp = regexp.MustCompile(fmt.Sprintf(`(?m)\A%v(.*)%v\z`, variableStartRegexp, variableEndRegexp))   // om
	tokenIsBlankRegexp      = regexp.MustCompile(`\A\s*\z`)
)

const (
	tagStartToken = "{%"
	varStartToken = "{{"
)

// Template is a parsed liquid string containing a list
// of Nodes that can be used to render an output
type Template struct {
	Nodes []Node
}

// Node must be implemented by all parts of a template, and
// provides the necessary rendering handlers to allow generating
// a final output
type Node interface {
	Render(Vars) (string, error)
	Blank() bool
}

type stringNode string

func (n stringNode) Render(v Vars) (string, error) {
	return string(n), nil
}

func (n stringNode) Blank() bool {
	return n == ""
}

// Tag implements a parsing interface for generating liquid Nodes
type Tag interface {
	Parse(name, markup string, tokenizer *Tokenizer, ctx *parseContext) Node
}

// An example tag
type commentTag struct{}

func (t *commentTag) Parse(name, markup string, tokenizer *Tokenizer, ctx *parseContext) Node {

	subctx := &parseContext{
		line: ctx.line,
		end:  fmt.Sprintf("end%v", name),
	}

	nodelist, err := tokensToNodeList(tokenizer, subctx)
	if err != nil {
		panic(err)
	}

	ctx.line = subctx.line

	return blockNode{
		tag:   name,
		Nodes: nodelist,
	}
}

// RegisterTag registers a new tag (big surprise)
// and probably needs a mutex?
func RegisterTag(name string, tag Tag) {
	RegisteredTags[name] = tag
}

// RegisteredTags are all known tags
var RegisteredTags = map[string]Tag{
	"comment": &commentTag{},
}

type parseContext struct {
	line int
	end  string
}

func (c *parseContext) String() string {
	return fmt.Sprintf("Line: %v, End: %v", c.line, c.end)
}

func tokensToNodeList(tokenizer *Tokenizer, ctx *parseContext) ([]Node, error) {
	var nodeList []Node

	blank := true

	var token string
	var done error

	for done == nil {
		token, done = tokenizer.Next()

		if token == "" {
			continue
		}

		switch {
		case strings.HasPrefix(token, tagStartToken):
			if matched := fullTokenRegexp.FindStringSubmatch(token); len(matched) > 0 {
				markup, tagName := matched[0], matched[1]
				// Check for end tag
				if strings.HasPrefix(tagName, "end") {
					var err error
					if tagName != ctx.end {
						err = LiquidError(fmt.Sprintf("Unexpected end tag: %v, %v", tagName, markup), ctx)
					}
					return nodeList, err
				} else if tag, ok := RegisteredTags[tagName]; ok {
					newTag := tag.Parse(tagName, markup, tokenizer, ctx)
					blank = blank && newTag.Blank()
					nodeList = append(nodeList, newTag)
				} else if tagName == "else" || tagName == "end" {
					return nil, ErrSyntax("Unexpected outer 'else' tag")
				} else {
					return nil, ErrSyntax(fmt.Sprintf("Unknown tag '%v'", tagName))
				}
			} else {
				return nil, LiquidError(fmt.Sprintf("Missing tag terminator: %v", token), ctx)
			}

		case strings.HasPrefix(token, varStartToken):
			nodeList = append(nodeList, createVariable(token, ctx))
			blank = false
		default:
			nodeList = append(nodeList, stringNode(token))
			blank = blank && tokenIsBlankRegexp.MatchString(token)
		}
		ctx.line += strings.Count(token, "\n")
	}

	return nodeList, nil
}

// ParseTemplate performs the parsing step from Liquid::BlockBody.parse
func ParseTemplate(template string) (*Template, error) {

	// tokenize the source
	tokenizer := NewTokenizer(template)
	ctx := &parseContext{line: 0}
	nodeList, err := tokensToNodeList(tokenizer, ctx)

	return &Template{nodeList}, err
}

// Render the template with the supplied variables
func (t *Template) Render(vars Vars) (string, error) {
	if len(t.Nodes) == 0 || t.Nodes[0].Blank() {
		return "", nil
	}

	// Obviously we need to actually render the rest of the Nodes.
	return t.Nodes[0].Render(vars)
}

//     def render_node(node, context)
//       node_output = (node.respond_to?(:render) ? node.render(context) : node)
//       node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s

//       context.resource_limits.render_length += node_output.length
//       if context.resource_limits.reached?
//         raise MemoryError.new("Memory limits exceeded".freeze)
//       end
//       node_output
//     end

//     def create_variable(token, parse_context)
//       token.scan(ContentOfVariable) do |content|
//         markup = content.first
//         return Variable.new(markup, parse_context)
//       end
//       raise_missing_variable_terminator(token, parse_context)
//     end

func createVariable(token string, ctx *parseContext) Node {
	parsed := contentOfVariableRegexp.FindStringSubmatch(token)

	v, err := CreateVariable(parsed[1])
	if err != nil {
		panic(err)
	}
	return v
}

type blockNode struct {
	tag   string
	Nodes []Node
}

func (n blockNode) Render(v Vars) (string, error) {
	panic("unimplemented")
}

func (n blockNode) Blank() bool {
	return len(n.Nodes) == 0
}

//     def raise_missing_tag_terminator(token, parse_context)
//       raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_termination".freeze, token: token, tag_end: TagEnd.inspect))
//     end

//     def raise_missing_variable_terminator(token, parse_context)
//       raise SyntaxError.new(parse_context.locale.t("errors.syntax.variable_termination".freeze, token: token, tag_end: VariableEnd.inspect))
//     end

//     def registered_tags
//       Template.tags
//     end
//   end
// end
<commit_msg>no panic<commit_after>package liquid

import (
	"fmt"
	"regexp"
	"strings"
)

// Combines template.rb, document.rb and block_body.rb

var (
	fullTokenRegexp         = regexp.MustCompile(fmt.Sprintf(`(?m)\A%v\s*(\w+)\s*(.*)?%v\z`, tagStartRegexp, tagEndRegexp)) // om
	contentOfVariableRegexp = regexp.MustCompile(fmt.Sprintf(`(?m)\A%v(.*)%v\z`, variableStartRegexp, variableEndRegexp))   // om
	tokenIsBlankRegexp      = regexp.MustCompile(`\A\s*\z`)
)

const (
	tagStartToken = "{%"
	varStartToken = "{{"
)

// Template is a parsed liquid string containing a list
// of Nodes that can be used to render an output
type Template struct {
	Nodes []Node
}

// Node must be implemented by all parts of a template, and
// provides the necessary rendering handlers to allow generating
// a final output
type Node interface {
	Render(Vars) (string, error)
	Blank() bool
}

type stringNode string

func (n stringNode) Render(v Vars) (string, error) {
	return string(n), nil
}

func (n stringNode) Blank() bool {
	return n == ""
}

// Tag implements a parsing interface for generating liquid Nodes
type Tag interface {
	Parse(name, markup string, tokenizer *Tokenizer, ctx *parseContext) Node
}

// An example tag
type commentTag struct{}

func (t *commentTag) Parse(name, markup string, tokenizer *Tokenizer, ctx *parseContext) Node {

	subctx := &parseContext{
		line: ctx.line,
		end:  fmt.Sprintf("end%v", name),
	}

	nodelist, err := tokensToNodeList(tokenizer, subctx)
	if err != nil {
		panic(err)
	}

	ctx.line = subctx.line

	return blockNode{
		tag:   name,
		Nodes: nodelist,
	}
}

// RegisterTag registers a new tag (big surprise)
// and probably needs a mutex?
func RegisterTag(name string, tag Tag) {
	RegisteredTags[name] = tag
}

// RegisteredTags are all known tags
var RegisteredTags = map[string]Tag{
	"comment": &commentTag{},
}

type parseContext struct {
	line int
	end  string
}

func (c *parseContext) String() string {
	return fmt.Sprintf("Line: %v, End: %v", c.line, c.end)
}

func tokensToNodeList(tokenizer *Tokenizer, ctx *parseContext) ([]Node, error) {
	var nodeList []Node

	blank := true

	var token string
	var done error

	for done == nil {
		token, done = tokenizer.Next()

		if token == "" {
			continue
		}

		switch {
		case strings.HasPrefix(token, tagStartToken):
			if matched := fullTokenRegexp.FindStringSubmatch(token); len(matched) > 0 {
				markup, tagName := matched[0], matched[1]
				// Check for end tag
				if strings.HasPrefix(tagName, "end") {
					var err error
					if tagName != ctx.end {
						err = LiquidError(fmt.Sprintf("Unexpected end tag: %v, %v", tagName, markup), ctx)
					}
					return nodeList, err
				} else if tag, ok := RegisteredTags[tagName]; ok {
					newTag := tag.Parse(tagName, markup, tokenizer, ctx)
					blank = blank && newTag.Blank()
					nodeList = append(nodeList, newTag)
				} else if tagName == "else" || tagName == "end" {
					return nil, ErrSyntax("Unexpected outer 'else' tag")
				} else {
					return nil, ErrSyntax(fmt.Sprintf("Unknown tag '%v'", tagName))
				}
			} else {
				return nil, LiquidError(fmt.Sprintf("Missing tag terminator: %v", token), ctx)
			}

		case strings.HasPrefix(token, varStartToken):
			myVar, err := createVariable(token, ctx)
			if err != nil {
				return nil, err
			}
			nodeList = append(nodeList, myVar)
			blank = false
		default:
			nodeList = append(nodeList, stringNode(token))
			blank = blank && tokenIsBlankRegexp.MatchString(token)
		}
		ctx.line += strings.Count(token, "\n")
	}

	return nodeList, nil
}

// ParseTemplate performs the parsing step from Liquid::BlockBody.parse
func ParseTemplate(template string) (*Template, error) {

	// tokenize the source
	tokenizer := NewTokenizer(template)
	ctx := &parseContext{line: 0}
	nodeList, err := tokensToNodeList(tokenizer, ctx)

	return &Template{nodeList}, err
}

// Render the template with the supplied variables
func (t *Template) Render(vars Vars) (string, error) {
	if len(t.Nodes) == 0 || t.Nodes[0].Blank() {
		return "", nil
	}

	// Obviously we need to actually render the rest of the Nodes.
	return t.Nodes[0].Render(vars)
}

//     def render_node(node, context)
//       node_output = (node.respond_to?(:render) ? node.render(context) : node)
//       node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s

//       context.resource_limits.render_length += node_output.length
//       if context.resource_limits.reached?
//         raise MemoryError.new("Memory limits exceeded".freeze)
//       end
//       node_output
//     end

//     def create_variable(token, parse_context)
//       token.scan(ContentOfVariable) do |content|
//         markup = content.first
//         return Variable.new(markup, parse_context)
//       end
//       raise_missing_variable_terminator(token, parse_context)
//     end

func createVariable(token string, ctx *parseContext) (Node, error) {
	parsed := contentOfVariableRegexp.FindStringSubmatch(token)

	v, err := CreateVariable(parsed[1])
	if err != nil {
		return nil, err
	}
	return v, nil
}

type blockNode struct {
	tag   string
	Nodes []Node
}

func (n blockNode) Render(v Vars) (string, error) {
	panic("unimplemented")
}

func (n blockNode) Blank() bool {
	return len(n.Nodes) == 0
}

//     def raise_missing_tag_terminator(token, parse_context)
//       raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_termination".freeze, token: token, tag_end: TagEnd.inspect))
//     end

//     def raise_missing_variable_terminator(token, parse_context)
//       raise SyntaxError.new(parse_context.locale.t("errors.syntax.variable_termination".freeze, token: token, tag_end: VariableEnd.inspect))
//     end

//     def registered_tags
//       Template.tags
//     end
//   end
// end
<|endoftext|>


<commit_before>// Package icmp provides icmp enhanced methods based on the golang icmp package
package icmp

import (
	"errors"
	"fmt"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
	"log"
	"math/rand"
	"net"
	"os"
	"os/signal"
	"regexp"
	"sync"
	"syscall"
	"time"

	"github.com/mehrdadrad/mylg/cli"
)

// IANA ICMP
const (
	ProtocolIPv4ICMP = 1  // IANA ICMP for IPv4
	ProtocolIPv6ICMP = 58 // IANA ICMP for IPv6
)

// packet represents ping packet
type packet struct {
	bytes []byte
	addr  net.Addr
	err   error
}

// Ping represents ping request
type Ping struct {
	m         icmp.Message
	id        int
	seq       int
	pSize     int
	count     int
	addr      *net.IPAddr
	isV4Avail bool
	isV6Avail bool
	forceV4   bool
	forcev6   bool
	network   string
	source    string
	timeout   time.Duration
	interval  time.Duration
	MaxRTT    time.Duration
}

// Response represent ping response
type Response struct {
	RTT      float64
	Size     int
	Sequence int
	Addr     string
	Timeout  bool
	Error    error
}

// NewPing creates a new ping object
func NewPing(args string) (*Ping, error) {
	var (
		timeout  time.Duration
		interval time.Duration
		err      error
	)
	target, flag := cli.Flag(args)

	// show help
	if _, ok := flag["help"]; ok || len(target) < 3 {
		help()
		return nil, nil
	}
	// resolve host
	ips, err := net.LookupIP(target)
	if err != nil {
		return nil, err
	}
	// timeout
	timeoutStr := cli.SetFlag(flag, "t", "2s").(string)
	timeoutStr = NormalizeDuration(timeoutStr)
	if timeout, err = time.ParseDuration(timeoutStr); err != nil {
		return nil, fmt.Errorf("timeout options is not valid")
	}
	// interval
	intervalStr := cli.SetFlag(flag, "i", "1s").(string)
	intervalStr = NormalizeDuration(intervalStr)
	if interval, err = time.ParseDuration(intervalStr); err != nil {
		return nil, fmt.Errorf("interval options is not valid")
	}

	p := Ping{
		id:        rand.Intn(0xffff),
		seq:       -1,
		pSize:     64,
		count:     cli.SetFlag(flag, "c", 4).(int),
		timeout:   timeout,
		interval:  interval,
		isV4Avail: false,
		isV6Avail: false,
		network:   "ip",
		source:    "",
		MaxRTT:    time.Second,
	}

	if err := p.SetIP(ips); err != nil {
		return nil, err
	}

	return &p, nil
}

// Run loops the ping and print it out
func (p *Ping) Run() chan Response {
	var r = make(chan Response, 1)
	go func() {
		for n := 0; n < p.count; n++ {
			p.Ping(r)
			if n != p.count-1 {
				time.Sleep(p.interval)
			}
		}
		close(r)
	}()
	return r
}

// IsIPv4 returns true if ip version is v4
func IsIPv4(ip net.IP) bool {
	return len(ip.To4()) == net.IPv4len
}

// IsIPv6 returns true if ip version is v6
func IsIPv6(ip net.IP) bool {
	return len(ip) == net.IPv6len
}

func (p *Ping) parseMessage(m *packet) (*ipv4.Header, *icmp.Message, error) {
	var proto = ProtocolIPv4ICMP
	if p.isV6Avail {
		proto = ProtocolIPv6ICMP
	}
	msg, err := icmp.ParseMessage(proto, m.bytes)
	if err != nil {
		return nil, nil, err
	}
	bytes, _ := msg.Body.Marshal(msg.Type.Protocol())
	h, err := icmp.ParseIPv4Header(bytes)
	return h, msg, err
}

// SetIP set ip address
func (p *Ping) SetIP(ips []net.IP) error {
	for _, ip := range ips {
		p.addr = &net.IPAddr{IP: ip}
		if IsIPv4(ip) {
			p.addr = &net.IPAddr{IP: ip}
			p.isV4Avail = true
			return nil
		} else {
			p.addr = &net.IPAddr{IP: ip}
			p.isV6Avail = true
			return nil
		}
	}
	return fmt.Errorf("there is not  A or AAAA record")
}

// DelIP removes ip adrress
func (p *Ping) DelIP(ipAddr string) {
	//todo
}

// PacketSize set packet size
func (p *Ping) PacketSize(s int) {
	p.pSize = s
}

// listen starts to listen incoming icmp
func (p *Ping) listen(network string) *icmp.PacketConn {
	c, err := icmp.ListenPacket(network, p.source)
	if err != nil {
		log.Fatalf("listen err, %s", err)
	}
	return c
}

// recv reads icmp message
func (p *Ping) recv(conn *icmp.PacketConn, rcvdChan chan<- *packet) {
	var err error
	bytes := make([]byte, 1500)
	conn.SetReadDeadline(time.Now().Add(p.timeout))
	n, dest, err := conn.ReadFrom(bytes)
	if err != nil {
		if neterr, ok := err.(*net.OpError); ok {
			if neterr.Timeout() {
				err = errors.New("Request timeout")
			}
		}
	}

	bytes = bytes[:n]
	select {
	case rcvdChan <- &packet{bytes: bytes, addr: dest, err: err}:
	}
}

func (p *Ping) payload() []byte {
	timeBytes := make([]byte, 8)
	ts := time.Now().UnixNano()
	for i := uint8(0); i < 8; i++ {
		timeBytes[i] = byte((ts >> (i * 8)) & 0xff)
	}
	payload := make([]byte, p.pSize-16)
	payload = append(payload, timeBytes...)
	return payload
}
func (p *Ping) send(conn *icmp.PacketConn) {
	var (
		wg sync.WaitGroup
	)
	var icmpType icmp.Type
	if IsIPv4(p.addr.IP) {
		icmpType = ipv4.ICMPTypeEcho
	} else if IsIPv6(p.addr.IP) {
		icmpType = ipv6.ICMPTypeEchoRequest
	}

	p.seq++
	bytes, err := (&icmp.Message{
		Type: icmpType, Code: 0,
		Body: &icmp.Echo{
			ID:   p.id,
			Seq:  p.seq,
			Data: p.payload(),
		},
	}).Marshal(nil)
	if err != nil {
		println(err)
	}

	wg.Add(1)
	go func(conn *icmp.PacketConn, dest net.Addr, b []byte) {
		defer wg.Done()
		for {
			if _, err := conn.WriteTo(bytes, dest); err != nil {
				println(err)
				if neterr, ok := err.(*net.OpError); ok {
					if neterr.Err == syscall.ENOBUFS {
						continue
					}
				}
			}
			break
		}
	}(conn, p.addr, bytes)

	wg.Wait()
}

// Ping tries to send and receive a packet
func (p *Ping) Ping(out chan Response) {
	var (
		conn     *icmp.PacketConn
		rcvdChan chan *packet = make(chan *packet, 1)
	)

	if p.isV4Avail {
		if conn = p.listen("ip4:icmp"); conn == nil {
			return
		}
		defer conn.Close()
	}

	if p.isV6Avail {
		if conn = p.listen("ip6:ipv6-icmp"); conn == nil {
			return
		}
		defer conn.Close()
	}

	p.send(conn)
	p.recv(conn, rcvdChan)
	rm := <-rcvdChan

	if rm.err != nil {
		out <- Response{Error: rm.err}
		return
	}
	_, m, err := p.parseMessage(rm)
	if err != nil {
		out <- Response{Error: err}
		return
	}

	switch m.Body.(type) {
	case *icmp.TimeExceeded:
		out <- Response{Error: fmt.Errorf("time exceeded")}
	case *icmp.PacketTooBig:
		out <- Response{Error: fmt.Errorf("packet too big")}
	case *icmp.DstUnreach:
		out <- Response{Error: fmt.Errorf("destination unreachable")}
	case *icmp.Echo:
		rtt := float64(time.Now().UnixNano()-getTimeStamp(rm.bytes)) / 1000000
		out <- Response{
			Size:     len(rm.bytes),
			Addr:     rm.addr.String(),
			RTT:      rtt,
			Sequence: p.seq,
			Error:    nil,
		}
	default:
		out <- Response{Error: fmt.Errorf("ICMP error")}
	}
}

// PrintPretty prints out the result pretty format
func (p *Ping) PrintPretty() {
	var (
		loop  = true
		sigCh = make(chan os.Signal, 1)
		pFmt  = "%d bytes from %s icmp_seq=%d time=%f ms"
		resp  = p.Run()
	)

	// capture interrupt w/ s channel
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	for loop {
		select {
		case r, ok := <-resp:
			if !ok {
				loop = false
				break
			}
			if r.Error != nil {
				println(r.Error.Error())
				continue
			}
			msg := fmt.Sprintf(pFmt, r.Size, r.Addr, r.Sequence, r.RTT)
			println(msg)
		case <-sigCh:
			loop = false
		}
	}
}

// getTimeStamp
func getTimeStamp(m []byte) int64 {
	var ts int64
	for i := uint(0); i < 8; i++ {
		ts += int64(m[uint(len(m))-8+i]) << (i * 8)
	}
	return ts
}

// NormalizeDuration adds default unit (seconds) as needed
func NormalizeDuration(d string) string {
	if match, _ := regexp.MatchString(`^\d+\.{0,1}\d*$`, d); match {
		return d + "s"
	}
	return d
}

// help represents ping help
func help() {
	fmt.Println(`
    usage:
          ping IP address / domain name
    options:
          -c count       Send 'count' requests (default: 4)
          -t timeout     Specifiy a timeout in format "ms", "s", "m" (default: 2s)
          -i interval    Wait interval between sending each packet (default: 1s)
    Example:
          ping 8.8.8.8
          ping 8.8.8.8 -c 10
          ping mylg.io
	`)
}
<commit_msg>added force ipv4 or ipv6 options<commit_after>// Package icmp provides icmp enhanced methods based on the golang icmp package
package icmp

import (
	"errors"
	"fmt"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
	"log"
	"math/rand"
	"net"
	"os"
	"os/signal"
	"regexp"
	"sync"
	"syscall"
	"time"

	"github.com/mehrdadrad/mylg/cli"
)

// IANA ICMP
const (
	ProtocolIPv4ICMP = 1  // IANA ICMP for IPv4
	ProtocolIPv6ICMP = 58 // IANA ICMP for IPv6
)

// packet represents ping packet
type packet struct {
	bytes []byte
	addr  net.Addr
	err   error
}

// Ping represents ping request
type Ping struct {
	m         icmp.Message
	id        int
	seq       int
	pSize     int
	count     int
	addr      *net.IPAddr
	isV4Avail bool
	isV6Avail bool
	forceV4   bool
	forceV6   bool
	network   string
	source    string
	timeout   time.Duration
	interval  time.Duration
	MaxRTT    time.Duration
}

// Response represent ping response
type Response struct {
	RTT      float64
	Size     int
	Sequence int
	Addr     string
	Timeout  bool
	Error    error
}

// NewPing creates a new ping object
func NewPing(args string) (*Ping, error) {
	var (
		timeout  time.Duration
		interval time.Duration
		err      error
	)
	target, flag := cli.Flag(args)

	// show help
	if _, ok := flag["help"]; ok || len(target) < 3 {
		help()
		return nil, nil
	}
	// resolve host
	ips, err := net.LookupIP(target)
	if err != nil {
		return nil, err
	}
	// timeout
	timeoutStr := cli.SetFlag(flag, "t", "2s").(string)
	timeoutStr = NormalizeDuration(timeoutStr)
	if timeout, err = time.ParseDuration(timeoutStr); err != nil {
		return nil, fmt.Errorf("timeout options is not valid")
	}
	// interval
	intervalStr := cli.SetFlag(flag, "i", "1s").(string)
	intervalStr = NormalizeDuration(intervalStr)
	if interval, err = time.ParseDuration(intervalStr); err != nil {
		return nil, fmt.Errorf("interval options is not valid")
	}

	p := Ping{
		id:        rand.Intn(0xffff),
		seq:       -1,
		pSize:     64,
		timeout:   timeout,
		interval:  interval,
		isV4Avail: false,
		isV6Avail: false,
		count:     cli.SetFlag(flag, "c", 4).(int),
		forceV4:   cli.SetFlag(flag, "4", false).(bool),
		forceV6:   cli.SetFlag(flag, "6", false).(bool),
		network:   "ip",
		source:    "",
		MaxRTT:    time.Second,
	}

	if err := p.SetIP(ips); err != nil {
		return nil, err
	}

	return &p, nil
}

// Run loops the ping and print it out
func (p *Ping) Run() chan Response {
	var r = make(chan Response, 1)
	go func() {
		for n := 0; n < p.count; n++ {
			p.Ping(r)
			if n != p.count-1 {
				time.Sleep(p.interval)
			}
		}
		close(r)
	}()
	return r
}

// IsIPv4 returns true if ip version is v4
func IsIPv4(ip net.IP) bool {
	return len(ip.To4()) == net.IPv4len
}

// IsIPv6 returns true if ip version is v6
func IsIPv6(ip net.IP) bool {
	return len(ip) == net.IPv6len
}

func (p *Ping) parseMessage(m *packet) (*ipv4.Header, *icmp.Message, error) {
	var proto = ProtocolIPv4ICMP
	if p.isV6Avail {
		proto = ProtocolIPv6ICMP
	}
	msg, err := icmp.ParseMessage(proto, m.bytes)
	if err != nil {
		return nil, nil, err
	}
	bytes, _ := msg.Body.Marshal(msg.Type.Protocol())
	h, err := icmp.ParseIPv4Header(bytes)
	return h, msg, err
}

// SetIP set ip address
func (p *Ping) SetIP(ips []net.IP) error {
	for _, ip := range ips {
		p.addr = &net.IPAddr{IP: ip}
		if IsIPv4(ip) && !p.forceV6 {
			p.addr = &net.IPAddr{IP: ip}
			p.isV4Avail = true
			return nil
		} else if IsIPv6(ip) && !p.forceV4 {
			p.addr = &net.IPAddr{IP: ip}
			p.isV6Avail = true
			return nil
		}
	}
	return fmt.Errorf("there is not  A or AAAA record")
}

// DelIP removes ip adrress
func (p *Ping) DelIP(ipAddr string) {
	//todo
}

// PacketSize set packet size
func (p *Ping) PacketSize(s int) {
	p.pSize = s
}

// listen starts to listen incoming icmp
func (p *Ping) listen(network string) *icmp.PacketConn {
	c, err := icmp.ListenPacket(network, p.source)
	if err != nil {
		log.Fatalf("listen err, %s", err)
	}
	return c
}

// recv reads icmp message
func (p *Ping) recv(conn *icmp.PacketConn, rcvdChan chan<- *packet) {
	var err error
	bytes := make([]byte, 1500)
	conn.SetReadDeadline(time.Now().Add(p.timeout))
	n, dest, err := conn.ReadFrom(bytes)
	if err != nil {
		if neterr, ok := err.(*net.OpError); ok {
			if neterr.Timeout() {
				err = errors.New("Request timeout")
			}
		}
	}

	bytes = bytes[:n]
	select {
	case rcvdChan <- &packet{bytes: bytes, addr: dest, err: err}:
	}
}

func (p *Ping) payload() []byte {
	timeBytes := make([]byte, 8)
	ts := time.Now().UnixNano()
	for i := uint8(0); i < 8; i++ {
		timeBytes[i] = byte((ts >> (i * 8)) & 0xff)
	}
	payload := make([]byte, p.pSize-16)
	payload = append(payload, timeBytes...)
	return payload
}
func (p *Ping) send(conn *icmp.PacketConn) {
	var (
		wg sync.WaitGroup
	)
	var icmpType icmp.Type
	if IsIPv4(p.addr.IP) {
		icmpType = ipv4.ICMPTypeEcho
	} else if IsIPv6(p.addr.IP) {
		icmpType = ipv6.ICMPTypeEchoRequest
	}

	p.seq++
	bytes, err := (&icmp.Message{
		Type: icmpType, Code: 0,
		Body: &icmp.Echo{
			ID:   p.id,
			Seq:  p.seq,
			Data: p.payload(),
		},
	}).Marshal(nil)
	if err != nil {
		println(err)
	}

	wg.Add(1)
	go func(conn *icmp.PacketConn, dest net.Addr, b []byte) {
		defer wg.Done()
		for {
			if _, err := conn.WriteTo(bytes, dest); err != nil {
				println(err)
				if neterr, ok := err.(*net.OpError); ok {
					if neterr.Err == syscall.ENOBUFS {
						continue
					}
				}
			}
			break
		}
	}(conn, p.addr, bytes)

	wg.Wait()
}

// Ping tries to send and receive a packet
func (p *Ping) Ping(out chan Response) {
	var (
		conn     *icmp.PacketConn
		rcvdChan chan *packet = make(chan *packet, 1)
	)

	if p.isV4Avail {
		if conn = p.listen("ip4:icmp"); conn == nil {
			return
		}
		defer conn.Close()
	}

	if p.isV6Avail {
		if conn = p.listen("ip6:ipv6-icmp"); conn == nil {
			return
		}
		defer conn.Close()
	}

	p.send(conn)
	p.recv(conn, rcvdChan)
	rm := <-rcvdChan

	if rm.err != nil {
		out <- Response{Error: rm.err}
		return
	}
	_, m, err := p.parseMessage(rm)
	if err != nil {
		out <- Response{Error: err}
		return
	}

	switch m.Body.(type) {
	case *icmp.TimeExceeded:
		out <- Response{Error: fmt.Errorf("time exceeded")}
	case *icmp.PacketTooBig:
		out <- Response{Error: fmt.Errorf("packet too big")}
	case *icmp.DstUnreach:
		out <- Response{Error: fmt.Errorf("destination unreachable")}
	case *icmp.Echo:
		rtt := float64(time.Now().UnixNano()-getTimeStamp(rm.bytes)) / 1000000
		out <- Response{
			Size:     len(rm.bytes),
			Addr:     rm.addr.String(),
			RTT:      rtt,
			Sequence: p.seq,
			Error:    nil,
		}
	default:
		out <- Response{Error: fmt.Errorf("ICMP error")}
	}
}

// PrintPretty prints out the result pretty format
func (p *Ping) PrintPretty() {
	var (
		loop  = true
		sigCh = make(chan os.Signal, 1)
		pFmt  = "%d bytes from %s icmp_seq=%d time=%f ms"
		resp  = p.Run()
	)

	// capture interrupt w/ s channel
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	for loop {
		select {
		case r, ok := <-resp:
			if !ok {
				loop = false
				break
			}
			if r.Error != nil {
				println(r.Error.Error())
				continue
			}
			msg := fmt.Sprintf(pFmt, r.Size, r.Addr, r.Sequence, r.RTT)
			println(msg)
		case <-sigCh:
			loop = false
		}
	}
}

// getTimeStamp
func getTimeStamp(m []byte) int64 {
	var ts int64
	for i := uint(0); i < 8; i++ {
		ts += int64(m[uint(len(m))-8+i]) << (i * 8)
	}
	return ts
}

// NormalizeDuration adds default unit (seconds) as needed
func NormalizeDuration(d string) string {
	if match, _ := regexp.MatchString(`^\d+\.{0,1}\d*$`, d); match {
		return d + "s"
	}
	return d
}

// help represents ping help
func help() {
	fmt.Println(`
    usage:
          ping IP address / domain name
    options:
          -c count       Send 'count' requests (default: 4)
          -t timeout     Specifiy a timeout in format "ms", "s", "m" (default: 2s)
          -i interval    Wait interval between sending each packet (default: 1s)
          -4             Forces the ping command to use IPv4 (target should be hostname)
          -6             Forces the ping command to use IPv6 (target should be hostname)
    Example:
          ping 8.8.8.8
          ping 8.8.8.8 -c 10
          ping google.com -6
          ping mylg.io -i 5s
	`)
}
<|endoftext|>


<commit_before>package main

import "github.com/nicholasjackson/building-microservices-in-go/chapter2/rpc/server"

//curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "method": "HelloWorldHandler.HelloWorld", "params": [{"name":"World"}]}' http://localhost:1234
func main() {
	server.StartServer()
}
<commit_msg>update instruction<commit_after>package main

import "github.com/nicholasjackson/building-microservices-in-go/chapter2/rpc/server"

// To execute a request with this server run the below command on your command line
// curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "method": "HelloWorldHandler.HelloWorld", "params": [{"name":"World"}]}' http://localhost:1234
func main() {
	server.StartServer()
}
<|endoftext|>


<commit_before>package api

import (
	"context"

	"github.com/bytom/errors"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
)

func (a *API) getWork() Response {
	work, err := a.GetWork()
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(work)
}

func (a *API) getWorkJSON() Response {
	work, err := a.GetWorkJSON()
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(work)
}

// SubmitWorkReq used to submitWork req
type SubmitWorkReq struct {
	BlockHeader *types.BlockHeader `json:"block_header"`
}

func (a *API) submitWork(ctx context.Context, req *SubmitWorkReq) Response {
	if err := a.SubmitWork(req.BlockHeader); err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(true)
}

func (a *API) submitWorkJSON(ctx context.Context, req *SubmitWorkReq) Response {
	if err := a.SubmitWork(req.BlockHeader); err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(true)
}

// GetWorkResp is resp struct for API
type GetWorkResp struct {
	BlockHeader *types.BlockHeader `json:"block_header"`
	Seed        *bc.Hash           `json:"seed"`
}

// GetWork get work
func (a *API) GetWork() (*GetWorkResp, error) {
	bh, err := a.miningPool.GetWork()
	if err != nil {
		return nil, err
	}

	seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash)
	if err != nil {
		return nil, err
	}

	return &GetWorkResp{
		BlockHeader: bh,
		Seed:        seed,
	}, nil
}

type BlockHeaderJSON struct {
	Version           uint64 				`json:"version"`  // The version of the block.
	Height            uint64 				`json:"height"`  // The height of the block.
	PreviousBlockHash bc.Hash 				`json:"previous_block_hash"` // The hash of the previous block.
	Timestamp         uint64  				`json:"timestamp"` // The time of the block in seconds.
	Nonce             uint64 				`json:"nonce"` // Nonce used to generate the block.
	Bits              uint64 				`json:"bits"` // Difficulty target for the block.
	BlockCommitment   *types.BlockCommitment `json:"block_commitment"` //Block commitment
}

// GetWorkResp is resp struct for API get-work-json
type GetWorkJSONResp struct {
	BlockHeader *BlockHeaderJSON 	`json:"block_header"`
	Seed        *bc.Hash           	`json:"seed"`
}

// GetWorkJSON get work in json
func (a *API) GetWorkJSON() (*GetWorkJSONResp, error) {
	bh, err := a.miningPool.GetWork()
	if err != nil {
		return nil, err
	}

	seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash)
	if err != nil {
		return nil, err
	}

	blockCommitment := &types.BlockCommitment{
							TransactionsMerkleRoot: bh.BlockCommitment.TransactionsMerkleRoot,
							TransactionStatusHash:  bh.BlockCommitment.TransactionStatusHash,
						}

	return &GetWorkJSONResp{
		BlockHeader: &BlockHeaderJSON{
			Version:			bh.Version,
			Height:				bh.Height,
			PreviousBlockHash:	bh.PreviousBlockHash,
			Timestamp:         	bh.Timestamp,
			Nonce:             	bh.Nonce,
			Bits:              	bh.Bits,
			BlockCommitment:	blockCommitment,
		},
		Seed:        seed,
	}, nil
}

// SubmitWork submit work
func (a *API) SubmitWork(bh *types.BlockHeader) error {
	return a.miningPool.SubmitWork(bh)
}

func (a *API) setMining(in struct {
	IsMining bool `json:"is_mining"`
}) Response {
	if in.IsMining {
		return a.startMining()
	}
	return a.stopMining()
}

func (a *API) startMining() Response {
	a.cpuMiner.Start()
	if !a.IsMining() {
		return NewErrorResponse(errors.New("Failed to start mining"))
	}
	return NewSuccessResponse("")
}

func (a *API) stopMining() Response {
	a.cpuMiner.Stop()
	if a.IsMining() {
		return NewErrorResponse(errors.New("Failed to stop mining"))
	}
	return NewSuccessResponse("")
}
<commit_msg>submit-work-json works<commit_after>package api

import (
	"context"

	"github.com/bytom/errors"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
)

type BlockHeaderJSON struct {
	Version           uint64 					`json:"version"`  // The version of the block.
	Height            uint64 					`json:"height"`  // The height of the block.
	PreviousBlockHash bc.Hash 					`json:"previous_block_hash"` // The hash of the previous block.
	Timestamp         uint64  					`json:"timestamp"` // The time of the block in seconds.
	Nonce             uint64 					`json:"nonce"` // Nonce used to generate the block.
	Bits              uint64 					`json:"bits"` // Difficulty target for the block.
	BlockCommitment   *types.BlockCommitment 	`json:"block_commitment"` //Block commitment
}

func (a *API) getWork() Response {
	work, err := a.GetWork()
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(work)
}

func (a *API) getWorkJSON() Response {
	work, err := a.GetWorkJSON()
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(work)
}

// SubmitWorkReq used to submitWork req
type SubmitWorkReq struct {
	BlockHeader *types.BlockHeader `json:"block_header"`
}

func (a *API) submitWork(ctx context.Context, req *SubmitWorkReq) Response {
	if err := a.SubmitWork(req.BlockHeader); err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(true)
}

// SubmitWorkJSONReq used to submitWork req
type SubmitWorkJSONReq struct {
	BlockHeader *BlockHeaderJSON 	`json:"block_header"`
}

func (a *API) submitWorkJSON(ctx context.Context, req *SubmitWorkJSONReq) Response {
	blockCommitment := types.BlockCommitment{
							TransactionsMerkleRoot: req.BlockHeader.BlockCommitment.TransactionsMerkleRoot,
							TransactionStatusHash:  req.BlockHeader.BlockCommitment.TransactionStatusHash,
						}

	bh := &types.BlockHeader{
			Version:			req.BlockHeader.Version,
			Height:				req.BlockHeader.Height,
			PreviousBlockHash:	req.BlockHeader.PreviousBlockHash,
			Timestamp:         	req.BlockHeader.Timestamp,
			Nonce:             	req.BlockHeader.Nonce,
			Bits:              	req.BlockHeader.Bits,
			BlockCommitment:	blockCommitment,
		}

	if err := a.SubmitWork(bh); err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(true)
}

// GetWorkResp is resp struct for API
type GetWorkResp struct {
	BlockHeader *types.BlockHeader `json:"block_header"`
	Seed        *bc.Hash           `json:"seed"`
}

// GetWork get work
func (a *API) GetWork() (*GetWorkResp, error) {
	bh, err := a.miningPool.GetWork()
	if err != nil {
		return nil, err
	}

	seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash)
	if err != nil {
		return nil, err
	}

	return &GetWorkResp{
		BlockHeader: bh,
		Seed:        seed,
	}, nil
}

// GetWorkJSONResp is resp struct for API get-work-json
type GetWorkJSONResp struct {
	BlockHeader *BlockHeaderJSON 	`json:"block_header"`
	Seed        *bc.Hash           	`json:"seed"`
}

// GetWorkJSON get work in json
func (a *API) GetWorkJSON() (*GetWorkJSONResp, error) {
	bh, err := a.miningPool.GetWork()
	if err != nil {
		return nil, err
	}

	seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash)
	if err != nil {
		return nil, err
	}

	blockCommitment := &types.BlockCommitment{
							TransactionsMerkleRoot: bh.BlockCommitment.TransactionsMerkleRoot,
							TransactionStatusHash:  bh.BlockCommitment.TransactionStatusHash,
						}

	return &GetWorkJSONResp{
		BlockHeader: &BlockHeaderJSON{
			Version:			bh.Version,
			Height:				bh.Height,
			PreviousBlockHash:	bh.PreviousBlockHash,
			Timestamp:         	bh.Timestamp,
			Nonce:             	bh.Nonce,
			Bits:              	bh.Bits,
			BlockCommitment:	blockCommitment,
		},
		Seed:        seed,
	}, nil
}

// SubmitWork submit work to the miningpool
func (a *API) SubmitWork(bh *types.BlockHeader) error {
	return a.miningPool.SubmitWork(bh)
}

func (a *API) setMining(in struct {
	IsMining bool `json:"is_mining"`
}) Response {
	if in.IsMining {
		return a.startMining()
	}
	return a.stopMining()
}

func (a *API) startMining() Response {
	a.cpuMiner.Start()
	if !a.IsMining() {
		return NewErrorResponse(errors.New("Failed to start mining"))
	}
	return NewSuccessResponse("")
}

func (a *API) stopMining() Response {
	a.cpuMiner.Stop()
	if a.IsMining() {
		return NewErrorResponse(errors.New("Failed to stop mining"))
	}
	return NewSuccessResponse("")
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"

	"github.com/ClusterHQ/dvol/pkg/datalayer"
	"github.com/spf13/cobra"
)

var basePath string
var echoTimes int
var disableDockerIntegration bool
var forceRemoveVolume bool

const DEFAULT_BRANCH string = "master"

var RootCmd = &cobra.Command{
	Use:   "dvol",
	Short: "dvol is a version control system for your development data in Docker",
	Long: `dvol
====
dvol lets you commit, reset and branch the containerized databases
running on your laptop so you can easily save a particular state
and come back to it later.`,
}

var cmdInit = &cobra.Command{
	Use:   "init",
	Short: "Create a volume and its default master branch, then switch to it",
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			fmt.Println("Please specify a volume name.")
			os.Exit(1)
		}
		volumeName := args[0]
		if !datalayer.ValidVolumeName(volumeName) {
			fmt.Println("Error: " + volumeName + " is not a valid name")
			os.Exit(1)
		}
		if datalayer.VolumeExists(basePath, volumeName) {
			fmt.Println("Error: volume " + volumeName + " already exists")
			os.Exit(1)
		}
		err := datalayer.CreateVolume(basePath, volumeName)
		if err != nil {
			fmt.Println("Error creating volume")
			os.Exit(1)
		}
		fmt.Println("Created volume", volumeName)

		err = datalayer.CreateVariant(basePath, volumeName, "master")
		if err != nil {
			fmt.Println("Error creating branch")
			os.Exit(1)
		}
		fmt.Println("Created branch " + volumeName + "/master")
	},
}

var cmdRm = &cobra.Command{
	// TODO: Improve the usage string to include a volume name to remove
	Use:   "rm",
	Short: "Destroy a dvol volume",
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			fmt.Println("Please specify a volume name.")
			os.Exit(1)
		}
		volumeName := args[0]
		if !datalayer.ValidVolumeName(volumeName) {
			fmt.Println("Error: " + volumeName + " is not a valid name")
			os.Exit(1)
		}
		if !datalayer.VolumeExists(basePath, volumeName) {
			msg := fmt.Sprintf("Volume '%s' does not exist, cannot remove it", volumeName)
			fmt.Println(msg)
			os.Exit(1)
		}
		err := datalayer.RemoveVolume(basePath, volumeName)
		if err != nil {
			fmt.Println("Error removing volume")
			os.Exit(1)
		}
		s := fmt.Sprintf("Deleting volume '%s'", volumeName)
		fmt.Println(s)
	},
}

func init() {
	// cobra.OnInitialize(initConfig)
	// TODO support: dvol -p <custom_path> init <volume_name>
	RootCmd.AddCommand(cmdInit)
	RootCmd.AddCommand(cmdRm)
	cmdRm.Flags().BoolVarP(&forceRemoveVolume, "force", "f", false, "Force remove")

	RootCmd.PersistentFlags().StringVarP(&basePath, "path", "p", "/var/lib/dvol/volumes",
		"The name of the directory to use")
	RootCmd.PersistentFlags().BoolVar(&disableDockerIntegration,
		"disable-docker-integration", false, "Do not attempt to list/stop/start"+
			" docker containers which are using dvol volumes")
}
<commit_msg>Add prompting to check if user is sure about deleting a volume<commit_after>package cmd

import (
	"fmt"
	"os"
	"strings"

	"github.com/ClusterHQ/dvol/pkg/datalayer"
	"github.com/spf13/cobra"
)

var basePath string
var echoTimes int
var disableDockerIntegration bool
var forceRemoveVolume bool

const DEFAULT_BRANCH string = "master"

var RootCmd = &cobra.Command{
	Use:   "dvol",
	Short: "dvol is a version control system for your development data in Docker",
	Long: `dvol
====
dvol lets you commit, reset and branch the containerized databases
running on your laptop so you can easily save a particular state
and come back to it later.`,
}

var cmdInit = &cobra.Command{
	Use:   "init",
	Short: "Create a volume and its default master branch, then switch to it",
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			fmt.Println("Please specify a volume name.")
			os.Exit(1)
		}
		volumeName := args[0]
		if !datalayer.ValidVolumeName(volumeName) {
			fmt.Println("Error: " + volumeName + " is not a valid name")
			os.Exit(1)
		}
		if datalayer.VolumeExists(basePath, volumeName) {
			fmt.Println("Error: volume " + volumeName + " already exists")
			os.Exit(1)
		}
		err := datalayer.CreateVolume(basePath, volumeName)
		if err != nil {
			fmt.Println("Error creating volume")
			os.Exit(1)
		}
		fmt.Println("Created volume", volumeName)

		err = datalayer.CreateVariant(basePath, volumeName, "master")
		if err != nil {
			fmt.Println("Error creating branch")
			os.Exit(1)
		}
		fmt.Println("Created branch " + volumeName + "/master")
	},
}

var cmdRm = &cobra.Command{
	// TODO: Improve the usage string to include a volume name to remove
	Use:   "rm",
	Short: "Destroy a dvol volume",
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			fmt.Println("Please specify a volume name.")
			os.Exit(1)
		}
		volumeName := args[0]
		if !datalayer.ValidVolumeName(volumeName) {
			fmt.Println("Error: " + volumeName + " is not a valid name")
			os.Exit(1)
		}
		if !datalayer.VolumeExists(basePath, volumeName) {
			msg := fmt.Sprintf("Volume '%s' does not exist, cannot remove it", volumeName)
			fmt.Println(msg)
			os.Exit(1)
		}
		if forceRemoveVolume || userIsSure("This will remove all containers using the volume") {
			s := fmt.Sprintf("Deleting volume '%s'", volumeName)
			fmt.Println(s)
			err := datalayer.RemoveVolume(basePath, volumeName)
			if err != nil {
				fmt.Println("Error removing volume")
				os.Exit(1)
			}
		} else {
			fmt.Println("Aborting.")
		}
	},
}

func userIsSure(extraMessage string) bool {
	message := fmt.Sprintf("Are you sure? %s (y/n): ", extraMessage)
	fmt.Print(message)
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		fmt.Println("Error reading response.")
		return false
	}
	response = strings.ToLower(response)
	if response == "y" || response == "yes" {
		return true
	}
	return false
}

func init() {
	// cobra.OnInitialize(initConfig)
	// TODO support: dvol -p <custom_path> init <volume_name>
	RootCmd.AddCommand(cmdInit)
	RootCmd.AddCommand(cmdRm)
	cmdRm.Flags().BoolVarP(&forceRemoveVolume, "force", "f", false, "Force remove")

	RootCmd.PersistentFlags().StringVarP(&basePath, "path", "p", "/var/lib/dvol/volumes",
		"The name of the directory to use")
	RootCmd.PersistentFlags().BoolVar(&disableDockerIntegration,
		"disable-docker-integration", false, "Do not attempt to list/stop/start"+
			" docker containers which are using dvol volumes")
}
<|endoftext|>


<commit_before>// +build ignore
package view

import (
	"bytes"
	"encoding/gob"
)

func (v *View) GobEncode() ([]byte, error) {
	buf := new(bytes.Buffer)
	encoder := gob.NewEncoder(buf)
	err := encoder.Encode(&v.Entries)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (v *View) GobDecode(b []byte) error {
	buf := bytes.NewReader(b)
	decoder := gob.NewDecoder(buf)
	err := decoder.Decode(&v.Entries)
	if err != nil {
		return err
	}

	v.Members = make(map[Process]bool)

	for u, _ := range v.Entries {
		switch u.Type {
		case Join:
			if !v.Entries[Update{Leave, u.Process}] {
				v.Members[u.Process] = true
			}
		case Leave:
			delete(v.Members, u.Process)
		}
	}

	return nil
}
<commit_msg>Remove view/gob.go<commit_after><|endoftext|>


<commit_before>package tasx

import (
	"context"
	"log"
	"os"
	"sync"
	"sync/atomic"
)

var (
	Logger = log.New(os.Stderr, "", log.Ldate|log.Ltime)
)

type Manager struct {
	wg       sync.WaitGroup
	workerN  int
	runningN int32
	fetcher  TaskFetcher
	waitCh   chan struct{}
}

func (m Manager) WorkerState() (workerN, runningN int) {
	return m.workerN, int(m.runningN)
}

func (m *Manager) Wait() {
	<-m.waitCh
	m.wg.Wait()
}

func (m *Manager) Run(ctx context.Context) {
	for i := 0; i < m.workerN; i++ {
		m.wg.Add(1)
		go func() {
			defer m.wg.Done()

			for {
				select {
				case <-ctx.Done():
					return
				default:
					task, err := m.fetcher.FetchTask(ctx)
					if err != nil {
						Logger.Println(err)
						continue
					}
					atomic.AddInt32(&m.runningN, 1)

					func() {
						defer func() {
							atomic.AddInt32(&m.runningN, -1)
							if err := recover(); err != nil {
								Logger.Println(err)
							}
						}()
						if err := task.Run(ctx); err != nil {
							Logger.Println(err)
						}
					}()
				}
			}
		}()
	}

	close(m.waitCh)
	m.wg.Wait()
}

func NewManager(workerN int, fetcher TaskFetcher) *Manager {
	return &Manager{
		workerN:  workerN,
		fetcher:  fetcher,
		runningN: 0,
		waitCh:   make(chan struct{}),
	}
}
<commit_msg>add logger field<commit_after>package tasx

import (
	"context"
	"log"
	"os"
	"sync"
	"sync/atomic"
)

type Manager struct {
	wg       sync.WaitGroup
	workerN  int
	runningN int32
	fetcher  TaskFetcher
	waitCh   chan struct{}
	logger   *log.Logger
}

func (m Manager) WorkerState() (workerN, runningN int) {
	return m.workerN, int(m.runningN)
}

func (m *Manager) Wait() {
	<-m.waitCh
	m.wg.Wait()
}

func (m *Manager) SetLogger(l *log.Logger) {
	m.logger = l
}

func (m *Manager) Run(ctx context.Context) {
	for i := 0; i < m.workerN; i++ {
		m.wg.Add(1)
		go func() {
			defer m.wg.Done()

			for {
				select {
				case <-ctx.Done():
					return
				default:
					task, err := m.fetcher.FetchTask(ctx)
					if err != nil {
						m.logger.Println(err)
						continue
					}
					atomic.AddInt32(&m.runningN, 1)

					func() {
						defer func() {
							atomic.AddInt32(&m.runningN, -1)
							if err := recover(); err != nil {
								m.logger.Println(err)
							}
						}()
						if err := task.Run(ctx); err != nil {
							m.logger.Println(err)
						}
					}()
				}
			}
		}()
	}

	close(m.waitCh)
	m.wg.Wait()
}

func NewManager(workerN int, fetcher TaskFetcher) *Manager {
	return &Manager{
		workerN:  workerN,
		fetcher:  fetcher,
		runningN: 0,
		waitCh:   make(chan struct{}),
		logger:   log.New(os.Stderr, "", log.Ldate|log.Ltime),
	}
}
<|endoftext|>


<commit_before>package main

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

func startup() error {
	fmt.Println("Detected Windows platform...")
	fmt.Println("Looking for existing task users...")
	out, err := exec.Command("C:\\Windows\\System32\\wbem\\WMIC.exe", "useraccount", "get", "name").Output()
	if err != nil {
		fmt.Printf("%v\n", err)
		return err
	}
	for _, line := range strings.Split(string(out), "\r\n") {
		trimmedLine := strings.Trim(line, "\r\n ")
		if strings.HasPrefix(trimmedLine, "Task_") {
			removeOSUser(trimmedLine)
		}
	}
	return nil
}

func removeOSUser(user string) {
	fmt.Println("Attempting to remove OS user " + user + "...")
	out, err := exec.Command("net", "user", user, "/delete").Output()
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	fmt.Println(string(out))
	out, err = exec.Command("wmic", "path", "win32_userprofile", "where", "name=\""+user+"\"", "delete").Output()
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	fmt.Println(string(out))
}

func (task *TaskRun) generateCommand() *exec.Cmd {
	// TODO: below is the *nix implementation, Windows needs to generate a .bat file
	// and run it using PsExec instead...
	cmd := exec.Command(task.Payload.Command[0], task.Payload.Command[1:]...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	task.prepEnvVars(cmd)
	return cmd
}
<commit_msg>Work in progress...<commit_after>package main

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

func startup() error {
	fmt.Println("Detected Windows platform...")
	fmt.Println("Looking for existing task users...")
	out, err := exec.Command("C:\\Windows\\System32\\wbem\\WMIC.exe", "useraccount", "get", "name").Output()
	if err != nil {
		fmt.Printf("%v\n", err)
		return err
	}
	for _, line := range strings.Split(string(out), "\r\n") {
		trimmedLine := strings.Trim(line, "\r\n ")
		if strings.HasPrefix(trimmedLine, "Task_") {
			removeOSUser(trimmedLine)
		}
	}
	return nil
}

func removeOSUser(user string) {
	fmt.Println("Attempting to remove OS user " + user + "...")
	out, err := exec.Command("net", "user", user, "/delete").Output()
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	fmt.Println(string(out))
	out, err = exec.Command("C:\\Windows\\System32\\wbem\\WMIC.exe", "path", "win32_userprofile", "where", "name=\""+user+"\"", "delete").Output()
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	fmt.Println(string(out))
}

func (task *TaskRun) generateCommand() *exec.Cmd {
	// TODO: below is the *nix implementation, Windows needs to generate a .bat file
	// and run it using PsExec instead...
	cmd := exec.Command(task.Payload.Command[0], task.Payload.Command[1:]...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	task.prepEnvVars(cmd)
	return cmd
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"log"
	"os/exec"
	"regexp"
)

const (
	autoconfig_start = "#VAGABONDAUTOCONFIG"
	autoconfig_end   = "#VAGABONDAUTOCONFIGEND"
)

type ConfigFile struct {
	Filename  string
	Generator Generator
	Append    bool
	Io        ConfigFileIo
	Flush     func(env *Environment, filename string)
}

func (cf ConfigFile) Update(env *Environment, force bool) {
	existing := cf.Io.Read(env, cf.Filename)
	block := cf.Generator.Generate(env)

	if cf.Append {
		block = appendBlock(existing, block)
	}

	if md5.Sum(existing) != md5.Sum(block) || force {
		log.Println("Updating " + cf.Filename)
		cf.Io.Write(env, cf.Filename, block)
		// Run any post-update actions.
		cf.Flush(env, cf.Filename)
	}
}

func appendBlock(existing []byte, block []byte) (newblock []byte) {
	re := regexp.MustCompile("(?s)" + autoconfig_start + ".*" + autoconfig_end)
	newblock = re.ReplaceAll(existing, []byte(""))
	newblock = bytes.TrimRight(newblock, "\n")

	newblock = append(newblock, "\n"+autoconfig_start+"\n"...)
	newblock = append(newblock, block...)
	newblock = append(newblock, "\n"+autoconfig_end+"\n"...)
	return
}

var NfsExportsConfigFile = ConfigFile{
	Filename: "/etc/exports",
	Io:       ConfigFileLocalIo{},
	Append:   true,
	Generator: Generator{
		Template: `{{.UsersDir}} {{.MachineIp}} -alldirs -mapall=501:20
{{.DataDir}} {{.MachineIp}} -alldirs -maproot=0`,
		TemplateName: "nfsexports",
	},
	Flush: func(env *Environment, filename string) {
		log.Println("Restarting NFS...")
		_, err := exec.Command("sudo", "nfsd", "restart").Output()
		if err != nil {
			log.Fatal("Failed restarting NFS daemon")
		}
	},
}

var ProfileConfigFile = ConfigFile{
	Filename: "/etc/profile",
	Io:       ConfigFileLocalIo{},
	Append:   true,
	Generator: Generator{
		TemplateName: "etcprofile",
		Template: `export DOCKER_TZ={{.Tz}}
export VAGABOND_SITES_DIR={{.SitesDir}}
export VAGABOND_DATA_DIR={{.DataDir}}
export VAGABOND_MACHINE_NAME={{.MachineName}}`,
	},
	Flush: func(env *Environment, filename string) {
		exec.Command("bash", "source", "/etc/profile")
		log.Println("Reload environment variables by running: source /etc/profile")
	},
}

var BootLocalConfigFile = ConfigFile{
	Filename: "/var/lib/boot2docker/bootlocal.sh",
	Io:       ConfigFileMachineIo{},
	Append:   false,
	Generator: Generator{
		TemplateName: "bootlocalsh",
		Template: `#!/bin/sh

sudo umount /Users
	sudo mkdir -p /Users
	sudo /usr/local/etc/init.d/nfs-client start
		sudo mount -t nfs -o noacl,async {{.HostIp}}:{{.UsersDir}} {{.UsersDir}}
		sudo mount -t nfs -o noacl,async {{.HostIp}}:{{.DataDir}} {{.DataDir}}

		docker stop vagabond_proxy vagabond_dnsmasq
		docker rm vagabond_proxy vagabond_dnsmasq
		docker run --name vagabond_proxy   -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
		docker run --name vagabond_dnsmasq -d -p 53:53/udp -p 53:53/tcp --cap-add NET_ADMIN andyshinn/dnsmasq --address=/docker/{{.MachineIp}}
		`,
	},
	Flush: func(env *Environment, filename string) {
		log.Println("Rebooting docker machine...")
		machine := env.GetMachine()
		// Make the script executable. Unless we sync, machine may
		// reboot before the file is written to the disk.
		_, err := machine.Exec(fmt.Sprintf("sudo chmod +x %s", filename)).Output()
		if err != nil {
			log.Fatalf("Failed making %s executable", filename)
		}
		_, err = machine.Reboot().Output()
		if err != nil {
			log.Fatal("Failed rebooting machine")
		}
	},
}

var ResolverConfigFile = ConfigFile{
	Filename: "/etc/resolver/docker",
	Io:       ConfigFileLocalIo{},
	Generator: Generator{
		TemplateName: "etchosts",
		Template:     "nameserver {{.MachineIp}}",
	},
	Flush: func(env *Environment, filename string) {
		// No-op
	},
}
<commit_msg>Remove VAGABOND_MACHINE_NAME variable from autoconfig in /etc/profile.  It is still possible to use, but it will be managed outside of our auto config system<commit_after>package config

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"log"
	"os/exec"
	"regexp"
)

const (
	autoconfig_start = "#VAGABONDAUTOCONFIG"
	autoconfig_end   = "#VAGABONDAUTOCONFIGEND"
)

type ConfigFile struct {
	Filename  string
	Generator Generator
	Append    bool
	Io        ConfigFileIo
	Flush     func(env *Environment, filename string)
}

func (cf ConfigFile) Update(env *Environment, force bool) {
	existing := cf.Io.Read(env, cf.Filename)
	block := cf.Generator.Generate(env)

	if cf.Append {
		block = appendBlock(existing, block)
	}

	if md5.Sum(existing) != md5.Sum(block) || force {
		log.Println("Updating " + cf.Filename)
		cf.Io.Write(env, cf.Filename, block)
		// Run any post-update actions.
		cf.Flush(env, cf.Filename)
	}
}

func appendBlock(existing []byte, block []byte) (newblock []byte) {
	re := regexp.MustCompile("(?s)" + autoconfig_start + ".*" + autoconfig_end)
	newblock = re.ReplaceAll(existing, []byte(""))
	newblock = bytes.TrimRight(newblock, "\n")

	newblock = append(newblock, "\n"+autoconfig_start+"\n"...)
	newblock = append(newblock, block...)
	newblock = append(newblock, "\n"+autoconfig_end+"\n"...)
	return
}

var NfsExportsConfigFile = ConfigFile{
	Filename: "/etc/exports",
	Io:       ConfigFileLocalIo{},
	Append:   true,
	Generator: Generator{
		Template: `{{.UsersDir}} {{.MachineIp}} -alldirs -mapall=501:20
{{.DataDir}} {{.MachineIp}} -alldirs -maproot=0`,
		TemplateName: "nfsexports",
	},
	Flush: func(env *Environment, filename string) {
		log.Println("Restarting NFS...")
		_, err := exec.Command("sudo", "nfsd", "restart").Output()
		if err != nil {
			log.Fatal("Failed restarting NFS daemon")
		}
	},
}

var ProfileConfigFile = ConfigFile{
	Filename: "/etc/profile",
	Io:       ConfigFileLocalIo{},
	Append:   true,
	Generator: Generator{
		TemplateName: "etcprofile",
		Template: `export DOCKER_TZ={{.Tz}}
export VAGABOND_SITES_DIR={{.SitesDir}}
export VAGABOND_DATA_DIR={{.DataDir}}`,
	},
	Flush: func(env *Environment, filename string) {
		exec.Command("bash", "source", "/etc/profile")
		log.Println("Reload environment variables by running: source /etc/profile")
	},
}

var BootLocalConfigFile = ConfigFile{
	Filename: "/var/lib/boot2docker/bootlocal.sh",
	Io:       ConfigFileMachineIo{},
	Append:   false,
	Generator: Generator{
		TemplateName: "bootlocalsh",
		Template: `#!/bin/sh

sudo umount /Users
	sudo mkdir -p /Users
	sudo /usr/local/etc/init.d/nfs-client start
		sudo mount -t nfs -o noacl,async {{.HostIp}}:{{.UsersDir}} {{.UsersDir}}
		sudo mount -t nfs -o noacl,async {{.HostIp}}:{{.DataDir}} {{.DataDir}}

		docker stop vagabond_proxy vagabond_dnsmasq
		docker rm vagabond_proxy vagabond_dnsmasq
		docker run --name vagabond_proxy   -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
		docker run --name vagabond_dnsmasq -d -p 53:53/udp -p 53:53/tcp --cap-add NET_ADMIN andyshinn/dnsmasq --address=/docker/{{.MachineIp}}
		`,
	},
	Flush: func(env *Environment, filename string) {
		log.Println("Rebooting docker machine...")
		machine := env.GetMachine()
		// Make the script executable. Unless we sync, machine may
		// reboot before the file is written to the disk.
		_, err := machine.Exec(fmt.Sprintf("sudo chmod +x %s", filename)).Output()
		if err != nil {
			log.Fatalf("Failed making %s executable", filename)
		}
		_, err = machine.Reboot().Output()
		if err != nil {
			log.Fatal("Failed rebooting machine")
		}
	},
}

var ResolverConfigFile = ConfigFile{
	Filename: "/etc/resolver/docker",
	Io:       ConfigFileLocalIo{},
	Generator: Generator{
		TemplateName: "etchosts",
		Template:     "nameserver {{.MachineIp}}",
	},
	Flush: func(env *Environment, filename string) {
		// No-op
	},
}
<|endoftext|>