<commit_before>package main

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/ec2"
	"gopkg.in/urfave/cli.v1"
	"log"
	"os"
	"regexp"
	"strings"
)

type ArgConfig struct {
	AwsRegion       string
	SecurityGroupId string
	// Port or ICMP Type
	From int
	// Port or ICMP Type
	To                   int
	Protocol             string
	ThisEc2Instance      bool
	AutoScalingGroupName string
}

func parseFlags(c *cli.Context) *ArgConfig {
	var result ArgConfig
	result.AwsRegion = c.String("r")
	re := regexp.MustCompile("^sg-([0-9a-z]{8})$")
	if re.MatchString(c.String("s")) == false {
		log.Printf("Error: -s must be specified as a Security Group ID. Example: sg-asdf1234\n\n")
		cli.ShowAppHelp(c)
		os.Exit(1)
	} else {
		result.SecurityGroupId = c.String("s")
	}
	if c.String("p") != "tcp" && c.String("p") != "udp" && c.String("p") != "icmp" {
		log.Printf("Error: -p must be one of 'tcp', 'udp', or 'icmp'.\n\n")
		cli.ShowAppHelp(c)
		os.Exit(1)
	} else {
		result.Protocol = c.String("p")
	}
	if result.Protocol == "tcp" || result.Protocol == "udp" {
		if c.Int("f") < 1 || c.Int("f") > 65535 {
			log.Printf("Error: -f must be a port number between 1 and 65535.\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		} else {
			result.From = c.Int("f")
		}
		if c.Int("t") < 1 || c.Int("t") > 65535 {
			log.Printf("Error: -t must be a port number between 1 and 65535.\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		} else {
			result.To = c.Int("t")
		}
	} else {
		if c.Int("f") < 0 || c.Int("f") > 255 {
			log.Printf("Error: -f must be an ICMP type between 0 and 255. See http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		} else {
			result.From = c.Int("f")
		}
		if c.Int("t") < 0 || c.Int("t") > 255 {
			log.Printf("Error: -t must be an ICMP type between 0 and 255. See http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		} else {
			result.To = c.Int("t")
		}
	}
	result.ThisEc2Instance = c.Bool("i")
	result.AutoScalingGroupName = c.String("a")
	return &result
}

func main() {
	app := cli.NewApp()
	app.Name = "ec2-sg-mangler"
	app.Version = "0.1.0"
	app.Usage = "Helper utility to manage the EC2 instance public IPs in an AWS Security Group"
	app.Action = func(c *cli.Context) {
		arg_config := parseFlags(c)

		ec2metadata_client := ec2metadata.New(session.New())
		err := setAwsRegion(ec2metadata_client, arg_config)
		if err != nil {
			log.Fatalf(err.Error())
		}

		// Reusable config session object for AWS services with current region attached.
		aws_config_session := session.New(&aws.Config{Region: aws.String(arg_config.AwsRegion)})

		asg_client := autoscaling.New(aws_config_session)
		ec2_client := ec2.New(aws_config_session)

		var proposed_ips []string
		if len(arg_config.AutoScalingGroupName) > 0 {
			log.Printf("Fetching list of Auto Scaling Group IPs...\n")
			proposed_ips, err = getAsgInstancePublicIps(asg_client, ec2_client, arg_config.AutoScalingGroupName)
			if err != nil {
				log.Fatalf(err.Error())
			}
		}
		var this_instance_ip string
		if arg_config.ThisEc2Instance == true {
			log.Printf("Fetching the public IP of this EC2 instance...\n")
			this_instance_ip, err = getThisInstancePublicIp(ec2metadata_client)
			if err != nil {
				log.Fatalf(err.Error())
			}
			proposed_ips = append(proposed_ips, this_instance_ip)
		}
		proposed_ips = removeSliceDuplicates(proposed_ips)

		log.Printf("Fetching the IPs currently attached to Security Group...\n")
		sg_ips, err := getCurrentMatchingSgIps(ec2_client, arg_config.SecurityGroupId, arg_config.From, arg_config.To, arg_config.Protocol)
		if err != nil {
			log.Fatalf(err.Error())
		}

		sg_actions := reconcileIps(sg_ips, proposed_ips)
		log.Printf("Commencing SG changes (if any)...\n")
		if len(sg_actions.Add) > 0 {
			err = doAddSgIps(ec2_client, arg_config.SecurityGroupId, arg_config.From, arg_config.To, arg_config.Protocol, sg_actions.Add)
			if err != nil {
				log.Fatalf(err.Error())
			}
			log.Printf("Added Ingress IPs %s to SG %s.\n", strings.Join(sg_actions.Add, ", "), arg_config.SecurityGroupId)
		}
		if len(sg_actions.Remove) > 0 {
			err = doRemoveSgIps(ec2_client, arg_config.SecurityGroupId, arg_config.From, arg_config.To, arg_config.Protocol, sg_actions.Remove)
			if err != nil {
				log.Fatalf(err.Error())
			}
			log.Printf("Removed Ingress IPs %s to SG %s.\n", strings.Join(sg_actions.Remove, ", "), arg_config.SecurityGroupId)
		}
		log.Printf("All done.")
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "r",
			Value: "",
			Usage: "AWS Region",
		},
		cli.StringFlag{
			Name:  "s",
			Value: "",
			Usage: "Security Group ID (must exist already)",
		},
		cli.IntFlag{
			Name:  "f",
			Value: 1,
			Usage: "From Port Number OR ICMP Type (-1 for all ICMP types)",
		},
		cli.IntFlag{
			Name:  "t",
			Value: 1,
			Usage: "To Port Number OR ICMP Type (-1 for all ICMP types )",
		},
		cli.StringFlag{
			Name:  "p",
			Value: "tcp",
			Usage: "IP Protocol Name (may be one of 'tcp', 'udp', or 'icmp' currently)",
		},
		cli.BoolFlag{
			Name:  "i",
			Usage: "Add this EC2 instance public IP? Not required if this instance is part of the nominated ASG",
		},
		cli.StringFlag{
			Name:  "a",
			Value: "",
			Usage: "Add the public IPs for all EC2 instances in this ASG Name",
		},
	}

	app.Run(os.Args)
}
<commit_msg>fix https://github.com/CpuID/ec2-sg-mangler/issues/1<commit_after>package main

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/ec2"
	"gopkg.in/urfave/cli.v1"
	"log"
	"os"
	"regexp"
	"strings"
)

type ArgConfig struct {
	AwsRegion       string
	SecurityGroupId string
	// Port or ICMP Type
	From int
	// Port or ICMP Type
	To                   int
	Protocol             string
	ThisEc2Instance      bool
	AutoScalingGroupName string
}

func parseFlags(c *cli.Context) *ArgConfig {
	var result ArgConfig
	result.AwsRegion = c.String("r")
	re := regexp.MustCompile("^sg-([0-9a-z]{8})$")
	if re.MatchString(c.String("s")) == false {
		log.Printf("Error: -s must be specified as a Security Group ID. Example: sg-asdf1234\n\n")
		cli.ShowAppHelp(c)
		os.Exit(1)
	} else {
		result.SecurityGroupId = c.String("s")
	}
	if c.String("p") != "tcp" && c.String("p") != "udp" && c.String("p") != "icmp" {
		log.Printf("Error: -p must be one of 'tcp', 'udp', or 'icmp'.\n\n")
		cli.ShowAppHelp(c)
		os.Exit(1)
	} else {
		result.Protocol = c.String("p")
	}
	if result.Protocol == "tcp" || result.Protocol == "udp" {
		if c.Int("f") < 1 || c.Int("f") > 65535 {
			log.Printf("Error: -f must be a port number between 1 and 65535.\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		} else {
			result.From = c.Int("f")
		}
		if c.Int("t") < 1 || c.Int("t") > 65535 {
			log.Printf("Error: -t must be a port number between 1 and 65535.\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		} else {
			result.To = c.Int("t")
		}
	} else {
		if c.Int("f") < 0 || c.Int("f") > 255 {
			log.Printf("Error: -f must be an ICMP type between 0 and 255. See http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		} else {
			result.From = c.Int("f")
		}
		if c.Int("t") < 0 || c.Int("t") > 255 {
			log.Printf("Error: -t must be an ICMP type between 0 and 255. See http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml\n\n")
			cli.ShowAppHelp(c)
			os.Exit(1)
		} else {
			result.To = c.Int("t")
		}
	}
	result.ThisEc2Instance = c.Bool("i")
	result.AutoScalingGroupName = c.String("a")
	return &result
}

func main() {
	app := cli.NewApp()
	app.Name = "ec2-sg-mangler"
	app.Version = "0.1.0"
	app.Usage = "Helper utility to manage the EC2 instance public IPs in an AWS Security Group"
	app.Action = func(c *cli.Context) error {
		arg_config := parseFlags(c)

		ec2metadata_client := ec2metadata.New(session.New())
		err := setAwsRegion(ec2metadata_client, arg_config)
		if err != nil {
			log.Fatalf(err.Error())
		}

		// Reusable config session object for AWS services with current region attached.
		aws_config_session := session.New(&aws.Config{Region: aws.String(arg_config.AwsRegion)})

		asg_client := autoscaling.New(aws_config_session)
		ec2_client := ec2.New(aws_config_session)

		var proposed_ips []string
		if len(arg_config.AutoScalingGroupName) > 0 {
			log.Printf("Fetching list of Auto Scaling Group IPs...\n")
			proposed_ips, err = getAsgInstancePublicIps(asg_client, ec2_client, arg_config.AutoScalingGroupName)
			if err != nil {
				log.Fatalf(err.Error())
			}
		}
		var this_instance_ip string
		if arg_config.ThisEc2Instance == true {
			log.Printf("Fetching the public IP of this EC2 instance...\n")
			this_instance_ip, err = getThisInstancePublicIp(ec2metadata_client)
			if err != nil {
				log.Fatalf(err.Error())
			}
			proposed_ips = append(proposed_ips, this_instance_ip)
		}
		proposed_ips = removeSliceDuplicates(proposed_ips)

		log.Printf("Fetching the IPs currently attached to Security Group...\n")
		sg_ips, err := getCurrentMatchingSgIps(ec2_client, arg_config.SecurityGroupId, arg_config.From, arg_config.To, arg_config.Protocol)
		if err != nil {
			log.Fatalf(err.Error())
		}

		sg_actions := reconcileIps(sg_ips, proposed_ips)
		log.Printf("Commencing SG changes (if any)...\n")
		if len(sg_actions.Add) > 0 {
			err = doAddSgIps(ec2_client, arg_config.SecurityGroupId, arg_config.From, arg_config.To, arg_config.Protocol, sg_actions.Add)
			if err != nil {
				log.Fatalf(err.Error())
			}
			log.Printf("Added Ingress IPs %s to SG %s.\n", strings.Join(sg_actions.Add, ", "), arg_config.SecurityGroupId)
		}
		if len(sg_actions.Remove) > 0 {
			err = doRemoveSgIps(ec2_client, arg_config.SecurityGroupId, arg_config.From, arg_config.To, arg_config.Protocol, sg_actions.Remove)
			if err != nil {
				log.Fatalf(err.Error())
			}
			log.Printf("Removed Ingress IPs %s to SG %s.\n", strings.Join(sg_actions.Remove, ", "), arg_config.SecurityGroupId)
		}
		log.Printf("All done.")
		return nil
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "r",
			Value: "",
			Usage: "AWS Region",
		},
		cli.StringFlag{
			Name:  "s",
			Value: "",
			Usage: "Security Group ID (must exist already)",
		},
		cli.IntFlag{
			Name:  "f",
			Value: 1,
			Usage: "From Port Number OR ICMP Type (-1 for all ICMP types)",
		},
		cli.IntFlag{
			Name:  "t",
			Value: 1,
			Usage: "To Port Number OR ICMP Type (-1 for all ICMP types )",
		},
		cli.StringFlag{
			Name:  "p",
			Value: "tcp",
			Usage: "IP Protocol Name (may be one of 'tcp', 'udp', or 'icmp' currently)",
		},
		cli.BoolFlag{
			Name:  "i",
			Usage: "Add this EC2 instance public IP? Not required if this instance is part of the nominated ASG",
		},
		cli.StringFlag{
			Name:  "a",
			Value: "",
			Usage: "Add the public IPs for all EC2 instances in this ASG Name",
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package igc

import (
	"fmt"
	"io"
	"math"
	"time"

	"github.com/cznic/mathutil"
	"github.com/twpayne/go-geom"
)

type Encoder struct {
	w io.Writer
}

func clamp(x, min, max int) int {
	return mathutil.Min(mathutil.Max(x, min), max)
}

func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w}
}

func (enc *Encoder) Encode(ls *geom.LineString) error {
	var t0 time.Time
	for i, n := 0, ls.NumCoords(); i < n; i++ {
		coord := ls.Coord(i)
		t := time.Unix(int64(coord[3]), 0).UTC()
		if t.Day() != t0.Day() || t.Month() != t0.Month() || t.Year() != t0.Year() {
			if _, err := fmt.Fprintf(enc.w, "HFDTE%02d%02d%02d\n", t.Day(), t.Month(), t.Year()%100); err != nil {
				return err
			}
			t0 = t
		}
		latMMin := mathutil.Min(int(math.Abs(60000*coord[1])), 90*60000)
		latDeg, latMMin := latMMin/60000, latMMin%60000
		var latHemi string
		if coord[1] < 0 {
			latHemi = "S"
		} else {
			latHemi = "N"
		}
		lngMMin := mathutil.Min(int(math.Abs(60000*coord[0])), 180*60000)
		lngDeg, lngMMin := lngMMin/60000, lngMMin%60000
		var lngHemi string
		if coord[0] < 0 {
			lngHemi = "W"
		} else {
			lngHemi = "E"
		}
		alt := clamp(int(coord[2]), 0, 10000)
		if _, err := fmt.Fprintf(enc.w, "B%02d%02d%02d%02d%05d%s%03d%05d%sA%05d%05d\n", t.Hour(), t.Minute(), t.Second(), latDeg, latMMin, latHemi, lngDeg, lngMMin, lngHemi, alt, alt); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Remove dependency on github.com/cznic/mathutil<commit_after>package igc

import (
	"fmt"
	"io"
	"math"
	"time"

	"github.com/twpayne/go-geom"
)

type Encoder struct {
	w io.Writer
}

func clamp(x, min, max int) int {
	switch {
	case x < min:
		return min
	case x > max:
		return max
	default:
		return x
	}
}

func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w}
}

func (enc *Encoder) Encode(ls *geom.LineString) error {
	var t0 time.Time
	for i, n := 0, ls.NumCoords(); i < n; i++ {
		coord := ls.Coord(i)
		t := time.Unix(int64(coord[3]), 0).UTC()
		if t.Day() != t0.Day() || t.Month() != t0.Month() || t.Year() != t0.Year() {
			if _, err := fmt.Fprintf(enc.w, "HFDTE%02d%02d%02d\n", t.Day(), t.Month(), t.Year()%100); err != nil {
				return err
			}
			t0 = t
		}
		latMMin := int(math.Abs(60000 * coord[1]))
		if latMMin > 90*60000 {
			latMMin = 90 * 60000
		}
		latDeg, latMMin := latMMin/60000, latMMin%60000
		var latHemi string
		if coord[1] < 0 {
			latHemi = "S"
		} else {
			latHemi = "N"
		}
		lngMMin := int(math.Abs(60000 * coord[0]))
		if lngMMin > 180*60000 {
			lngMMin = 180 * 60000
		}
		lngDeg, lngMMin := lngMMin/60000, lngMMin%60000
		var lngHemi string
		if coord[0] < 0 {
			lngHemi = "W"
		} else {
			lngHemi = "E"
		}
		alt := clamp(int(coord[2]), 0, 10000)
		if _, err := fmt.Fprintf(enc.w, "B%02d%02d%02d%02d%05d%s%03d%05d%sA%05d%05d\n", t.Hour(), t.Minute(), t.Second(), latDeg, latMMin, latHemi, lngDeg, lngMMin, lngHemi, alt, alt); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package environs

import (
	"bufio"
	"bytes"
	"fmt"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/testing"
	"strings"
)

type imageSuite struct {
	testing.LoggingSuite
}

var _ = Suite(&imageSuite{})

func (s *imageSuite) SetUpSuite(c *C) {
	s.LoggingSuite.SetUpSuite(c)
}

func (s *imageSuite) TearDownSuite(c *C) {
	s.LoggingSuite.TearDownTest(c)
}

var imagesData = imagesFields(
	"instance-store amd64 us-east-1 ami-00000011 paravirtual",
	"ebs amd64 eu-west-1 ami-00000016 paravirtual",
	"ebs i386 ap-northeast-1 ami-00000023 paravirtual",
	"ebs amd64 ap-northeast-1 ami-00000026 paravirtual",
	"ebs amd64 ap-northeast-1 ami-00000087 hvm",
	"ebs amd64 test ami-00000033 paravirtual",
	"ebs i386 test ami-00000034 paravirtual",
	"ebs amd64 test ami-00000035 hvm",
)

func imagesFields(srcs ...string) string {
	strs := make([]string, len(srcs))
	for i, src := range srcs {
		parts := strings.Split(src, " ")
		if len(parts) != 5 {
			panic("bad clouddata field input")
		}
		args := make([]interface{}, len(parts))
		for i, part := range parts {
			args[i] = part
		}
		// Ignored fields are left empty for clarity's sake, and two additional
		// tabs are tacked on to the end to verify extra columns are ignored.
		strs[i] = fmt.Sprintf("\t\t\t\t%s\t%s\t%s\t%s\t\t\t%s\t\t\n", args...)
	}
	return strings.Join(strs, "")
}

var getImagesTests = []struct {
	region string
	series string
	arches []string
	images []Image
	err    string
}{
	{
		region: "us-east-1",
		series: "precise",
		arches: Both,
		err:    `no "precise" images in us-east-1 with arches \[amd64 i386\]`,
	}, {
		region: "eu-west-1",
		series: "precise",
		arches: []string{"i386"},
		err:    `no "precise" images in eu-west-1 with arches \[i386\]`,
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: Both,
		images: []Image{
			{"ami-00000026", "amd64", false},
			{"ami-00000087", "amd64", true},
			{"ami-00000023", "i386", false},
		},
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: []string{"amd64"},
		images: []Image{
			{"ami-00000026", "amd64", false},
			{"ami-00000087", "amd64", true},
		},
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: []string{"i386"},
		images: []Image{
			{"ami-00000023", "i386", false},
		},
	},
}

func (s *imageSuite) TestGetImages(c *C) {
	var ebs = "ebs"
	var cluster = "hvm"
	for i, t := range getImagesTests {
		c.Logf("test %d", i)
		r := bufio.NewReader(bytes.NewBufferString(imagesData))
		images, err := getImages(r, &InstanceConstraint{
			Region:  t.region,
			Series:  t.series,
			Arches:  t.arches,
			Storage: &ebs,
			Cluster: &cluster,
		})
		if t.err != "" {
			c.Check(err, ErrorMatches, t.err)
			continue
		}
		if !c.Check(err, IsNil) {
			continue
		}
		c.Check(images, DeepEquals, t.images)
	}
}

var imageMatchtests = []struct {
	image Image
	itype InstanceType
	match bool
}{
	{
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"amd64"}},
		match: true,
	}, {
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"i386", "amd64"}},
		match: true,
	}, {
		image: Image{Arch: "amd64", Clustered: true},
		itype: InstanceType{Arches: []string{"amd64"}, Clustered: true},
		match: true,
	}, {
		image: Image{Arch: "i386"},
		itype: InstanceType{Arches: []string{"amd64"}},
	}, {
		image: Image{Arch: "amd64", Clustered: true},
		itype: InstanceType{Arches: []string{"amd64"}},
	}, {
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"amd64"}, Clustered: true},
	},
}

func (s *imageSuite) TestImageMatch(c *C) {
	for i, t := range imageMatchtests {
		c.Logf("test %d", i)
		c.Check(t.image.match(t.itype), Equals, t.match)
	}
}
<commit_msg>Add FindInstanceSpec unit tests<commit_after>package environs

import (
	"bufio"
	"bytes"
	"fmt"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/testing"
	"strings"
)

type imageSuite struct {
	testing.LoggingSuite
}

var _ = Suite(&imageSuite{})

func (s *imageSuite) SetUpSuite(c *C) {
	s.LoggingSuite.SetUpSuite(c)
}

func (s *imageSuite) TearDownSuite(c *C) {
	s.LoggingSuite.TearDownTest(c)
}

var imagesData = imagesFields(
	"instance-store amd64 us-east-1 ami-00000011 paravirtual",
	"ebs amd64 eu-west-1 ami-00000016 paravirtual",
	"ebs i386 ap-northeast-1 ami-00000023 paravirtual",
	"ebs amd64 ap-northeast-1 ami-00000026 paravirtual",
	"ebs amd64 ap-northeast-1 ami-00000087 hvm",
	"ebs amd64 test ami-00000033 paravirtual",
	"ebs i386 test ami-00000034 paravirtual",
	"ebs amd64 test ami-00000035 hvm",
)

func imagesFields(srcs ...string) string {
	strs := make([]string, len(srcs))
	for i, src := range srcs {
		parts := strings.Split(src, " ")
		if len(parts) != 5 {
			panic("bad clouddata field input")
		}
		args := make([]interface{}, len(parts))
		for i, part := range parts {
			args[i] = part
		}
		// Ignored fields are left empty for clarity's sake, and two additional
		// tabs are tacked on to the end to verify extra columns are ignored.
		strs[i] = fmt.Sprintf("\t\t\t\t%s\t%s\t%s\t%s\t\t\t%s\t\t\n", args...)
	}
	return strings.Join(strs, "")
}

type instanceSpecTestParams struct {
	desc string
	region string
	arches []string
	instanceTypes []InstanceType
	defaultImageId string
	defaultInstanceType string
	imageId string
	instanceTypeId string
	instanceTypeName string
	err    string
}

func (p *instanceSpecTestParams) init() {
	if p.arches == nil {
		p.arches = Both
	}
	if p.instanceTypes == nil {
		p.instanceTypes = []InstanceType{{Id: "1", Name: "it-1", Arches: Both}}
		p.instanceTypeId = "1"
		p.instanceTypeName = "it-1"
	}
}

var findInstanceSpecTests = []instanceSpecTestParams{
	{
		desc: "image exists in metadata",
		region: "test",
		defaultImageId: "1234",
		imageId: "ami-00000033",
	},
	{
		desc: "no image exists in metadata, use supplied default",
		region: "invalid-region",
		defaultImageId: "1234",
		imageId: "1234",
	},
	{
		desc: "no image exists in metadata, no default supplied",
		region: "invalid-region",
		imageId: "1234",
		err: `no "raring" images in invalid-region with arches \[amd64 i386\], and no default specified`,
	},
	{
		desc: "no valid instance types",
		region: "test",
		instanceTypes: []InstanceType{},
		err: `no "raring" images in test with arches \[amd64 i386\], and no default specified`,
	},
}

func (s *imageSuite) TestFindInstanceSpec(c *C) {
	for _, t := range findInstanceSpecTests {
		c.Logf("test: %v", t.desc)
		t.init()
		r := bufio.NewReader(bytes.NewBufferString(imagesData))
		spec, err := FindInstanceSpec(r, &InstanceConstraint{
				Series: "raring",
				Region:  t.region,
				Arches:  t.arches,
				DefaultImageId: t.defaultImageId,
				}, t.instanceTypes)
		if t.err != "" {
			c.Check(err, ErrorMatches, t.err)
			continue
		}
		if !c.Check(err, IsNil) {
			continue
		}
		c.Check(spec.Image.Id, Equals, t.imageId)
		c.Check(spec.InstanceTypeId, Equals, t.instanceTypeId)
		c.Check(spec.InstanceTypeName, Equals, t.instanceTypeName)
	}
}

var getImagesTests = []struct {
	region string
	series string
	arches []string
	images []Image
	err    string
}{
	{
		region: "us-east-1",
		series: "precise",
		arches: Both,
		err:    `no "precise" images in us-east-1 with arches \[amd64 i386\]`,
	}, {
		region: "eu-west-1",
		series: "precise",
		arches: []string{"i386"},
		err:    `no "precise" images in eu-west-1 with arches \[i386\]`,
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: Both,
		images: []Image{
			{"ami-00000026", "amd64", false},
			{"ami-00000087", "amd64", true},
			{"ami-00000023", "i386", false},
		},
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: []string{"amd64"},
		images: []Image{
			{"ami-00000026", "amd64", false},
			{"ami-00000087", "amd64", true},
		},
	}, {
		region: "ap-northeast-1",
		series: "precise",
		arches: []string{"i386"},
		images: []Image{
			{"ami-00000023", "i386", false},
		},
	},
}

func (s *imageSuite) TestGetImages(c *C) {
	var ebs = "ebs"
	var cluster = "hvm"
	for i, t := range getImagesTests {
		c.Logf("test %d", i)
		r := bufio.NewReader(bytes.NewBufferString(imagesData))
		images, err := getImages(r, &InstanceConstraint{
			Region:  t.region,
			Series:  t.series,
			Arches:  t.arches,
			Storage: &ebs,
			Cluster: &cluster,
		})
		if t.err != "" {
			c.Check(err, ErrorMatches, t.err)
			continue
		}
		if !c.Check(err, IsNil) {
			continue
		}
		c.Check(images, DeepEquals, t.images)
	}
}

var imageMatchtests = []struct {
	image Image
	itype InstanceType
	match bool
}{
	{
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"amd64"}},
		match: true,
	}, {
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"i386", "amd64"}},
		match: true,
	}, {
		image: Image{Arch: "amd64", Clustered: true},
		itype: InstanceType{Arches: []string{"amd64"}, Clustered: true},
		match: true,
	}, {
		image: Image{Arch: "i386"},
		itype: InstanceType{Arches: []string{"amd64"}},
	}, {
		image: Image{Arch: "amd64", Clustered: true},
		itype: InstanceType{Arches: []string{"amd64"}},
	}, {
		image: Image{Arch: "amd64"},
		itype: InstanceType{Arches: []string{"amd64"}, Clustered: true},
	},
}

func (s *imageSuite) TestImageMatch(c *C) {
	for i, t := range imageMatchtests {
		c.Logf("test %d", i)
		c.Check(t.image.match(t.itype), Equals, t.match)
	}
}
<|endoftext|>


<commit_before>package hookit

import (
	"fmt"
	"strings"
	"io"

	"github.com/nanobox-io/nanobox/commands/registry"
	"github.com/nanobox-io/nanobox/util"
	"github.com/nanobox-io/nanobox/util/console"
	"github.com/nanobox-io/nanobox/util/display"
)

var combined bool

// Exec executes a hook inside of a container
func Exec(container, hook, payload, displayLevel string) (string, error) {

	var stream io.Writer

	if !combined {
		stream = display.NewStreamer(displayLevel)
	}

	out, err := util.DockerExec(container, "root", "/opt/nanobox/hooks/"+hook, []string{payload}, stream)
	if err != nil && (strings.Contains(string(out), "such file or directory") && strings.Contains(err.Error(), "bad exit code(126)")) {
		// if its a 126 the hook didnt exist
		return "", nil
	}

	if err != nil {
		return out, util.ErrorfQuiet("failed to execute hook (%s) on %s: %s", hook, container, err)
	}
	return out, nil
}

func DebugExec(container, hook, payload, displayLevel string) (string, error) {
	res, err := Exec(container, hook, payload, displayLevel)

	// leave early if no error
	if err == nil {
		return res, err
	}
	display.ErrorTask()

	combined = true
	res, err = Exec(container, hook, payload, displayLevel)

	fmt.Printf("failed to execute %s hook: %s\n", hook, err.Error())
	if registry.GetBool("debug") {

		fmt.Printf("An error has occurred: \"%s\"\n", res)
		fmt.Println("Entering Debug Mode")
		fmt.Printf("  container: %s\n", container)
		fmt.Printf("  hook:      %s\n", hook)
		fmt.Printf("  payload:   %s\n", payload)
		err := console.Run(container, console.ConsoleConfig{})
		if err != nil {
			return res, fmt.Errorf("failed to establish a debug session: %s", err.Error())
		}
	}

	// try running the exec one more time.
	return Exec(container, hook, payload, displayLevel)
}
<commit_msg>quickly set back combined<commit_after>package hookit

import (
	"fmt"
	"strings"
	"io"

	"github.com/nanobox-io/nanobox/commands/registry"
	"github.com/nanobox-io/nanobox/util"
	"github.com/nanobox-io/nanobox/util/console"
	"github.com/nanobox-io/nanobox/util/display"
)

var combined bool

// Exec executes a hook inside of a container
func Exec(container, hook, payload, displayLevel string) (string, error) {

	var stream io.Writer

	if !combined {
		stream = display.NewStreamer(displayLevel)
	}

	out, err := util.DockerExec(container, "root", "/opt/nanobox/hooks/"+hook, []string{payload}, stream)
	if err != nil && (strings.Contains(string(out), "such file or directory") && strings.Contains(err.Error(), "bad exit code(126)")) {
		// if its a 126 the hook didnt exist
		return "", nil
	}

	if err != nil {
		return out, util.ErrorfQuiet("failed to execute hook (%s) on %s: %s", hook, container, err)
	}
	return out, nil
}

func DebugExec(container, hook, payload, displayLevel string) (string, error) {
	res, err := Exec(container, hook, payload, displayLevel)

	// leave early if no error
	if err == nil {
		return res, err
	}
	display.ErrorTask()

	combined = true
	res, err = Exec(container, hook, payload, displayLevel)

	fmt.Printf("failed to execute %s hook: %s\n", hook, err.Error())
	if registry.GetBool("debug") {

		fmt.Printf("An error has occurred: \"%s\"\n", res)
		fmt.Println("Entering Debug Mode")
		fmt.Printf("  container: %s\n", container)
		fmt.Printf("  hook:      %s\n", hook)
		fmt.Printf("  payload:   %s\n", payload)
		err := console.Run(container, console.ConsoleConfig{})
		if err != nil {
			return res, fmt.Errorf("failed to establish a debug session: %s", err.Error())
		}
	}
	combined = false

	// try running the exec one more time.
	return Exec(container, hook, payload, displayLevel)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
	isatty "github.com/mattn/go-isatty"
)

const (
	HTTPS_TEMPLATE = `` +
		`  DNS Lookup   TCP Connection   TLS Handshake   Server Processing   Content Transfer` + "\n" +
		`[%s  |     %s  |    %s  |        %s  |       %s  ]` + "\n" +
		`            |                |               |                   |                  |` + "\n" +
		`   namelookup:%s      |               |                   |                  |` + "\n" +
		`                       connect:%s     |                   |                  |` + "\n" +
		`                                   pretransfer:%s         |                  |` + "\n" +
		`                                                     starttransfer:%s        |` + "\n" +
		`                                                                                total:%s` + "\n"

	HTTP_TEMPLATE = `` +
		`   DNS Lookup   TCP Connection   Server Processing   Content Transfer` + "\n" +
		`[ %s  |     %s  |        %s  |       %s  ]` + "\n" +
		`             |                |                   |                  |` + "\n" +
		`    namelookup:%s      |                   |                  |` + "\n" +
		`                        connect:%s         |                  |` + "\n" +
		`                                      starttransfer:%s        |` + "\n" +
		`                                                                 total:%s` + "\n"
)

var (
	ISATTY = isatty.IsTerminal(os.Stdout.Fd())

	grayscale = func(code int) func(string) string {
		if !ISATTY {
			return func(s string) string { return s }
		}
		return func(s string) string {
			return fmt.Sprintf("\x1b[;38;5;%dm%s\x1b[0m", code+232, s)
		}
	}
)

func main() {
	flag.Parse()

	args := flag.Args()
	if len(args) != 1 {
		log.Fatalf("usage: %s URL", os.Args[0])
	}

	url, err := url.Parse(args[0])
	if err != nil {
		log.Fatalf("could not parse url %q: %v", args[0], err)
	}

	scheme := url.Scheme
	hostport := url.Host
	host, port := func() (string, int) {
		switch scheme {
		case "https":
			return hostport, 443
		case "http":
			return hostport, 80
		default:
			log.Fatalf("unsupported url scheme %q", scheme)
			return "", 0 // not reached
		}
	}()

	t0 := time.Now() // before dns resolution
	raddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		log.Fatalf("unable to resolve host: %v", err)
	}

	t1 := time.Now() // after dns resolution, before connect
	conn, err := net.DialTCP("tcp", nil, raddr)
	if err != nil {
		log.Fatalf("unable to connect to host %vv %v", raddr, err)
	}

	t2 := time.Now() // after connect, before request
	req, err := http.NewRequest("GET", url.String(), nil)
	if err != nil {
		log.Fatalf("unable to create request: %v", err)
	}

	if err := req.Write(conn); err != nil {
		log.Fatalf("failed to write request: %v", err)
	}

	t3 := time.Now() // after request, before read response
	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		log.Fatalf("failed to read response: %v", err)
	}

	t4 := time.Now() // after read request, before read body
	if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
		log.Fatalf("failed to read response body: %v", err)
	}

	t5 := time.Now() // after read body
	resp.Body.Close()

	// print status line and headers
	fmt.Printf("\n%s%s%s\n", color.GreenString("HTTP"), grayscale(14)("/"), color.CyanString("%d.%d %s", resp.ProtoMajor, resp.ProtoMinor, resp.Status))

	for k, v := range resp.Header {
		fmt.Println(grayscale(14)(k+":"), color.GreenString(strings.Join(v, ",")))
	}

	fmt.Println("\nBody discarded\n")

	fmta := func(d time.Duration) string {
		return color.CyanString("%7dms", int(d/time.Millisecond))
	}

	fmtb := func(d time.Duration) string {
		return color.CyanString("%-9s", strconv.Itoa(int(d/time.Millisecond))+"ms")
	}

	colorize := func(s string) string {
		v := strings.Split(s, "\n")
		v[0] = grayscale(16)(v[0])
		return strings.Join(v, "\n")
	}

	switch scheme {
	case "https":
		fmt.Printf(colorize(HTTPS_TEMPLATE),
			fmta(t1.Sub(t0)), // dns lookup
			fmta(t2.Sub(t1)), // tcp connection
			fmta(t3.Sub(t2)), // tls handshake
			fmta(t4.Sub(t3)), // server processing
			fmta(t5.Sub(t4)), // content transfer
			fmtb(t1.Sub(t0)), // namelookup
			fmtb(t2.Sub(t0)), // connect
			fmtb(t3.Sub(t0)), // pretransfer
			fmtb(t4.Sub(t0)), // starttransfer
			fmtb(t5.Sub(t0)), // total
		)

	case "http":
		fmt.Printf(colorize(HTTP_TEMPLATE),
			fmta(t1.Sub(t0)), // dns lookup
			fmta(t2.Sub(t1)), // tcp connection
			fmta(t4.Sub(t2)), // server processing
			fmta(t5.Sub(t4)), // content transfer
			fmtb(t1.Sub(t0)), // namelookup
			fmtb(t2.Sub(t0)), // connect
			fmtb(t4.Sub(t0)), // starttransfer
			fmtb(t5.Sub(t0)), // total
		)

	}
}
<commit_msg>fix accidental cyan -> green substitution<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
	isatty "github.com/mattn/go-isatty"
)

const (
	HTTPS_TEMPLATE = `` +
		`  DNS Lookup   TCP Connection   TLS Handshake   Server Processing   Content Transfer` + "\n" +
		`[%s  |     %s  |    %s  |        %s  |       %s  ]` + "\n" +
		`            |                |               |                   |                  |` + "\n" +
		`   namelookup:%s      |               |                   |                  |` + "\n" +
		`                       connect:%s     |                   |                  |` + "\n" +
		`                                   pretransfer:%s         |                  |` + "\n" +
		`                                                     starttransfer:%s        |` + "\n" +
		`                                                                                total:%s` + "\n"

	HTTP_TEMPLATE = `` +
		`   DNS Lookup   TCP Connection   Server Processing   Content Transfer` + "\n" +
		`[ %s  |     %s  |        %s  |       %s  ]` + "\n" +
		`             |                |                   |                  |` + "\n" +
		`    namelookup:%s      |                   |                  |` + "\n" +
		`                        connect:%s         |                  |` + "\n" +
		`                                      starttransfer:%s        |` + "\n" +
		`                                                                 total:%s` + "\n"
)

var (
	ISATTY = isatty.IsTerminal(os.Stdout.Fd())

	grayscale = func(code int) func(string) string {
		if !ISATTY {
			return func(s string) string { return s }
		}
		return func(s string) string {
			return fmt.Sprintf("\x1b[;38;5;%dm%s\x1b[0m", code+232, s)
		}
	}
)

func main() {
	flag.Parse()

	args := flag.Args()
	if len(args) != 1 {
		log.Fatalf("usage: %s URL", os.Args[0])
	}

	url, err := url.Parse(args[0])
	if err != nil {
		log.Fatalf("could not parse url %q: %v", args[0], err)
	}

	scheme := url.Scheme
	hostport := url.Host
	host, port := func() (string, int) {
		switch scheme {
		case "https":
			return hostport, 443
		case "http":
			return hostport, 80
		default:
			log.Fatalf("unsupported url scheme %q", scheme)
			return "", 0 // not reached
		}
	}()

	t0 := time.Now() // before dns resolution
	raddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		log.Fatalf("unable to resolve host: %v", err)
	}

	t1 := time.Now() // after dns resolution, before connect
	conn, err := net.DialTCP("tcp", nil, raddr)
	if err != nil {
		log.Fatalf("unable to connect to host %vv %v", raddr, err)
	}

	t2 := time.Now() // after connect, before request
	req, err := http.NewRequest("GET", url.String(), nil)
	if err != nil {
		log.Fatalf("unable to create request: %v", err)
	}

	if err := req.Write(conn); err != nil {
		log.Fatalf("failed to write request: %v", err)
	}

	t3 := time.Now() // after request, before read response
	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		log.Fatalf("failed to read response: %v", err)
	}

	t4 := time.Now() // after read request, before read body
	if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
		log.Fatalf("failed to read response body: %v", err)
	}

	t5 := time.Now() // after read body
	resp.Body.Close()

	// print status line and headers
	fmt.Printf("\n%s%s%s\n", color.GreenString("HTTP"), grayscale(14)("/"), color.CyanString("%d.%d %s", resp.ProtoMajor, resp.ProtoMinor, resp.Status))

	for k, v := range resp.Header {
		fmt.Println(grayscale(14)(k+":"), color.CyanString(strings.Join(v, ",")))
	}

	fmt.Println("\nBody discarded\n")

	fmta := func(d time.Duration) string {
		return color.CyanString("%7dms", int(d/time.Millisecond))
	}

	fmtb := func(d time.Duration) string {
		return color.CyanString("%-9s", strconv.Itoa(int(d/time.Millisecond))+"ms")
	}

	colorize := func(s string) string {
		v := strings.Split(s, "\n")
		v[0] = grayscale(16)(v[0])
		return strings.Join(v, "\n")
	}

	switch scheme {
	case "https":
		fmt.Printf(colorize(HTTPS_TEMPLATE),
			fmta(t1.Sub(t0)), // dns lookup
			fmta(t2.Sub(t1)), // tcp connection
			fmta(t3.Sub(t2)), // tls handshake
			fmta(t4.Sub(t3)), // server processing
			fmta(t5.Sub(t4)), // content transfer
			fmtb(t1.Sub(t0)), // namelookup
			fmtb(t2.Sub(t0)), // connect
			fmtb(t3.Sub(t0)), // pretransfer
			fmtb(t4.Sub(t0)), // starttransfer
			fmtb(t5.Sub(t0)), // total
		)

	case "http":
		fmt.Printf(colorize(HTTP_TEMPLATE),
			fmta(t1.Sub(t0)), // dns lookup
			fmta(t2.Sub(t1)), // tcp connection
			fmta(t4.Sub(t2)), // server processing
			fmta(t5.Sub(t4)), // content transfer
			fmtb(t1.Sub(t0)), // namelookup
			fmtb(t2.Sub(t0)), // connect
			fmtb(t4.Sub(t0)), // starttransfer
			fmtb(t5.Sub(t0)), // total
		)

	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"math/rand"
	"net/http"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"

	log "github.com/Sirupsen/logrus"
)

const (
	waitTime  = 3 * time.Second
	execDigit = 5
	baseURL   = "https://git.io"
)

var (
	n = kingpin.Flag("dry-run", "dry run mode.").Short('n').Bool()
	c = new(http.Client)
)

func init() {
	kingpin.Parse()
	c.CheckRedirect = func(req *http.Request, via []*http.Request) error { return errors.New("not redirect") }
}

func main() {
	runes := []rune{'0'}

	for {
		checkPath("/" + string(runes))

		runes = advanceRunes(runes)

		if len(runes) > execDigit {
			// exit
			break
		}

		time.Sleep(waitTime)
	}
}

func checkPath(path string) {
	if *n {
		dryRun(path)
		return
	}

	resp, err := c.Get(baseURL + path)
	if err == nil {
		resp.Body.Close()
	}

	if resp.StatusCode == 404 {
		okLog(path)
	} else {
		ngLog(path, resp.Header.Get("Location"))
	}
}

func advanceRunes(runes []rune) []rune {
	if runes[len(runes)-1] == 'Z' {
		// carry over
		addFlg := true
		for i := len(runes) - 1; i > -1; i-- {
			beforeRune := runes[i]
			runes[i] = getNextRune(runes[i])
			if beforeRune != 'Z' {
				addFlg = false
				break
			}
		}

		if addFlg {
			runes = append(runes, '0')
		}
	} else {
		runes[len(runes)-1] = getNextRune(runes[len(runes)-1])
	}

	return runes
}

func getNextRune(r rune) rune {
	// 0 -> 9, a -> z, A -> Z
	if r == '9' {
		return 'a'
	} else if r == 'z' {
		return 'A'
	} else if r == 'Z' {
		return '0'
	}

	r++
	return r
}

func okLog(path string) {
	log.WithFields(log.Fields{"path": path}).Info("ok!")
}

func ngLog(path string, location string) {
	log.WithFields(log.Fields{"path": path, "location": location}).Error("ng.")
}

func dryRun(path string) {
	if rand.Intn(2) == 1 {
		okLog(path)
	} else {
		ngLog(path, "https://example.com/")
	}
}
<commit_msg>rename waitTime -> interval<commit_after>package main

import (
	"errors"
	"math/rand"
	"net/http"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"

	log "github.com/Sirupsen/logrus"
)

const (
	interval  = 3 * time.Second
	execDigit = 5
	baseURL   = "https://git.io"
)

var (
	n = kingpin.Flag("dry-run", "dry run mode.").Short('n').Bool()
	c = new(http.Client)
)

func init() {
	kingpin.Parse()
	c.CheckRedirect = func(req *http.Request, via []*http.Request) error { return errors.New("not redirect") }
}

func main() {
	runes := []rune{'0'}

	for {
		checkPath("/" + string(runes))

		runes = advanceRunes(runes)

		if len(runes) > execDigit {
			// exit
			break
		}

		time.Sleep(interval)
	}
}

func checkPath(path string) {
	if *n {
		dryRun(path)
		return
	}

	resp, err := c.Get(baseURL + path)
	if err == nil {
		resp.Body.Close()
	}

	if resp.StatusCode == 404 {
		okLog(path)
	} else {
		ngLog(path, resp.Header.Get("Location"))
	}
}

func advanceRunes(runes []rune) []rune {
	if runes[len(runes)-1] == 'Z' {
		// carry over
		addFlg := true
		for i := len(runes) - 1; i > -1; i-- {
			beforeRune := runes[i]
			runes[i] = getNextRune(runes[i])
			if beforeRune != 'Z' {
				addFlg = false
				break
			}
		}

		if addFlg {
			runes = append(runes, '0')
		}
	} else {
		runes[len(runes)-1] = getNextRune(runes[len(runes)-1])
	}

	return runes
}

func getNextRune(r rune) rune {
	// 0 -> 9, a -> z, A -> Z
	if r == '9' {
		return 'a'
	} else if r == 'z' {
		return 'A'
	} else if r == 'Z' {
		return '0'
	}

	r++
	return r
}

func okLog(path string) {
	log.WithFields(log.Fields{"path": path}).Info("ok!")
}

func ngLog(path string, location string) {
	log.WithFields(log.Fields{"path": path, "location": location}).Error("ng.")
}

func dryRun(path string) {
	if rand.Intn(2) == 1 {
		okLog(path)
	} else {
		ngLog(path, "https://example.com/")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/aodin/volta/auth"
	"github.com/aodin/volta/config"
	"github.com/codegangsta/envy/lib"
	"golang.org/x/oauth2"
)

// server holds the oauth config and session map
type server struct {
	config      config.Config
	oauthConfig *oauth2.Config
	sessions    map[string]*oauth2.Token // session key : oauth token
}

// SessionRequired wraps the root handler. It checks that the client has a
// valid session on the test application.
func (s *server) SessionRequired(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		session, err := r.Cookie(config.DefaultCookie.Name)
		if err != nil {
			session = &http.Cookie{}
		}
		if _, ok := s.sessions[session.Value]; !ok {
			// prompt to signin
			w.Write([]byte(`<a href="/signin">Sign-in</a>`))
			return
		}
		f(w, r)
	}
}

// callback is hit when the user has successfully authenticated on the
// oauth2 provider
func (s *server) callback(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	// TODO state should be persisted for the session
	// TODO validate state in a crypto secure manner

	// Set an auth token
	token, err := s.oauthConfig.Exchange(oauth2.NoContext, r.FormValue("code"))
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// TODO pull from the /me endpoint to populate session information
	log.Println("creating session token")

	// Set the cookie
	session := auth.Session{
		Key:     auth.RandomKey(),
		Expires: time.Now().AddDate(4, 0, 0),
	}

	// TODO cookie name should come from the volta config
	auth.SetCookie(w, config.DefaultCookie, session)

	// Create a new session key and save it to match the token
	s.sessions[session.Key] = token

	// Redirect to home!
	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}

// root handler will proxy a call the v2 me endpoint
func (s *server) root(w http.ResponseWriter, r *http.Request) {
	// Get the oauth token from the sessions
	// TODO cookie name should come from the volta config
	cookieName := config.DefaultCookie.Name
	session, err := r.Cookie(cookieName)
	if err != nil {
		http.Error(w, "Failed to find session cookie "+cookieName, 401)
		return
	}

	// Exchange the session value for an oauth2 token
	token := s.sessions[session.Value]

	// Create the proxy URI
	uri := os.Getenv("OAUTH_URL") + "/api/v2/me"

	// Create a new request to a protect resource
	req, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// Create a bearer request using the given token
	// TODO TODO Attach extra info?
	token.SetAuthHeader(req)

	client := oauth2.NewClient(oauth2.NoContext, nil)
	resp, err := client.Do(req)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	me := map[string]interface{}{}
	if err = json.NewDecoder(resp.Body).Decode(&me); err != nil {
		http.Error(w, err.Error(), 500)
	}

	w.Write([]byte(fmt.Sprintf(
		"response (%d): %s", resp.StatusCode, time.Now().UTC(),
	)))

	pretty, _ := json.MarshalIndent(me, "", "  ")
	w.Write(pretty)
}

func (s *server) signin(w http.ResponseWriter, r *http.Request) {
	// TODO generate a random state
	url := s.oauthConfig.AuthCodeURL("state?")
	http.Redirect(w, r, url, http.StatusTemporaryRedirect)
	return
}

func (s *server) signout(w http.ResponseWriter, r *http.Request) {
	// Get the oauth token from the sessions
	// TODO cookie name should come from the volta config
	cookieName := config.DefaultCookie.Name
	session, err := r.Cookie(cookieName)
	if err != nil || session == nil {
		w.Write([]byte("no session exists"))
		return
	}

	delete(s.sessions, session.Value)
	w.Write([]byte(fmt.Sprintf("
signed out of session %s
", session.Name)))
	w.Write([]byte(`<p><a href="/">Home</a></p>`))
}

// NewServer creates a new server by parsing an oauth2 config and initializing
// a sessions map
func NewServer() *server {
	return &server{
		oauthConfig: ParseConfig(),
		sessions:    make(map[string]*oauth2.Token),
	}
}

// ParseConfig builds an oauth2 config from environmental variables
func ParseConfig() *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     os.Getenv("OAUTH_ID"),
		ClientSecret: os.Getenv("OAUTH_SECRET"),
		Endpoint: oauth2.Endpoint{
			AuthURL:  os.Getenv("OAUTH_URL") + "/accounts/authorize",
			TokenURL: os.Getenv("OAUTH_URL") + "/accounts/token",
		},
		RedirectURL: os.Getenv("CALLBACK_URL"),
		Scopes:      []string{}, // TODO pull from environment
	}

	if c.ClientID == "" || c.ClientSecret == "" || c.RedirectURL == "" {
		log.Panic("Invalid oauth2 config", c)
	}
	return c
}

func main() {
	// Bootstrap the environment
	envy.Bootstrap()

	// Create a new server
	// TODO Parse a volta config
	s := NewServer()

	port := os.Getenv("PORT")

	// Create the test server
	http.HandleFunc("/callback", s.callback)
	http.HandleFunc("/signin", s.signin)
	http.HandleFunc("/signout", s.signout)
	http.HandleFunc("/", s.SessionRequired(s.root))
	log.Printf("Starting server on :%s\n", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
<commit_msg>Log the session token during creation<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/aodin/volta/auth"
	"github.com/aodin/volta/config"
	"github.com/codegangsta/envy/lib"
	"golang.org/x/oauth2"
)

// server holds the oauth config and session map
type server struct {
	config      config.Config
	oauthConfig *oauth2.Config
	sessions    map[string]*oauth2.Token // session key : oauth token
}

// SessionRequired wraps the root handler. It checks that the client has a
// valid session on the test application.
func (s *server) SessionRequired(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		session, err := r.Cookie(config.DefaultCookie.Name)
		if err != nil {
			session = &http.Cookie{}
		}
		if _, ok := s.sessions[session.Value]; !ok {
			// prompt to signin
			w.Write([]byte(`<a href="/signin">Sign-in</a>`))
			return
		}
		f(w, r)
	}
}

// callback is hit when the user has successfully authenticated on the
// oauth2 provider
func (s *server) callback(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	// TODO state should be persisted for the session
	// TODO validate state in a crypto secure manner

	// Set an auth token
	token, err := s.oauthConfig.Exchange(oauth2.NoContext, r.FormValue("code"))
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// TODO pull from the /me endpoint to populate session information
	log.Println("creating session token:", token)

	// Set the cookie
	session := auth.Session{
		Key:     auth.RandomKey(),
		Expires: time.Now().AddDate(4, 0, 0),
	}

	// TODO cookie name should come from the volta config
	auth.SetCookie(w, config.DefaultCookie, session)

	// Create a new session key and save it to match the token
	s.sessions[session.Key] = token

	// Redirect to home!
	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}

// root handler will proxy a call the v2 me endpoint
func (s *server) root(w http.ResponseWriter, r *http.Request) {
	// Get the oauth token from the sessions
	// TODO cookie name should come from the volta config
	cookieName := config.DefaultCookie.Name
	session, err := r.Cookie(cookieName)
	if err != nil {
		http.Error(w, "Failed to find session cookie "+cookieName, 401)
		return
	}

	// Exchange the session value for an oauth2 token
	token := s.sessions[session.Value]

	// Create the proxy URI
	uri := os.Getenv("OAUTH_URL") + "/api/v2/me"

	// Create a new request to a protect resource
	req, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// Create a bearer request using the given token
	// TODO TODO Attach extra info?
	token.SetAuthHeader(req)

	client := oauth2.NewClient(oauth2.NoContext, nil)
	resp, err := client.Do(req)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	me := map[string]interface{}{}
	if err = json.NewDecoder(resp.Body).Decode(&me); err != nil {
		http.Error(w, err.Error(), 500)
	}

	w.Write([]byte(fmt.Sprintf(
		"response (%d): %s", resp.StatusCode, time.Now().UTC(),
	)))

	pretty, _ := json.MarshalIndent(me, "", "  ")
	w.Write(pretty)
}

func (s *server) signin(w http.ResponseWriter, r *http.Request) {
	// TODO generate a random state
	url := s.oauthConfig.AuthCodeURL("state?")
	http.Redirect(w, r, url, http.StatusTemporaryRedirect)
	return
}

func (s *server) signout(w http.ResponseWriter, r *http.Request) {
	// Get the oauth token from the sessions
	// TODO cookie name should come from the volta config
	cookieName := config.DefaultCookie.Name
	session, err := r.Cookie(cookieName)
	if err != nil || session == nil {
		w.Write([]byte("no session exists"))
		return
	}

	delete(s.sessions, session.Value)
	w.Write([]byte(fmt.Sprintf("
signed out of session %s
", session.Name)))
	w.Write([]byte(`<p><a href="/">Home</a></p>`))
}

// NewServer creates a new server by parsing an oauth2 config and initializing
// a sessions map
func NewServer() *server {
	return &server{
		oauthConfig: ParseConfig(),
		sessions:    make(map[string]*oauth2.Token),
	}
}

// ParseConfig builds an oauth2 config from environmental variables
func ParseConfig() *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     os.Getenv("OAUTH_ID"),
		ClientSecret: os.Getenv("OAUTH_SECRET"),
		Endpoint: oauth2.Endpoint{
			AuthURL:  os.Getenv("OAUTH_URL") + "/accounts/authorize",
			TokenURL: os.Getenv("OAUTH_URL") + "/accounts/token",
		},
		RedirectURL: os.Getenv("CALLBACK_URL"),
		Scopes:      []string{}, // TODO pull from environment?
	}

	if c.ClientID == "" || c.ClientSecret == "" || c.RedirectURL == "" {
		log.Panic("Invalid oauth2 config", c)
	}
	return c
}

func main() {
	// Bootstrap the environment
	envy.Bootstrap()

	// Create a new server
	// TODO Parse a volta config
	s := NewServer()

	port := os.Getenv("PORT")

	// Create the test server
	http.HandleFunc("/callback", s.callback)
	http.HandleFunc("/signin", s.signin)
	http.HandleFunc("/signout", s.signout)
	http.HandleFunc("/", s.SessionRequired(s.root))
	log.Printf("Starting server on :%s\n", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"net/http"
	"os"
	"path/filepath"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/boatilus/peppercorn/cookie"
	"github.com/boatilus/peppercorn/db"
	"github.com/boatilus/peppercorn/middleware"
	"github.com/boatilus/peppercorn/paths"
	"github.com/boatilus/peppercorn/routes"
	"github.com/evalphobia/logrus_sentry"
	"github.com/pressly/chi"
	chiMiddleware "github.com/pressly/chi/middleware"
	"github.com/spf13/viper"
	"rsc.io/letsencrypt"
)

// Follows semantic versioning: http://semver.org/
const (
	versionMajor = 0
	versionMinor = 1
	versionPatch = 1
)

func init() {
	viper.SetConfigName("config")
	viper.AddConfigPath(".")

	if err := viper.ReadInConfig(); err != nil {
		log.Fatal(err)
	}

	// Merely return and skip configuring the Sentry hook if no Sentry DSN specified in the config.
	dsn := viper.GetString("sentry.dsn")
	if dsn == "" {
		return
	}

	hook, err := logrus_sentry.NewSentryHook(dsn, []log.Level{
		log.PanicLevel,
		log.FatalLevel,
		log.ErrorLevel,
	})

	if err != nil {
		log.Error("Could not add Sentry logging hook:", err)
	}

	log.AddHook(hook)
	log.Print("Configured Sentry for logging hook")
}

func main() {
	if err := db.Connect(); err != nil {
		log.Fatal(err)
	}

	// Instantiate the secure cookie generator.
	cookie.CreateGenerator()

	r := chi.NewRouter()
	r.Use(chiMiddleware.RealIP)
	r.Use(chiMiddleware.Logger)
	r.Use(chiMiddleware.Recoverer)
	r.Use(chiMiddleware.CloseNotify)
	r.Use(chiMiddleware.Timeout(60 * time.Second))

	// TODO: Refactor these routes by protected/unprotected
	// GET
	r.With(middleware.Validate).Get("/", routes.IndexGetHandler)
	r.Get(paths.Get.SignIn, routes.SignInGetHandler)
	r.With(middleware.Validate).Get(paths.Get.SignOut, routes.SignOutGetHandler)
	r.With(middleware.Validate).Get(paths.Get.Page, routes.PageGetHandler)
	r.With(middleware.Validate).Get(paths.Get.Single, routes.SingleGetHandler)
	r.With(middleware.Validate).Get(paths.Get.TotalPostCount, routes.CountGetHandler)
	r.With(middleware.Validate).Get(paths.Get.Me, routes.MeGetHandler)

	// POST
	r.Post(paths.Post.SignIn, routes.SignInPostHandler)
	r.With(middleware.Validate).Post(paths.Post.Me, routes.MePostHandler)

	port := viper.GetString("port")
	if port == "" {
		log.Fatal("No port specified; aborting..")
	}

	// TODO: How do we serve pre-gzip'ed files?
	workDir, _ := os.Getwd()
	filesDir := filepath.Join(workDir, "static")
	r.FileServer("/static", http.Dir(filesDir))

	srv := &http.Server{Addr: port, Handler: r}

	log.Printf("Listening on %s..", port)

	// TODO: Handle this much more elegantly
	if port != ":8000" {
		var m letsencrypt.Manager
		if err := m.CacheFile("letsencrypt.cache"); err != nil {
			log.Fatal(err)
		}

		srv.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}

		log.Fatal(srv.ListenAndServeTLS("", ""))
	} else {
		log.Fatal(srv.ListenAndServe())
	}
}
<commit_msg>Remove Logrus, because complete insanity<commit_after>package main

import (
	"crypto/tls"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/boatilus/peppercorn/cookie"
	"github.com/boatilus/peppercorn/db"
	"github.com/boatilus/peppercorn/middleware"
	"github.com/boatilus/peppercorn/paths"
	"github.com/boatilus/peppercorn/routes"
	"github.com/pressly/chi"
	chiMiddleware "github.com/pressly/chi/middleware"
	"github.com/spf13/viper"
	"rsc.io/letsencrypt"
)

// Follows semantic versioning: http://semver.org/
const (
	versionMajor = 0
	versionMinor = 1
	versionPatch = 1
)

func init() {
	viper.SetConfigName("config")
	viper.AddConfigPath(".")

	if err := viper.ReadInConfig(); err != nil {
		log.Fatal(err)
	}

	// Merely return and skip configuring the Sentry hook if no Sentry DSN specified in the config.
	dsn := viper.GetString("sentry.dsn")
	if dsn == "" {
		return
	}
}

func main() {
	if err := db.Connect(); err != nil {
		log.Fatal(err)
	}

	// Instantiate the secure cookie generator.
	cookie.CreateGenerator()

	r := chi.NewRouter()
	r.Use(chiMiddleware.RealIP)
	r.Use(chiMiddleware.Logger)
	r.Use(chiMiddleware.Recoverer)
	r.Use(chiMiddleware.CloseNotify)
	r.Use(chiMiddleware.Timeout(60 * time.Second))

	// TODO: Refactor these routes by protected/unprotected
	// GET
	r.With(middleware.Validate).Get("/", routes.IndexGetHandler)
	r.Get(paths.Get.SignIn, routes.SignInGetHandler)
	r.With(middleware.Validate).Get(paths.Get.SignOut, routes.SignOutGetHandler)
	r.With(middleware.Validate).Get(paths.Get.Page, routes.PageGetHandler)
	r.With(middleware.Validate).Get(paths.Get.Single, routes.SingleGetHandler)
	r.With(middleware.Validate).Get(paths.Get.TotalPostCount, routes.CountGetHandler)
	r.With(middleware.Validate).Get(paths.Get.Me, routes.MeGetHandler)

	// POST
	r.Post(paths.Post.SignIn, routes.SignInPostHandler)
	r.With(middleware.Validate).Post(paths.Post.Me, routes.MePostHandler)

	port := viper.GetString("port")
	if port == "" {
		log.Fatal("No port specified; aborting..")
	}

	// TODO: How do we serve pre-gzip'ed files?
	workDir, _ := os.Getwd()
	filesDir := filepath.Join(workDir, "static")
	r.FileServer("/static", http.Dir(filesDir))

	srv := &http.Server{Addr: port, Handler: r}

	log.Printf("Listening on %s..", port)

	// TODO: Handle this much more elegantly
	if port != ":8000" {
		var m letsencrypt.Manager
		if err := m.CacheFile("letsencrypt.cache"); err != nil {
			log.Fatal(err)
		}

		srv.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}

		log.Fatal(srv.ListenAndServeTLS("", ""))
	} else {
		log.Fatal(srv.ListenAndServe())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"math"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/jawher/mow.cli"
	"github.com/nlopes/slack"
)

const (
	NAME = "standup-slackbot"
	DESC = "A slackbot for standups"
)

func main() {
	app := cli.App(NAME, DESC)
	slackToken := app.String(cli.StringOpt{
		Name:   "slack-token",
		Desc:   "Slack API token",
		EnvVar: "SLACK_TOKEN",
	})
	standupChannelName := app.String(cli.StringOpt{
		Name:   "standup-channel",
		Desc:   "The Slack channel to use for standups",
		EnvVar: "STANDUP_CHANNEL",
	})
	standupTime := app.String(cli.StringOpt{
		Name:   "standup-time",
		Desc:   "The time standup should start in 24hr 00:00 format",
		EnvVar: "STANDUP_TIME",
	})
	standupLengthMins := app.Int(cli.IntOpt{
		Name:   "standup-length-mins",
		Desc:   "The standup length time in minutes",
		EnvVar: "STANDUP_LENGTH_MINS",
		Value:  60,
	})
	timeZone := app.String(cli.StringOpt{
		Name:   "time-zone",
		Desc:   "The timezone IANA format e.g. Europe/London",
		EnvVar: "TIME_ZONE",
		Value:  "Europe/London",
	})
	app.Action = func() {
		var lastStandupDay *int = nil
		tz, err := time.LoadLocation(*timeZone)
		if err != nil {
			log.Fatalf("Error getting location for timezone: %v", err)
		}
		for {
			<-time.After(1 * time.Minute)
			now := time.Now().In(tz)

			isWeekend := now.Weekday() < 1 || now.Weekday() > 5
			if isWeekend {
				continue
			}

			day := now.Day()

			standupAlreadyDone := lastStandupDay != nil && *lastStandupDay == day
			if standupAlreadyDone {
				continue
			}

			hour, mins, err := parseStandupStartTime(standupTime)
			if err != nil {
				log.Fatalf("Error parsing standup start time: %v", err)
			}

			notTimeYet := now.Hour() < *hour || now.Minute() > *mins
			if notTimeYet {
				continue
			}

			standupStartTime := time.Date(now.Year(), now.Month(), now.Day(), *hour, *mins, 0, 0, tz)
			standupDuration := time.Minute * time.Duration(*standupLengthMins)

			// this prevents us doing standup in the case where we have no prior state (due to a restart)
			// but have already done standup for the day
			standupEndTimePassed := lastStandupDay == nil && standupStartTime.Add(standupDuration).Before(now)
			if standupEndTimePassed {
				continue
			}

			if err := DoStandup(*slackToken, *standupChannelName, *standupLengthMins); err != nil {
				log.Fatalf("Error doing standup: %v", err)
			}

			lastStandupDay = &day
		}
	}
	app.Run(os.Args)
}

func parseStandupStartTime(standupTime *string) (*int, *int, error) {
	hoursAndMins := strings.Split(*standupTime, ":")
	hour, err := strconv.ParseInt(hoursAndMins[0], 10, 8)
	if err != nil {
		return nil, nil, fmt.Errorf("Could not parse hours from standup start time: %v", err)
	}
	mins, err := strconv.ParseInt(hoursAndMins[1], 10, 8)
	if err != nil {
		return nil, nil, fmt.Errorf("Could not parse mins from standup start time: %v", err)
	}
	hourInt := int(hour)
	minsInt := int(mins)
	return &hourInt, &minsInt, nil
}

func DoStandup(slackToken string, standupChannelName string, standupLengthMins int) error {
	baseParams := slack.NewPostMessageParameters()
	baseParams.Username = "Standup Bot"

	slackClient := &Slack{
		slack.New(slackToken),
		make(map[string]string),
		make(map[string]func(event *slack.MessageEvent)),
		sync.Mutex{},
		baseParams,
	}

	channelId, err := slackClient.GetChannelIdForChannel(standupChannelName)
	if err != nil {
		return fmt.Errorf("Could not get channel ID for channel %s: %v", standupChannelName, err)
	}

	members, err := slackClient.GetChannelMembers(*channelId)
	if err != nil {
		return fmt.Errorf("Error getting standup channel members: %v", err)
	}

	standup := NewStandup(slackClient, time.Now().Add(time.Minute*time.Duration(standupLengthMins)), members)
	results := standup.Start()

	for i := 0; i < 5; i++ {
		_, _, err = slackClient.apiClient.PostMessage(
			*channelId,
			"Standup is finished, keep up the good work team!",
			BuildSlackReport(baseParams, results),
		)
		if err == nil {
			break
		}
		log.Printf("Error posting standup result to Slack: %v", err)
		<-time.After(time.Duration(3*math.Pow(2, float64(i+1))) * time.Second)
	}
	return nil
}

func BuildSlackReport(baseParams slack.PostMessageParameters, questionnaires map[string]*StandupQuestionnaire) slack.PostMessageParameters {
	postParams := baseParams
	attachments := make([]slack.Attachment, len(questionnaires))
	for _, questionnaire := range questionnaires {
		attachment := slack.Attachment{
			Fallback:   "There is no fallback text...",
			AuthorIcon: questionnaire.Member.Profile.Image48,
			AuthorName: questionnaire.Member.RealName,
			Fields: []slack.AttachmentField{
				{
					Title: "What did you get done since last standup?",
					Short: false,
					Value: questionnaire.yesterday,
				},
				{
					Title: "What are you working on today?",
					Short: false,
					Value: questionnaire.today,
				},
				{
					Title: "When do you think you'll be finished?",
					Short: false,
					Value: questionnaire.finishedWhen,
				},
				{
					Title: "Is there anything blocking you?",
					Short: false,
					Value: questionnaire.blockers,
				},
			},
		}
		attachments = append(attachments, attachment)
	}
	postParams.Attachments = attachments
	return postParams
}
<commit_msg>fix predicate for checking if standup time<commit_after>package main

import (
	"fmt"
	"log"
	"math"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/jawher/mow.cli"
	"github.com/nlopes/slack"
)

const (
	NAME = "standup-slackbot"
	DESC = "A slackbot for standups"
)

func main() {
	app := cli.App(NAME, DESC)
	slackToken := app.String(cli.StringOpt{
		Name:   "slack-token",
		Desc:   "Slack API token",
		EnvVar: "SLACK_TOKEN",
	})
	standupChannelName := app.String(cli.StringOpt{
		Name:   "standup-channel",
		Desc:   "The Slack channel to use for standups",
		EnvVar: "STANDUP_CHANNEL",
	})
	standupTime := app.String(cli.StringOpt{
		Name:   "standup-time",
		Desc:   "The time standup should start in 24hr 00:00 format",
		EnvVar: "STANDUP_TIME",
	})
	standupLengthMins := app.Int(cli.IntOpt{
		Name:   "standup-length-mins",
		Desc:   "The standup length time in minutes",
		EnvVar: "STANDUP_LENGTH_MINS",
		Value:  60,
	})
	timeZone := app.String(cli.StringOpt{
		Name:   "time-zone",
		Desc:   "The timezone IANA format e.g. Europe/London",
		EnvVar: "TIME_ZONE",
		Value:  "Europe/London",
	})
	app.Action = func() {
		var lastStandupDay *int = nil
		tz, err := time.LoadLocation(*timeZone)
		if err != nil {
			log.Fatalf("Error getting location for timezone: %v", err)
		}
		for {
			<-time.After(1 * time.Minute)
			now := time.Now().In(tz)

			isWeekend := now.Weekday() < 1 || now.Weekday() > 5
			if isWeekend {
				continue
			}

			day := now.Day()

			standupAlreadyDone := lastStandupDay != nil && *lastStandupDay == day
			if standupAlreadyDone {
				continue
			}

			hour, mins, err := parseStandupStartTime(standupTime)
			if err != nil {
				log.Fatalf("Error parsing standup start time: %v", err)
			}

			notTimeYet := now.Hour() < *hour || now.Minute() < *mins
			if notTimeYet {
				continue
			}

			standupStartTime := time.Date(now.Year(), now.Month(), now.Day(), *hour, *mins, 0, 0, tz)
			standupDuration := time.Minute * time.Duration(*standupLengthMins)

			// this prevents us doing standup in the case where we have no prior state (due to a restart)
			// but have already done standup for the day
			standupEndTimePassed := lastStandupDay == nil && standupStartTime.Add(standupDuration).Before(now)
			if standupEndTimePassed {
				continue
			}

			if err := DoStandup(*slackToken, *standupChannelName, *standupLengthMins); err != nil {
				log.Fatalf("Error doing standup: %v", err)
			}

			lastStandupDay = &day
		}
	}
	app.Run(os.Args)
}

func parseStandupStartTime(standupTime *string) (*int, *int, error) {
	hoursAndMins := strings.Split(*standupTime, ":")
	hour, err := strconv.ParseInt(hoursAndMins[0], 10, 8)
	if err != nil {
		return nil, nil, fmt.Errorf("Could not parse hours from standup start time: %v", err)
	}
	mins, err := strconv.ParseInt(hoursAndMins[1], 10, 8)
	if err != nil {
		return nil, nil, fmt.Errorf("Could not parse mins from standup start time: %v", err)
	}
	hourInt := int(hour)
	minsInt := int(mins)
	return &hourInt, &minsInt, nil
}

func DoStandup(slackToken string, standupChannelName string, standupLengthMins int) error {
	baseParams := slack.NewPostMessageParameters()
	baseParams.Username = "Standup Bot"

	slackClient := &Slack{
		slack.New(slackToken),
		make(map[string]string),
		make(map[string]func(event *slack.MessageEvent)),
		sync.Mutex{},
		baseParams,
	}

	channelId, err := slackClient.GetChannelIdForChannel(standupChannelName)
	if err != nil {
		return fmt.Errorf("Could not get channel ID for channel %s: %v", standupChannelName, err)
	}

	members, err := slackClient.GetChannelMembers(*channelId)
	if err != nil {
		return fmt.Errorf("Error getting standup channel members: %v", err)
	}

	standup := NewStandup(slackClient, time.Now().Add(time.Minute*time.Duration(standupLengthMins)), members)
	results := standup.Start()

	for i := 0; i < 5; i++ {
		_, _, err = slackClient.apiClient.PostMessage(
			*channelId,
			"Standup is finished, keep up the good work team!",
			BuildSlackReport(baseParams, results),
		)
		if err == nil {
			break
		}
		log.Printf("Error posting standup result to Slack: %v", err)
		<-time.After(time.Duration(3*math.Pow(2, float64(i+1))) * time.Second)
	}
	return nil
}

func BuildSlackReport(baseParams slack.PostMessageParameters, questionnaires map[string]*StandupQuestionnaire) slack.PostMessageParameters {
	postParams := baseParams
	attachments := make([]slack.Attachment, len(questionnaires))
	for _, questionnaire := range questionnaires {
		attachment := slack.Attachment{
			Fallback:   "There is no fallback text...",
			AuthorIcon: questionnaire.Member.Profile.Image48,
			AuthorName: questionnaire.Member.RealName,
			Fields: []slack.AttachmentField{
				{
					Title: "What did you get done since last standup?",
					Short: false,
					Value: questionnaire.yesterday,
				},
				{
					Title: "What are you working on today?",
					Short: false,
					Value: questionnaire.today,
				},
				{
					Title: "When do you think you'll be finished?",
					Short: false,
					Value: questionnaire.finishedWhen,
				},
				{
					Title: "Is there anything blocking you?",
					Short: false,
					Value: questionnaire.blockers,
				},
			},
		}
		attachments = append(attachments, attachment)
	}
	postParams.Attachments = attachments
	return postParams
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"strconv"
	"time"

	"github.com/stretchr/gomniauth"
	"github.com/stretchr/gomniauth/providers/facebook"
	"github.com/stretchr/objx"

	"github.com/stripe/stripe-go"
	"github.com/stripe/stripe-go/customer"
	"github.com/stripe/stripe-go/sub"

	"github.com/gorilla/mux"
	"gopkg.in/pg.v5"
)

var config struct {
	StripeSecretKey string
	// StripePlan should be a Plan in Stripe with the price of
	// 0.01 and billed monthly.
	StripePlan string

	DB *pg.DB
}

// Org is a table of the organizations that we will be supporting for
// donations..
type Org struct {
	Id int64

	Name string
	EIN  string

	Address string
	City    string
	State   string
	Country string

	Category string

	Verified bool

	StripeCustomerID string `json:"-"`

	CreatedAt time.Time
	UpdatedAt time.Time

	Users   []User   `pg:",many2many:user_orgs"`
	Ledgers []Ledger `pg:",many2many:ledgers"`
}

// User contains the information about the user who will be doing the
// donations.
type User struct {
	Id int64

	FacebookID string

	StripeCustomerID     string `json:"-"`
	StripeSubscriptionID string `json:"-"`

	CreatedAt time.Time
	UpdatedAt time.Time

	Orgs    []Org    `pg:",many2many:user_orgs"`
	Ledgers []Ledger `pg:",many2many:ledgers"`
}

// UserOrg creates a map of the org that the user is donating to. A
// user can have more than one of the same user and org map. That is
// how we make donating more to a certain cause easier.
type UserOrg struct {
	UserId int64 `sql:",pk"`
	OrgId  int64 `sql:",pk"`

	CreatedAt time.Time
	UpdatedAt time.Time
}

// PaymentLedger contains the leger of all the transations that have
// happened in the system. So we have a complete list of transactions
// as they have happened.
type Ledger struct {
	UserId int64 `sql:",pk"`
	OrgId  int64 `sql:",pk"`
	Amount float64

	CreatedAt time.Time
	UpdatedAt time.Time
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	provider, err := gomniauth.Provider("facebook")
	if err != nil {
		panic(err)
	}

	state := gomniauth.NewState("after", "success")

	// This code borrowed from goweb example and not fixed.
	// if you want to request additional scopes from the provider,
	// pass them as login?scope=scope1,scope2
	//options := objx.MSI("scope", ctx.QueryValue("scope"))

	authUrl, err := provider.GetBeginAuthURL(state, nil)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// redirect
	http.Redirect(w, r, authUrl, http.StatusFound)

}

func loginCallbackHandler(w http.ResponseWriter, r *http.Request) {
	provider, err := gomniauth.Provider("facebook")
	if err != nil {
		panic(err)
	}

	omap, err := objx.FromURLQuery(r.URL.RawQuery)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	creds, err := provider.CompleteAuth(omap)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	/*
		    // This code borrowed from goweb example and not fixed.
		    // get the state
		    state, err := gomniauth.StateFromParam(ctx.QueryValue("state"))
		    if err != nil {
			    http.Error(w, err.Error(), http.StatusInternalServerError)
			    return
		    }
		    // redirect to the 'after' URL
		    afterUrl := state.GetStringOrDefault("after", "error?e=No after parameter was set in the state")
	*/

	// load the user
	user, userErr := provider.GetUser(creds)

	if userErr != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	data := fmt.Sprintf("%#v", user)
	io.WriteString(w, data)

	// redirect
	//return goweb.Respond.WithRedirect(ctx, afterUrl)

}

func showOrgsHandler(w http.ResponseWriter, r *http.Request) {
	var (
		vars  = mux.Vars(r)
		orgId = vars["orgId"]
		org   Org
		err   error
	)

	if org.Id, err = strconv.ParseInt(orgId, 10, 64); err != nil {
		respondJson(w, r, err)
		return
	}

	if err = config.DB.Select(&org); err != nil {
		respondJson(w, r, err)
		return
	}

	respondJson(w, r, org)
}

func searchOrgsHandler(w http.ResponseWriter, r *http.Request) {
	var (
		orgs []Org
	)

	err := config.DB.Model(&orgs).Where().Limit(50).Select()
	if err != nil {
		respondJson(w, r, err)
	}

	respondJson(w, r, orgs)
}

func createPaymentsHandler(w http.ResponseWriter, r *http.Request) {
	var plan struct {
		// Amount is the amount User wants to donate in cents
		Amount      uint64
		StripeToken string
	}

	customerParams := &stripe.CustomerParams{
		Desc: "Customer for jacob.jackson@example.com",
	}
	customerParams.SetSource(plan.StripeToken) // obtained with Stripe.js
	c, err := customer.New(customerParams)

	s, err := sub.New(&stripe.SubParams{
		Customer: "cus_9sek9eRTNJ0BdG",
		Plan:     config.StripePlan,
		Quantity: plan.Amount,
	})

}

func updatePaymentsHandler(w http.ResponseWriter, r *http.Request) {
	var plan struct {
		// NewAmount is the amount User wants to donate in cents
		NewAmount uint64
	}

	// TODO: Check amount > 0

	s, err := sub.Update(
		"sub_9sed4J2K4jurwS", // TODO: Get User's subscription
		&stripe.SubParams{
			Plan:     config.StripePlan,
			Quantity: plan.NewAmount,
		},
	)

	renderJson(w, r, struct{}{})
}

func deletePaymentsHandler(w http.ResponseWriter, r *http.Request) {
	err := sub.Cancel(
		"sub_9sed4J2K4jurwS", // TODO: Cancel user's subscription
	)

	renderJson(w, r, struct{}{})
}

func putUserOrgsHandler(w http.ResponseWriter, r *http.Request) {
	var (
		vars    = mux.Vars(r)
		orgId   = vars["orgId"]
		userOrg UserOrg
		err     error
	)

	if userOrg.OrgId, err = strconv.ParseInt(orgId, 10, 64); err != nil {
		respondJson(w, r, err)
		return
	}

	if err = config.DB.Insert(&userOrg); err != nil {
		respondJson(w, r, err)
		return
	}

	respondJson(w, r, userOrg)
}

func deleteUserOrgsHandler(w http.ResponseWriter, r *http.Request) {
	var (
		vars    = mux.Vars(r)
		orgId   = vars["orgId"]
		userOrg UserOrg
		err     error
	)

	if userOrg.OrgId, err = strconv.ParseInt(orgId, 10, 64); err != nil {
		respondJson(w, r, err)
		return
	}

	_, err = config.DB.Model(&userOrg).Where("user_id = ?user_id and org_id = ?org_id").Limit(1).Delete()
	if err != nil {
		respondJson(w, r, err)
		return

	}

	respondJson(w, r, struct{}{})
}

func showUserHandler(w http.ResponseWriter, r *http.Request) {
	var (
		vars   = mux.Vars(r)
		userId = vars["userId"]
		user   User
		err    error
	)

	if user.Id, err = strconv.ParseInt(userId, 10, 64); err != nil {
		respondJson(w, r, err)
		return
	}

	if err = config.DB.Select(&user).Column("orgs.*", "Orgs").Column("ledgers.*", "Ledgers"); err != nil {
		respondJson(w, r, err)
		return
	}

	respondJson(w, r, user)
}

func main() {
	config.DB = pg.Connect(&pg.Options{
		User: "postgres",
	})

	stripe.Key = config.StripeSecretKey

	// TODO Fix this
	gomniauth.SetSecurityKey("yLiCQYG7CAflDavqGH461IO0MHp7TEbpg6TwHBWdJzNwYod1i5ZTbrIF5bEoO3oP") // NOTE: DO NOT COPY THIS - MAKE YOR OWN!
	gomniauth.WithProviders(
		// TODO Move this to config and get actual keys.
		facebook.New("537611606322077", "f9f4d77b3d3f4f5775369f5c9f88f65e", "http://localhost:8080/auth/facebook/callback"),
	)

	r := mux.NewRouter()
	r.HandleFunc("/auth/facebook", loginHandler)
	r.HandleFunc("/auth/facebook/callback", loginCallbackHandler)

	r.HandleFunc("/v1/orgs/{orgId}", showOrgsHandler).Methods("GET")
	r.HandleFunc("/v1/orgs", searchOrgsHandler).Methods("GET")

	r.HandleFunc("/v1/payments", createPaymentsHandler).Methods("POST")
	r.HandleFunc("/v1/payments", updatePaymentsHandler).Methods("UPDATE")
	r.HandleFunc("/v1/payments", deletePaymentsHandler).Methods("DELETE")
	//	r.HandleFunc("/v1/payments/stripe-callback", callbackPaymentsHandler).Methods("POST")

	r.HandleFunc("/v1/user/orgs/{orgId}", putUserOrgsHandler).Methods("PUT")
	r.HandleFunc("/v1/user/orgs/{orgId}", deleteUserOrgsHandler).Methods("DELETE")

	r.HandleFunc("/v1/user", showUserHandler)

	http.Handle("/", r)
}
<commit_msg>Fix a few more bugs<commit_after>package main

import (
	"net/http"
	"strconv"
	"time"

	"github.com/stretchr/gomniauth"
	"github.com/stretchr/gomniauth/providers/facebook"
	"github.com/stretchr/objx"

	"github.com/stripe/stripe-go"
	"github.com/stripe/stripe-go/customer"
	"github.com/stripe/stripe-go/sub"

	"github.com/gorilla/mux"
	"gopkg.in/pg.v5"
)

var config struct {
	StripeSecretKey string
	// StripePlan should be a Plan in Stripe with the price of
	// 0.01 and billed monthly.
	StripePlan string

	DB *pg.DB
}

// Org is a table of the organizations that we will be supporting for
// donations..
type Org struct {
	Id int64

	Name string
	EIN  string

	Address string
	City    string
	State   string
	Country string

	Category string

	Verified bool

	StripeCustomerID string `json:"-"`

	CreatedAt time.Time
	UpdatedAt time.Time

	Users   []User   `pg:",many2many:user_orgs"`
	Ledgers []Ledger `pg:",many2many:ledgers"`
}

// User contains the information about the user who will be doing the
// donations.
type User struct {
	Id int64

	FacebookID string

	StripeCustomerID     string `json:"-"`
	StripeSubscriptionID string `json:"-"`

	CreatedAt time.Time
	UpdatedAt time.Time

	Orgs    []Org    `pg:",many2many:user_orgs"`
	Ledgers []Ledger `pg:",many2many:ledgers"`
}

// UserOrg creates a map of the org that the user is donating to. A
// user can have more than one of the same user and org map. That is
// how we make donating more to a certain cause easier.
type UserOrg struct {
	UserId int64 `sql:",pk"`
	OrgId  int64 `sql:",pk"`

	CreatedAt time.Time
	UpdatedAt time.Time
}

// PaymentLedger contains the leger of all the transations that have
// happened in the system. So we have a complete list of transactions
// as they have happened.
type Ledger struct {
	UserId int64 `sql:",pk"`
	OrgId  int64 `sql:",pk"`
	Amount float64

	CreatedAt time.Time
	UpdatedAt time.Time
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	provider, err := gomniauth.Provider("facebook")
	if err != nil {
		panic(err)
	}

	state := gomniauth.NewState("after", "success")

	// This code borrowed from goweb example and not fixed.
	// if you want to request additional scopes from the provider,
	// pass them as login?scope=scope1,scope2
	//options := objx.MSI("scope", ctx.QueryValue("scope"))

	authUrl, err := provider.GetBeginAuthURL(state, nil)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// redirect
	http.Redirect(w, r, authUrl, http.StatusFound)

}

func loginCallbackHandler(w http.ResponseWriter, r *http.Request) {
	provider, err := gomniauth.Provider("facebook")
	if err != nil {
		panic(err)
	}

	omap, err := objx.FromURLQuery(r.URL.RawQuery)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	creds, err := provider.CompleteAuth(omap)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	/*
		    // This code borrowed from goweb example and not fixed.
		    // get the state
		    state, err := gomniauth.StateFromParam(ctx.QueryValue("state"))
		    if err != nil {
			    http.Error(w, err.Error(), http.StatusInternalServerError)
			    return
		    }
		    // redirect to the 'after' URL
		    afterUrl := state.GetStringOrDefault("after", "error?e=No after parameter was set in the state")
	*/

	// load the user
	user, userErr := provider.GetUser(creds)

	if userErr != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	data := fmt.Sprintf("%#v", user)
	io.WriteString(w, data)

	// redirect
	//return goweb.Respond.WithRedirect(ctx, afterUrl)

}

func showOrgsHandler(w http.ResponseWriter, r *http.Request) {
	var (
		vars  = mux.Vars(r)
		orgId = vars["orgId"]
		org   Org
		err   error
	)

	if org.Id, err = strconv.ParseInt(orgId, 10, 64); err != nil {
		respondJson(w, r, err)
		return
	}

	if err = config.DB.Select(&org); err != nil {
		respondJson(w, r, err)
		return
	}

	respondJson(w, r, org)
}

func searchOrgsHandler(w http.ResponseWriter, r *http.Request) {
	var (
		orgs []Org
	)

	err := config.DB.Model(&orgs).Where().Limit(50).Select()
	if err != nil {
		respondJson(w, r, err)
	}

	respondJson(w, r, orgs)
}

func createPaymentsHandler(w http.ResponseWriter, r *http.Request) {
	var plan struct {
		// Amount is the amount User wants to donate in cents
		Amount      uint64
		StripeToken string
	}

	customerParams := &stripe.CustomerParams{
		Desc: "Customer for jacob.jackson@example.com",
	}
	customerParams.SetSource(plan.StripeToken) // obtained with Stripe.js
	c, err := customer.New(customerParams)

	s, err := sub.New(&stripe.SubParams{
		Customer: "cus_9sek9eRTNJ0BdG",
		Plan:     config.StripePlan,
		Quantity: plan.Amount,
	})

}

func updatePaymentsHandler(w http.ResponseWriter, r *http.Request) {
	var plan struct {
		// NewAmount is the amount User wants to donate in cents
		NewAmount uint64
	}

	// TODO: Check amount > 0

	s, err := sub.Update(
		"sub_9sed4J2K4jurwS", // TODO: Get User's subscription
		&stripe.SubParams{
			Plan:     config.StripePlan,
			Quantity: plan.NewAmount,
		},
	)

	respondJson(w, r, struct{}{})
}

func deletePaymentsHandler(w http.ResponseWriter, r *http.Request) {
	err := sub.Cancel(
		"sub_9sed4J2K4jurwS", // TODO: Cancel user's subscription
	)

	respondJson(w, r, struct{}{})
}

func putUserOrgsHandler(w http.ResponseWriter, r *http.Request) {
	var (
		vars    = mux.Vars(r)
		orgId   = vars["orgId"]
		userOrg UserOrg
		err     error
	)

	if userOrg.OrgId, err = strconv.ParseInt(orgId, 10, 64); err != nil {
		respondJson(w, r, err)
		return
	}

	if err = config.DB.Insert(&userOrg); err != nil {
		respondJson(w, r, err)
		return
	}

	respondJson(w, r, userOrg)
}

func deleteUserOrgsHandler(w http.ResponseWriter, r *http.Request) {
	var (
		vars    = mux.Vars(r)
		orgId   = vars["orgId"]
		userOrg UserOrg
		err     error
	)

	if userOrg.OrgId, err = strconv.ParseInt(orgId, 10, 64); err != nil {
		respondJson(w, r, err)
		return
	}

	_, err = config.DB.Model(&userOrg).Where("user_id = ?user_id and org_id = ?org_id").Limit(1).Delete()
	if err != nil {
		respondJson(w, r, err)
		return

	}

	respondJson(w, r, struct{}{})
}

func showUserHandler(w http.ResponseWriter, r *http.Request) {
	var (
		vars   = mux.Vars(r)
		userID = vars["userId"]
		user   User
		err    error
	)

	if user.Id, err = strconv.ParseInt(userID, 10, 64); err != nil {
		respondJson(w, r, err)
		return
	}

	if err = config.DB.Model(&user).Column("orgs.*", "Orgs").Column("ledgers.*", "Ledgers").Select(); err != nil {
		respondJson(w, r, err)
		return
	}

	respondJson(w, r, user)
}

func main() {
	config.DB = pg.Connect(&pg.Options{
		User: "postgres",
	})

	stripe.Key = config.StripeSecretKey

	// TODO Fix this
	gomniauth.SetSecurityKey("yLiCQYG7CAflDavqGH461IO0MHp7TEbpg6TwHBWdJzNwYod1i5ZTbrIF5bEoO3oP") // NOTE: DO NOT COPY THIS - MAKE YOR OWN!
	gomniauth.WithProviders(
		// TODO Move this to config and get actual keys.
		facebook.New("537611606322077", "f9f4d77b3d3f4f5775369f5c9f88f65e", "http://localhost:8080/auth/facebook/callback"),
	)

	r := mux.NewRouter()
	r.HandleFunc("/auth/facebook", loginHandler)
	r.HandleFunc("/auth/facebook/callback", loginCallbackHandler)

	r.HandleFunc("/v1/orgs/{orgId}", showOrgsHandler).Methods("GET")
	r.HandleFunc("/v1/orgs", searchOrgsHandler).Methods("GET")

	r.HandleFunc("/v1/payments", createPaymentsHandler).Methods("POST")
	r.HandleFunc("/v1/payments", updatePaymentsHandler).Methods("UPDATE")
	r.HandleFunc("/v1/payments", deletePaymentsHandler).Methods("DELETE")
	//	r.HandleFunc("/v1/payments/stripe-callback", callbackPaymentsHandler).Methods("POST")

	r.HandleFunc("/v1/user/orgs/{orgId}", putUserOrgsHandler).Methods("PUT")
	r.HandleFunc("/v1/user/orgs/{orgId}", deleteUserOrgsHandler).Methods("DELETE")

	r.HandleFunc("/v1/user", showUserHandler)

	http.Handle("/", r)
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

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

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

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

// 也可以以服务的方式启动nging
// 服务支持的操作有：
// nging service install  	-- 安装服务
// nging service uninstall  -- 卸载服务
// nging service start 		-- 启动服务
// nging service stop 		-- 停止服务
// nging service restart 	-- 重启服务
package main

//go:generate go get github.com/admpub/go-bindata/...
//go:generate go-bindata -fs -o bindata_assetfs.go -ignore "\\.(git|svn|DS_Store|less|scss)$" -minify "\\.(js|css)$" -tags bindata public/assets/... template/... config/i18n/...

import (
	"github.com/admpub/log"
	"github.com/admpub/nging/application/cmd"
	"github.com/webx-top/echo"

	//register
	_ "github.com/admpub/nging/application"
	_ "github.com/admpub/nging/application/initialize/manager"
	_ "github.com/admpub/nging/application/library/sqlite"
)

var (
	BUILD_TIME string
	CLOUD_GOX  string
	COMMIT     string
	LABEL      = `dev` //beta/alpha/stable
	VERSION    = `2.0.5`

	version   string
	schemaVer = 2.3 //数据表结构版本
)

func main() {
	defer log.Sync()
	echo.Set(`BUILD_TIME`, BUILD_TIME)
	echo.Set(`COMMIT`, COMMIT)
	echo.Set(`LABEL`, LABEL)
	echo.Set(`VERSION`, VERSION)
	echo.Set(`SCHEMA_VER`, schemaVer)
	exec()
}

func exec() {
	cmd.Execute()
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

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

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

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

// 也可以以服务的方式启动nging
// 服务支持的操作有：
// nging service install  	-- 安装服务
// nging service uninstall  -- 卸载服务
// nging service start 		-- 启动服务
// nging service stop 		-- 停止服务
// nging service restart 	-- 重启服务
package main

//go:generate go get github.com/admpub/bindata/...
//go:generate go-bindata -fs -o bindata_assetfs.go -ignore "\\.(git|svn|DS_Store|less|scss)$" -minify "\\.(js|css)$" -tags bindata public/assets/... template/... config/i18n/...

import (
	"github.com/admpub/log"
	"github.com/admpub/nging/application/cmd"
	"github.com/webx-top/echo"

	//register
	_ "github.com/admpub/nging/application"
	_ "github.com/admpub/nging/application/initialize/manager"
	_ "github.com/admpub/nging/application/library/sqlite"
)

var (
	BUILD_TIME string
	CLOUD_GOX  string
	COMMIT     string
	LABEL      = `dev` //beta/alpha/stable
	VERSION    = `2.0.5`

	version   string
	schemaVer = 2.3 //数据表结构版本
)

func main() {
	defer log.Sync()
	echo.Set(`BUILD_TIME`, BUILD_TIME)
	echo.Set(`COMMIT`, COMMIT)
	echo.Set(`LABEL`, LABEL)
	echo.Set(`VERSION`, VERSION)
	echo.Set(`SCHEMA_VER`, schemaVer)
	exec()
}

func exec() {
	cmd.Execute()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"encoding/csv"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"
	"github.com/iomz/go-llrp"
)

type Tag struct {
	pcBits        string
	length        int64
	epcLengthBits int64
	epc           []byte
	readData      []byte
}

const (
	VERSION     = "0.0.1"
	BUFSIZE     = 512
)

var (
	app                = kingpin.New("gologir", "A mock LLRP-based logical reader for RFID Tags.")
	verbose            = app.Flag("verbose", "Enable verbose mode.").Bool()
	initalMessageID    = app.Flag("initialMessageID", "The initial messageID to start from.").Default("1000").Int()
	initialKeepaliveID = app.Flag("initialKeepaliveID", "The initial keepaliveID to start from.").Default("80000").Int()
	port               = app.Flag("port", "LLRP listening port.").Default("5084").Int()
	ip                 = app.Flag("ip", "LLRP listening address.").Default("127.0.0.1").IP()

	stream = app.Command("stream", "Run as a tag streamer.")
	//sub  = stream.Arg("sub", "Sub option.")

	client = app.Command("client", "Run as a client mode.")

	messageID   = *initalMessageID
	keepaliveID = *initialKeepaliveID
)

func Check(e error) {
	if e != nil {
		panic(e)
	}
}

func BuildTag(record []string) (Tag, error) {
	// If the row is incomplete
	if len(record) != 4 {
		var t Tag
		return t, io.EOF
	}

	pcBits := record[0]
	length, err := strconv.ParseInt(record[1], 10, 16)
	Check(err)
	epcLengthBits, err := strconv.ParseInt(record[2], 10, 16)
	Check(err)
	epc, err := hex.DecodeString(record[3])
	Check(err)
	readData, err := hex.DecodeString("a896")
	Check(err)

	tag := Tag{pcBits, length, epcLengthBits, epc, readData}
	return tag, nil
}

func readTagsFromCSV(csvfile string) []*Tag {
	csv_in, err := ioutil.ReadFile(csvfile)
	Check(err)
	r := csv.NewReader(strings.NewReader(string(csv_in)))

	tags := []*Tag{}
	for {
		record, err := r.Read()
		// If reached at the end
		if err == io.EOF {
			break
		}
		Check(err)

		// Construct a tag read data
		tag, err := BuildTag(record)
		if err != nil {
			continue
		}
		tags = append(tags, &tag)
	}
	return tags
}

func emit(conn net.Conn, tags []*Tag) {
	t := time.NewTicker(1 * time.Second)
	count := 0
	for {
		for _, tag := range tags {
			// Log the tag for debug
			//log.Printf("%+v\n", tag)

			// PeakRSSI
			peakRSSI := llrp.PeakRSSI()

			// AirProtocolTagData
			airProtocolTagData := llrp.C1G2PC(tag.pcBits)

			// EPCData
			epcData := llrp.EPCData(tag.length, tag.epcLengthBits, tag.epc)

			// OpSpecResult
			opSpecResult := llrp.C1G2ReadOpSpecResult(tag.readData)

			// Merge them into TagReportData
			tagReportData :=
				llrp.TagReportData(epcData,
					peakRSSI, airProtocolTagData,
					opSpecResult)

			/*

			   TODO: Here, maybe stack more TRDs to ROAR

			*/

			// Append TagReportData to ROAccessReport
			roAccessReport :=
				llrp.ROAccessReport(tagReportData, messageID)
			messageID += 1

			// Send
			conn.Write(roAccessReport)

			// Wait until ACK received
			time.Sleep(time.Millisecond)
		}
		select {
		case <-t.C:
			count += 1
		}
		if count >= 10 {
			conn.Write(llrp.Keepalive())
			buf := make([]byte, BUFSIZE)
			reqLen, err := conn.Read(buf)
			if err == io.EOF {
				// Close the connection when you're done with it.
				return
			} else if err != nil {
				log.Println("Error reading:", err.Error())
				log.Println("reqLen: " + string(reqLen))
				conn.Close()
			}
			header := binary.BigEndian.Uint16(buf[:2])
			if header != llrp.HEADER_KAA {
				log.Printf("Unknown header: %v\n", header)
				return
			}
			count = 0
		}
	}
}

// Handles incoming requests.
func handleRequest(conn net.Conn) {
	// Make a buffer to hold incoming data.
	buf := make([]byte, BUFSIZE)
	// Read the incoming connection into the buffer.
	reqLen, err := conn.Read(buf)
	if err == io.EOF {
		// Close the connection when you're done with it.
		return
	} else if err != nil {
		log.Println("Error reading:", err.Error())
		log.Println("reqLen: " + string(reqLen))
		conn.Close()
	}

	header := binary.BigEndian.Uint16(buf[:2])
	if header == llrp.HEADER_SRC {
		log.Println(">>> SET_READER_CONFIG")
		conn.Write(llrp.SetReaderConfigResponse())
		// Read virtual tags from a csv file
		tags := readTagsFromCSV("tags.csv")
		// Emit LLRP
		go emit(conn, tags)
	} else if header == llrp.HEADER_KAA {
		tags := readTagsFromCSV("tags.csv")
		go emit(conn, tags)
	} else {
		log.Printf("Unknown header: %v\n", header)
		fmt.Println("Message: %v", buf)
		return
	}
}

// stream mode
func runStream() {
	// Listen for incoming connections.
	l, err := net.Listen("tcp", ip.String()+":"+strconv.Itoa(*port))
	if err != nil {
		log.Println("Error listening:", err.Error())
		os.Exit(1)
	}
	// Close the listener when the application closes.
	defer l.Close()
	log.Println("Listening on " + ip.String() + ":" + strconv.Itoa(*port))

	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			fmt.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}

		// Send back READER_EVENT_NOTIFICATION
		conn.Write(llrp.ReaderEventNotification(messageID))
		messageID += 1
		time.Sleep(time.Millisecond)

		// Handle connections in a new goroutine.
		go handleRequest(conn)
	}

	// Handle SIGINT and SIGTERM.
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	log.Println(<-ch)
}

// client mode
func runClient() {
	// Establish a connection to the llrp client
	conn, err := net.Dial("tcp", ip.String()+":"+strconv.Itoa(*port))
	Check(err)

	buf := make([]byte, BUFSIZE)
	for {
		// Read the incoming connection into the buffer.
		reqLen, err := conn.Read(buf)
		if err == io.EOF {
			// Close the connection when you're done with it.
			return
		} else if err != nil {
			fmt.Println("Error reading:", err.Error())
			fmt.Println("reqLen: " + string(reqLen))
			conn.Close()
			break
		}

		header := binary.BigEndian.Uint16(buf[:2])
		if header == llrp.HEADER_REN {
			fmt.Println(">>> READER_EVENT_NOTIFICATION")
			conn.Write(llrp.SetReaderConfig(messageID))
		} else if header == llrp.HEADER_SRCR {
			fmt.Println(">>> SET_READER_CONFIG_RESPONSE")
		} else if header == llrp.HEADER_ROAR {
			fmt.Println(">>> RO_ACCESS_REPORT")
			fmt.Printf("Packet size: %v\n", reqLen)
			fmt.Printf("% x\n", buf[:reqLen])
		} else {
			fmt.Printf("Unknown header: %v\n", header)
		}
	}
}

func main() {
	app.Version(VERSION)
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case stream.FullCommand():
		runStream()
	case client.FullCommand():
		runClient()
	}
}
<commit_msg>rename header const from go-llrp<commit_after>package main

import (
	"encoding/binary"
	"encoding/csv"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"gopkg.in/alecthomas/kingpin.v2"
	"github.com/iomz/go-llrp"
)

type Tag struct {
	pcBits        string
	length        int64
	epcLengthBits int64
	epc           []byte
	readData      []byte
}

const (
	VERSION     = "0.0.1"
	BUFSIZE     = 512
)

var (
	app                = kingpin.New("gologir", "A mock LLRP-based logical reader for RFID Tags.")
	verbose            = app.Flag("verbose", "Enable verbose mode.").Bool()
	initalMessageID    = app.Flag("initialMessageID", "The initial messageID to start from.").Default("1000").Int()
	initialKeepaliveID = app.Flag("initialKeepaliveID", "The initial keepaliveID to start from.").Default("80000").Int()
	port               = app.Flag("port", "LLRP listening port.").Default("5084").Int()
	ip                 = app.Flag("ip", "LLRP listening address.").Default("127.0.0.1").IP()

	stream = app.Command("stream", "Run as a tag streamer.")
	//sub  = stream.Arg("sub", "Sub option.")

	client = app.Command("client", "Run as a client mode.")

	messageID   = *initalMessageID
	keepaliveID = *initialKeepaliveID
)

func Check(e error) {
	if e != nil {
		panic(e)
	}
}

func BuildTag(record []string) (Tag, error) {
	// If the row is incomplete
	if len(record) != 4 {
		var t Tag
		return t, io.EOF
	}

	pcBits := record[0]
	length, err := strconv.ParseInt(record[1], 10, 16)
	Check(err)
	epcLengthBits, err := strconv.ParseInt(record[2], 10, 16)
	Check(err)
	epc, err := hex.DecodeString(record[3])
	Check(err)
	readData, err := hex.DecodeString("a896")
	Check(err)

	tag := Tag{pcBits, length, epcLengthBits, epc, readData}
	return tag, nil
}

func readTagsFromCSV(csvfile string) []*Tag {
	csv_in, err := ioutil.ReadFile(csvfile)
	Check(err)
	r := csv.NewReader(strings.NewReader(string(csv_in)))

	tags := []*Tag{}
	for {
		record, err := r.Read()
		// If reached at the end
		if err == io.EOF {
			break
		}
		Check(err)

		// Construct a tag read data
		tag, err := BuildTag(record)
		if err != nil {
			continue
		}
		tags = append(tags, &tag)
	}
	return tags
}

func emit(conn net.Conn, tags []*Tag) {
	t := time.NewTicker(1 * time.Second)
	count := 0
	for {
		for _, tag := range tags {
			// Log the tag for debug
			//log.Printf("%+v\n", tag)

			// PeakRSSI
			peakRSSI := llrp.PeakRSSI()

			// AirProtocolTagData
			airProtocolTagData := llrp.C1G2PC(tag.pcBits)

			// EPCData
			epcData := llrp.EPCData(tag.length, tag.epcLengthBits, tag.epc)

			// OpSpecResult
			opSpecResult := llrp.C1G2ReadOpSpecResult(tag.readData)

			// Merge them into TagReportData
			tagReportData :=
				llrp.TagReportData(epcData,
					peakRSSI, airProtocolTagData,
					opSpecResult)

			/*

			   TODO: Here, maybe stack more TRDs to ROAR

			*/

			// Append TagReportData to ROAccessReport
			roAccessReport :=
				llrp.ROAccessReport(tagReportData, messageID)
			messageID += 1

			// Send
			conn.Write(roAccessReport)

			// Wait until ACK received
			time.Sleep(time.Millisecond)
		}
		select {
		case <-t.C:
			count += 1
		}
		if count >= 10 {
			conn.Write(llrp.Keepalive())
			buf := make([]byte, BUFSIZE)
			reqLen, err := conn.Read(buf)
			if err == io.EOF {
				// Close the connection when you're done with it.
				return
			} else if err != nil {
				log.Println("Error reading:", err.Error())
				log.Println("reqLen: " + string(reqLen))
				conn.Close()
			}
			header := binary.BigEndian.Uint16(buf[:2])
			if header != llrp.H_KeepaliveAck {
				log.Printf("Unknown header: %v\n", header)
				return
			}
			count = 0
		}
	}
}

// Handles incoming requests.
func handleRequest(conn net.Conn) {
	// Make a buffer to hold incoming data.
	buf := make([]byte, BUFSIZE)
	// Read the incoming connection into the buffer.
	reqLen, err := conn.Read(buf)
	if err == io.EOF {
		// Close the connection when you're done with it.
		return
	} else if err != nil {
		log.Println("Error reading:", err.Error())
		log.Println("reqLen: " + string(reqLen))
		conn.Close()
	}

	header := binary.BigEndian.Uint16(buf[:2])
	if header == llrp.H_SetReaderConfig {
		log.Println(">>> SET_READER_CONFIG")
		conn.Write(llrp.SetReaderConfigResponse())
		// Read virtual tags from a csv file
		tags := readTagsFromCSV("tags.csv")
		// Emit LLRP
		go emit(conn, tags)
	} else if header == llrp.H_KeepaliveAck {
		tags := readTagsFromCSV("tags.csv")
		go emit(conn, tags)
	} else {
		log.Printf("Unknown header: %v\n", header)
		fmt.Println("Message: %v", buf)
		return
	}
}

// stream mode
func runStream() {
	// Listen for incoming connections.
	l, err := net.Listen("tcp", ip.String()+":"+strconv.Itoa(*port))
	if err != nil {
		log.Println("Error listening:", err.Error())
		os.Exit(1)
	}
	// Close the listener when the application closes.
	defer l.Close()
	log.Println("Listening on " + ip.String() + ":" + strconv.Itoa(*port))

	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			fmt.Println("Error accepting: ", err.Error())
			os.Exit(1)
		}

		// Send back READER_EVENT_NOTIFICATION
		conn.Write(llrp.ReaderEventNotification(messageID))
		messageID += 1
		time.Sleep(time.Millisecond)

		// Handle connections in a new goroutine.
		go handleRequest(conn)
	}

	// Handle SIGINT and SIGTERM.
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	log.Println(<-ch)
}

// client mode
func runClient() {
	// Establish a connection to the llrp client
	conn, err := net.Dial("tcp", ip.String()+":"+strconv.Itoa(*port))
	Check(err)

	buf := make([]byte, BUFSIZE)
	for {
		// Read the incoming connection into the buffer.
		reqLen, err := conn.Read(buf)
		if err == io.EOF {
			// Close the connection when you're done with it.
			return
		} else if err != nil {
			fmt.Println("Error reading:", err.Error())
			fmt.Println("reqLen: " + string(reqLen))
			conn.Close()
			break
		}

		header := binary.BigEndian.Uint16(buf[:2])
		if header == llrp.H_ReaderEventNotification {
			fmt.Println(">>> READER_EVENT_NOTIFICATION")
			conn.Write(llrp.SetReaderConfig(messageID))
		} else if header == llrp.H_SetReaderConfigResponse {
			fmt.Println(">>> SET_READER_CONFIG_RESPONSE")
		} else if header == llrp.H_ROAccessReport {
			fmt.Println(">>> RO_ACCESS_REPORT")
			fmt.Printf("Packet size: %v\n", reqLen)
			fmt.Printf("% x\n", buf[:reqLen])
		} else {
			fmt.Printf("Unknown header: %v\n", header)
		}
	}
}

func main() {
	app.Version(VERSION)
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case stream.FullCommand():
		runStream()
	case client.FullCommand():
		runClient()
	}
}
<|endoftext|>