<commit_before>
package sre2

import (
  "fmt"
  "testing"
)

// Check the given state to be true.
func checkState(t *testing.T, state bool, err string) {
  if !state {
    t.Error(err)
  }
}

// Check the equality of two []int slices.
func checkIntSlice(t *testing.T, expected []int, result []int, err string) {
  match := true
  if len(expected) != len(result) {
    match = false
  } else {
    for i := 0; i < len(expected); i++ {
      if expected[i] != result[i] {
        match = false
      }
    }
  }
  checkState(t, match, fmt.Sprintf("%s: got %s, expected %s", err, result, expected))
}

// Run a selection of basic regular expressions against this package.
func TestSimpleRe(t *testing.T) {
  r := MustParse("^(a|b)+c*$")
  checkState(t, !r.RunSimple("abd"), "not a valid match")
  checkState(t, r.RunSimple("a"), "basic string should match")
  checkState(t, !r.RunSimple(""), "empty string should not match")
  checkState(t, r.RunSimple("abcccc"), "longer string should match")

  r = MustParse("(\\w*)\\s*(\\w*)")
  ok, res := r.RunSubMatch("zing hello there")
  checkState(t, ok, "should match generally")
  checkIntSlice(t, []int{0, 10, 0, 4, 5, 10}, res, "did not match first two words as expected")

  r = MustParse(".*?(\\w+)$")
  ok, res = r.RunSubMatch("zing hello there")
  checkState(t, ok, "should match generally")
  checkIntSlice(t, []int{0, 16, 11, 16}, res, "did not match last word as expected")
}

// Test parsing an invalid RE returns an error.
func TestInvalidRe(t *testing.T) {
  r, err := Parse("a**")
  checkState(t, err != nil, "must fail parsing")
  checkState(t, r == nil, "regexp must be nil")

  pass := false
  func() {
    defer func() {
      if r := recover(); r != nil {
        pass = true
      }
    }()
    MustParse("z(((a")
  }()
  checkState(t, pass, "should panic")
}

// Test behaviour related to character classes expressed within [...].
func TestCharClass(t *testing.T) {
  r := MustParse("^[\t[:word:]]+$") // Match tabs and word characters.
  checkState(t, r.RunSimple("c"), "non-space should match")
  checkState(t, !r.RunSimple("c t"), "space should not match")
  checkState(t, r.RunSimple("c\tt"), "tab should match")

  r = MustParse("^[:ascii:]*$")
  checkState(t, r.RunSimple(""), "nothing should match")
  checkState(t, r.RunSimple("c"), "ascii should match")
  checkState(t, !r.RunSimple("Π"), "unicode should not match")

  r = MustParse("^\\pN$")
  checkState(t, r.RunSimple("〩"), "character from Nl should match")
  checkState(t, r.RunSimple("¾"), "character from Nu should match")

  r = MustParse("^\\p{Nl}$")
  checkState(t, r.RunSimple("〩"), "character from Nl should match")
  checkState(t, !r.RunSimple("¾"), "character from Nu should not match")
}

// Test regexp generated by escape sequences (e.g. \n, \. etc).
func TestEscapeSequences(t *testing.T) {
  r := MustParse("^\\.\n\\044$") // Match '.\n$'
  checkState(t, r.RunSimple(".\n$"), "should match")
  checkState(t, !r.RunSimple(" \n$"), "space should not match")
  checkState(t, !r.RunSimple(".\n"), "# should not be treated as end char")

  r = MustParse("^\\x{03a0}\\x25$") // Match 'Π%'.
  checkState(t, r.RunSimple("Π%"), "should match pi+percent")

  func() {
    defer func() {
      if r := recover(); r != nil {
        // ok
      } else {
        t.Error("should have panic()ed on trying to escape Π, not punctuation")
      }
    }()
    r = MustParse("^\\Π$")
  }()
}

// Tests string literals between \Q...\E.
func TestStringLiteral(t *testing.T) {
  r := MustParse("^\\Qhello\\E$")
  checkState(t, r.RunSimple("hello"), "should match hello")

  r = MustParse("^\\Q.$\\\\E$") // match ".$\\"
  checkState(t, r.RunSimple(".$\\"), "should match")
  checkState(t, !r.RunSimple(" $\\"), "should not match")

  r = MustParse("^a\\Q\\E*b$") // match absolutely nothing between 'ab'
  checkState(t, r.RunSimple("ab"), "should match")
  checkState(t, !r.RunSimple("acb"), "should not match")
}

// Test closure expansion types, such as {..}, ?, +, * etc.
func TestClosureExpansion(t *testing.T) {
  r := MustParse("^za?$")
  checkState(t, r.RunSimple("z"), "should match none")
  checkState(t, r.RunSimple("za"), "should match single")
  checkState(t, !r.RunSimple("zaa"), "should not match more")

  r = MustParse("^a{2,2}$")
  checkState(t, !r.RunSimple(""), "0 should fail")
  checkState(t, !r.RunSimple("a"), "1 should fail")
  checkState(t, r.RunSimple("aa"), "2 should succeed")
  checkState(t, r.RunSimple("aaa"), "3 should succeed")
  checkState(t, r.RunSimple("aaaa"), "4 should succeed")
  checkState(t, !r.RunSimple("aaaaa"), "5 should fail")

  r = MustParse("^a{2}$")
  checkState(t, !r.RunSimple(""), "0 should fail")
  checkState(t, !r.RunSimple("a"), "1 should fail")
  checkState(t, r.RunSimple("aa"), "2 should succeed")
  checkState(t, !r.RunSimple("aaa"), "3 should fail")

  r = MustParse("^a{3,}$")
  checkState(t, !r.RunSimple("aa"), "2 should fail")
  checkState(t, r.RunSimple("aaa"), "3 should succeed")
  checkState(t, r.RunSimple("aaaaaa"), "more should succeed")
}

// Test specific greedy/non-greedy closure types.
func TestClosureGreedy(t *testing.T) {
  r := MustParse("^(a{0,2}?)(a*)$")
  ok, res := r.RunSubMatch("aaa")
  checkState(t, ok, "should match")
  checkIntSlice(t, []int{0, 3, 0, 0, 0, 3}, res, "did not match expected")

  r = MustParse("^(a{0,2})?(a*)$")
  ok, res = r.RunSubMatch("aaa")
  checkState(t, ok, "should match")
  checkIntSlice(t, []int{0, 3, 0, 2, 2, 3}, res, "did not match expected")

  r = MustParse("^(a{2,}?)(a*)$")
  ok, res = r.RunSubMatch("aaa")
  checkState(t, ok, "should match")
  checkIntSlice(t, []int{0, 3, 0, 2, 2, 3}, res, "did not match expected")
}

// Test simple left/right matchers.
func TestLeftRight(t *testing.T) {
  r := MustParse("^.\\b.$")
  checkState(t, r.RunSimple("a "), "left char is word")
  checkState(t, r.RunSimple(" a"), "right char is word")
  checkState(t, !r.RunSimple("  "), "not a boundary")
  checkState(t, !r.RunSimple("aa"), "not a boundary")
}

// Test general flags in sre2.
func TestFlags(t *testing.T) {
  r := MustParse("^(?i:AbC)zz$")
  checkState(t, r.RunSimple("abczz"), "success")
  checkState(t, !r.RunSimple("abcZZ"), "fail, flag should not escape")
  ok, res := r.RunSubMatch("ABCzz")
  checkState(t, ok, "should pass")
  checkIntSlice(t, []int{0,5}, res, "should just have a single outer paren")

  r = MustParse("^(?U)(a+)(.+)$")
  ok, res = r.RunSubMatch("aaaabb")
  checkState(t, ok, "should pass")
  checkIntSlice(t, []int{0,6,0,1,1,6}, res, "should be ungreedy")

  r = MustParse("^(?i)a*(?-i)b*$")
  checkState(t, r.RunSimple("AAaaAAaabbbbb"), "success")
  checkState(t, !r.RunSimple("AAaaAAaaBBBa"), "should fail, flag should not escape")
}

// Test the behaviour of rune classes.
func TestRuneClass(t *testing.T) {
  c := NewRuneClass()
  checkState(t, c.MatchRune('B'), "should implicitly match everything")

  c.AddRune(false, '#')
  checkState(t, !c.MatchRune('B'), "should no longer implicitly match everything")
  checkState(t, c.MatchRune('#'), "should match added rune")

  c.AddRuneRange(false, 'A', 'Z')
  checkState(t, c.MatchRune('A'), "should match rune 'A' in range")
  checkState(t, c.MatchRune('B'), "should match rune 'B' in range")

  c.AddRuneRange(true, 'B', 'C')
  checkState(t, c.MatchRune('A'), "should match rune 'A' in range")
  checkState(t, !c.MatchRune('B'), "should not match rune 'B' in range")

  c = NewRuneClass()
  c.AddUnicodeClass(false, "Greek")
  c.AddRune(true, 'Π')
  c.AddRune(false, 'A')
  checkState(t, !c.MatchRune('Π'), "should not match pi")
  checkState(t, c.MatchRune('Ω'), "should match omega")
  checkState(t, !c.MatchRune('Z'), "should not match regular latin char")
  checkState(t, c.MatchRune('A'), "should match included latin char")

  c = NewRuneClass()
  c.AddUnicodeClass(true, "Cyrillic")
  checkState(t, c.MatchRune('%'), "should match random char, class is exclude-only")
  c.AddRune(false, '')
  checkState(t, !c.MatchRune('%'), "should no longer match random char")
  checkState(t, c.MatchRune(''), "should match single opt-in char")
}
<commit_msg>Simplified test<commit_after>
package sre2

import (
  "fmt"
  "testing"
)

// Check the given state to be true.
func checkState(t *testing.T, state bool, err string) {
  if !state {
    t.Error(err)
  }
}

// Check the equality of two []int slices.
func checkIntSlice(t *testing.T, expected []int, result []int, err string) {
  match := true
  if len(expected) != len(result) {
    match = false
  } else {
    for i := 0; i < len(expected); i++ {
      if expected[i] != result[i] {
        match = false
      }
    }
  }
  checkState(t, match, fmt.Sprintf("%s: got %s, expected %s", err, result, expected))
}

// Run a selection of basic regular expressions against this package.
func TestSimpleRe(t *testing.T) {
  r := MustParse("^(a|b)+c*$")
  checkState(t, !r.RunSimple("abd"), "not a valid match")
  checkState(t, r.RunSimple("a"), "basic string should match")
  checkState(t, !r.RunSimple(""), "empty string should not match")
  checkState(t, r.RunSimple("abcccc"), "longer string should match")

  r = MustParse("(\\w*)\\s*(\\w*)")
  ok, res := r.RunSubMatch("zing hello there")
  checkState(t, ok, "should match generally")
  checkIntSlice(t, []int{0, 10, 0, 4, 5, 10}, res, "did not match first two words as expected")

  r = MustParse(".*?(\\w+)$")
  ok, res = r.RunSubMatch("zing hello there")
  checkState(t, ok, "should match generally")
  checkIntSlice(t, []int{0, 16, 11, 16}, res, "did not match last word as expected")
}

// Test parsing an invalid RE returns an error.
func TestInvalidRe(t *testing.T) {
  r, err := Parse("a**")
  checkState(t, err != nil, "must fail parsing")
  checkState(t, r == nil, "regexp must be nil")

  pass := false
  func() {
    defer func() {
      if r := recover(); r != nil {
        pass = true
      }
    }()
    MustParse("z(((a")
  }()
  checkState(t, pass, "should panic")
}

// Test behaviour related to character classes expressed within [...].
func TestCharClass(t *testing.T) {
  r := MustParse("^[\t[:word:]]+$") // Match tabs and word characters.
  checkState(t, r.RunSimple("c"), "non-space should match")
  checkState(t, !r.RunSimple("c t"), "space should not match")
  checkState(t, r.RunSimple("c\tt"), "tab should match")

  r = MustParse("^[:ascii:]*$")
  checkState(t, r.RunSimple(""), "nothing should match")
  checkState(t, r.RunSimple("c"), "ascii should match")
  checkState(t, !r.RunSimple("Π"), "unicode should not match")

  r = MustParse("^\\pN$")
  checkState(t, r.RunSimple("〩"), "character from Nl should match")
  checkState(t, r.RunSimple("¾"), "character from Nu should match")

  r = MustParse("^\\p{Nl}$")
  checkState(t, r.RunSimple("〩"), "character from Nl should match")
  checkState(t, !r.RunSimple("¾"), "character from Nu should not match")
}

// Test regexp generated by escape sequences (e.g. \n, \. etc).
func TestEscapeSequences(t *testing.T) {
  r := MustParse("^\\.\n\\044$") // Match '.\n$'
  checkState(t, r.RunSimple(".\n$"), "should match")
  checkState(t, !r.RunSimple(" \n$"), "space should not match")
  checkState(t, !r.RunSimple(".\n"), "# should not be treated as end char")

  r = MustParse("^\\x{03a0}\\x25$") // Match 'Π%'.
  checkState(t, r.RunSimple("Π%"), "should match pi+percent")

  r, err := Parse("^\\Π$")
  checkState(t, err != nil && r == nil,
      "should have failed on trying to escape Π, not punctuation")
}

// Tests string literals between \Q...\E.
func TestStringLiteral(t *testing.T) {
  r := MustParse("^\\Qhello\\E$")
  checkState(t, r.RunSimple("hello"), "should match hello")

  r = MustParse("^\\Q.$\\\\E$") // match ".$\\"
  checkState(t, r.RunSimple(".$\\"), "should match")
  checkState(t, !r.RunSimple(" $\\"), "should not match")

  r = MustParse("^a\\Q\\E*b$") // match absolutely nothing between 'ab'
  checkState(t, r.RunSimple("ab"), "should match")
  checkState(t, !r.RunSimple("acb"), "should not match")
}

// Test closure expansion types, such as {..}, ?, +, * etc.
func TestClosureExpansion(t *testing.T) {
  r := MustParse("^za?$")
  checkState(t, r.RunSimple("z"), "should match none")
  checkState(t, r.RunSimple("za"), "should match single")
  checkState(t, !r.RunSimple("zaa"), "should not match more")

  r = MustParse("^a{2,2}$")
  checkState(t, !r.RunSimple(""), "0 should fail")
  checkState(t, !r.RunSimple("a"), "1 should fail")
  checkState(t, r.RunSimple("aa"), "2 should succeed")
  checkState(t, r.RunSimple("aaa"), "3 should succeed")
  checkState(t, r.RunSimple("aaaa"), "4 should succeed")
  checkState(t, !r.RunSimple("aaaaa"), "5 should fail")

  r = MustParse("^a{2}$")
  checkState(t, !r.RunSimple(""), "0 should fail")
  checkState(t, !r.RunSimple("a"), "1 should fail")
  checkState(t, r.RunSimple("aa"), "2 should succeed")
  checkState(t, !r.RunSimple("aaa"), "3 should fail")

  r = MustParse("^a{3,}$")
  checkState(t, !r.RunSimple("aa"), "2 should fail")
  checkState(t, r.RunSimple("aaa"), "3 should succeed")
  checkState(t, r.RunSimple("aaaaaa"), "more should succeed")
}

// Test specific greedy/non-greedy closure types.
func TestClosureGreedy(t *testing.T) {
  r := MustParse("^(a{0,2}?)(a*)$")
  ok, res := r.RunSubMatch("aaa")
  checkState(t, ok, "should match")
  checkIntSlice(t, []int{0, 3, 0, 0, 0, 3}, res, "did not match expected")

  r = MustParse("^(a{0,2})?(a*)$")
  ok, res = r.RunSubMatch("aaa")
  checkState(t, ok, "should match")
  checkIntSlice(t, []int{0, 3, 0, 2, 2, 3}, res, "did not match expected")

  r = MustParse("^(a{2,}?)(a*)$")
  ok, res = r.RunSubMatch("aaa")
  checkState(t, ok, "should match")
  checkIntSlice(t, []int{0, 3, 0, 2, 2, 3}, res, "did not match expected")
}

// Test simple left/right matchers.
func TestLeftRight(t *testing.T) {
  r := MustParse("^.\\b.$")
  checkState(t, r.RunSimple("a "), "left char is word")
  checkState(t, r.RunSimple(" a"), "right char is word")
  checkState(t, !r.RunSimple("  "), "not a boundary")
  checkState(t, !r.RunSimple("aa"), "not a boundary")
}

// Test general flags in sre2.
func TestFlags(t *testing.T) {
  r := MustParse("^(?i:AbC)zz$")
  checkState(t, r.RunSimple("abczz"), "success")
  checkState(t, !r.RunSimple("abcZZ"), "fail, flag should not escape")
  ok, res := r.RunSubMatch("ABCzz")
  checkState(t, ok, "should pass")
  checkIntSlice(t, []int{0,5}, res, "should just have a single outer paren")

  r = MustParse("^(?U)(a+)(.+)$")
  ok, res = r.RunSubMatch("aaaabb")
  checkState(t, ok, "should pass")
  checkIntSlice(t, []int{0,6,0,1,1,6}, res, "should be ungreedy")

  r = MustParse("^(?i)a*(?-i)b*$")
  checkState(t, r.RunSimple("AAaaAAaabbbbb"), "success")
  checkState(t, !r.RunSimple("AAaaAAaaBBBa"), "should fail, flag should not escape")
}

// Test the behaviour of rune classes.
func TestRuneClass(t *testing.T) {
  c := NewRuneClass()
  checkState(t, c.MatchRune('B'), "should implicitly match everything")

  c.AddRune(false, '#')
  checkState(t, !c.MatchRune('B'), "should no longer implicitly match everything")
  checkState(t, c.MatchRune('#'), "should match added rune")

  c.AddRuneRange(false, 'A', 'Z')
  checkState(t, c.MatchRune('A'), "should match rune 'A' in range")
  checkState(t, c.MatchRune('B'), "should match rune 'B' in range")

  c.AddRuneRange(true, 'B', 'C')
  checkState(t, c.MatchRune('A'), "should match rune 'A' in range")
  checkState(t, !c.MatchRune('B'), "should not match rune 'B' in range")

  c = NewRuneClass()
  c.AddUnicodeClass(false, "Greek")
  c.AddRune(true, 'Π')
  c.AddRune(false, 'A')
  checkState(t, !c.MatchRune('Π'), "should not match pi")
  checkState(t, c.MatchRune('Ω'), "should match omega")
  checkState(t, !c.MatchRune('Z'), "should not match regular latin char")
  checkState(t, c.MatchRune('A'), "should match included latin char")

  c = NewRuneClass()
  c.AddUnicodeClass(true, "Cyrillic")
  checkState(t, c.MatchRune('%'), "should match random char, class is exclude-only")
  c.AddRune(false, '')
  checkState(t, !c.MatchRune('%'), "should no longer match random char")
  checkState(t, c.MatchRune(''), "should match single opt-in char")
}
<|endoftext|>


<commit_before>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

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

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

package obcpbft

import (
	"testing"

	pb "github.com/openblockchain/obc-peer/protos"
)

func makeTestnetSieve(inst *instance) {
	config := readConfig()
	inst.consenter = newObcSieve(uint64(inst.id), config, inst)
	sieve := inst.consenter.(*obcSieve)
	sieve.pbft.replicaCount = len(inst.net.replicas)
	sieve.pbft.f = inst.net.f
	inst.deliver = func(msg []byte) {
		sieve.RecvMsg(&pb.OpenchainMessage{Type: pb.OpenchainMessage_CONSENSUS, Payload: msg})
	}
}

func TestSieveNetwork(t *testing.T) {
	net := makeTestnet(1, makeTestnetSieve)
	defer net.close()

	err := net.replicas[1].consenter.RecvMsg(createExternalRequest(1))
	if err != nil {
		t.Fatalf("External request was not processed by backup: %v", err)
	}

	err = net.process()
	if err != nil {
		t.Fatalf("Processing failed: %s", err)
	}

	for _, inst := range net.replicas {
		if len(inst.blocks) != 1 {
			t.Errorf("replica %d executed %d requests, expected %d",
				inst.id, len(net.replicas[3].blocks), 1)
		}

		if inst.consenter.(*obcSieve).view != 0 {
			t.Errorf("replica %d in view %d, expected 0",
				inst.id, inst.consenter.(*obcSieve).view)
		}
	}
}
<commit_msg>Fix replica reference for block check<commit_after>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

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

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

package obcpbft

import (
	"testing"

	pb "github.com/openblockchain/obc-peer/protos"
)

func makeTestnetSieve(inst *instance) {
	config := readConfig()
	inst.consenter = newObcSieve(uint64(inst.id), config, inst)
	sieve := inst.consenter.(*obcSieve)
	sieve.pbft.replicaCount = len(inst.net.replicas)
	sieve.pbft.f = inst.net.f
	inst.deliver = func(msg []byte) {
		sieve.RecvMsg(&pb.OpenchainMessage{Type: pb.OpenchainMessage_CONSENSUS, Payload: msg})
	}
}

func TestSieveNetwork(t *testing.T) {
	net := makeTestnet(1, makeTestnetSieve)
	defer net.close()

	err := net.replicas[1].consenter.RecvMsg(createExternalRequest(1))
	if err != nil {
		t.Fatalf("External request was not processed by backup: %v", err)
	}

	err = net.process()
	if err != nil {
		t.Fatalf("Processing failed: %s", err)
	}

	for i, inst := range net.replicas {
		if len(inst.blocks) != 1 {
			t.Errorf("Replica %d executed %d requests, expected %d",
				inst.id, len(net.replicas[i].blocks), 1)
		}

		if inst.consenter.(*obcSieve).view != 0 {
			t.Errorf("Replica %d in view %d, expected 0",
				inst.id, inst.consenter.(*obcSieve).view)
		}
	}
}
<|endoftext|>


<commit_before>/*
   Copyright 2014 CoreOS, Inc.

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

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

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

package raft

import (
	"log"

	pb "github.com/coreos/etcd/raft/raftpb"
)

// unstable.entris[i] has raft log position i+unstable.offset.
// Note that unstable.offset may be less than the highest log
// position in storage; this means that the next write to storage
// might need to truncate the log before persisting unstable.entries.
type unstable struct {
	// the incoming unstable snapshot, if any.
	snapshot *pb.Snapshot
	// all entries that have not yet been written to storage.
	entries []pb.Entry
	offset  uint64
}

// maybeFirstIndex returns the first index if it has a snapshot.
func (u *unstable) maybeFirstIndex() (uint64, bool) {
	if u.snapshot != nil {
		return u.snapshot.Metadata.Index, true
	}
	return 0, false
}

// maybeLastIndex returns the last index if it has at least one
// unstable entry or snapshot.
func (u *unstable) maybeLastIndex() (uint64, bool) {
	if l := len(u.entries); l != 0 {
		return u.offset + uint64(l) - 1, true
	}
	if u.snapshot != nil {
		return u.snapshot.Metadata.Index, true
	}
	return 0, false
}

// myabeTerm returns the term of the entry at index i, if there
// is any.
func (u *unstable) maybeTerm(i uint64) (uint64, bool) {
	if i < u.offset {
		if u.snapshot == nil {
			return 0, false
		}
		if u.snapshot.Metadata.Index == i {
			return u.snapshot.Metadata.Term, true
		}
		return 0, false
	}

	last, ok := u.maybeLastIndex()
	if !ok {
		return 0, false
	}
	if i > last {
		return 0, false
	}
	return u.entries[i-u.offset].Term, true
}

func (u *unstable) stableTo(i, t uint64) {
	gt, ok := u.maybeTerm(i)
	if !ok {
		return
	}
	// if i < offest, term is matched with the snapshot
	// only update the unstalbe entries if term is matched with
	// an unstable entry.
	if gt == t && i >= u.offset {
		u.entries = u.entries[i+1-u.offset:]
		u.offset = i + 1
	}
}

func (u *unstable) stableSnapTo(i uint64) {
	if u.snapshot != nil && u.snapshot.Metadata.Index == i {
		u.snapshot = nil
	}
}

func (u *unstable) restore(s pb.Snapshot) {
	u.offset = s.Metadata.Index + 1
	u.entries = nil
	u.snapshot = &s
}

func (u *unstable) truncateAndAppend(after uint64, ents []pb.Entry) {
	switch {
	case after < u.offset:
		log.Printf("raftlog: replace the unstable entries from index %d", after+1)
		// The log is being truncated to before our current offset
		// portion, so set the offset and replace the entries
		u.offset = after + 1
		u.entries = ents
	case after == u.offset+uint64(len(u.entries))-1:
		// after is the last index in the u.entries
		// directly append
		u.entries = append(u.entries, ents...)
	default:
		// truncate to after and copy to u.entries
		// then append
		log.Printf("raftlog: truncate the unstable entries to index %d", after)
		u.entries = append([]pb.Entry{}, u.slice(u.offset, after+1)...)
		u.entries = append(u.entries, ents...)
	}
}

func (u *unstable) slice(lo uint64, hi uint64) []pb.Entry {
	if lo >= hi {
		return nil
	}
	if u.isOutOfBounds(lo) || u.isOutOfBounds(hi-1) {
		return nil
	}
	return u.entries[lo-u.offset : hi-u.offset]
}

func (u *unstable) isOutOfBounds(i uint64) bool {
	if len(u.entries) == 0 {
		return true
	}
	last := u.offset + uint64(len(u.entries)) - 1
	if i < u.offset || i > last {
		return true
	}
	return false
}
<commit_msg>raft: move good case of truncateAndAppend to the first place<commit_after>/*
   Copyright 2014 CoreOS, Inc.

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

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

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

package raft

import (
	"log"

	pb "github.com/coreos/etcd/raft/raftpb"
)

// unstable.entris[i] has raft log position i+unstable.offset.
// Note that unstable.offset may be less than the highest log
// position in storage; this means that the next write to storage
// might need to truncate the log before persisting unstable.entries.
type unstable struct {
	// the incoming unstable snapshot, if any.
	snapshot *pb.Snapshot
	// all entries that have not yet been written to storage.
	entries []pb.Entry
	offset  uint64
}

// maybeFirstIndex returns the first index if it has a snapshot.
func (u *unstable) maybeFirstIndex() (uint64, bool) {
	if u.snapshot != nil {
		return u.snapshot.Metadata.Index, true
	}
	return 0, false
}

// maybeLastIndex returns the last index if it has at least one
// unstable entry or snapshot.
func (u *unstable) maybeLastIndex() (uint64, bool) {
	if l := len(u.entries); l != 0 {
		return u.offset + uint64(l) - 1, true
	}
	if u.snapshot != nil {
		return u.snapshot.Metadata.Index, true
	}
	return 0, false
}

// myabeTerm returns the term of the entry at index i, if there
// is any.
func (u *unstable) maybeTerm(i uint64) (uint64, bool) {
	if i < u.offset {
		if u.snapshot == nil {
			return 0, false
		}
		if u.snapshot.Metadata.Index == i {
			return u.snapshot.Metadata.Term, true
		}
		return 0, false
	}

	last, ok := u.maybeLastIndex()
	if !ok {
		return 0, false
	}
	if i > last {
		return 0, false
	}
	return u.entries[i-u.offset].Term, true
}

func (u *unstable) stableTo(i, t uint64) {
	gt, ok := u.maybeTerm(i)
	if !ok {
		return
	}
	// if i < offest, term is matched with the snapshot
	// only update the unstalbe entries if term is matched with
	// an unstable entry.
	if gt == t && i >= u.offset {
		u.entries = u.entries[i+1-u.offset:]
		u.offset = i + 1
	}
}

func (u *unstable) stableSnapTo(i uint64) {
	if u.snapshot != nil && u.snapshot.Metadata.Index == i {
		u.snapshot = nil
	}
}

func (u *unstable) restore(s pb.Snapshot) {
	u.offset = s.Metadata.Index + 1
	u.entries = nil
	u.snapshot = &s
}

func (u *unstable) truncateAndAppend(after uint64, ents []pb.Entry) {
	switch {
	case after == u.offset+uint64(len(u.entries))-1:
		// after is the last index in the u.entries
		// directly append
		u.entries = append(u.entries, ents...)
	case after < u.offset:
		log.Printf("raftlog: replace the unstable entries from index %d", after+1)
		// The log is being truncated to before our current offset
		// portion, so set the offset and replace the entries
		u.offset = after + 1
		u.entries = ents
	default:
		// truncate to after and copy to u.entries
		// then append
		log.Printf("raftlog: truncate the unstable entries to index %d", after)
		u.entries = append([]pb.Entry{}, u.slice(u.offset, after+1)...)
		u.entries = append(u.entries, ents...)
	}
}

func (u *unstable) slice(lo uint64, hi uint64) []pb.Entry {
	if lo >= hi {
		return nil
	}
	if u.isOutOfBounds(lo) || u.isOutOfBounds(hi-1) {
		return nil
	}
	return u.entries[lo-u.offset : hi-u.offset]
}

func (u *unstable) isOutOfBounds(i uint64) bool {
	if len(u.entries) == 0 {
		return true
	}
	last := u.offset + uint64(len(u.entries)) - 1
	if i < u.offset || i > last {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package raftutil

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"os"
)

// ReadPeersJSON returns the peers from the PeerStore file
func ReadPeersJSON(path string) ([]string, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}

	if len(b) == 0 {
		return nil, nil
	}

	var peers []string
	dec := json.NewDecoder(bytes.NewReader(b))
	if err := dec.Decode(&peers); err != nil {
		return nil, err
	}

	return peers, nil
}
<commit_msg>Removing raftutil<commit_after><|endoftext|>


<commit_before>//
// Copyright (c) 2011, Yanko D Sanchez Bolanos
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the author nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

package goprowl

import (
	"fmt"
	"net/http"
	"net/url"
)

const (
	API_SERVER = "https://api.prowlapp.com"
	ADD_PATH   = "/publicapi/add"
	API_URL    = API_SERVER + ADD_PATH
)

type Notification struct {
	Application string
	Description string
	Event       string
	Priority    string
	Providerkey string
	Url         string
}

type Goprowl struct {
	apikeys []string
}

func (gp *Goprowl) RegisterKey(key string) {

	if len(key) != 40 {

		fmt.Printf("Error, Apikey must be 40 characters long.\n")
		// need to raise an error.
	}

	gp.apikeys = append(gp.apikeys, key)

}

func (gp *Goprowl) DelKey(key string) {
}

func (gp *Goprowl) Push(n *Notification) {

	ch := make(chan string)

	for _, apikey := range gp.apikeys {

		apikeyList := []string{apikey}
		applicationList := []string{n.Application}
		eventList := []string{n.Event}
		descriptionList := []string{n.Description}
		priorityList := []string{n.Priority}
		vals := url.Values{"apikey": apikeyList,
			"application": applicationList,
			"description": descriptionList,
			"event":       eventList,
			"priority":    priorityList}

		if n.Url != "" {
			vals["url"] = []string{n.Url}
		}

		if n.Providerkey != "" {
			vals["providerkey"] = []string{n.Providerkey}
		}

		// overkill?
		go func(key string) {
			r, err := http.PostForm(API_URL, vals)

			if err != nil {
				fmt.Printf("%s\n", err)
				ch <- key
			} else {
				if r.StatusCode != 200 {
					ch <- key
				} else {
					ch <- ""
				}
			}

		}(apikey)

	}

	//fmt.Printf("Waiting...\n")
	for i := 0; ; i++ {

		if i == len(gp.apikeys) {
			break
		}

		rc := <-ch
		if rc != "" {
			fmt.Printf("The following key failed: %s\n", rc)
		}

	}

}
<commit_msg>Send multiple requests in a single HTTP request.<commit_after>//
// Copyright (c) 2011, Yanko D Sanchez Bolanos
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the author nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

package goprowl

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

const (
	API_SERVER = "https://api.prowlapp.com"
	ADD_PATH   = "/publicapi/add"
	API_URL    = API_SERVER + ADD_PATH
)

type Notification struct {
	Application string
	Description string
	Event       string
	Priority    string
	Providerkey string
	Url         string
}

type Goprowl struct {
	apikeys []string
}

func (gp *Goprowl) RegisterKey(key string) {

	if len(key) != 40 {

		fmt.Printf("Error, Apikey must be 40 characters long.\n")
		// need to raise an error.
	}

	gp.apikeys = append(gp.apikeys, key)

}

func (gp *Goprowl) DelKey(key string) {
}

func (gp *Goprowl) Push(n *Notification) {

	ch := make(chan string)

	keycsv := strings.Join(gp.apikeys, ",")
	applicationList := []string{n.Application}
	eventList := []string{n.Event}
	descriptionList := []string{n.Description}
	priorityList := []string{n.Priority}

	vals := url.Values{
		"apikey":      []string{keycsv},
		"application": applicationList,
		"description": descriptionList,
		"event":       eventList,
		"priority":    priorityList,
	}

	if n.Url != "" {
		vals["url"] = []string{n.Url}
	}

	if n.Providerkey != "" {
		vals["providerkey"] = []string{n.Providerkey}
	}

	// overkill?
	go func() {
		r, err := http.PostForm(API_URL, vals)

		if err != nil {
			fmt.Printf("%s\n", err)
			ch <- ""
		} else {
			if r.StatusCode != 200 {
				ch <- ""
			} else {
				ch <- ""
			}
		}
	}()

	rc := <-ch
	if rc != "" {
		fmt.Printf("The following key failed: %s\n", keycsv)
	}
}
<|endoftext|>


<commit_before>package gostats

import (
  "os"
  "fmt"
  "strings"
  "regexp"
  "time"
  "runtime"
  "github.com/quipo/statsd"
)

type collectorList []func() map[string]float64

type GoStats struct{
  ClientName      string
  Hostname        string
  PushInterval    time.Duration
  StatsdHost      string
  PushTicker      *time.Ticker
  Conn            *statsd.StatsdClient
  Collectors      collectorList
}

func sanitizeMetricName(name string) string{
  for _,c := range []string{"/",".", " "}{
    name = strings.Replace(name, c, "_", -1)
  }

  r := regexp.MustCompile("[^a-zA-Z0-9-_]")
  name = r.ReplaceAllString(name, "")

  return name
}

func New() *GoStats{
  s := GoStats{}
  s.ClientName = "gostats"
  host, _ := os.Hostname()
  s.Hostname = sanitizeMetricName(host)

  s.Collectors = collectorList{memStats, goRoutines, cgoCalls, gcs}

  return &s
}

func Start(statsdHost string, pushInterval int, clientName string) (*GoStats, error){
  s := New()

  s.StatsdHost = statsdHost
  s.PushInterval = time.Duration(pushInterval) * time.Second
  s.ClientName = clientName

  err := s.Start()

  return s, err
}

func (s *GoStats) MetricBase() string{
  return strings.Join([]string{"gostats", s.ClientName, s.Hostname, ""}, ".")
}

func (s *GoStats) Start() error{
  s.Conn = statsd.NewStatsdClient(s.StatsdHost, s.MetricBase())
  err := s.Conn.CreateSocket()
  if err != nil{
    return err
  }

  s.PushTicker = time.NewTicker(s.PushInterval)

  go s.startSender()

  return nil
}

func (s *GoStats) Stop(){
  s.PushTicker.Stop()
}

func (s *GoStats) startSender(){
  buffer := statsd.NewStatsdBuffer(s.PushInterval, s.Conn)
  for {
    select{
    case <-s.PushTicker.C:
      s.doSend(buffer)
    }
  }
}

func (s *GoStats) doSend(b *statsd.StatsdBuffer){
  for _,collector := range s.Collectors{
    metrics := collector()

    for metricName, metricValue := range metrics{
      b.FGauge(metricName, metricValue)
    }
  }
}

func memStats() map[string]float64{
  m := runtime.MemStats{}
  runtime.ReadMemStats(&m)
  metrics := map[string]float64{
    "memory.objects.HeapObjects": float64(m.HeapObjects),
    "memory.summary.Alloc": float64(m.Alloc),
    "memory.counters.Mallocs": perSecondCounter("mallocs", int64(m.Mallocs)),
    "memory.counters.Frees": perSecondCounter("frees", int64(m.Frees)),
    "memory.summary.System": float64(m.HeapSys),
    "memory.heap.Idle": float64(m.HeapIdle),
    "memory.heap.InUse": float64(m.HeapInuse),
  }

  return metrics
}

func goRoutines() map[string]float64{
  return map[string]float64{
    "goroutines.total": float64(runtime.NumGoroutine()),
  }
}

func cgoCalls() map[string]float64{
  return map[string]float64{
    "cgo.calls": perSecondCounter("cgoCalls", runtime.NumCgoCall()),
  }
}

var lastGcPause float64
var lastGcTime uint64
var lastGcPeriod float64

func gcs() map[string]float64{
  m := runtime.MemStats{}
  runtime.ReadMemStats(&m)
  gcPause := float64(m.PauseNs[(m.NumGC+255)%256])
  if gcPause > 0 {
    lastGcPause = gcPause
  }

  if m.LastGC > lastGcTime{
    lastGcPeriod = float64(m.LastGC - lastGcTime)
    if lastGcPeriod == float64(m.LastGC){
      lastGcPeriod = 0
    }

    lastGcPeriod = lastGcPeriod/1000000

    lastGcTime = m.LastGC
  }

  return map[string]float64{
    "gc.perSecond": perSecondCounter("gcs-total", int64(m.NumGC)),
    "gc.pauseTimeNs": lastGcPause,
    "gc.pauseTimeMs": lastGcPause/float64(1000000),
    "gc.period": lastGcPeriod,
  }
}
<commit_msg>Debug code<commit_after>package gostats

import (
  "os"
  "strings"
  "regexp"
  "time"
  "runtime"
  "github.com/quipo/statsd"
)

type collectorList []func() map[string]float64

type GoStats struct{
  ClientName      string
  Hostname        string
  PushInterval    time.Duration
  StatsdHost      string
  PushTicker      *time.Ticker
  Conn            *statsd.StatsdClient
  Collectors      collectorList
}

func sanitizeMetricName(name string) string{
  for _,c := range []string{"/",".", " "}{
    name = strings.Replace(name, c, "_", -1)
  }

  r := regexp.MustCompile("[^a-zA-Z0-9-_]")
  name = r.ReplaceAllString(name, "")

  return name
}

func New() *GoStats{
  s := GoStats{}
  s.ClientName = "gostats"
  host, _ := os.Hostname()
  s.Hostname = sanitizeMetricName(host)

  s.Collectors = collectorList{memStats, goRoutines, cgoCalls, gcs}

  return &s
}

func Start(statsdHost string, pushInterval int, clientName string) (*GoStats, error){
  s := New()

  s.StatsdHost = statsdHost
  s.PushInterval = time.Duration(pushInterval) * time.Second
  s.ClientName = clientName

  err := s.Start()

  return s, err
}

func (s *GoStats) MetricBase() string{
  return strings.Join([]string{"gostats", s.ClientName, s.Hostname, ""}, ".")
}

func (s *GoStats) Start() error{
  s.Conn = statsd.NewStatsdClient(s.StatsdHost, s.MetricBase())
  err := s.Conn.CreateSocket()
  if err != nil{
    return err
  }

  s.PushTicker = time.NewTicker(s.PushInterval)

  go s.startSender()

  return nil
}

func (s *GoStats) Stop(){
  s.PushTicker.Stop()
}

func (s *GoStats) startSender(){
  buffer := statsd.NewStatsdBuffer(s.PushInterval, s.Conn)
  for {
    select{
    case <-s.PushTicker.C:
      s.doSend(buffer)
    }
  }
}

func (s *GoStats) doSend(b *statsd.StatsdBuffer){
  for _,collector := range s.Collectors{
    metrics := collector()

    for metricName, metricValue := range metrics{
      b.FGauge(metricName, metricValue)
    }
  }
}

func memStats() map[string]float64{
  m := runtime.MemStats{}
  runtime.ReadMemStats(&m)
  metrics := map[string]float64{
    "memory.objects.HeapObjects": float64(m.HeapObjects),
    "memory.summary.Alloc": float64(m.Alloc),
    "memory.counters.Mallocs": perSecondCounter("mallocs", int64(m.Mallocs)),
    "memory.counters.Frees": perSecondCounter("frees", int64(m.Frees)),
    "memory.summary.System": float64(m.HeapSys),
    "memory.heap.Idle": float64(m.HeapIdle),
    "memory.heap.InUse": float64(m.HeapInuse),
  }

  return metrics
}

func goRoutines() map[string]float64{
  return map[string]float64{
    "goroutines.total": float64(runtime.NumGoroutine()),
  }
}

func cgoCalls() map[string]float64{
  return map[string]float64{
    "cgo.calls": perSecondCounter("cgoCalls", runtime.NumCgoCall()),
  }
}

var lastGcPause float64
var lastGcTime uint64
var lastGcPeriod float64

func gcs() map[string]float64{
  m := runtime.MemStats{}
  runtime.ReadMemStats(&m)
  gcPause := float64(m.PauseNs[(m.NumGC+255)%256])
  if gcPause > 0 {
    lastGcPause = gcPause
  }

  if m.LastGC > lastGcTime{
    lastGcPeriod = float64(m.LastGC - lastGcTime)
    if lastGcPeriod == float64(m.LastGC){
      lastGcPeriod = 0
    }

    lastGcPeriod = lastGcPeriod/1000000

    lastGcTime = m.LastGC
  }

  return map[string]float64{
    "gc.perSecond": perSecondCounter("gcs-total", int64(m.NumGC)),
    "gc.pauseTimeNs": lastGcPause,
    "gc.pauseTimeMs": lastGcPause/float64(1000000),
    "gc.period": lastGcPeriod,
  }
}
<|endoftext|>


<commit_before>package kong

import (
	"fmt"
	"net/http"

	"github.com/dghubble/sling"
	"github.com/hashicorp/terraform/helper/schema"
)

type JWTCredential struct {
	ID           string `json:"id,omitempty"`
	Key          string `json:"key,omitempty"`
	Algorithm    string `json:"algorithm,omitempty"`
	RSAPublicKey string `json:"rsa_public_key,omitempty"`
	Secret       string `json:"secret,omitempty"`
	Consumer     string `json:"-"`
}

func resourceKongJWTCredential() *schema.Resource {
	return &schema.Resource{
		Create: resourceKongJWTCredentialCreate,
		Read:   resourceKongJWTCredentialRead,
		Update: resourceKongJWTCredentialUpdate,
		Delete: resourceKongJWTCredentialDelete,

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

		Schema: map[string]*schema.Schema{
			"id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"key": {
				Type:        schema.TypeString,
				Optional:    true,
				Default:     nil,
				Description: "TA unique string identifying the credential. If left out, it will be auto-generated.",
			},

			"algorithm": {
				Type:        schema.TypeString,
				Optional:    true,
				Default:     nil,
				Description: "The algorithm used to verify the token's signature. Can be HS256 or RS256.",
			},

			"rsa_public_key": {
				Type:        schema.TypeString,
				Optional:    true,
				Default:     nil,
				Description: "If algorithm is RS256, the public key (in PEM format) to use to verify the token's signature.",
			},

			"secret": {
				Type:        schema.TypeString,
				Optional:    true,
				Default:     nil,
				Description: "If algorithm is HS256, the secret used to sign JWTs for this credential. If left out, will be auto-generated.",
			},

			"consumer": {
				Type:     schema.TypeString,
				Required: true,
			},
		},
	}
}

func resourceKongJWTCredentialCreate(d *schema.ResourceData, meta interface{}) error {
	sling := meta.(*sling.Sling)

	jwtCredential := getJWTCredentialFromResourceData(d)

	createdJWTCredential := getJWTCredentialFromResourceData(d)

	response, error := sling.New().BodyJSON(jwtCredential).Path("consumers/").Path(jwtCredential.Consumer + "/").Post("jwt/").ReceiveSuccess(createdJWTCredential)
	if error != nil {
		return fmt.Errorf("Error while creating jwtCredential.")
	}

	if response.StatusCode != http.StatusCreated {
		return fmt.Errorf(response.Status)
	}

	setJWTCredentialToResourceData(d, createdJWTCredential)

	return nil
}

func resourceKongJWTCredentialRead(d *schema.ResourceData, meta interface{}) error {
	sling := meta.(*sling.Sling)

	jwtCredential := getJWTCredentialFromResourceData(d)

	response, error := sling.New().Path("consumers/").Path(jwtCredential.Consumer + "/").Path("jwt/").Get(jwtCredential.ID).ReceiveSuccess(jwtCredential)
	if error != nil {
		return fmt.Errorf("Error while updating jwtCredential.")
	}

	if response.StatusCode == http.StatusNotFound {
		d.SetId("")
		return nil
	} else if response.StatusCode != http.StatusOK {
		return fmt.Errorf(response.Status)
	}

	setJWTCredentialToResourceData(d, jwtCredential)

	return nil
}

func resourceKongJWTCredentialUpdate(d *schema.ResourceData, meta interface{}) error {
	sling := meta.(*sling.Sling)

	jwtCredential := getJWTCredentialFromResourceData(d)

	updatedJWTCredential := getJWTCredentialFromResourceData(d)

	response, error := sling.New().BodyJSON(jwtCredential).Path("consumers/").Path(jwtCredential.Consumer + "/").Patch("jwt/").Path(jwtCredential.ID).ReceiveSuccess(updatedJWTCredential)
	if error != nil {
		return fmt.Errorf("Error while updating jwtCredential.")
	}

	if response.StatusCode != http.StatusOK {
		return fmt.Errorf(response.Status)
	}

	setJWTCredentialToResourceData(d, updatedJWTCredential)

	return nil
}

func resourceKongJWTCredentialDelete(d *schema.ResourceData, meta interface{}) error {
	sling := meta.(*sling.Sling)

	jwtCredential := getJWTCredentialFromResourceData(d)

	response, error := sling.New().Path("consumers/").Path(jwtCredential.Consumer + "/").Path("jwt/").Delete(jwtCredential.ID).ReceiveSuccess(nil)
	if error != nil {
		return fmt.Errorf("Error while deleting jwtCredential.")
	}

	if response.StatusCode != http.StatusNoContent {
		return fmt.Errorf(response.Status)
	}

	return nil
}

func getJWTCredentialFromResourceData(d *schema.ResourceData) *JWTCredential {
	jwtCredential := &JWTCredential{
		Key:          d.Get("key").(string),
		Algorithm:    d.Get("algorithm").(string),
		RSAPublicKey: d.Get("rsa_public_key").(string),
		Secret:       d.Get("secret").(string),
		Consumer:     d.Get("consumer").(string),
	}

	if id, ok := d.GetOk("id"); ok {
		jwtCredential.ID = id.(string)
	}

	return jwtCredential
}

func setJWTCredentialToResourceData(d *schema.ResourceData, jwtCredential *JWTCredential) {
	d.SetId(jwtCredential.ID)
	d.Set("key", jwtCredential.Key)
	d.Set("algorithm", jwtCredential.Algorithm)
	d.Set("rsa_public_key", jwtCredential.RSAPublicKey)
	d.Set("secret", jwtCredential.Secret)
	d.Set("consumer", jwtCredential.Consumer)
}
<commit_msg>Fix JWT asymmetric key constant diff<commit_after>package kong

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/dghubble/sling"
	"github.com/hashicorp/terraform/helper/schema"
)

type JWTCredential struct {
	ID           string `json:"id,omitempty"`
	Key          string `json:"key,omitempty"`
	Algorithm    string `json:"algorithm,omitempty"`
	RSAPublicKey string `json:"rsa_public_key,omitempty"`
	Secret       string `json:"secret,omitempty"`
	Consumer     string `json:"-"`
}

func resourceKongJWTCredential() *schema.Resource {
	return &schema.Resource{
		Create: resourceKongJWTCredentialCreate,
		Read:   resourceKongJWTCredentialRead,
		Update: resourceKongJWTCredentialUpdate,
		Delete: resourceKongJWTCredentialDelete,

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

		Schema: map[string]*schema.Schema{
			"id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"key": {
				Type:        schema.TypeString,
				Optional:    true,
				Default:     nil,
				Description: "TA unique string identifying the credential. If left out, it will be auto-generated.",
			},

			"algorithm": {
				Type:        schema.TypeString,
				Optional:    true,
				Default:     nil,
				Description: "The algorithm used to verify the token's signature. Can be HS256 or RS256.",
			},

			"rsa_public_key": {
				Type:        schema.TypeString,
				Optional:    true,
				Default:     nil,
				Description: "If algorithm is RS256, the public key (in PEM format) to use to verify the token's signature.",
				DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
					return strings.TrimSpace(old) == strings.TrimSpace(new)
				},
			},

			"secret": {
				Type:        schema.TypeString,
				Optional:    true,
				Default:     nil,
				Description: "If algorithm is HS256, the secret used to sign JWTs for this credential. If left out, will be auto-generated.",
				DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
					return new == "" || new != old
				},
			},

			"consumer": {
				Type:     schema.TypeString,
				Required: true,
			},
		},
	}
}

func resourceKongJWTCredentialCreate(d *schema.ResourceData, meta interface{}) error {
	sling := meta.(*sling.Sling)

	jwtCredential := getJWTCredentialFromResourceData(d)

	createdJWTCredential := getJWTCredentialFromResourceData(d)

	response, error := sling.New().BodyJSON(jwtCredential).Path("consumers/").Path(jwtCredential.Consumer + "/").Post("jwt/").ReceiveSuccess(createdJWTCredential)
	if error != nil {
		return fmt.Errorf("Error while creating jwtCredential.")
	}

	if response.StatusCode != http.StatusCreated {
		return fmt.Errorf(response.Status)
	}

	setJWTCredentialToResourceData(d, createdJWTCredential)

	return nil
}

func resourceKongJWTCredentialRead(d *schema.ResourceData, meta interface{}) error {
	sling := meta.(*sling.Sling)

	jwtCredential := getJWTCredentialFromResourceData(d)

	response, error := sling.New().Path("consumers/").Path(jwtCredential.Consumer + "/").Path("jwt/").Get(jwtCredential.ID).ReceiveSuccess(jwtCredential)
	if error != nil {
		return fmt.Errorf("Error while updating jwtCredential.")
	}

	if response.StatusCode == http.StatusNotFound {
		d.SetId("")
		return nil
	} else if response.StatusCode != http.StatusOK {
		return fmt.Errorf(response.Status)
	}

	setJWTCredentialToResourceData(d, jwtCredential)

	return nil
}

func resourceKongJWTCredentialUpdate(d *schema.ResourceData, meta interface{}) error {
	sling := meta.(*sling.Sling)

	jwtCredential := getJWTCredentialFromResourceData(d)

	updatedJWTCredential := getJWTCredentialFromResourceData(d)

	response, error := sling.New().BodyJSON(jwtCredential).Path("consumers/").Path(jwtCredential.Consumer + "/").Patch("jwt/").Path(jwtCredential.ID).ReceiveSuccess(updatedJWTCredential)
	if error != nil {
		return fmt.Errorf("Error while updating jwtCredential.")
	}

	if response.StatusCode != http.StatusOK {
		return fmt.Errorf(response.Status)
	}

	setJWTCredentialToResourceData(d, updatedJWTCredential)

	return nil
}

func resourceKongJWTCredentialDelete(d *schema.ResourceData, meta interface{}) error {
	sling := meta.(*sling.Sling)

	jwtCredential := getJWTCredentialFromResourceData(d)

	response, error := sling.New().Path("consumers/").Path(jwtCredential.Consumer + "/").Path("jwt/").Delete(jwtCredential.ID).ReceiveSuccess(nil)
	if error != nil {
		return fmt.Errorf("Error while deleting jwtCredential.")
	}

	if response.StatusCode != http.StatusNoContent {
		return fmt.Errorf(response.Status)
	}

	return nil
}

func getJWTCredentialFromResourceData(d *schema.ResourceData) *JWTCredential {
	jwtCredential := &JWTCredential{
		Key:          d.Get("key").(string),
		Algorithm:    d.Get("algorithm").(string),
		RSAPublicKey: d.Get("rsa_public_key").(string),
		Secret:       d.Get("secret").(string),
		Consumer:     d.Get("consumer").(string),
	}

	if id, ok := d.GetOk("id"); ok {
		jwtCredential.ID = id.(string)
	}

	return jwtCredential
}

func setJWTCredentialToResourceData(d *schema.ResourceData, jwtCredential *JWTCredential) {
	d.SetId(jwtCredential.ID)
	d.Set("key", jwtCredential.Key)
	d.Set("algorithm", jwtCredential.Algorithm)
	d.Set("rsa_public_key", jwtCredential.RSAPublicKey)
	d.Set("secret", jwtCredential.Secret)
	d.Set("consumer", jwtCredential.Consumer)
}
<|endoftext|>


<commit_before>// Declarative web grabber
// dRbiG, 2014
// See LICENSE.txt

package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/moovweb/gokogiri"
	"github.com/moovweb/gokogiri/html"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type Action struct {
	XPath  string
	Mode   string
	Action string
	Do     *Action
}

type Target struct {
	Name string
	URL  string
	Bail int
	Path string
	Do   *Action
}

type Stats struct {
	Number int
	Size   int64
	Took   time.Duration
	Mtx    sync.Mutex
}

func (s *Stats) Update(n int64, took time.Duration) {
	s.Mtx.Lock()
	s.Number++
	s.Size += n
	s.Took += took
	s.Mtx.Unlock()
}

func (s *Stats) IsEmpty() (b bool) {
	s.Mtx.Lock()
	if s.Number < 1 {
		b = true
	}
	s.Mtx.Unlock()
	return
}

func (s *Stats) String() string {
	s.Mtx.Lock()
	num := s.Number
	size := float64(s.Size) / (1024.0 * 1024.0)
	speed := size / s.Took.Seconds()
	s.Mtx.Unlock()
	return fmt.Sprintf("%d files for %0.2f MB with avg. dl. speed %0.3f MB/s", num, size, speed)
}

var (
	downloader chan *url.URL
	root       string
	bail       int
	counter    int
	stats      *Stats
	client     *http.Client
	mtx        sync.RWMutex
	wg         sync.WaitGroup
)

func LoadRules(name string) (t []Target, err error) {
	handle, err := os.Open(name)
	if err != nil {
		return
	}
	defer handle.Close()

	raw, err := ioutil.ReadAll(handle)
	if err != nil {
		return
	}

	err = json.Unmarshal(raw, &t)
	if err != nil {
		return
	}

	return
}

func DownloadPage(path string) (doc *html.HtmlDocument, err error) {
	res, err := client.Get(path)
	if err != nil {
		return
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		err = errors.New(fmt.Sprintf("DownloadPage: %d %s", res.StatusCode, path))
		return
	}

	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}

	doc, err = gokogiri.ParseHtml(data)
	return
}

func DownloadFile(target string, fullpath string) (n int64, took time.Duration, err error) {
	start := time.Now()

	res, err := client.Get(target)
	if err != nil {
		return
	}
	defer res.Body.Close()

	handle, err := os.Create(fullpath)
	if err != nil {
		return
	}
	defer handle.Close()

	n, err = io.Copy(handle, res.Body)
	took = time.Since(start)
	return
}

func DoAction(base *url.URL, target string, act *Action) (res *url.URL, err error) {
	res = &url.URL{}

	if act.Action == "raw" {
		fmt.Println(target)
		return
	}

	urlTarget, err := url.Parse(target)
	if err != nil {
		return
	}
	if len(urlTarget.Path) < 1 {
		return
	}

	if urlTarget.IsAbs() {
		*res = *urlTarget
	} else {
		*res = *base
		if urlTarget.Path[0] == '/' {
			res.Path = urlTarget.Path
		} else {
			res.Path += urlTarget.Path
		}
	}

	switch act.Action {
	case "print":
		fmt.Println(res)
	case "log":
		log.Println(res)
	case "download":
		if bail > 0 {
			mtx.RLock()
			cnt := counter
			mtx.RUnlock()
			if cnt >= bail {
				err = errors.New(fmt.Sprintf("DoAction: bailout after %d", counter))
				return
			}
		}
		downloader <- res
	case "none":
		// nop
	default:
		err = errors.New(fmt.Sprintf("DoAction: unknown action %s", act.Action))
	}

	return
}

func Process(base *url.URL, act *Action) (err error) {
	doc, err := DownloadPage(base.String())
	if err != nil {
		return err
	}
	defer doc.Free()

	res, err := doc.Search(act.XPath)
	if err != nil {
		return err
	}

	switch act.Mode {
	case "follow":
		if act.Do != nil {
			err = Process(base, act.Do)
			if err != nil {
				return err
			}
		}

		if len(res) < 1 {
			err = errors.New(fmt.Sprintf("Process: %s no results", base))
			return err
		}

		target, err := DoAction(base, res[0].String(), act)
		if err != nil {
			return err
		}

		err = Process(target, act)
		if err != nil {
			return err
		}
	case "every":
		if len(res) < 1 {
			if act.Do != nil {
				err = errors.New(fmt.Sprintf("Process: %s no results", base))
				return err
			} else {
				return
			}
		}

		for _, v := range res {
			target, err := DoAction(base, v.String(), act)
			if err != nil {
				return err
			}

			if act.Do != nil {
				err = Process(target, act.Do)
				if err != nil {
					return err
				}
			}
		}
	default:
		err = errors.New(fmt.Sprintf("Process: unknown mode %s", act.Mode))
	}

	return err
}

func Downloader() {
	for target := range downloader {
		parts := strings.Split(target.Path, "/")
		name, err := url.QueryUnescape(parts[len(parts)-1])
		if err != nil {
			name = parts[len(parts)-1]
		}

		fullpath := filepath.Join(root, name)

		if _, err := os.Stat(fullpath); err == nil {
			mtx.Lock()
			counter++
			mtx.Unlock()
		} else {
			log.Println("Downloading", fullpath)
			n, took, err := DownloadFile(target.String(), fullpath)
			if err != nil {
				log.Printf("ERROR Downloader: %s\n", err)
			} else {
				stats.Update(n, took)
			}
		}
	}
	wg.Done()
}

func main() {
	start := time.Now()

	var flgDls = flag.Int("dls", 3, "number of downloaders")
	var flgLog = flag.Bool("log", false, "log to stdout")
	flag.Parse()

	if *flgLog {
		log.SetOutput(os.Stdout)
	} else {
		log.SetOutput(os.Stderr)
	}

	if len(flag.Args()) != 1 {
		log.Fatalln("Please specify rules file.")
	}

	log.Println("Loading rules...")
	rs, err := LoadRules(flag.Arg(0))
	if err != nil {
		log.Fatalln(err)
	}

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}

	downloader = make(chan *url.URL, *flgDls)
	stats = &Stats{}

	for i := 0; i < *flgDls; i++ {
		wg.Add(1)
		go Downloader()
	}

	log.Println("Executing...")
	for _, target := range rs {
		startTarget := time.Now()
		log.Println("Target:", target.Name)

		root, err = filepath.Abs(target.Path)
		if err != nil {
			log.Println("ERROR", err)
			continue
		}

		if _, err := os.Stat(root); os.IsNotExist(err) {
			log.Println("ERROR", err)
			continue
		}

		base, err := url.Parse(target.URL)
		if err != nil {
			log.Println("ERROR", err)
			continue
		}

		bail = target.Bail
		counter = 1

		err = Process(base, target.Do)
		if err != nil {
			log.Println("ERROR", err)
		}

		log.Printf("Finished target: %s (took %s)\n", target.Name, time.Since(startTarget))
	}

	close(downloader)
	log.Println("Waiting for downloaders to finish...")
	wg.Wait()
	if !stats.IsEmpty() {
		log.Println("Download statistics:", stats)
	}
	log.Printf("All done (took %s).", time.Since(start))
}
<commit_msg>Proper URL query handling<commit_after>// Declarative web grabber
// dRbiG, 2014
// See LICENSE.txt

package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/moovweb/gokogiri"
	"github.com/moovweb/gokogiri/html"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type Action struct {
	XPath  string
	Mode   string
	Action string
	Do     *Action
}

type Target struct {
	Name string
	URL  string
	Bail int
	Path string
	Do   *Action
}

type Stats struct {
	Number int
	Size   int64
	Took   time.Duration
	Mtx    sync.Mutex
}

func (s *Stats) Update(n int64, took time.Duration) {
	s.Mtx.Lock()
	s.Number++
	s.Size += n
	s.Took += took
	s.Mtx.Unlock()
}

func (s *Stats) IsEmpty() (b bool) {
	s.Mtx.Lock()
	if s.Number < 1 {
		b = true
	}
	s.Mtx.Unlock()
	return
}

func (s *Stats) String() string {
	s.Mtx.Lock()
	num := s.Number
	size := float64(s.Size) / (1024.0 * 1024.0)
	speed := size / s.Took.Seconds()
	s.Mtx.Unlock()
	return fmt.Sprintf("%d files for %0.2f MB with avg. dl. speed %0.3f MB/s", num, size, speed)
}

var (
	downloader chan *url.URL
	root       string
	bail       int
	counter    int
	stats      *Stats
	client     *http.Client
	mtx        sync.RWMutex
	wg         sync.WaitGroup
)

func LoadRules(name string) (t []Target, err error) {
	handle, err := os.Open(name)
	if err != nil {
		return
	}
	defer handle.Close()

	raw, err := ioutil.ReadAll(handle)
	if err != nil {
		return
	}

	err = json.Unmarshal(raw, &t)
	if err != nil {
		return
	}

	return
}

func DownloadPage(path string) (doc *html.HtmlDocument, err error) {
	res, err := client.Get(path)
	if err != nil {
		return
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		err = errors.New(fmt.Sprintf("DownloadPage: %d %s", res.StatusCode, path))
		return
	}

	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}

	doc, err = gokogiri.ParseHtml(data)
	return
}

func DownloadFile(target string, fullpath string) (n int64, took time.Duration, err error) {
	start := time.Now()

	res, err := client.Get(target)
	if err != nil {
		return
	}
	defer res.Body.Close()

	handle, err := os.Create(fullpath)
	if err != nil {
		return
	}
	defer handle.Close()

	n, err = io.Copy(handle, res.Body)
	took = time.Since(start)
	return
}

func DoAction(base *url.URL, target string, act *Action) (res *url.URL, err error) {
	res = &url.URL{}

	if act.Action == "raw" {
		fmt.Println(target)
		return
	}

	urlTarget, err := url.Parse(target)
	if err != nil {
		return
	}
	if len(urlTarget.Path) < 1 {
		return
	}

	if urlTarget.IsAbs() {
		*res = *urlTarget
	} else {
		*res = *base
		if urlTarget.Path[0] == '/' {
			res.Path = urlTarget.Path
		} else {
			res.Path += urlTarget.Path
		}
		res.RawQuery = urlTarget.RawQuery
	}

	switch act.Action {
	case "print":
		fmt.Println(res)
	case "log":
		log.Println(res)
	case "download":
		if bail > 0 {
			mtx.RLock()
			cnt := counter
			mtx.RUnlock()
			if cnt >= bail {
				err = errors.New(fmt.Sprintf("DoAction: bailout after %d", counter))
				return
			}
		}
		downloader <- res
	case "none":
		// nop
	default:
		err = errors.New(fmt.Sprintf("DoAction: unknown action %s", act.Action))
	}

	return
}

func Process(base *url.URL, act *Action) (err error) {
	doc, err := DownloadPage(base.String())
	if err != nil {
		return err
	}
	defer doc.Free()

	res, err := doc.Search(act.XPath)
	if err != nil {
		return err
	}

	switch act.Mode {
	case "follow":
		if act.Do != nil {
			err = Process(base, act.Do)
			if err != nil {
				return err
			}
		}

		if len(res) < 1 {
			err = errors.New(fmt.Sprintf("Process: %s no results", base))
			return err
		}

		target, err := DoAction(base, res[0].String(), act)
		if err != nil {
			return err
		}

		err = Process(target, act)
		if err != nil {
			return err
		}
	case "every":
		if len(res) < 1 {
			if act.Do != nil {
				err = errors.New(fmt.Sprintf("Process: %s no results", base))
				return err
			} else {
				return
			}
		}

		for _, v := range res {
			target, err := DoAction(base, v.String(), act)
			if err != nil {
				return err
			}

			if act.Do != nil {
				err = Process(target, act.Do)
				if err != nil {
					return err
				}
			}
		}
	default:
		err = errors.New(fmt.Sprintf("Process: unknown mode %s", act.Mode))
	}

	return err
}

func Downloader() {
	for target := range downloader {
		parts := strings.Split(target.Path, "/")
		name, err := url.QueryUnescape(parts[len(parts)-1])
		if err != nil {
			name = parts[len(parts)-1]
		}

		fullpath := filepath.Join(root, name)

		if _, err := os.Stat(fullpath); err == nil {
			mtx.Lock()
			counter++
			mtx.Unlock()
		} else {
			log.Println("Downloading", fullpath)
			n, took, err := DownloadFile(target.String(), fullpath)
			if err != nil {
				log.Printf("ERROR Downloader: %s\n", err)
			} else {
				stats.Update(n, took)
			}
		}
	}
	wg.Done()
}

func main() {
	start := time.Now()

	var flgDls = flag.Int("dls", 3, "number of downloaders")
	var flgLog = flag.Bool("log", false, "log to stdout")
	flag.Parse()

	if *flgLog {
		log.SetOutput(os.Stdout)
	} else {
		log.SetOutput(os.Stderr)
	}

	if len(flag.Args()) != 1 {
		log.Fatalln("Please specify rules file.")
	}

	log.Println("Loading rules...")
	rs, err := LoadRules(flag.Arg(0))
	if err != nil {
		log.Fatalln(err)
	}

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}

	downloader = make(chan *url.URL, *flgDls)
	stats = &Stats{}

	for i := 0; i < *flgDls; i++ {
		wg.Add(1)
		go Downloader()
	}

	log.Println("Executing...")
	for _, target := range rs {
		startTarget := time.Now()
		log.Println("Target:", target.Name)

		root, err = filepath.Abs(target.Path)
		if err != nil {
			log.Println("ERROR", err)
			continue
		}

		if _, err := os.Stat(root); os.IsNotExist(err) {
			log.Println("ERROR", err)
			continue
		}

		base, err := url.Parse(target.URL)
		if err != nil {
			log.Println("ERROR", err)
			continue
		}

		bail = target.Bail
		counter = 1

		err = Process(base, target.Do)
		if err != nil {
			log.Println("ERROR", err)
		}

		log.Printf("Finished target: %s (took %s)\n", target.Name, time.Since(startTarget))
	}

	close(downloader)
	log.Println("Waiting for downloaders to finish...")
	wg.Wait()
	if !stats.IsEmpty() {
		log.Println("Download statistics:", stats)
	}
	log.Printf("All done (took %s).", time.Since(start))
}
<|endoftext|>


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

package maps

import (
	"context"
	"fmt"
	"image"
	"image/png"
	"net/http"
	"net/http/httptest"
	"testing"
)

// mockServerForQueryWithImage returns a mock server that only responds to a particular query string, and responds with an encoded Image.
func mockServerForQueryWithImage(query string, code int, img image.Image) *countingServer {
	server := &countingServer{}

	server.s = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if query != "" && r.URL.RawQuery != query {
			server.failed = append(server.failed, r.URL.RawQuery)
			http.Error(w, fmt.Sprintf("Expected '%s', got '%s'", query, r.URL.RawQuery), 999)
			return
		}
		server.successful++

		w.WriteHeader(code)
		w.Header().Set("Content-Type", "image/png")
		png.Encode(w, img)
	}))

	return server
}

func TestStaticMode(t *testing.T) {

	response := image.NewRGBA(image.Rect(0, 0, 640, 400))

	server := mockServerForQueryWithImage("center=Brooklyn+Bridge%2CNew+York%2CNY&format=PNG&key=AIzaNotReallyAnAPIKey&language=EN-us&maptype=roadmap&region=US&scale=2&size=600x300&zoom=13", 200, response)
	defer server.s.Close()
	c, _ := NewClient(WithAPIKey(apiKey), WithBaseURL(server.s.URL))
	r := &StaticMapRequest{
		Center:   "Brooklyn Bridge,New York,NY",
		Size:     "600x300",
		Zoom:     13,
		Scale:    2,
		Language: "EN-us",
		Format:   "PNG",
		Region:   "US",
		MapType:  MapType("roadmap"),
	}

	resp, err := c.StaticMap(context.Background(), r)
	if err != nil {
		t.Errorf("r.StaticMap returned non nil error: %+v", err)
	}

	if resp.Bounds().Min.X != 0 || resp.Bounds().Min.Y != 0 || resp.Bounds().Max.X != 640 || resp.Bounds().Max.Y != 400 {
		t.Errorf("Response image not of the correct dimensions")
	}
}

func TestMapStyles(t *testing.T) {
	r := StaticMapRequest{
		MapStyles: []string{
			"x", "y",
		},
	}
	values := r.params()
	if q := values.Encode(); q != "style=x&style=y" {
		t.Errorf("Generated query string is wrong: %s", q)
	}
}
<commit_msg>Add unit test for custom icon markers<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package maps

import (
	"context"
	"fmt"
	"image"
	"image/png"
	"net/http"
	"net/http/httptest"
	"testing"
)

// mockServerForQueryWithImage returns a mock server that only responds to a particular query string, and responds with an encoded Image.
func mockServerForQueryWithImage(query string, code int, img image.Image) *countingServer {
	server := &countingServer{}

	server.s = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if query != "" && r.URL.RawQuery != query {
			server.failed = append(server.failed, r.URL.RawQuery)
			http.Error(w, fmt.Sprintf("Expected '%s', got '%s'", query, r.URL.RawQuery), 999)
			return
		}
		server.successful++

		w.WriteHeader(code)
		w.Header().Set("Content-Type", "image/png")
		png.Encode(w, img)
	}))

	return server
}

func TestStaticMode(t *testing.T) {

	response := image.NewRGBA(image.Rect(0, 0, 640, 400))

	server := mockServerForQueryWithImage("center=Brooklyn+Bridge%2CNew+York%2CNY&format=PNG&key=AIzaNotReallyAnAPIKey&language=EN-us&maptype=roadmap&region=US&scale=2&size=600x300&zoom=13", 200, response)
	defer server.s.Close()
	c, _ := NewClient(WithAPIKey(apiKey), WithBaseURL(server.s.URL))
	r := &StaticMapRequest{
		Center:   "Brooklyn Bridge,New York,NY",
		Size:     "600x300",
		Zoom:     13,
		Scale:    2,
		Language: "EN-us",
		Format:   "PNG",
		Region:   "US",
		MapType:  MapType("roadmap"),
	}

	resp, err := c.StaticMap(context.Background(), r)
	if err != nil {
		t.Errorf("r.StaticMap returned non nil error: %+v", err)
	}

	if resp.Bounds().Min.X != 0 || resp.Bounds().Min.Y != 0 || resp.Bounds().Max.X != 640 || resp.Bounds().Max.Y != 400 {
		t.Errorf("Response image not of the correct dimensions")
	}
}

func TestMapStyles(t *testing.T) {
	r := StaticMapRequest{
		MapStyles: []string{
			"x", "y",
		},
	}
	values := r.params()
	if q := values.Encode(); q != "style=x&style=y" {
		t.Errorf("Generated query string is wrong: %s", q)
	}
}

func TestCustomIconMarkers(t *testing.T) {
	marker := Marker{
		CustomIcon: CustomIcon{
			IconURL: "icon@2x.png",
			Anchor:  "topleft",
			Scale:   2,
		},
		Location: []LatLng{
			{
				Lat: 3.1225951401,
				Lng: 101.6404967928,
			},
		},
	}

	r := StaticMapRequest{
		Markers: []Marker{marker},
	}

	values := r.params()
	if q := values.Encode(); q != "markers=icon%3Aicon%402x.png%7Canchor%3Atopleft%7Cscale%3A2%7C3.1225951401%2C101.6404967928" {
		t.Errorf("Generated query string is wrong: %s", q)
	}
}
<|endoftext|>


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

package mqtt

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/liangdas/mqant/conf"
	"github.com/liangdas/mqant/network"
	"runtime"
	"sync"
	"time"
)

// Tcp write queue
type PackQueue struct {
	conf conf.Mqtt
	// The last error in the tcp connection
	writeError error
	// Notice read the error
	fch       chan struct{}
	writelock sync.Mutex
	recover   func(pAndErr *packAndErr) (err error)
	// Pack connection
	r *bufio.Reader
	w *bufio.Writer

	conn network.Conn

	alive int

	MaxPackSize int // mqtt包最大长度

	status int
}

type packAndErr struct {
	pack *Pack
	err  error
}

// 1 is delay, 0 is no delay, 2 is just flush.
const (
	NO_DELAY = iota
	DELAY
	FLUSH

	DISCONNECTED = iota
	CONNECTED
	CLOSED
	RECONNECTING
	CONNECTING
)

type packAndType struct {
	pack *Pack
	typ  byte
}

// Init a pack queue
func NewPackQueue(conf conf.Mqtt, r *bufio.Reader, w *bufio.Writer, conn network.Conn, recover func(pAndErr *packAndErr) (err error), alive, MaxPackSize int) *PackQueue {
	if alive < 1 {
		alive = conf.ReadTimeout
	}
	if MaxPackSize < 1 {
		MaxPackSize = 65535
	}
	alive = int(float32(alive)*1.5 + 1)
	return &PackQueue{
		conf:        conf,
		alive:       alive,
		MaxPackSize: MaxPackSize,
		r:           r,
		w:           w,
		conn:        conn,
		recover:     recover,
		fch:         make(chan struct{}, 256),
		status:      CONNECTED,
	}
}

func (queue *PackQueue) isConnected() bool {
	return queue.status == CONNECTED
}

// Get a read pack queue
// Only call once
func (queue *PackQueue) Flusher() {
	for queue.isConnected() {
		if _, ok := <-queue.fch; !ok {
			break
		}
		queue.writelock.Lock()
		if !queue.isConnected() {
			queue.writelock.Unlock()
			break
		}
		if queue.w.Buffered() > 0 {
			if err := queue.w.Flush(); err != nil {
				queue.writelock.Unlock()
				break
			}
		}
		queue.writelock.Unlock()
	}
	//log.Info("flusher_loop Groutine will esc.")
}

// Write a pack , and get the last error
func (queue *PackQueue) WritePack(pack *Pack) (err error) {
	defer func() {
		if r := recover(); r != nil {
			buf := make([]byte, 1024)
			l := runtime.Stack(buf, false)
			errstr := string(buf[:l])
			err = fmt.Errorf("WritePack error %v", errstr)
			queue.Close(err)
		}
	}()
	queue.writelock.Lock()
	if !queue.isConnected() {
		queue.writelock.Unlock()
		return errors.New("disconnect")
	}
	if queue.writeError != nil {
		queue.writelock.Unlock()
		return queue.writeError
	}
	if queue.w.Available() <= 0 {
		queue.writelock.Unlock()
		return fmt.Errorf("bufio.Writer is full")
	}
	err = DelayWritePack(pack, queue.w)
	queue.writelock.Unlock()
	queue.fch <- struct{}{}
	if err != nil {
		// Tell listener the error
		// Notice the read
		queue.Close(err)
	}
	return err
}

func (queue *PackQueue) SetAlive(alive int) error {
	if alive < 1 {
		alive = queue.conf.ReadTimeout
	}
	alive = int(float32(alive)*1.5 + 1)
	queue.alive = alive
	return nil
}

// Get a read pack queue
// Only call once
func (queue *PackQueue) ReadPackInLoop() {
	// defer recover()
	p := new(packAndErr)
loop:
	for queue.isConnected() {
		if queue.alive > 0 {
			queue.conn.SetDeadline(time.Now().Add(time.Second * time.Duration(int(float64(queue.alive)*1.5))))
		} else {
			queue.conn.SetDeadline(time.Now().Add(time.Second * 90))
		}
		p.pack, p.err = ReadPack(queue.r, queue.MaxPackSize)
		if p.err != nil {
			queue.Close(p.err)
			break loop
		}
		err := queue.recover(p)
		if err != nil {
			queue.Close(err)
			break loop
		}
		p = new(packAndErr)
	}

	//log.Info("read_loop Groutine will esc.")
}
func (queue *PackQueue) CloseFch() {
	defer func() {
		if recover() != nil {
			// close(ch) panic occur
		}
	}()

	close(queue.fch) // panic if ch is closed
}

// Close the all of queue's channels
func (queue *PackQueue) Close(err error) error {
	queue.writeError = err
	queue.CloseFch()
	queue.status = CLOSED
	return nil
}

// Buffer
type buffer struct {
	index int
	data  []byte
}

func newBuffer(data []byte) *buffer {
	return &buffer{
		data:  data,
		index: 0,
	}
}
func (b *buffer) readString(length int) (s string, err error) {
	if (length + b.index) > len(b.data) {
		err = fmt.Errorf("Out of range error:%v", length)
		return
	}
	s = string(b.data[b.index:(length + b.index)])
	b.index += length
	return
}
func (b *buffer) readByte() (c byte, err error) {
	if (1 + b.index) > len(b.data) {
		err = fmt.Errorf("Out of range error")
		return
	}
	c = b.data[b.index]
	b.index++
	return
}
<commit_msg>=调整超时时间<commit_after>// Copyright 2014 mqant Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mqtt

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/liangdas/mqant/conf"
	"github.com/liangdas/mqant/network"
	"runtime"
	"sync"
	"time"
)

// Tcp write queue
type PackQueue struct {
	conf conf.Mqtt
	// The last error in the tcp connection
	writeError error
	// Notice read the error
	fch       chan struct{}
	writelock sync.Mutex
	recover   func(pAndErr *packAndErr) (err error)
	// Pack connection
	r *bufio.Reader
	w *bufio.Writer

	conn network.Conn

	alive int

	MaxPackSize int // mqtt包最大长度

	status int
}

type packAndErr struct {
	pack *Pack
	err  error
}

// 1 is delay, 0 is no delay, 2 is just flush.
const (
	NO_DELAY = iota
	DELAY
	FLUSH

	DISCONNECTED = iota
	CONNECTED
	CLOSED
	RECONNECTING
	CONNECTING
)

type packAndType struct {
	pack *Pack
	typ  byte
}

// Init a pack queue
func NewPackQueue(conf conf.Mqtt, r *bufio.Reader, w *bufio.Writer, conn network.Conn, recover func(pAndErr *packAndErr) (err error), alive, MaxPackSize int) *PackQueue {
	if alive < 1 {
		alive = conf.ReadTimeout
	}
	if MaxPackSize < 1 {
		MaxPackSize = 65535
	}
	alive = int(float32(alive)*1.5 + 1)
	return &PackQueue{
		conf:        conf,
		alive:       alive,
		MaxPackSize: MaxPackSize,
		r:           r,
		w:           w,
		conn:        conn,
		recover:     recover,
		fch:         make(chan struct{}, 256),
		status:      CONNECTED,
	}
}

func (queue *PackQueue) isConnected() bool {
	return queue.status == CONNECTED
}

// Get a read pack queue
// Only call once
func (queue *PackQueue) Flusher() {
	for queue.isConnected() {
		if _, ok := <-queue.fch; !ok {
			break
		}
		queue.writelock.Lock()
		if !queue.isConnected() {
			queue.writelock.Unlock()
			break
		}
		if queue.w.Buffered() > 0 {
			if err := queue.w.Flush(); err != nil {
				queue.writelock.Unlock()
				break
			}
		}
		queue.writelock.Unlock()
	}
	//log.Info("flusher_loop Groutine will esc.")
}

// Write a pack , and get the last error
func (queue *PackQueue) WritePack(pack *Pack) (err error) {
	defer func() {
		if r := recover(); r != nil {
			buf := make([]byte, 1024)
			l := runtime.Stack(buf, false)
			errstr := string(buf[:l])
			err = fmt.Errorf("WritePack error %v", errstr)
			queue.Close(err)
		}
	}()
	queue.writelock.Lock()
	if !queue.isConnected() {
		queue.writelock.Unlock()
		return errors.New("disconnect")
	}
	if queue.writeError != nil {
		queue.writelock.Unlock()
		return queue.writeError
	}
	if queue.w.Available() <= 0 {
		queue.writelock.Unlock()
		return fmt.Errorf("bufio.Writer is full")
	}
	err = DelayWritePack(pack, queue.w)
	queue.writelock.Unlock()
	queue.fch <- struct{}{}
	if err != nil {
		// Tell listener the error
		// Notice the read
		queue.Close(err)
	}
	return err
}

func (queue *PackQueue) SetAlive(alive int) error {
	if alive < 1 {
		alive = queue.conf.ReadTimeout
	}
	alive = int(float32(alive)*1.5 + 1)
	queue.alive = alive
	return nil
}

// Get a read pack queue
// Only call once
func (queue *PackQueue) ReadPackInLoop() {
	// defer recover()
	p := new(packAndErr)
loop:
	for queue.isConnected() {
		if queue.alive > 0 {
			timeout:=int(float64(queue.alive)*3)
			if timeout>60{
				timeout=60
			}else if timeout<10{
				timeout=10
			}
			queue.conn.SetDeadline(time.Now().Add(time.Second * time.Duration(timeout)))
		} else {
			queue.conn.SetDeadline(time.Now().Add(time.Second * 90))
		}
		p.pack, p.err = ReadPack(queue.r, queue.MaxPackSize)
		if p.err != nil {
			queue.Close(p.err)
			break loop
		}
		err := queue.recover(p)
		if err != nil {
			queue.Close(err)
			break loop
		}
		p = new(packAndErr)
	}

	//log.Info("read_loop Groutine will esc.")
}
func (queue *PackQueue) CloseFch() {
	defer func() {
		if recover() != nil {
			// close(ch) panic occur
		}
	}()

	close(queue.fch) // panic if ch is closed
}

// Close the all of queue's channels
func (queue *PackQueue) Close(err error) error {
	queue.writeError = err
	queue.CloseFch()
	queue.status = CLOSED
	return nil
}

// Buffer
type buffer struct {
	index int
	data  []byte
}

func newBuffer(data []byte) *buffer {
	return &buffer{
		data:  data,
		index: 0,
	}
}
func (b *buffer) readString(length int) (s string, err error) {
	if (length + b.index) > len(b.data) {
		err = fmt.Errorf("Out of range error:%v", length)
		return
	}
	s = string(b.data[b.index:(length + b.index)])
	b.index += length
	return
}
func (b *buffer) readByte() (c byte, err error) {
	if (1 + b.index) > len(b.data) {
		err = fmt.Errorf("Out of range error")
		return
	}
	c = b.data[b.index]
	b.index++
	return
}
<|endoftext|>


<commit_before>package eventmaster

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sort"
	"strings"
	"text/template"
	"time"

	"github.com/julienschmidt/httprouter"
	"github.com/pkg/errors"

	eventmaster "github.com/ContextLogic/eventmaster/proto"
)

// AnnotationsReq encodes the information provided by Grafana in its requests.
type AnnotationsReq struct {
	Range      Range      `json:"range"`
	Annotation Annotation `json:"annotation"`
}

// Range specifies the time range the request is valid for.
type Range struct {
	From time.Time `json:"from"`
	To   time.Time `json:"to"`
}

// Annotation is the object passed by Grafana when it fetches annotations.
//
// http://docs.grafana.org/plugins/developing/datasources/#annotation-query
type Annotation struct {
	// Name must match in the request and response
	Name string `json:"name"`

	Datasource string `json:"datasource"`
	IconColor  string `json:"iconColor"`
	Enable     bool   `json:"enable"`
	ShowLine   bool   `json:"showLine"`
	Query      string `json:"query"`
}

// AnnotationResponse contains all the information needed to render an
// annotation event.
//
// https://github.com/grafana/simple-json-datasource#annotation-api
type AnnotationResponse struct {
	// The original annotation sent from Grafana.
	Annotation Annotation `json:"annotation"`
	// Time since UNIX Epoch in milliseconds. (required)
	Time int64 `json:"time"`
	// The title for the annotation tooltip. (required)
	Title string `json:"title"`
	// Tags for the annotation. (optional)
	Tags string `json:"tags"`
	// Text for the annotation. (optional)
	Text string `json:"text"`
}

// AnnotationQuery is a collection of possible filters for a Grafana annotation
// request.
//
// These values are set in gear icon > annotations > edit > Query and can have
// such useful values as:
//
//		{"topic": "$topic", "dc": "$dc"}
//
// or if you do not want filtering leave it blank.
type AnnotationQuery struct {
	Topic string `json:"topic"`
	DC    string `json:"dc"`
}

// cors adds headers that Grafana requires to work as a direct access data
// source.
//
// forgetting to add these manifests itself as an unintellible error when
// adding a datasource.
//
// These are not required if using "proxy" access.
func cors(h httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		w.Header().Set("Access-Control-Allow-Headers", "accept, content-type")
		w.Header().Set("Access-Control-Allow-Methods", "POST")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		h(w, r, p)
	}
}

func (h *Server) grafanaOK(w http.ResponseWriter, r *http.Request, p httprouter.Params) {}

// grafanaAnnotations handles the POST requests from Grafana asking for
// a window of annotations.
//
// It accepts an AnnotationsReq in the request body, and parses the
// AnnotationsReq.Query for a AnnotationQuery. If one is found it parses it,
// and interprets the value "all" as "do not apply this filter".
func (h *Server) grafanaAnnotations(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	switch r.Method {
	case http.MethodPost:
		ar := AnnotationsReq{}
		if err := json.NewDecoder(r.Body).Decode(&ar); err != nil {
			http.Error(w, fmt.Sprintf("json decode failure: %v", err), http.StatusBadRequest)
			return
		}

		q := &eventmaster.Query{
			StartEventTime: ar.Range.From.Unix(),
			EndEventTime:   ar.Range.To.Unix(),
		}

		if rq := ar.Annotation.Query; rq != "" {
			aq := AnnotationQuery{}
			if err := json.Unmarshal([]byte(rq), &aq); err != nil {
				http.Error(w, fmt.Sprintf("json decode failure: %v", err), http.StatusBadRequest)
				return
			}
			if aq.DC != "all" {
				q.Dc = []string{aq.DC}
			}
			if aq.Topic != "all" {
				q.TopicName = []string{aq.Topic}
			}
		}

		evs, err := h.store.Find(q)
		if err != nil {
			e := errors.Wrapf(err, "grafana search with %v", q)
			http.Error(w, e.Error(), http.StatusInternalServerError)
			return
		}

		ars := []AnnotationResponse{}
		for _, ev := range evs {
			ar, err := FromEvent(h.store, ev)
			if err != nil {
				http.Error(w, errors.Wrap(err, "from event").Error(), http.StatusInternalServerError)
				return
			}
			ars = append(ars, ar)
		}

		if err := json.NewEncoder(w).Encode(ars); err != nil {
			log.Printf("json enc: %+v", err)
		}
	default:
		http.Error(w, "bad method; supported POST", http.StatusBadRequest)
		return
	}
}

func (h *Server) grafanaSearch(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	req := &struct {
		Target string `json:"target"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(req); err != nil {
		http.Error(w, errors.Wrap(err, "json decode").Error(), http.StatusBadRequest)
		return
	}

	tags := []string{"all"}
	switch req.Target {
	case "dc":
		dcs, err := h.store.GetDcs()
		if err != nil {
			http.Error(w, errors.Wrap(err, "get dcs").Error(), http.StatusInternalServerError)
			return
		}
		for _, dc := range dcs {
			tags = append(tags, dc.Name)
		}
	case "topic":
		topics, err := h.store.GetTopics()
		if err != nil {
			http.Error(w, errors.Wrap(err, "get topics").Error(), http.StatusInternalServerError)
			return
		}
		for _, topic := range topics {
			tags = append(tags, topic.Name)
		}
	default:
		http.Error(w, fmt.Sprintf("unknown target: got %q, want [%q, %q]", req.Target, "dc", "topic"), http.StatusBadRequest)
		return
	}

	sort.Strings(tags[1:])
	if err := json.NewEncoder(w).Encode(tags); err != nil {
		log.Printf("json encode failure: %+v", err)
	}
}

type topicNamer interface {
	getTopicName(string) string
	getDcName(string) string
}

func FromEvent(store topicNamer, ev *Event) (AnnotationResponse, error) {
	fm := template.FuncMap{
		"trim": strings.TrimSpace,
	}
	t := `

Host: {{ .Host }}
Target Hosts:
{{- range .TargetHosts }}
	{{ trim . -}}
{{ end }}
User: {{ .User }}
Data: {{ .Data }}

`
	tmpl, err := template.New("text").Funcs(fm).Parse(t)
	if err != nil {
		return AnnotationResponse{}, errors.Wrap(err, "making template")
	}
	buf := &bytes.Buffer{}
	tmpl.Execute(buf, ev)
	r := AnnotationResponse{
		Time:  ev.EventTime * 1000,
		Title: fmt.Sprintf("%v in %v", store.getTopicName(ev.TopicID), store.getDcName(ev.DcID)),
		Text:  buf.String(),
		Tags:  strings.Join(ev.Tags, ","),
	}
	return r, nil
}
<commit_msg>Move TemplateRequest so it can be used in tests<commit_after>package eventmaster

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sort"
	"strings"
	"text/template"
	"time"

	"github.com/julienschmidt/httprouter"
	"github.com/pkg/errors"

	eventmaster "github.com/ContextLogic/eventmaster/proto"
)

// AnnotationsReq encodes the information provided by Grafana in its requests.
type AnnotationsReq struct {
	Range      Range      `json:"range"`
	Annotation Annotation `json:"annotation"`
}

// Range specifies the time range the request is valid for.
type Range struct {
	From time.Time `json:"from"`
	To   time.Time `json:"to"`
}

// Annotation is the object passed by Grafana when it fetches annotations.
//
// http://docs.grafana.org/plugins/developing/datasources/#annotation-query
type Annotation struct {
	// Name must match in the request and response
	Name string `json:"name"`

	Datasource string `json:"datasource"`
	IconColor  string `json:"iconColor"`
	Enable     bool   `json:"enable"`
	ShowLine   bool   `json:"showLine"`
	Query      string `json:"query"`
}

// AnnotationResponse contains all the information needed to render an
// annotation event.
//
// https://github.com/grafana/simple-json-datasource#annotation-api
type AnnotationResponse struct {
	// The original annotation sent from Grafana.
	Annotation Annotation `json:"annotation"`
	// Time since UNIX Epoch in milliseconds. (required)
	Time int64 `json:"time"`
	// The title for the annotation tooltip. (required)
	Title string `json:"title"`
	// Tags for the annotation. (optional)
	Tags string `json:"tags"`
	// Text for the annotation. (optional)
	Text string `json:"text"`
}

// AnnotationQuery is a collection of possible filters for a Grafana annotation
// request.
//
// These values are set in gear icon > annotations > edit > Query and can have
// such useful values as:
//
//		{"topic": "$topic", "dc": "$dc"}
//
// or if you do not want filtering leave it blank.
type AnnotationQuery struct {
	Topic string `json:"topic"`
	DC    string `json:"dc"`
}

type TemplateRequest struct {
	Target string `json:"target"`
}

// cors adds headers that Grafana requires to work as a direct access data
// source.
//
// forgetting to add these manifests itself as an unintellible error when
// adding a datasource.
//
// These are not required if using "proxy" access.
func cors(h httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		w.Header().Set("Access-Control-Allow-Headers", "accept, content-type")
		w.Header().Set("Access-Control-Allow-Methods", "POST")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		h(w, r, p)
	}
}

func (h *Server) grafanaOK(w http.ResponseWriter, r *http.Request, p httprouter.Params) {}

// grafanaAnnotations handles the POST requests from Grafana asking for
// a window of annotations.
//
// It accepts an AnnotationsReq in the request body, and parses the
// AnnotationsReq.Query for a AnnotationQuery. If one is found it parses it,
// and interprets the value "all" as "do not apply this filter".
func (h *Server) grafanaAnnotations(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	switch r.Method {
	case http.MethodPost:
		ar := AnnotationsReq{}
		if err := json.NewDecoder(r.Body).Decode(&ar); err != nil {
			http.Error(w, fmt.Sprintf("json decode failure: %v", err), http.StatusBadRequest)
			return
		}

		q := &eventmaster.Query{
			StartEventTime: ar.Range.From.Unix(),
			EndEventTime:   ar.Range.To.Unix(),
		}

		if rq := ar.Annotation.Query; rq != "" {
			aq := AnnotationQuery{}
			if err := json.Unmarshal([]byte(rq), &aq); err != nil {
				http.Error(w, fmt.Sprintf("json decode failure: %v", err), http.StatusBadRequest)
				return
			}
			if aq.DC != "all" {
				q.Dc = []string{aq.DC}
			}
			if aq.Topic != "all" {
				q.TopicName = []string{aq.Topic}
			}
		}

		evs, err := h.store.Find(q)
		if err != nil {
			e := errors.Wrapf(err, "grafana search with %v", q)
			http.Error(w, e.Error(), http.StatusInternalServerError)
			return
		}

		ars := []AnnotationResponse{}
		for _, ev := range evs {
			ar, err := FromEvent(h.store, ev)
			if err != nil {
				http.Error(w, errors.Wrap(err, "from event").Error(), http.StatusInternalServerError)
				return
			}
			ars = append(ars, ar)
		}

		if err := json.NewEncoder(w).Encode(ars); err != nil {
			log.Printf("json enc: %+v", err)
		}
	default:
		http.Error(w, "bad method; supported POST", http.StatusBadRequest)
		return
	}
}

func (h *Server) grafanaSearch(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	req := TemplateRequest{}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, errors.Wrap(err, "json decode").Error(), http.StatusBadRequest)
		return
	}

	tags := []string{"all"}
	switch req.Target {
	case "dc":
		dcs, err := h.store.GetDcs()
		if err != nil {
			http.Error(w, errors.Wrap(err, "get dcs").Error(), http.StatusInternalServerError)
			return
		}
		for _, dc := range dcs {
			tags = append(tags, dc.Name)
		}
	case "topic":
		topics, err := h.store.GetTopics()
		if err != nil {
			http.Error(w, errors.Wrap(err, "get topics").Error(), http.StatusInternalServerError)
			return
		}
		for _, topic := range topics {
			tags = append(tags, topic.Name)
		}
	default:
		http.Error(w, fmt.Sprintf("unknown target: got %q, want [%q, %q]", req.Target, "dc", "topic"), http.StatusBadRequest)
		return
	}

	sort.Strings(tags[1:])
	if err := json.NewEncoder(w).Encode(tags); err != nil {
		log.Printf("json encode failure: %+v", err)
	}
}

type topicNamer interface {
	getTopicName(string) string
	getDcName(string) string
}

func FromEvent(store topicNamer, ev *Event) (AnnotationResponse, error) {
	fm := template.FuncMap{
		"trim": strings.TrimSpace,
	}
	t := `

Host: {{ .Host }}
Target Hosts:
{{- range .TargetHosts }}
	{{ trim . -}}
{{ end }}
User: {{ .User }}
Data: {{ .Data }}

`
	tmpl, err := template.New("text").Funcs(fm).Parse(t)
	if err != nil {
		return AnnotationResponse{}, errors.Wrap(err, "making template")
	}
	buf := &bytes.Buffer{}
	tmpl.Execute(buf, ev)
	r := AnnotationResponse{
		Time:  ev.EventTime * 1000,
		Title: fmt.Sprintf("%v in %v", store.getTopicName(ev.TopicID), store.getDcName(ev.DcID)),
		Text:  buf.String(),
		Tags:  strings.Join(ev.Tags, ","),
	}
	return r, nil
}
<|endoftext|>