<commit_before>// functions (not methods) related to AWS go here

package master

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/aaronang/cong-the-ripper/lib"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
)

// createSlaves creates a new slave instance.
func createSlaves(svc *ec2.EC2, count int, slavePort, masterIP, masterPort string) ([]*ec2.Instance, error) {
	var script = `#!/bin/bash

set -x

su centos <<'EOF'
source ~/.bashrc
go get github.com/aaronang/cong-the-ripper/cmd/slave
go install github.com/aaronang/cong-the-ripper/cmd/slave
~/go/bin/slave --port=%v --master-ip=%v --master-port=%v > ~/console.log 2>&1 &
EOF
`
	userData := []byte(fmt.Sprintf(script, slavePort, masterIP, masterPort))
	params := &ec2.RunInstancesInput{
		ImageId:      aws.String(lib.SlaveImage),
		InstanceType: aws.String(lib.SlaveType),
		MinCount:     aws.Int64(int64(count)),
		MaxCount:     aws.Int64(int64(count)),
		IamInstanceProfile: &ec2.IamInstanceProfileSpecification{
			Arn: aws.String(lib.SlaveARN),
		},
		KeyName:          aws.String("Cong the Ripper"),
		SecurityGroupIds: []*string{aws.String("sg-646fbb02")},
		UserData:         aws.String(base64.StdEncoding.EncodeToString(userData)),
	}
	resp, err := svc.RunInstances(params)
	return resp.Instances, err
}

func instancesFromIPs(svc *ec2.EC2, ips []string) []*ec2.Instance {
	if ips == nil || len(ips) == 0 {
		return nil
	}

	awsIPs := make([]*string, len(ips))
	for i := range awsIPs {
		awsIPs[i] = aws.String(ips[i])
	}

	params := ec2.DescribeInstancesInput{
		Filters: []*ec2.Filter{
			{
				Name:   aws.String("ip-address"),
				Values: awsIPs,
			},
		},
	}

	res, err := svc.DescribeInstances(&params)
	if err != nil {
		log.Println("Failed to find instance from its public IP", err)
		return nil
	}

	// the index should be valid, if not we crash
	return res.Reservations[0].Instances
}

// terminateSlaves terminates a slave instance.
func terminateSlaves(svc *ec2.EC2, instances []*ec2.Instance) (*ec2.TerminateInstancesOutput, error) {
	params := &ec2.TerminateInstancesInput{
		InstanceIds: instanceIds(instances),
	}
	return svc.TerminateInstances(params)
}

// sendTask sends a task to a slave instance.
func sendTask(t *lib.Task, addr string) (*http.Response, error) {
	url := lib.Protocol + addr + lib.TasksCreatePath
	body, err := t.ToJSON()
	if err != nil {
		log.Panicln(err)
	}
	return http.Post(url, lib.BodyType, bytes.NewBuffer(body))
}

func newEC2() *ec2.EC2 {
	sess, err := session.NewSession(&aws.Config{
		Region: aws.String(lib.AWSRegion)},
	)
	if err != nil {
		panic(err)
	}
	return ec2.New(sess)
}

func instanceIds(instances []*ec2.Instance) []*string {
	instanceIds := make([]*string, len(instances))
	for i, instance := range instances {
		instanceIds[i] = instance.InstanceId
	}
	return instanceIds
}

func getPublicIP(svc *ec2.EC2, instance *ec2.Instance) *string {
	params := ec2.DescribeInstancesInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("instance-state-name"),
				Values: []*string{
					aws.String("pending"),
					aws.String("running"),
				},
			},
		},
		InstanceIds: []*string{
			instance.InstanceId,
		},
	}

	var i int
	for {
		res, err := svc.DescribeInstances(&params)

		// ignore the error because we may try again
		if err == nil &&
			len(res.Reservations) == 1 &&
			len(res.Reservations[0].Instances) == 1 {

			if res.Reservations[0].Instances[0].PublicIpAddress != nil {
				return res.Reservations[0].Instances[0].PublicIpAddress
			}

		}
		time.Sleep(10 * time.Second)
		i++
		if i > 12 {
			log.Println("Unable to find public IP")
			return nil
		}
	}
}

func mapToKeys(mymap map[string]slave) []string {
	keys := make([]string, len(mymap))
	i := 0
	for k := range mymap {
		keys[i] = k
		i++
	}
	return keys
}
<commit_msg>Add update flag to go get for setting up the slave<commit_after>// functions (not methods) related to AWS go here

package master

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/aaronang/cong-the-ripper/lib"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
)

// createSlaves creates a new slave instance.
func createSlaves(svc *ec2.EC2, count int, slavePort, masterIP, masterPort string) ([]*ec2.Instance, error) {
	var script = `#!/bin/bash

set -x

su centos <<'EOF'
source ~/.bashrc
go get -u github.com/aaronang/cong-the-ripper/cmd/slave
go install github.com/aaronang/cong-the-ripper/cmd/slave
~/go/bin/slave --port=%v --master-ip=%v --master-port=%v > ~/console.log 2>&1 &
EOF
`
	userData := []byte(fmt.Sprintf(script, slavePort, masterIP, masterPort))
	params := &ec2.RunInstancesInput{
		ImageId:      aws.String(lib.SlaveImage),
		InstanceType: aws.String(lib.SlaveType),
		MinCount:     aws.Int64(int64(count)),
		MaxCount:     aws.Int64(int64(count)),
		IamInstanceProfile: &ec2.IamInstanceProfileSpecification{
			Arn: aws.String(lib.SlaveARN),
		},
		KeyName:          aws.String("Cong the Ripper"),
		SecurityGroupIds: []*string{aws.String("sg-646fbb02")},
		UserData:         aws.String(base64.StdEncoding.EncodeToString(userData)),
	}
	resp, err := svc.RunInstances(params)
	return resp.Instances, err
}

func instancesFromIPs(svc *ec2.EC2, ips []string) []*ec2.Instance {
	if ips == nil || len(ips) == 0 {
		return nil
	}

	awsIPs := make([]*string, len(ips))
	for i := range awsIPs {
		awsIPs[i] = aws.String(ips[i])
	}

	params := ec2.DescribeInstancesInput{
		Filters: []*ec2.Filter{
			{
				Name:   aws.String("ip-address"),
				Values: awsIPs,
			},
		},
	}

	res, err := svc.DescribeInstances(&params)
	if err != nil {
		log.Println("Failed to find instance from its public IP", err)
		return nil
	}

	// the index should be valid, if not we crash
	return res.Reservations[0].Instances
}

// terminateSlaves terminates a slave instance.
func terminateSlaves(svc *ec2.EC2, instances []*ec2.Instance) (*ec2.TerminateInstancesOutput, error) {
	params := &ec2.TerminateInstancesInput{
		InstanceIds: instanceIds(instances),
	}
	return svc.TerminateInstances(params)
}

// sendTask sends a task to a slave instance.
func sendTask(t *lib.Task, addr string) (*http.Response, error) {
	url := lib.Protocol + addr + lib.TasksCreatePath
	body, err := t.ToJSON()
	if err != nil {
		log.Panicln(err)
	}
	return http.Post(url, lib.BodyType, bytes.NewBuffer(body))
}

func newEC2() *ec2.EC2 {
	sess, err := session.NewSession(&aws.Config{
		Region: aws.String(lib.AWSRegion)},
	)
	if err != nil {
		panic(err)
	}
	return ec2.New(sess)
}

func instanceIds(instances []*ec2.Instance) []*string {
	instanceIds := make([]*string, len(instances))
	for i, instance := range instances {
		instanceIds[i] = instance.InstanceId
	}
	return instanceIds
}

func getPublicIP(svc *ec2.EC2, instance *ec2.Instance) *string {
	params := ec2.DescribeInstancesInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("instance-state-name"),
				Values: []*string{
					aws.String("pending"),
					aws.String("running"),
				},
			},
		},
		InstanceIds: []*string{
			instance.InstanceId,
		},
	}

	var i int
	for {
		res, err := svc.DescribeInstances(&params)

		// ignore the error because we may try again
		if err == nil &&
			len(res.Reservations) == 1 &&
			len(res.Reservations[0].Instances) == 1 {

			if res.Reservations[0].Instances[0].PublicIpAddress != nil {
				return res.Reservations[0].Instances[0].PublicIpAddress
			}

		}
		time.Sleep(10 * time.Second)
		i++
		if i > 12 {
			log.Println("Unable to find public IP")
			return nil
		}
	}
}

func mapToKeys(mymap map[string]slave) []string {
	keys := make([]string, len(mymap))
	i := 0
	for k := range mymap {
		keys[i] = k
		i++
	}
	return keys
}
<|endoftext|>


<commit_before>package helpers

import (
	"bytes"
	"fmt"
	got "html/template"
	"io"
	"strings"

	parser "github.com/fragmenta/view/internal/html"
)

// NB all HTML with user input must be escaped, see https://www.owasp.org/index.php/XSS_Prevention_Cheatsheet

// These two should instead be using assets package?

// Style inserts a css tag
func Style(name string) got.HTML {
	return got.HTML(fmt.Sprintf("<link href=\"/assets/styles/%s.css\" media=\"all\" rel=\"stylesheet\" type=\"text/css\" />", EscapeURL(name)))
}

// Script inserts a script tag
func Script(name string) got.HTML {
	return got.HTML(fmt.Sprintf("", EscapeURL(name)))
}

// Escape escapes HTML using HTMLEscapeString
func Escape(s string) string {
	return got.HTMLEscapeString(s)
}

// EscapeURL escapes URLs using HTMLEscapeString
func EscapeURL(s string) string {
	return got.URLQueryEscaper(s)
}

// Link returns got.HTML with an anchor link given text and URL required
// Attributes (if supplied) should not contain user input
func Link(t string, u string, a ...string) got.HTML {
	attributes := ""
	if len(a) > 0 {
		attributes = strings.Join(a, " ")
	}
	return got.HTML(fmt.Sprintf("%s", Escape(u), Escape(attributes), Escape(t)))
}

// HTML returns a string (which must not contain user input) as go template HTML
func HTML(s string) got.HTML {
	return got.HTML(s)
}

// HTMLAttribute returns a string (which must not contain user input) as go template HTMLAttr
func HTMLAttribute(s string) got.HTMLAttr {
	return got.HTMLAttr(s)
}

// URL returns returns a string (which must not contain user input) as go template URL
func URL(s string) got.URL {
	return got.URL(s)
}

// Strip all html tags and returns as go template HTML
func Strip(s string) got.HTML {
	return Sanitize(s, []string{}, []string{})
}

// Sanitize sanitises html, allowing some tags using the html parser from golang.org/x/net/html and returns as go template HTML
// Usage: sanitize.HTMLAllowing("my html",[]string{"b"},[]string{"id")
func Sanitize(s string, args ...[]string) got.HTML {

	var ignoreTags = []string{"title", "script", "style", "iframe", "frame", "frameset", "noframes", "noembed", "embed", "applet", "object"}
	var defaultTags = []string{"h1", "h2", "h3", "h4", "h5", "h6", "div", "span", "hr", "p", "br", "b", "i", "ol", "ul", "li", "strong", "em", "a", "img"}
	var defaultAttributes = []string{"id", "class", "src", "title", "alt", "name", "rel", "href"}

	allowedTags := defaultTags
	if len(args) > 0 {
		allowedTags = args[0]
	}
	allowedAttributes := defaultAttributes
	if len(args) > 1 {
		allowedAttributes = args[1]
	}

	// Parse the html
	tokenizer := parser.NewTokenizer(strings.NewReader(s))

	buffer := bytes.NewBufferString("")
	ignore := ""

	for {
		tokenType := tokenizer.Next()
		token := tokenizer.Token()

		switch tokenType {

		case parser.ErrorToken:
			err := tokenizer.Err()
			if err == io.EOF {
				return got.HTML(buffer.String())
			}

			fmt.Println("Error parsing html") // we should perhaps return an error
			return got.HTML("")

		case parser.StartTagToken:

			if len(ignore) == 0 && includes(allowedTags, token.Data) {
				token.Attr = cleanAttributes(token.Attr, allowedAttributes)
				buffer.WriteString(token.String())
			} else if includes(ignoreTags, token.Data) {
				ignore = token.Data
			}

		case parser.SelfClosingTagToken:

			if len(ignore) == 0 && includes(allowedTags, token.Data) {
				token.Attr = cleanAttributes(token.Attr, allowedAttributes)
				buffer.WriteString(token.String())
			} else if token.Data == ignore {
				ignore = ""
			}

		case parser.EndTagToken:
			if len(ignore) == 0 && includes(allowedTags, token.Data) {
				token.Attr = []parser.Attribute{}
				buffer.WriteString(token.String())
			} else if token.Data == ignore {
				ignore = ""
			}

		case parser.TextToken:
			// We allow text content through, unless ignoring this entire tag and its contents (including other tags)
			if ignore == "" {
				buffer.WriteString(token.String())
			}
		case parser.CommentToken:
			// We ignore comments by default
		case parser.DoctypeToken:
			// We ignore doctypes by default - html5 does not require them and this is intended for sanitizing snippets of text
		default:
			// We ignore unknown token types by default

		}

	}

}

// cleanAttributes removes all attributes except those in the allowed list
func cleanAttributes(a []parser.Attribute, allowed []string) []parser.Attribute {
	if len(a) == 0 {
		return a
	}

	var cleaned []parser.Attribute
	for _, attr := range a {
		if includes(allowed, attr.Key) {
			cleaned = append(cleaned, attr)
		}
	}
	return cleaned
}

// includes returns true if this array of strings contains string s
func includes(a []string, s string) bool {
	for _, as := range a {
		if as == s {
			return true
		}
	}
	return false
}
<commit_msg>Added pre and code to default sanitize allowed tags<commit_after>package helpers

import (
	"bytes"
	"fmt"
	got "html/template"
	"io"
	"strings"

	parser "github.com/fragmenta/view/internal/html"
)

// NB all HTML with user input must be escaped, see https://www.owasp.org/index.php/XSS_Prevention_Cheatsheet

// These two should instead be using assets package?

// Style inserts a css tag
func Style(name string) got.HTML {
	return got.HTML(fmt.Sprintf("<link href=\"/assets/styles/%s.css\" media=\"all\" rel=\"stylesheet\" type=\"text/css\" />", EscapeURL(name)))
}

// Script inserts a script tag
func Script(name string) got.HTML {
	return got.HTML(fmt.Sprintf("", EscapeURL(name)))
}

// Escape escapes HTML using HTMLEscapeString
func Escape(s string) string {
	return got.HTMLEscapeString(s)
}

// EscapeURL escapes URLs using HTMLEscapeString
func EscapeURL(s string) string {
	return got.URLQueryEscaper(s)
}

// Link returns got.HTML with an anchor link given text and URL required
// Attributes (if supplied) should not contain user input
func Link(t string, u string, a ...string) got.HTML {
	attributes := ""
	if len(a) > 0 {
		attributes = strings.Join(a, " ")
	}
	return got.HTML(fmt.Sprintf("%s", Escape(u), Escape(attributes), Escape(t)))
}

// HTML returns a string (which must not contain user input) as go template HTML
func HTML(s string) got.HTML {
	return got.HTML(s)
}

// HTMLAttribute returns a string (which must not contain user input) as go template HTMLAttr
func HTMLAttribute(s string) got.HTMLAttr {
	return got.HTMLAttr(s)
}

// URL returns returns a string (which must not contain user input) as go template URL
func URL(s string) got.URL {
	return got.URL(s)
}

// Strip all html tags and returns as go template HTML
func Strip(s string) got.HTML {
	return Sanitize(s, []string{}, []string{})
}

// Sanitize sanitises html, allowing some tags using the html parser from golang.org/x/net/html and returns as go template HTML
// Usage: sanitize.HTMLAllowing("my html",[]string{"b"},[]string{"id")
func Sanitize(s string, args ...[]string) got.HTML {

	var ignoreTags = []string{"title", "script", "style", "iframe", "frame", "frameset", "noframes", "noembed", "embed", "applet", "object"}
	var defaultTags = []string{"h1", "h2", "h3", "h4", "h5", "h6", "div", "span", "hr", "p", "br", "b", "i", "ol", "ul", "li", "strong", "em", "a", "img", "pre", "code"}
	var defaultAttributes = []string{"id", "class", "src", "title", "alt", "name", "rel", "href"}

	allowedTags := defaultTags
	if len(args) > 0 {
		allowedTags = args[0]
	}
	allowedAttributes := defaultAttributes
	if len(args) > 1 {
		allowedAttributes = args[1]
	}

	// Parse the html
	tokenizer := parser.NewTokenizer(strings.NewReader(s))

	buffer := bytes.NewBufferString("")
	ignore := ""

	for {
		tokenType := tokenizer.Next()
		token := tokenizer.Token()

		switch tokenType {

		case parser.ErrorToken:
			err := tokenizer.Err()
			if err == io.EOF {
				return got.HTML(buffer.String())
			}

			fmt.Println("Error parsing html") // we should perhaps return an error
			return got.HTML("")

		case parser.StartTagToken:

			if len(ignore) == 0 && includes(allowedTags, token.Data) {
				token.Attr = cleanAttributes(token.Attr, allowedAttributes)
				buffer.WriteString(token.String())
			} else if includes(ignoreTags, token.Data) {
				ignore = token.Data
			}

		case parser.SelfClosingTagToken:

			if len(ignore) == 0 && includes(allowedTags, token.Data) {
				token.Attr = cleanAttributes(token.Attr, allowedAttributes)
				buffer.WriteString(token.String())
			} else if token.Data == ignore {
				ignore = ""
			}

		case parser.EndTagToken:
			if len(ignore) == 0 && includes(allowedTags, token.Data) {
				token.Attr = []parser.Attribute{}
				buffer.WriteString(token.String())
			} else if token.Data == ignore {
				ignore = ""
			}

		case parser.TextToken:
			// We allow text content through, unless ignoring this entire tag and its contents (including other tags)
			if ignore == "" {
				buffer.WriteString(token.String())
			}
		case parser.CommentToken:
			// We ignore comments by default
		case parser.DoctypeToken:
			// We ignore doctypes by default - html5 does not require them and this is intended for sanitizing snippets of text
		default:
			// We ignore unknown token types by default

		}

	}

}

// cleanAttributes removes all attributes except those in the allowed list
func cleanAttributes(a []parser.Attribute, allowed []string) []parser.Attribute {
	if len(a) == 0 {
		return a
	}

	var cleaned []parser.Attribute
	for _, attr := range a {
		if includes(allowed, attr.Key) {
			cleaned = append(cleaned, attr)
		}
	}
	return cleaned
}

// includes returns true if this array of strings contains string s
func includes(a []string, s string) bool {
	for _, as := range a {
		if as == s {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package hhr

import (
	"flag"
	"fmt"
	"log"
	"os"
)

var (
	flagReadFile = ""
)

func init() {
	flag.StringVar(&flagReadFile, "hhr", flagReadFile,
		"The HHR file to use for tests.")
	flag.Parse()

	log.SetFlags(0)
}

func ExampleRead() {
	r := getFile()

	hhr, err := Read(r)
	if err != nil {
		log.Fatalf("%s", err)
	}

	hit := hhr.Hits[9]
	fmt.Println(hit.Num)
	fmt.Println(hit.Name)
	fmt.Printf("%0.3f\n", hit.Prob)
	fmt.Println(hit.EValue)
	fmt.Println(hit.PValue)
	fmt.Println(hit.ViterbiScore)
	fmt.Println(hit.SSScore)
	fmt.Println(hit.NumAlignedCols)
	fmt.Println(hit.QueryStart)
	fmt.Println(hit.QueryEnd)
	fmt.Println(hit.TemplateStart)
	fmt.Println(hit.TemplateEnd)
	fmt.Println(hit.NumTemplateCols)
	fmt.Printf("%s\n", hit.Aligned.QSeq)
	fmt.Printf("%s\n", hit.Aligned.TSeq)
	// Output:
	// 10
	// 2fxaD
	// 0.571
	// 0.24
	// 0.00011
	// 34.3
	// 0
	// 47
	// 106
	// 154
	// 46
	// 94
	// 207
	// IGNSAFELLLEVAKSGEKGINTMDLAQVTGQDPRSVTGRIKKINH--LLTS
	// LNINEHHILWIAY--QLNGASISEIAKFGVMHVSTAFNFSKKLEERGYLRF
}

func getFile() *os.File {
	if len(flagReadFile) == 0 {
		log.Fatalf("Please set the '--hhr path/to/file.hhr' flag.")
	}

	r, err := os.Open(flagReadFile)
	if err != nil {
		log.Fatalf("%s", err)
	}
	return r
}
<commit_msg>gofmt<commit_after>package hhr

import (
	"flag"
	"fmt"
	"log"
	"os"
)

var (
	flagReadFile = ""
)

func init() {
	flag.StringVar(&flagReadFile, "hhr", flagReadFile,
		"The HHR file to use for tests.")
	flag.Parse()

	log.SetFlags(0)
}

func ExampleRead() {
	r := getFile()

	hhr, err := Read(r)
	if err != nil {
		log.Fatalf("%s", err)
	}

	hit := hhr.Hits[9]
	fmt.Println(hit.Num)
	fmt.Println(hit.Name)
	fmt.Printf("%0.3f\n", hit.Prob)
	fmt.Println(hit.EValue)
	fmt.Println(hit.PValue)
	fmt.Println(hit.ViterbiScore)
	fmt.Println(hit.SSScore)
	fmt.Println(hit.NumAlignedCols)
	fmt.Println(hit.QueryStart)
	fmt.Println(hit.QueryEnd)
	fmt.Println(hit.TemplateStart)
	fmt.Println(hit.TemplateEnd)
	fmt.Println(hit.NumTemplateCols)
	fmt.Printf("%s\n", hit.Aligned.QSeq)
	fmt.Printf("%s\n", hit.Aligned.TSeq)
	// Output:
	// 10
	// 2fxaD
	// 0.571
	// 0.24
	// 0.00011
	// 34.3
	// 0
	// 47
	// 106
	// 154
	// 46
	// 94
	// 207
	// IGNSAFELLLEVAKSGEKGINTMDLAQVTGQDPRSVTGRIKKINH--LLTS
	// LNINEHHILWIAY--QLNGASISEIAKFGVMHVSTAFNFSKKLEERGYLRF
}

func getFile() *os.File {
	if len(flagReadFile) == 0 {
		log.Fatalf("Please set the '--hhr path/to/file.hhr' flag.")
	}

	r, err := os.Open(flagReadFile)
	if err != nil {
		log.Fatalf("%s", err)
	}
	return r
}
<|endoftext|>


<commit_before>// Copyright 2015 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// validhtml.go contains checks to slighty validate a HTML document.

package ht

import (
	"fmt"
	"io"
	"net/url"
	"strings"

	"golang.org/x/net/html"
	"golang.org/x/text/language"
)

func init() {
	RegisterCheck(ValidHTML{})
}

// ValidHTML checks for valid HTML 5; well kinda: It make sure that some
// common but easy to detect fuckups are not present. The following issues
// are detected:
//   * 'doctype':   not exactly one DOCTYPE
//   * 'structure': ill-formed tag nesting / tag closing
//   * 'uniqueids': uniqness of id attribute values
//   * 'lang':      ill-formed lang attributes
//   * 'attr':      dupplicate attributes
//   * 'escaping':  unescaped &, < and > characters or unknown entities
//   * 'label':     reference to nonexisting id in label tags
//   * 'url':       malformed URLs
//
// Notes:
//  - HTML5 allows unescaped & in several circumstances but ValidHTML
//    reports all stray & as an error.
//  - The lang attributes are parse very lax, e.g. the non-canonical form
//    'de_CH' is considered valid (and equivalent to 'de-CH'). I don't
//    know how browser handle this.
type ValidHTML struct {
	// Ignore is a space separated list of issues to ignore.
	// You normaly won't skip detection of these issues as all issues
	// are fundamental flaws which are easy to fix.
	Ignore string `json:",omitempty"`
}

// Execute implements Check's Execute method.
func (v ValidHTML) Execute(t *Test) error {
	if t.Response.BodyErr != nil {
		return ErrBadBody
	}

	mask, _ := ignoreMask(v.Ignore)
	state := newHTMLState(t.Response.BodyStr, mask)

	// Parse document and record local errors in state.
	z := html.NewTokenizer(state)
	depth := 0
done:
	for {
		tt := z.Next()
		// fmt.Printf("%s%s: ", strings.Repeat("  ", depth), tt)
		switch tt {
		case html.ErrorToken:
			if z.Err() == io.EOF {
				break done
			}
			return z.Err()
		case html.TextToken:
			// fmt.Printf(" %q\n", z.Text())
			// fmt.Println()
			if depth > 0 {
				state.checkEscaping(string(z.Raw()))
			}
		case html.StartTagToken, html.SelfClosingTagToken:
			raw := string(z.Raw())
			if len(raw) > 3 {
				state.checkEscaping(raw[1 : len(raw)-1])
			}
			tn, hasAttr := z.TagName()
			// Some tags are empty and may be written in the self-closing
			// variant "
" or simply empty like "
".
			// TODO: Maybe allow the non-compliant form "

" too?
			if tt != html.SelfClosingTagToken && !emptyHTMLElement[string(tn)] {
				state.push(string(tn))
				depth++
			}
			tag := string(tn)
			// fmt.Printf(" %s  ", tag)
			state.count(tag)
			attrs := map[string]string{}
			var bkey, bval []byte
			for hasAttr {
				bkey, bval, hasAttr = z.TagAttr()
				key, val := string(bkey), string(bval)
				// fmt.Printf("%s=%s ", key, val)
				if _, ok := attrs[key]; ok {
					if state.ignore&issueAttr == 0 {
						state.err(fmt.Errorf("duplicate attribute '%s'", key))
					}
				}
				attrs[key] = val
				switch {
				case key == "id":
					state.checkID(val)
				case tag == "label" && key == "for":
					state.recordLabel(val)
				case key == "lang":
					state.checkLang(val)
				case (tag == "a" && key == "href") ||
					(tag == "img" && key == "src"): // TODO link?
					state.checkURL(val)
				}
			}
			// fmt.Println()
		case html.EndTagToken:
			tn, _ := z.TagName()
			// fmt.Println(" ", string(tn))
			state.pop(string(tn))
			depth--
		case html.CommentToken:
		case html.DoctypeToken:
			state.count("DOCTYPE")
		}
	}

	// Check for global errors.
	state.line++ // Global errors are reported "after the last line".
	if state.ignore&issueDoctype == 0 {
		if d := state.elementCount["DOCTYPE"]; d != 1 {
			state.err(fmt.Errorf("found %d DOCTYPE", d))
		}
	}
	if state.ignore&issueLabelRef == 0 {
		for _, id := range state.labelFor {
			if _, ok := state.seenIDs[id]; !ok {
				state.err(fmt.Errorf("label references unknown id '%s'", id))
			}
		}
	}

	if len(state.errors) == 0 {
		return nil
	}

	if len(state.errors) == 0 {
		return nil
	}
	return state.errors
}

// Prepare implements Check's Prepare method.
func (v ValidHTML) Prepare() error {
	_, err := ignoreMask(v.Ignore)
	return err
}

// emptyHTMLElement is the list of all HTML5 elements which are empty, that
// is they are implecitely self-closing and can be written either in
// XML-style like e.g. "
" or in HTML5-style just "
".
// The list was taken from:
//   - http://www.elharo.com/blog/software-development/web-development/2007/01/29/all-empty-tags-in-html/
// Someone should check the whole list here: http://www.w3schools.com/tags/default.asp
var emptyHTMLElement = map[string]bool{
	"br":    true,
	"hr":    true,
	"meta":  true,
	"base":  true,
	"link":  true,
	"img":   true,
	"embed": true,
	"param": true,
	"area":  true,
	"col":   true,
	"input": true,
}

// ----------------------------------------------------------------------------
// Types of issues to ignore

type htmlIssue uint32

const (
	issueIgnoreNone htmlIssue = 0
	issueDoctype    htmlIssue = 1 << (iota - 1)
	issueStructure
	issueUniqIDs
	issueLangTag
	issueAttr
	issueEscaping
	issueLabelRef
	issueURL
)

func ignoreMask(s string) (htmlIssue, error) {
	// what an ugly hack
	const issueNames = "doctype  structureuniqueidslang     attr     escaping label    url      "
	mask := htmlIssue(0)
	s = strings.ToLower(s)
	for _, p := range strings.Split(s, " ") {
		if p == "" {
			continue
		}
		i := strings.Index(issueNames, p)
		if i == -1 {
			return mask, fmt.Errorf("no such html issue '%s'", p)
		}
		mask |= 1 << uint(i/9)
	}
	return mask, nil
}

// ----------------------------------------------------------------------------
// htmlState

// htmlState collects information about a HTML document.
type htmlState struct {
	body string
	i    int
	line int

	elementCount map[string]int
	seenIDs      map[string]bool
	openTags     []string
	labelFor     []string
	errors       ErrorList

	ignore     htmlIssue
	badNesting bool
}

func newHTMLState(body string, ignore htmlIssue) *htmlState {
	return &htmlState{
		body:         body,
		i:            0,
		line:         0,
		elementCount: make(map[string]int, 50),
		seenIDs:      make(map[string]bool),
		openTags:     make([]string, 0, 50),
		labelFor:     make([]string, 0, 10),
		errors:       make(ErrorList, 0),
		badNesting:   false,
		ignore:       ignore,
	}
}

func (s *htmlState) Read(buf []byte) (int, error) {
	n := 0
	last := byte(0)
	for n < len(buf) && s.i < len(s.body) && last != '\n' {
		buf[n] = s.body[s.i]
		last = s.body[s.i]
		n++
		s.i++
	}
	s.line++
	if s.i == len(s.body) {
		return n, io.EOF
	}
	return n, nil
}

// err records the error e.
func (s *htmlState) err(e error) {
	s.errors = append(s.errors, PosError{Err: e, Line: s.line})
}

// count the tag
func (s *htmlState) count(tag string) {
	s.elementCount[tag] = s.elementCount[tag] + 1
}

// checkID chesk for duplicate ids.
func (s *htmlState) checkID(id string) {
	if s.ignore&issueUniqIDs == 0 {
		if _, seen := s.seenIDs[id]; seen {
			s.err(fmt.Errorf("duplicate id '%s'", id))
		}
	}
	s.seenIDs[id] = true
}

// record the id from a  tag.
func (s *htmlState) recordLabel(id string) {
	s.labelFor = append(s.labelFor, id)
}

// checkEscaping of text
func (s *htmlState) checkEscaping(text string) {
	if s.ignore&issueEscaping != 0 {
		return
	}

	// Javascript is full of unescaped <, > and && and content of 'noscript'
	// and 'iframe' seems to be unparsed by package html: Skip check of
	// proper escaping inside these elements.
	if n := len(s.openTags); n > 0 &&
		(s.openTags[n-1] == "script" || s.openTags[n-1] == "noscript" || s.openTags[n-1] == "iframe") {
		return
	}

	if strings.Index(text, "<") != -1 {
		s.err(fmt.Errorf("unescaped '<'"))
	}
	if strings.Index(text, ">") != -1 {
		s.err(fmt.Errorf("unescaped '>'"))
	}
	for len(text) > 0 {
		if i := strings.Index(text, "&"); i != -1 {
			text = text[i:]
			if strings.HasPrefix(text, "&") {
				text = text[5:]
			} else {
				ue := html.UnescapeString(text)
				if strings.HasPrefix(ue, "&") {
					s.err(fmt.Errorf("unescaped '&' or unknow entity"))
					break
				}
				text = text[1:]
			}
		} else {
			break
		}
	}
}

// checkLang tries to parse the language tag lang.
func (s *htmlState) checkLang(lang string) {
	if s.ignore&issueLangTag != 0 {
		return
	}
	_, err := language.Parse(lang)
	switch e := err.(type) {
	case nil:
		// No error.
	case language.ValueError:
		s.err(fmt.Errorf("language tag '%s' has bad part %s", lang, e.Subtag()))
	default:
		// A syntax error.
		s.err(fmt.Errorf("language tag '%s' is ill-formed", lang))
	}
}

// checkURL raw to be properly encoded. HTML escaping has been checked already.
// for now just dissalow spaces
func (s *htmlState) checkURL(raw string) {
	if s.ignore&issueURL != 0 {
		return
	}

	if strings.HasPrefix(raw, "mailto:") {
		if strings.Index(raw, "@") == -1 {
			s.err(fmt.Errorf("not an email address"))
		}
		return
	}

	u, err := url.Parse(raw)
	if err != nil {
		s.err(fmt.Errorf("bad URL '%s': %s", raw, err.Error()))
		return
	}
	if u.Opaque != "" {
		s.err(fmt.Errorf("bad URL part '%s'", u.Opaque))
		return
	}

	if strings.Index(raw, " ") != -1 {
		s.err(fmt.Errorf("unencoded space in URL"))
	}
}

// push tag on stack of open tags
func (s *htmlState) push(tag string) {
	s.openTags = append(s.openTags, tag)
}

// try to pop tag from stack of open tags, record error if failed.
func (s *htmlState) pop(tag string) {
	n := len(s.openTags)
	if n == 0 {
		if s.ignore&issueStructure == 0 {
			s.err(fmt.Errorf("no open tags left to close %s", tag))
		}
		return
	}
	pop := s.openTags[n-1]
	s.openTags = s.openTags[:n-1]
	if s.ignore&issueStructure != 0 {
		return
	}
	if pop != tag && !s.badNesting { // report broken structure just once.
		s.err(fmt.Errorf("tag '%s' closed by '%s'", pop, tag))
		s.badNesting = true
	}
}
<commit_msg>ht: refactor and improve link checking<commit_after>// Copyright 2015 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// validhtml.go contains checks to slighty validate a HTML document.

package ht

import (
	"fmt"
	"io"
	"net/url"
	"strings"

	"golang.org/x/net/html"
	"golang.org/x/text/language"
)

func init() {
	RegisterCheck(ValidHTML{})
}

// ValidHTML checks for valid HTML 5; well kinda: It make sure that some
// common but easy to detect fuckups are not present. The following issues
// are detected:
//   * 'doctype':   not exactly one DOCTYPE
//   * 'structure': ill-formed tag nesting / tag closing
//   * 'uniqueids': uniqness of id attribute values
//   * 'lang':      ill-formed lang attributes
//   * 'attr':      dupplicate attributes
//   * 'escaping':  unescaped &, < and > characters or unknown entities
//   * 'label':     reference to nonexisting id in label tags
//   * 'url':       malformed URLs
//
// Notes:
//  - HTML5 allows unescaped & in several circumstances but ValidHTML
//    reports all stray & as an error.
//  - The lang attributes are parse very lax, e.g. the non-canonical form
//    'de_CH' is considered valid (and equivalent to 'de-CH'). I don't
//    know how browser handle this.
type ValidHTML struct {
	// Ignore is a space separated list of issues to ignore.
	// You normaly won't skip detection of these issues as all issues
	// are fundamental flaws which are easy to fix.
	Ignore string `json:",omitempty"`
}

// Execute implements Check's Execute method.
func (v ValidHTML) Execute(t *Test) error {
	if t.Response.BodyErr != nil {
		return ErrBadBody
	}

	mask, _ := ignoreMask(v.Ignore)
	state := newHTMLState(t.Response.BodyStr, mask)

	// Parse document and record local errors in state.
	z := html.NewTokenizer(state)
	depth := 0
done:
	for {
		tt := z.Next()
		// fmt.Printf("%s%s: ", strings.Repeat("  ", depth), tt)
		switch tt {
		case html.ErrorToken:
			if z.Err() == io.EOF {
				break done
			}
			return z.Err()
		case html.TextToken:
			// fmt.Printf(" %q\n", z.Text())
			// fmt.Println()
			if depth > 0 {
				state.checkEscaping(string(z.Raw()))
			}
		case html.StartTagToken, html.SelfClosingTagToken:
			raw := string(z.Raw())
			if len(raw) > 3 {
				state.checkEscaping(raw[1 : len(raw)-1])
			}
			tn, hasAttr := z.TagName()
			// Some tags are empty and may be written in the self-closing
			// variant "
" or simply empty like "
".
			// TODO: Maybe allow the non-compliant form "

" too?
			if tt != html.SelfClosingTagToken && !emptyHTMLElement[string(tn)] {
				state.push(string(tn))
				depth++
			}
			tag := string(tn)
			// fmt.Printf(" %s  ", tag)
			state.count(tag)
			attrs := map[string]string{}
			var bkey, bval []byte
			for hasAttr {
				bkey, bval, hasAttr = z.TagAttr()
				key, val := string(bkey), string(bval)
				// fmt.Printf("%s=%s ", key, val)
				if _, ok := attrs[key]; ok {
					if state.ignore&issueAttr == 0 {
						state.err(fmt.Errorf("duplicate attribute '%s'", key))
					}
				}
				attrs[key] = val
				switch {
				case key == "id":
					state.checkID(val)
				case tag == "label" && key == "for":
					state.recordLabel(val)
				case key == "lang":
					state.checkLang(val)
				case isURLAttr(tag, key):
					state.checkURL(val)
				}
			}
			// fmt.Println()
		case html.EndTagToken:
			tn, _ := z.TagName()
			// fmt.Println(" ", string(tn))
			state.pop(string(tn))
			depth--
		case html.CommentToken:
		case html.DoctypeToken:
			state.count("DOCTYPE")
		}
	}

	// Check for global errors.
	state.line++ // Global errors are reported "after the last line".
	if state.ignore&issueDoctype == 0 {
		if d := state.elementCount["DOCTYPE"]; d != 1 {
			state.err(fmt.Errorf("found %d DOCTYPE", d))
		}
	}
	if state.ignore&issueLabelRef == 0 {
		for _, id := range state.labelFor {
			if _, ok := state.seenIDs[id]; !ok {
				state.err(fmt.Errorf("label references unknown id '%s'", id))
			}
		}
	}

	if len(state.errors) == 0 {
		return nil
	}

	if len(state.errors) == 0 {
		return nil
	}
	return state.errors
}

// return true if attr contains the URL of the tag.
func isURLAttr(tag, attr string) bool {
	if a, ok := linkURLattr[tag]; ok {
		return attr == a.attr
	}
	return false
}

// Prepare implements Check's Prepare method.
func (v ValidHTML) Prepare() error {
	_, err := ignoreMask(v.Ignore)
	return err
}

// emptyHTMLElement is the list of all HTML5 elements which are empty, that
// is they are implecitely self-closing and can be written either in
// XML-style like e.g. "
" or in HTML5-style just "
".
// The list was taken from:
//   - http://www.elharo.com/blog/software-development/web-development/2007/01/29/all-empty-tags-in-html/
// Someone should check the whole list here: http://www.w3schools.com/tags/default.asp
var emptyHTMLElement = map[string]bool{
	"br":    true,
	"hr":    true,
	"meta":  true,
	"base":  true,
	"link":  true,
	"img":   true,
	"embed": true,
	"param": true,
	"area":  true,
	"col":   true,
	"input": true,
}

// ----------------------------------------------------------------------------
// Types of issues to ignore

type htmlIssue uint32

const (
	issueIgnoreNone htmlIssue = 0
	issueDoctype    htmlIssue = 1 << (iota - 1)
	issueStructure
	issueUniqIDs
	issueLangTag
	issueAttr
	issueEscaping
	issueLabelRef
	issueURL
)

func ignoreMask(s string) (htmlIssue, error) {
	// what an ugly hack
	const issueNames = "doctype  structureuniqueidslang     attr     escaping label    url      "
	mask := htmlIssue(0)
	s = strings.ToLower(s)
	for _, p := range strings.Split(s, " ") {
		if p == "" {
			continue
		}
		i := strings.Index(issueNames, p)
		if i == -1 {
			return mask, fmt.Errorf("no such html issue '%s'", p)
		}
		mask |= 1 << uint(i/9)
	}
	return mask, nil
}

// ----------------------------------------------------------------------------
// htmlState

// htmlState collects information about a HTML document.
type htmlState struct {
	body string
	i    int
	line int

	elementCount map[string]int
	seenIDs      map[string]bool
	openTags     []string
	labelFor     []string
	errors       ErrorList

	ignore     htmlIssue
	badNesting bool
}

func newHTMLState(body string, ignore htmlIssue) *htmlState {
	return &htmlState{
		body:         body,
		i:            0,
		line:         0,
		elementCount: make(map[string]int, 50),
		seenIDs:      make(map[string]bool),
		openTags:     make([]string, 0, 50),
		labelFor:     make([]string, 0, 10),
		errors:       make(ErrorList, 0),
		badNesting:   false,
		ignore:       ignore,
	}
}

func (s *htmlState) Read(buf []byte) (int, error) {
	n := 0
	last := byte(0)
	for n < len(buf) && s.i < len(s.body) && last != '\n' {
		buf[n] = s.body[s.i]
		last = s.body[s.i]
		n++
		s.i++
	}
	s.line++
	if s.i == len(s.body) {
		return n, io.EOF
	}
	return n, nil
}

// err records the error e.
func (s *htmlState) err(e error) {
	s.errors = append(s.errors, PosError{Err: e, Line: s.line})
}

// count the tag
func (s *htmlState) count(tag string) {
	s.elementCount[tag] = s.elementCount[tag] + 1
}

// checkID chesk for duplicate ids.
func (s *htmlState) checkID(id string) {
	if s.ignore&issueUniqIDs == 0 {
		if _, seen := s.seenIDs[id]; seen {
			s.err(fmt.Errorf("duplicate id '%s'", id))
		}
	}
	s.seenIDs[id] = true
}

// record the id from a  tag.
func (s *htmlState) recordLabel(id string) {
	s.labelFor = append(s.labelFor, id)
}

// checkEscaping of text
func (s *htmlState) checkEscaping(text string) {
	if s.ignore&issueEscaping != 0 {
		return
	}

	// Javascript is full of unescaped <, > and && and content of 'noscript'
	// and 'iframe' seems to be unparsed by package html: Skip check of
	// proper escaping inside these elements.
	if n := len(s.openTags); n > 0 &&
		(s.openTags[n-1] == "script" || s.openTags[n-1] == "noscript" || s.openTags[n-1] == "iframe") {
		return
	}

	if strings.Index(text, "<") != -1 {
		s.err(fmt.Errorf("unescaped '<'"))
	}
	if strings.Index(text, ">") != -1 {
		s.err(fmt.Errorf("unescaped '>'"))
	}
	for len(text) > 0 {
		if i := strings.Index(text, "&"); i != -1 {
			text = text[i:]
			if strings.HasPrefix(text, "&") {
				text = text[5:]
			} else {
				ue := html.UnescapeString(text)
				if strings.HasPrefix(ue, "&") {
					s.err(fmt.Errorf("unescaped '&' or unknow entity"))
					break
				}
				text = text[1:]
			}
		} else {
			break
		}
	}
}

// checkLang tries to parse the language tag lang.
func (s *htmlState) checkLang(lang string) {
	if s.ignore&issueLangTag != 0 {
		return
	}
	_, err := language.Parse(lang)
	switch e := err.(type) {
	case nil:
		// No error.
	case language.ValueError:
		s.err(fmt.Errorf("language tag '%s' has bad part %s", lang, e.Subtag()))
	default:
		// A syntax error.
		s.err(fmt.Errorf("language tag '%s' is ill-formed", lang))
	}
}

// checkURL raw to be properly encoded. HTML escaping has been checked already.
// for now just dissalow spaces
func (s *htmlState) checkURL(raw string) {
	if s.ignore&issueURL != 0 {
		return
	}

	if strings.HasPrefix(raw, "mailto:") {
		if strings.Index(raw, "@") == -1 {
			s.err(fmt.Errorf("not an email address"))
		}
		return
	}

	u, err := url.Parse(raw)
	if err != nil {
		s.err(fmt.Errorf("bad URL '%s': %s", raw, err.Error()))
		return
	}
	if u.Opaque != "" {
		s.err(fmt.Errorf("bad URL part '%s'", u.Opaque))
		return
	}

	if strings.Index(raw, " ") != -1 {
		s.err(fmt.Errorf("unencoded space in URL"))
	}
}

// push tag on stack of open tags
func (s *htmlState) push(tag string) {
	s.openTags = append(s.openTags, tag)
}

// try to pop tag from stack of open tags, record error if failed.
func (s *htmlState) pop(tag string) {
	n := len(s.openTags)
	if n == 0 {
		if s.ignore&issueStructure == 0 {
			s.err(fmt.Errorf("no open tags left to close %s", tag))
		}
		return
	}
	pop := s.openTags[n-1]
	s.openTags = s.openTags[:n-1]
	if s.ignore&issueStructure != 0 {
		return
	}
	if pop != tag && !s.badNesting { // report broken structure just once.
		s.err(fmt.Errorf("tag '%s' closed by '%s'", pop, tag))
		s.badNesting = true
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"

	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/mux"
)

func defineRoutes() {
	r := mux.NewRouter()

	r.HandleFunc("/api/controllers/{controller_id}/sensors", getCoordinatorSensors).Methods("GET")
	r.HandleFunc("/api/controllers/{controller_id}", putCoordinator).Methods("POST", "PUT")
	r.HandleFunc("/api/controllers/{controller_id}/{hash}", getCoordinator).Methods("GET")

	r.HandleFunc("/api/sensors/{sensor_id}", putSensor).Methods("POST", "PUT")
	r.HandleFunc("/api/sensors/{sensor_id}/ticks", getSensorTicks).Methods("GET")
	r.HandleFunc("/api/sensors/{sensor_id}/dots", getSensorDots).Methods("GET")

	// FIXME: deprecated
	r.HandleFunc("/api/log", getLogsV1).Methods("GET")
	r.HandleFunc("/api/logs", getLogsV1).Methods("GET")

	r.HandleFunc("/api/debug_log", getDebugLogs).Methods("GET")
	r.HandleFunc("/api/debug_logs", getDebugLogs).Methods("GET")

	r.HandleFunc("/api/v1/log", getLogsV1).Methods("GET")
	r.HandleFunc("/api/v1/logs", getLogsV1).Methods("GET")
	r.HandleFunc("/api/v2/log", getLogsV2).Methods("GET")
	r.HandleFunc("/api/v2/logs", getLogsV2).Methods("GET")

	http.Handle("/", r)
}

func getCoordinator(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["controller_id"]
	if !ok {
		http.Error(w, "Missing controller_id", http.StatusBadRequest)
		return
	}
	hashToken, ok := mux.Vars(r)["hash"]
	if !ok {
		http.Error(w, "Missing token hash", http.StatusBadRequest)
		return
	}

	redisClient := redisPool.Get()
	defer redisClient.Close()

	c := &coordinator{ID: coordinatorID, Token: hashToken}

	coordinatorHash, err := redis.String(redisClient.Do("HGET", c.key(), "token"))
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if coordinatorHash != c.Token {
		http.Error(w, "Incorrect hash for this coordinator", http.StatusUnauthorized)
	}

	coordinatorName, err := redis.String(redisClient.Do("HGET", c.key(), "name"))
	if err != nil {
		if err != redis.ErrNil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		coordinatorName = c.ID
	}
	c.Name = coordinatorName

	b, err := json.Marshal(c)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func putCoordinator(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["controller_id"]
	if !ok {
		http.Error(w, "Missing controller_id", http.StatusBadRequest)
		return
	}

	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var c coordinator
	if err := json.Unmarshal(b, &c); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	c.ID = coordinatorID

	redisClient := redisPool.Get()
	defer redisClient.Close()

	_, err = redisClient.Do("HSET", c.key(), "name", c.Name)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func putSensor(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing or invalid sensor_id", http.StatusBadRequest)
		return
	}

	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var s sensor
	if err := json.Unmarshal(b, &s); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	redisClient := redisPool.Get()
	defer redisClient.Close()

	_, err = redisClient.Do("HMSET", keyOfSensor(sensorID), "lat", s.Lat, "lng", s.Lng)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func getCoordinatorSensors(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["controller_id"]
	if !ok {
		http.Error(w, "Missing controller_id", http.StatusBadRequest)
		return
	}

	redisClient := redisPool.Get()
	defer redisClient.Close()

	ids, err := redis.Strings(redisClient.Do("SMEMBERS", keyOfCoordinatorSensors(coordinatorID)))
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	sensors := make([]*sensor, 0)
	for _, sensorID := range ids {
		if len(sensorID) == 0 {
			log.Println(err)
			http.Error(w, "Invalid or missing sensor ID", http.StatusInternalServerError)
			return
		}
		s := &sensor{ID: sensorID, ControllerID: coordinatorID}

		// Get lat, lng of sensor
		bb, err := redisClient.Do("HMGET", keyOfSensor(sensorID), "lat", "lng")
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if bb != nil {
			list := bb.([]interface{})
			if len(list) > 0 {
				if list[0] != nil {
					s.Lat = string(list[0].([]byte))
				}
				if list[1] != nil {
					s.Lng = string(list[1].([]byte))
				}
			}
		}

		// Get last tick of sensor
		ticks, err := findTicksByRange(sensorID, 0, 0)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if len(ticks) > 0 {
			s.LastTick = &ticks[0].Datetime

		}

		sensors = append(sensors, s)
	}

	b, err := json.Marshal(sensors)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getSensorDots(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing sensor_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Invalid end", http.StatusBadRequest)
		return
	}

	dotsPerDay, err := strconv.Atoi(r.FormValue("dots_per_day"))
	if err != nil {
		http.Error(w, "Invalid dots_per_day", http.StatusBadRequest)
		return
	}
	if dotsPerDay < 0 || dotsPerDay > 24 {
		http.Error(w, "dots_per_day must be in range 0-24", http.StatusBadRequest)
		return
	}

	redisClient := redisPool.Get()
	defer redisClient.Close()

	ticks, err := findTicksByScore(sensorID, start, end)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var dots []*tick
	if dotsPerDay > 0 {
		dots = findAverages(ticks, dotsPerDay, start, end)
	} else {
		dots = ticks
	}

	b, err := json.Marshal(dots)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getSensorTicks(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing sensor_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Missing or invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Missing or invalid end", http.StatusBadRequest)
		return
	}

	result, err := findTicksByScore(sensorID, start, end)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err := json.Marshal(result)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getLogsV1(w http.ResponseWriter, r *http.Request) {
	getLogs(w, r, loggingKeyV1)
}

func getLogsV2(w http.ResponseWriter, r *http.Request) {
	getLogs(w, r, loggingKeyV2)
}

func getLogs(w http.ResponseWriter, r *http.Request, key string) {
	redisClient := redisPool.Get()
	defer redisClient.Close()

	bb, err := redisClient.Do("LRANGE", key, 0, 1000)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "text/plain")
	for _, item := range bb.([]interface{}) {
		s := string(item.([]byte))
		s = strconv.Quote(s)
		w.Write([]byte(s))
		w.Write([]byte("\n\r"))
	}
}

func getDebugLogs(w http.ResponseWriter, r *http.Request) {
	getLogs(w, r, debugLogKey)
}
<commit_msg>rename controller routes<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"

	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/mux"
)

func defineRoutes() {
	r := mux.NewRouter()

	r.HandleFunc("/api/coordinators/{coordinator_id}/sensors", getCoordinatorSensors).Methods("GET")
	r.HandleFunc("/api/coordinators/{coordinator_id}", putCoordinator).Methods("POST", "PUT")
	r.HandleFunc("/api/coordinators/{coordinator_id}/{hash}", getCoordinator).Methods("GET")

	r.HandleFunc("/api/sensors/{sensor_id}", putSensor).Methods("POST", "PUT")
	r.HandleFunc("/api/sensors/{sensor_id}/ticks", getSensorTicks).Methods("GET")
	r.HandleFunc("/api/sensors/{sensor_id}/dots", getSensorDots).Methods("GET")

	// FIXME: deprecated
	r.HandleFunc("/api/log", getLogsV1).Methods("GET")
	r.HandleFunc("/api/logs", getLogsV1).Methods("GET")

	r.HandleFunc("/api/debug_log", getDebugLogs).Methods("GET")
	r.HandleFunc("/api/debug_logs", getDebugLogs).Methods("GET")

	r.HandleFunc("/api/v1/log", getLogsV1).Methods("GET")
	r.HandleFunc("/api/v1/logs", getLogsV1).Methods("GET")
	r.HandleFunc("/api/v2/log", getLogsV2).Methods("GET")
	r.HandleFunc("/api/v2/logs", getLogsV2).Methods("GET")

	http.Handle("/", r)
}

func getCoordinator(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}
	hashToken, ok := mux.Vars(r)["hash"]
	if !ok {
		http.Error(w, "Missing token hash", http.StatusBadRequest)
		return
	}

	redisClient := redisPool.Get()
	defer redisClient.Close()

	c := &coordinator{ID: coordinatorID, Token: hashToken}

	coordinatorHash, err := redis.String(redisClient.Do("HGET", c.key(), "token"))
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if coordinatorHash != c.Token {
		http.Error(w, "Incorrect hash for this coordinator", http.StatusUnauthorized)
	}

	coordinatorName, err := redis.String(redisClient.Do("HGET", c.key(), "name"))
	if err != nil {
		if err != redis.ErrNil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		coordinatorName = c.ID
	}
	c.Name = coordinatorName

	b, err := json.Marshal(c)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func putCoordinator(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}

	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var c coordinator
	if err := json.Unmarshal(b, &c); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	c.ID = coordinatorID

	redisClient := redisPool.Get()
	defer redisClient.Close()

	_, err = redisClient.Do("HSET", c.key(), "name", c.Name)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func putSensor(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing or invalid sensor_id", http.StatusBadRequest)
		return
	}

	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var s sensor
	if err := json.Unmarshal(b, &s); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	redisClient := redisPool.Get()
	defer redisClient.Close()

	_, err = redisClient.Do("HMSET", keyOfSensor(sensorID), "lat", s.Lat, "lng", s.Lng)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func getCoordinatorSensors(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}

	redisClient := redisPool.Get()
	defer redisClient.Close()

	ids, err := redis.Strings(redisClient.Do("SMEMBERS", keyOfCoordinatorSensors(coordinatorID)))
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	sensors := make([]*sensor, 0)
	for _, sensorID := range ids {
		if len(sensorID) == 0 {
			log.Println(err)
			http.Error(w, "Invalid or missing sensor ID", http.StatusInternalServerError)
			return
		}
		s := &sensor{ID: sensorID, ControllerID: coordinatorID}

		// Get lat, lng of sensor
		bb, err := redisClient.Do("HMGET", keyOfSensor(sensorID), "lat", "lng")
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if bb != nil {
			list := bb.([]interface{})
			if len(list) > 0 {
				if list[0] != nil {
					s.Lat = string(list[0].([]byte))
				}
				if list[1] != nil {
					s.Lng = string(list[1].([]byte))
				}
			}
		}

		// Get last tick of sensor
		ticks, err := findTicksByRange(sensorID, 0, 0)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if len(ticks) > 0 {
			s.LastTick = &ticks[0].Datetime

		}

		sensors = append(sensors, s)
	}

	b, err := json.Marshal(sensors)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getSensorDots(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing sensor_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Invalid end", http.StatusBadRequest)
		return
	}

	dotsPerDay, err := strconv.Atoi(r.FormValue("dots_per_day"))
	if err != nil {
		http.Error(w, "Invalid dots_per_day", http.StatusBadRequest)
		return
	}
	if dotsPerDay < 0 || dotsPerDay > 24 {
		http.Error(w, "dots_per_day must be in range 0-24", http.StatusBadRequest)
		return
	}

	redisClient := redisPool.Get()
	defer redisClient.Close()

	ticks, err := findTicksByScore(sensorID, start, end)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var dots []*tick
	if dotsPerDay > 0 {
		dots = findAverages(ticks, dotsPerDay, start, end)
	} else {
		dots = ticks
	}

	b, err := json.Marshal(dots)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getSensorTicks(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing sensor_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Missing or invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Missing or invalid end", http.StatusBadRequest)
		return
	}

	result, err := findTicksByScore(sensorID, start, end)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err := json.Marshal(result)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getLogsV1(w http.ResponseWriter, r *http.Request) {
	getLogs(w, r, loggingKeyV1)
}

func getLogsV2(w http.ResponseWriter, r *http.Request) {
	getLogs(w, r, loggingKeyV2)
}

func getLogs(w http.ResponseWriter, r *http.Request, key string) {
	redisClient := redisPool.Get()
	defer redisClient.Close()

	bb, err := redisClient.Do("LRANGE", key, 0, 1000)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "text/plain")
	for _, item := range bb.([]interface{}) {
		s := string(item.([]byte))
		s = strconv.Quote(s)
		w.Write([]byte(s))
		w.Write([]byte("\n\r"))
	}
}

func getDebugLogs(w http.ResponseWriter, r *http.Request) {
	getLogs(w, r, debugLogKey)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/toggl/bugsnag"
)

func defineRoutes() {
	r := mux.NewRouter()

	r.HandleFunc("/api/coordinators/{coordinator_id}/sensors", getCoordinatorSensors).Methods("GET")
	r.HandleFunc("/api/coordinators/{coordinator_id}/readings", getCoordinatorReadings).Methods("GET")
	r.HandleFunc("/api/coordinators/{coordinator_id}/log", getCoordinatorLog).Methods("GET")
	r.HandleFunc("/api/coordinators/{coordinator_id}", putCoordinator).Methods("POST", "PUT")
	r.HandleFunc("/api/coordinators/{coordinator_id}/{hash}", getCoordinator).Methods("GET")

	r.HandleFunc("/api/sensors/{sensor_id}", putSensor).Methods("POST", "PUT")
	r.HandleFunc("/api/sensors/{sensor_id}/ticks", getSensorTicks).Methods("GET")
	r.HandleFunc("/api/sensors/{sensor_id}/dots", getSensorDots).Methods("GET")

	r.HandleFunc("/api/admin/coordinators", getAdminCoordinators).Methods("GET")

	r.HandleFunc("/api/v1/log", getCSVLogs).Methods("GET")
	r.HandleFunc("/api/v1/logs", getCSVLogs).Methods("GET")
	r.HandleFunc("/api/v2/log", getJSONLogs).Methods("GET")
	r.HandleFunc("/api/v2/logs", getJSONLogs).Methods("GET")

	http.Handle("/", r)
}

func getAdminCoordinators(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	auth, err := parseToken(r)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if auth == nil {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"Ardusensor admin\"")
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	if auth.Username != *adminUsername || auth.Password != *adminPassword {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"Ardusensor admin\"")
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

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

	b, err := json.MarshalIndent(coordinators, "", "\t")
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getCoordinatorLog(w http.ResponseWriter, r *http.Request) {
	coordinatorID, err := strconv.Atoi(mux.Vars(r)["coordinator_id"])
	if err != nil {
		http.Error(w, "Missing or invalid coordinator_id", http.StatusBadRequest)
		return
	}
	writeLogs(w, r, loggingKeyJSON, coordinatorID)
}

func getCoordinator(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}
	hashToken, ok := mux.Vars(r)["hash"]
	if !ok {
		http.Error(w, "Missing token hash", http.StatusBadRequest)
		return
	}

	c, err := loadCoordinator(coordinatorID)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if hashToken != c.Token {
		http.Error(w, "Incorrect token for this coordinator", http.StatusUnauthorized)
		return
	}

	b, err := json.Marshal(c)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func putCoordinator(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}

	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var c coordinator
	if err := json.Unmarshal(b, &c); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if err := setCoordinatorLabel(coordinatorID, c.Label); err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func putSensor(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing or invalid sensor_id", http.StatusBadRequest)
		return
	}

	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var s sensor
	if err := json.Unmarshal(b, &s); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	s.ID = sensorID

	if err := s.save(); err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err = json.Marshal(s)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getCoordinatorSensors(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}

	sensors, err := sensorsOfCoordinator(coordinatorID)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err := json.Marshal(sensors)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getSensorDots(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing sensor_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Invalid end", http.StatusBadRequest)
		return
	}

	dotsPerDay, err := strconv.Atoi(r.FormValue("dots_per_day"))
	if err != nil {
		http.Error(w, "Invalid dots_per_day", http.StatusBadRequest)
		return
	}
	if dotsPerDay < 0 || dotsPerDay > 24 {
		http.Error(w, "dots_per_day must be in range 0-24", http.StatusBadRequest)
		return
	}

	ticks, err := findTicksByScore(sensorID, start, end)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var dots []*tick
	if dotsPerDay > 0 {
		dots = findAverages(ticks, dotsPerDay, start, end)
	} else {
		dots = ticks
	}

	b, err := json.Marshal(dots)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getSensorTicks(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing sensor_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Missing or invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Missing or invalid end", http.StatusBadRequest)
		return
	}

	result, err := findTicksByScore(sensorID, start, end)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err := json.Marshal(result)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getCoordinatorReadings(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	s, exists := mux.Vars(r)["coordinator_id"]
	if !exists {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}
	coordinatorID, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		http.Error(w, "Invalid coordinator_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Missing or invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Missing or invalid end", http.StatusBadRequest)
		return
	}

	result, err := coordinatorReadings(coordinatorID, start, end)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err := json.Marshal(result)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getCSVLogs(w http.ResponseWriter, r *http.Request) {
	writeLogs(w, r, loggingKeyCSV, 0)
}

func getJSONLogs(w http.ResponseWriter, r *http.Request) {
	writeLogs(w, r, loggingKeyJSON, 0)
}

func writeLogs(w http.ResponseWriter, r *http.Request, key string, coordinatorID int) {
	b, err := getLogs(key, coordinatorID)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "text/plain")
	w.Write(b)
}
<commit_msg>/api subrouter<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/toggl/bugsnag"
)

func defineRoutes() {
	r := mux.NewRouter()

	api := r.PathPrefix("/api").Subrouter()

	api.HandleFunc("/coordinators/{coordinator_id}/sensors", getCoordinatorSensors).Methods("GET")
	api.HandleFunc("/coordinators/{coordinator_id}/readings", getCoordinatorReadings).Methods("GET")
	api.HandleFunc("/coordinators/{coordinator_id}/log", getCoordinatorLog).Methods("GET")
	api.HandleFunc("/coordinators/{coordinator_id}", putCoordinator).Methods("POST", "PUT")
	api.HandleFunc("/coordinators/{coordinator_id}/{hash}", getCoordinator).Methods("GET")

	api.HandleFunc("/sensors/{sensor_id}", putSensor).Methods("POST", "PUT")
	api.HandleFunc("/sensors/{sensor_id}/ticks", getSensorTicks).Methods("GET")
	api.HandleFunc("/sensors/{sensor_id}/dots", getSensorDots).Methods("GET")

	api.HandleFunc("/admin/coordinators", getAdminCoordinators).Methods("GET")

	api.HandleFunc("/log", getCSVLogs).Methods("GET")
	api.HandleFunc("/v1/logs", getCSVLogs).Methods("GET")
	api.HandleFunc("/v2/log", getJSONLogs).Methods("GET")
	api.HandleFunc("/v2/logs", getJSONLogs).Methods("GET")

	http.Handle("/", r)
}

func getAdminCoordinators(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	auth, err := parseToken(r)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if auth == nil {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"Ardusensor admin\"")
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	if auth.Username != *adminUsername || auth.Password != *adminPassword {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"Ardusensor admin\"")
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

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

	b, err := json.MarshalIndent(coordinators, "", "\t")
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getCoordinatorLog(w http.ResponseWriter, r *http.Request) {
	coordinatorID, err := strconv.Atoi(mux.Vars(r)["coordinator_id"])
	if err != nil {
		http.Error(w, "Missing or invalid coordinator_id", http.StatusBadRequest)
		return
	}
	writeLogs(w, r, loggingKeyJSON, coordinatorID)
}

func getCoordinator(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}
	hashToken, ok := mux.Vars(r)["hash"]
	if !ok {
		http.Error(w, "Missing token hash", http.StatusBadRequest)
		return
	}

	c, err := loadCoordinator(coordinatorID)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if hashToken != c.Token {
		http.Error(w, "Incorrect token for this coordinator", http.StatusUnauthorized)
		return
	}

	b, err := json.Marshal(c)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func putCoordinator(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}

	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var c coordinator
	if err := json.Unmarshal(b, &c); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if err := setCoordinatorLabel(coordinatorID, c.Label); err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func putSensor(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing or invalid sensor_id", http.StatusBadRequest)
		return
	}

	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var s sensor
	if err := json.Unmarshal(b, &s); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	s.ID = sensorID

	if err := s.save(); err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err = json.Marshal(s)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getCoordinatorSensors(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	coordinatorID, ok := mux.Vars(r)["coordinator_id"]
	if !ok {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}

	sensors, err := sensorsOfCoordinator(coordinatorID)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err := json.Marshal(sensors)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getSensorDots(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing sensor_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Invalid end", http.StatusBadRequest)
		return
	}

	dotsPerDay, err := strconv.Atoi(r.FormValue("dots_per_day"))
	if err != nil {
		http.Error(w, "Invalid dots_per_day", http.StatusBadRequest)
		return
	}
	if dotsPerDay < 0 || dotsPerDay > 24 {
		http.Error(w, "dots_per_day must be in range 0-24", http.StatusBadRequest)
		return
	}

	ticks, err := findTicksByScore(sensorID, start, end)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var dots []*tick
	if dotsPerDay > 0 {
		dots = findAverages(ticks, dotsPerDay, start, end)
	} else {
		dots = ticks
	}

	b, err := json.Marshal(dots)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getSensorTicks(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	sensorID, exists := mux.Vars(r)["sensor_id"]
	if !exists {
		http.Error(w, "Missing sensor_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Missing or invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Missing or invalid end", http.StatusBadRequest)
		return
	}

	result, err := findTicksByScore(sensorID, start, end)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err := json.Marshal(result)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getCoordinatorReadings(w http.ResponseWriter, r *http.Request) {
	log.Println(r)

	s, exists := mux.Vars(r)["coordinator_id"]
	if !exists {
		http.Error(w, "Missing coordinator_id", http.StatusBadRequest)
		return
	}
	coordinatorID, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		http.Error(w, "Invalid coordinator_id", http.StatusBadRequest)
		return
	}

	start, err := strconv.Atoi(r.FormValue("start"))
	if err != nil {
		http.Error(w, "Missing or invalid start", http.StatusBadRequest)
		return
	}

	end, err := strconv.Atoi(r.FormValue("end"))
	if err != nil {
		http.Error(w, "Missing or invalid end", http.StatusBadRequest)
		return
	}

	result, err := coordinatorReadings(coordinatorID, start, end)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, err := json.Marshal(result)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(b)
}

func getCSVLogs(w http.ResponseWriter, r *http.Request) {
	writeLogs(w, r, loggingKeyCSV, 0)
}

func getJSONLogs(w http.ResponseWriter, r *http.Request) {
	writeLogs(w, r, loggingKeyJSON, 0)
}

func writeLogs(w http.ResponseWriter, r *http.Request, key string, coordinatorID int) {
	b, err := getLogs(key, coordinatorID)
	if err != nil {
		bugsnag.Notify(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "text/plain")
	w.Write(b)
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/mediaconvert"

	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceAwsMediaConvertQueue() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsMediaConvertQueueCreate,
		Read:   resourceAwsMediaConvertQueueRead,
		Update: resourceAwsMediaConvertQueueUpdate,
		Delete: resourceAwsMediaConvertQueueDelete,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"pricing_plan": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  mediaconvert.PricingPlanOnDemand,
				ValidateFunc: validation.StringInSlice([]string{
					mediaconvert.PricingPlanOnDemand,
					mediaconvert.PricingPlanReserved,
				}, false),
			},
			"reservation_plan_settings": {
				Type:     schema.TypeList,
				Computed: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"commitment": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								mediaconvert.CommitmentOneYear,
							}, false),
						},
						"renewal_type": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								mediaconvert.RenewalTypeAutoRenew,
								mediaconvert.RenewalTypeExpire,
							}, false),
						},
						"reserved_slots": {
							Type:     schema.TypeInt,
							Required: true,
						},
					},
				},
			},
			"status": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  mediaconvert.QueueStatusActive,
				ValidateFunc: validation.StringInSlice([]string{
					mediaconvert.QueueStatusActive,
					mediaconvert.QueueStatusPaused,
				}, false),
			},
			"tags": tagsSchema(),
		},
	}
}

func resourceAwsMediaConvertQueueCreate(d *schema.ResourceData, meta interface{}) error {
	originalConn := meta.(*AWSClient).mediaconvertconn

	endpointURL, err := getAwsMediaConvertEndpoint(originalConn)
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Endpoint: %s", err)
	}

	sess, err := session.NewSession(&originalConn.Config)
	if err != nil {
		return fmt.Errorf("Error creating AWS session: %s", err)
	}
	conn := mediaconvert.New(sess.Copy(&aws.Config{Endpoint: aws.String(endpointURL)}))

	createOpts := &mediaconvert.CreateQueueInput{
		Name:        aws.String(d.Get("name").(string)),
		Status:      aws.String(d.Get("status").(string)),
		PricingPlan: aws.String(d.Get("pricing_plan").(string)),
		Tags:        tagsFromMapGeneric(d.Get("tags").(map[string]interface{})),
	}

	if v, ok := d.GetOk("description"); ok {
		createOpts.Description = aws.String(v.(string))
	}

	if v, ok := d.GetOk("reservation_plan_settings"); ok {
		reservationPlanSettings := v.([]interface{})[0].(map[string]interface{})
		createOpts.ReservationPlanSettings = expandMediaConvertReservationPlanSettings(reservationPlanSettings)
	}

	resp, err := conn.CreateQueue(createOpts)
	if err != nil {
		return fmt.Errorf("Error creating Media Convert Queue: %s", err)
	}

	d.SetId(aws.StringValue(resp.Queue.Name))

	return resourceAwsMediaConvertQueueRead(d, meta)
}

func resourceAwsMediaConvertQueueRead(d *schema.ResourceData, meta interface{}) error {
	originalConn := meta.(*AWSClient).mediaconvertconn

	endpointURL, err := getAwsMediaConvertEndpoint(originalConn)
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Endpoint: %s", err)
	}

	sess, err := session.NewSession(&originalConn.Config)
	if err != nil {
		return fmt.Errorf("Error creating AWS session: %s", err)
	}
	conn := mediaconvert.New(sess.Copy(&aws.Config{Endpoint: aws.String(endpointURL)}))

	getOpts := &mediaconvert.GetQueueInput{
		Name: aws.String(d.Id()),
	}

	resp, err := conn.GetQueue(getOpts)
	if isAWSErr(err, mediaconvert.ErrCodeNotFoundException, "") {
		log.Printf("[WARN] Media Convert Queue (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Queue: %s", err)
	}

	d.Set("arn", resp.Queue.Arn)
	d.Set("name", resp.Queue.Name)
	d.Set("description", resp.Queue.Description)
	d.Set("pricing_plan", resp.Queue.PricingPlan)
	d.Set("status", resp.Queue.Status)

	if err := d.Set("reservation_plan_settings", flattenMediaConvertReservationPlan(resp.Queue.ReservationPlan)); err != nil {
		return fmt.Errorf("Error setting Media Convert Queue reservation_plan_settings: %s", err)
	}

	if err := saveTagsMediaConvert(conn, d, aws.StringValue(resp.Queue.Arn)); err != nil {
		return fmt.Errorf("Error setting Media Convert Queue tags: %s", err)
	}

	return nil
}

func resourceAwsMediaConvertQueueUpdate(d *schema.ResourceData, meta interface{}) error {
	originalConn := meta.(*AWSClient).mediaconvertconn

	endpointURL, err := getAwsMediaConvertEndpoint(originalConn)
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Endpoint: %s", err)
	}

	sess, err := session.NewSession(&originalConn.Config)
	if err != nil {
		return fmt.Errorf("Error creating AWS session: %s", err)
	}
	conn := mediaconvert.New(sess.Copy(&aws.Config{Endpoint: aws.String(endpointURL)}))

	updateOpts := &mediaconvert.UpdateQueueInput{
		Name:   aws.String(d.Id()),
		Status: aws.String(d.Get("status").(string)),
	}

	if v, ok := d.GetOk("description"); ok {
		updateOpts.Description = aws.String(v.(string))
	}

	if v, ok := d.GetOk("reservation_plan_settings"); ok {
		reservationPlanSettings := v.([]interface{})[0].(map[string]interface{})
		updateOpts.ReservationPlanSettings = expandMediaConvertReservationPlanSettings(reservationPlanSettings)
	}

	_, err = conn.UpdateQueue(updateOpts)
	if isAWSErr(err, mediaconvert.ErrCodeNotFoundException, "") {
		log.Printf("[WARN] Media Convert Queue (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error updating Media Convert Queue: %s", err)
	}

	if err := setTagsMediaConvert(conn, d, d.Get("arn").(string)); err != nil {
		return fmt.Errorf("error updating Media Convert Queue (%s) tags: %s", d.Id(), err)
	}

	return resourceAwsMediaConvertQueueRead(d, meta)
}

func resourceAwsMediaConvertQueueDelete(d *schema.ResourceData, meta interface{}) error {
	originalConn := meta.(*AWSClient).mediaconvertconn

	endpointURL, err := getAwsMediaConvertEndpoint(originalConn)
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Endpoint: %s", err)
	}

	sess, err := session.NewSession(&originalConn.Config)
	if err != nil {
		return fmt.Errorf("Error creating AWS session: %s", err)
	}
	conn := mediaconvert.New(sess.Copy(&aws.Config{Endpoint: aws.String(endpointURL)}))

	delOpts := &mediaconvert.DeleteQueueInput{
		Name: aws.String(d.Id()),
	}

	_, err = conn.DeleteQueue(delOpts)
	if isAWSErr(err, mediaconvert.ErrCodeNotFoundException, "") {
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error deleting Media Convert Queue: %s", err)
	}

	return nil
}

func getAwsMediaConvertEndpoint(conn *mediaconvert.MediaConvert) (string, error) {
	descOpts := &mediaconvert.DescribeEndpointsInput{
		Mode: aws.String(mediaconvert.DescribeEndpointsModeDefault),
	}

	resp, err := conn.DescribeEndpoints(descOpts)
	if err != nil {
		return "", err
	}

	endpoint := resp.Endpoints[0]

	return aws.StringValue(endpoint.Url), nil
}
<commit_msg>fix to use keyvaluetags for aws_mediaconvert_queue resource<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/mediaconvert"

	"github.com/hashicorp/terraform-plugin-sdk/helper/keyvaluetags"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceAwsMediaConvertQueue() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsMediaConvertQueueCreate,
		Read:   resourceAwsMediaConvertQueueRead,
		Update: resourceAwsMediaConvertQueueUpdate,
		Delete: resourceAwsMediaConvertQueueDelete,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"pricing_plan": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  mediaconvert.PricingPlanOnDemand,
				ValidateFunc: validation.StringInSlice([]string{
					mediaconvert.PricingPlanOnDemand,
					mediaconvert.PricingPlanReserved,
				}, false),
			},
			"reservation_plan_settings": {
				Type:     schema.TypeList,
				Computed: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"commitment": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								mediaconvert.CommitmentOneYear,
							}, false),
						},
						"renewal_type": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								mediaconvert.RenewalTypeAutoRenew,
								mediaconvert.RenewalTypeExpire,
							}, false),
						},
						"reserved_slots": {
							Type:     schema.TypeInt,
							Required: true,
						},
					},
				},
			},
			"status": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  mediaconvert.QueueStatusActive,
				ValidateFunc: validation.StringInSlice([]string{
					mediaconvert.QueueStatusActive,
					mediaconvert.QueueStatusPaused,
				}, false),
			},
			"tags": tagsSchema(),
		},
	}
}

func resourceAwsMediaConvertQueueCreate(d *schema.ResourceData, meta interface{}) error {
	originalConn := meta.(*AWSClient).mediaconvertconn

	endpointURL, err := getAwsMediaConvertEndpoint(originalConn)
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Endpoint: %s", err)
	}

	sess, err := session.NewSession(&originalConn.Config)
	if err != nil {
		return fmt.Errorf("Error creating AWS session: %s", err)
	}
	conn := mediaconvert.New(sess.Copy(&aws.Config{Endpoint: aws.String(endpointURL)}))

	createOpts := &mediaconvert.CreateQueueInput{
		Name:        aws.String(d.Get("name").(string)),
		Status:      aws.String(d.Get("status").(string)),
		PricingPlan: aws.String(d.Get("pricing_plan").(string)),
		Tags:        keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().MediaconvertTags(),
	}

	if v, ok := d.GetOk("description"); ok {
		createOpts.Description = aws.String(v.(string))
	}

	if v, ok := d.GetOk("reservation_plan_settings"); ok {
		reservationPlanSettings := v.([]interface{})[0].(map[string]interface{})
		createOpts.ReservationPlanSettings = expandMediaConvertReservationPlanSettings(reservationPlanSettings)
	}

	resp, err := conn.CreateQueue(createOpts)
	if err != nil {
		return fmt.Errorf("Error creating Media Convert Queue: %s", err)
	}

	d.SetId(aws.StringValue(resp.Queue.Name))

	return resourceAwsMediaConvertQueueRead(d, meta)
}

func resourceAwsMediaConvertQueueRead(d *schema.ResourceData, meta interface{}) error {
	originalConn := meta.(*AWSClient).mediaconvertconn

	endpointURL, err := getAwsMediaConvertEndpoint(originalConn)
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Endpoint: %s", err)
	}

	sess, err := session.NewSession(&originalConn.Config)
	if err != nil {
		return fmt.Errorf("Error creating AWS session: %s", err)
	}
	conn := mediaconvert.New(sess.Copy(&aws.Config{Endpoint: aws.String(endpointURL)}))

	getOpts := &mediaconvert.GetQueueInput{
		Name: aws.String(d.Id()),
	}

	resp, err := conn.GetQueue(getOpts)
	if isAWSErr(err, mediaconvert.ErrCodeNotFoundException, "") {
		log.Printf("[WARN] Media Convert Queue (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Queue: %s", err)
	}

	d.Set("arn", resp.Queue.Arn)
	d.Set("name", resp.Queue.Name)
	d.Set("description", resp.Queue.Description)
	d.Set("pricing_plan", resp.Queue.PricingPlan)
	d.Set("status", resp.Queue.Status)

	if err := d.Set("reservation_plan_settings", flattenMediaConvertReservationPlan(resp.Queue.ReservationPlan)); err != nil {
		return fmt.Errorf("Error setting Media Convert Queue reservation_plan_settings: %s", err)
	}

	tags, err := keyvaluetags.MediaconvertListTags(conn, aws.StringValue(resp.Queue.Arn))

	if err != nil {
		return fmt.Errorf("error listing tags for Media Convert Queue (%s): %s", d.Id(), err)
	}

	if err := d.Set("tags", tags.IgnoreAws().Map()); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}

func resourceAwsMediaConvertQueueUpdate(d *schema.ResourceData, meta interface{}) error {
	originalConn := meta.(*AWSClient).mediaconvertconn

	endpointURL, err := getAwsMediaConvertEndpoint(originalConn)
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Endpoint: %s", err)
	}

	sess, err := session.NewSession(&originalConn.Config)
	if err != nil {
		return fmt.Errorf("Error creating AWS session: %s", err)
	}
	conn := mediaconvert.New(sess.Copy(&aws.Config{Endpoint: aws.String(endpointURL)}))

		updateOpts := &mediaconvert.UpdateQueueInput{
			Name:   aws.String(d.Id()),
			Status: aws.String(d.Get("status").(string)),
		}

		if v, ok := d.GetOk("description"); ok {
			updateOpts.Description = aws.String(v.(string))
		}

		if v, ok := d.GetOk("reservation_plan_settings"); ok {
			reservationPlanSettings := v.([]interface{})[0].(map[string]interface{})
			updateOpts.ReservationPlanSettings = expandMediaConvertReservationPlanSettings(reservationPlanSettings)
		}

		_, err = conn.UpdateQueue(updateOpts)
		if isAWSErr(err, mediaconvert.ErrCodeNotFoundException, "") {
			log.Printf("[WARN] Media Convert Queue (%s) not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		if err != nil {
			return fmt.Errorf("Error updating Media Convert Queue: %s", err)
		}
	}

	if d.HasChange("tags") {
		o, n := d.GetChange("tags")
		if err := keyvaluetags.MediaconvertUpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
			return fmt.Errorf("error updating tags: %s", err)
		}
	}

	return resourceAwsMediaConvertQueueRead(d, meta)
}

func resourceAwsMediaConvertQueueDelete(d *schema.ResourceData, meta interface{}) error {
	originalConn := meta.(*AWSClient).mediaconvertconn

	endpointURL, err := getAwsMediaConvertEndpoint(originalConn)
	if err != nil {
		return fmt.Errorf("Error getting Media Convert Endpoint: %s", err)
	}

	sess, err := session.NewSession(&originalConn.Config)
	if err != nil {
		return fmt.Errorf("Error creating AWS session: %s", err)
	}
	conn := mediaconvert.New(sess.Copy(&aws.Config{Endpoint: aws.String(endpointURL)}))

	delOpts := &mediaconvert.DeleteQueueInput{
		Name: aws.String(d.Id()),
	}

	_, err = conn.DeleteQueue(delOpts)
	if isAWSErr(err, mediaconvert.ErrCodeNotFoundException, "") {
		return nil
	}
	if err != nil {
		return fmt.Errorf("Error deleting Media Convert Queue: %s", err)
	}

	return nil
}

func getAwsMediaConvertEndpoint(conn *mediaconvert.MediaConvert) (string, error) {
	descOpts := &mediaconvert.DescribeEndpointsInput{
		Mode: aws.String(mediaconvert.DescribeEndpointsModeDefault),
	}

	resp, err := conn.DescribeEndpoints(descOpts)
	if err != nil {
		return "", err
	}

	endpoint := resp.Endpoints[0]

	return aws.StringValue(endpoint.Url), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/template"

	"github.com/codegangsta/cli"
	"github.com/hashicorp/atlas-go/v1"
)

var (
	Version string
)

func init() {
	log.SetOutput(ioutil.Discard)
}

func ToJson(obj interface{}) (interface{}, error) {
	data, err := json.Marshal(obj)
	if err != nil {
		return nil, err
	}
	return string(data), nil
}

func search(c *cli.Context) {

	if c.String("user") == "" || c.String("artifact") == "" || c.String("type") == "" {
		cli.ShowAppHelp(c)
		os.Exit(1)
	}

	searchOpts := &atlas.ArtifactSearchOpts{
		User: c.String("user"),
		Name: c.String("artifact"),
		Type: c.String("type"),
	}
	if len(c.StringSlice("meta")) > 0 {
		filter := map[string]string{}
		for _, m := range c.StringSlice("meta") {
			pair := strings.Split(m, "=")
			filter[pair[0]] = pair[1]
		}
		fmt.Fprintf(os.Stderr, "FILTER: %#v\n", filter)
		searchOpts.Metadata = filter
	}
	client := atlas.DefaultClient()

	versions, err := client.ArtifactSearch(searchOpts)
	if err != nil {
		fmt.Errorf("search error: %#v", err)
		os.Exit(1)
	}

	fnMap := template.FuncMap{}
	fnMap["json"] = ToJson
	tmpl, err := template.New("artifact").Funcs(fnMap).Parse(c.String("format") + "\n")
	if err != nil {
		panic(err)
	}

	for _, v := range versions {
		//fmt.Println("ver: %#v", v)
		err = tmpl.Execute(os.Stdout, v)
		if err != nil {
			panic(err)
		}
	}

}

func main() {
	fmt.Fprintln(os.Stderr, "Search atlas.hashicorp artifacts ...")

	app := cli.NewApp()
	app.Name = "atlifacts"
	app.Usage = "query atlas.hashicorp.com artifacts"
	app.Version = Version

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "user, u",
			Usage:  "atlas user",
			EnvVar: "ATLAS_USER",
		},
		cli.StringFlag{
			Name:   "artifact, a",
			Usage:  "atlas artifact",
			EnvVar: "ATLAS_ARTIFACT_NAME",
		},
		cli.StringFlag{
			Name:   "type, t",
			Usage:  "atlas artifact type",
			EnvVar: "ATLAS_ARTIFACT_TYPE",
		},
		cli.StringFlag{
			Name:  "format, f",
			Value: "{{.Slug}}",
			Usage: "output format in golang template",
		},
		cli.StringSliceFlag{
			Name:  "meta, m",
			Usage: "meta field as fielter",
		},
	}
	app.Action = search

	app.Run(os.Args)

}
<commit_msg>Fixing typo<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/template"

	"github.com/codegangsta/cli"
	"github.com/hashicorp/atlas-go/v1"
)

var (
	Version string
)

func init() {
	log.SetOutput(ioutil.Discard)
}

func ToJson(obj interface{}) (interface{}, error) {
	data, err := json.Marshal(obj)
	if err != nil {
		return nil, err
	}
	return string(data), nil
}

func search(c *cli.Context) {

	if c.String("user") == "" || c.String("artifact") == "" || c.String("type") == "" {
		cli.ShowAppHelp(c)
		os.Exit(1)
	}

	searchOpts := &atlas.ArtifactSearchOpts{
		User: c.String("user"),
		Name: c.String("artifact"),
		Type: c.String("type"),
	}
	if len(c.StringSlice("meta")) > 0 {
		filter := map[string]string{}
		for _, m := range c.StringSlice("meta") {
			pair := strings.Split(m, "=")
			filter[pair[0]] = pair[1]
		}
		fmt.Fprintf(os.Stderr, "FILTER: %#v\n", filter)
		searchOpts.Metadata = filter
	}
	client := atlas.DefaultClient()

	versions, err := client.ArtifactSearch(searchOpts)
	if err != nil {
		fmt.Errorf("search error: %#v", err)
		os.Exit(1)
	}

	fnMap := template.FuncMap{}
	fnMap["json"] = ToJson
	tmpl, err := template.New("artifact").Funcs(fnMap).Parse(c.String("format") + "\n")
	if err != nil {
		panic(err)
	}

	for _, v := range versions {
		//fmt.Println("ver: %#v", v)
		err = tmpl.Execute(os.Stdout, v)
		if err != nil {
			panic(err)
		}
	}

}

func main() {
	fmt.Fprintln(os.Stderr, "Search atlas.hashicorp artifacts ...")

	app := cli.NewApp()
	app.Name = "atlifacts"
	app.Usage = "query atlas.hashicorp.com artifacts"
	app.Version = Version

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "user, u",
			Usage:  "atlas user",
			EnvVar: "ATLAS_USER",
		},
		cli.StringFlag{
			Name:   "artifact, a",
			Usage:  "atlas artifact",
			EnvVar: "ATLAS_ARTIFACT_NAME",
		},
		cli.StringFlag{
			Name:   "type, t",
			Usage:  "atlas artifact type",
			EnvVar: "ATLAS_ARTIFACT_TYPE",
		},
		cli.StringFlag{
			Name:  "format, f",
			Value: "{{.Slug}}",
			Usage: "output format in golang template",
		},
		cli.StringSliceFlag{
			Name:  "meta, m",
			Usage: "meta field as filter",
		},
	}
	app.Action = search

	app.Run(os.Args)

}
<|endoftext|>