<commit_before>// Copyright 2021 The Matrix.org Foundation C.I.C.
//
// 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 postgres

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/lib/pq"
	"github.com/matrix-org/dendrite/internal"
	"github.com/matrix-org/dendrite/internal/sqlutil"
	"github.com/matrix-org/dendrite/syncapi/storage/tables"
	"github.com/matrix-org/dendrite/syncapi/types"
	"github.com/matrix-org/gomatrixserverlib"
)

// The memberships table is designed to track the last time that
// the user was a given state. This allows us to find out the
// most recent time that a user was invited to, joined or left
// a room, either by choice or otherwise. This is important for
// building history visibility.

const membershipsSchema = `
CREATE TABLE IF NOT EXISTS syncapi_memberships (
    -- The 'room_id' key for the state event.
    room_id TEXT NOT NULL,
    -- The state event ID
	user_id TEXT NOT NULL,
	-- The status of the membership
	membership TEXT NOT NULL,
	-- The event ID that last changed the membership
	event_id TEXT NOT NULL,
	-- The stream position of the change
	stream_pos BIGINT NOT NULL,
	-- The topological position of the change in the room
	topological_pos BIGINT NOT NULL,
	-- Unique index
	CONSTRAINT syncapi_memberships_unique UNIQUE (room_id, user_id, membership)
);
`

const upsertMembershipSQL = "" +
	"INSERT INTO syncapi_memberships (room_id, user_id, membership, event_id, stream_pos, topological_pos)" +
	" VALUES ($1, $2, $3, $4, $5, $6)" +
	" ON CONFLICT ON CONSTRAINT syncapi_memberships_unique" +
	" DO UPDATE SET event_id = $4, stream_pos = $5, topological_pos = $6"

const selectMembershipCountSQL = "" +
	"SELECT COUNT(*) FROM (" +
	" SELECT DISTINCT ON (room_id, user_id) room_id, user_id, membership FROM syncapi_memberships WHERE room_id = $1 AND stream_pos <= $2 ORDER BY room_id, user_id, stream_pos DESC" +
	") t WHERE t.membership = $3"

const selectHeroesSQL = "" +
	"SELECT user_id FROM syncapi_memberships WHERE room_id = $1 AND user_id != $2 AND membership = ANY($3) LIMIT 5"

type membershipsStatements struct {
	upsertMembershipStmt      *sql.Stmt
	selectMembershipCountStmt *sql.Stmt
	selectHeroesStmt          *sql.Stmt
}

func NewPostgresMembershipsTable(db *sql.DB) (tables.Memberships, error) {
	s := &membershipsStatements{}
	_, err := db.Exec(membershipsSchema)
	if err != nil {
		return nil, err
	}
	return s, sqlutil.StatementList{
		{&s.upsertMembershipStmt, upsertMembershipSQL},
		{&s.selectMembershipCountStmt, selectMembershipCountSQL},
		{&s.selectHeroesStmt, selectHeroesSQL},
	}.Prepare(db)
}

func (s *membershipsStatements) UpsertMembership(
	ctx context.Context, txn *sql.Tx, event *gomatrixserverlib.HeaderedEvent,
	streamPos, topologicalPos types.StreamPosition,
) error {
	membership, err := event.Membership()
	if err != nil {
		return fmt.Errorf("event.Membership: %w", err)
	}
	_, err = sqlutil.TxStmt(txn, s.upsertMembershipStmt).ExecContext(
		ctx,
		event.RoomID(),
		*event.StateKey(),
		membership,
		event.EventID(),
		streamPos,
		topologicalPos,
	)
	return err
}

func (s *membershipsStatements) SelectMembershipCount(
	ctx context.Context, txn *sql.Tx, roomID, membership string, pos types.StreamPosition,
) (count int, err error) {
	stmt := sqlutil.TxStmt(txn, s.selectMembershipCountStmt)
	err = stmt.QueryRowContext(ctx, roomID, pos, membership).Scan(&count)
	return
}

func (s *membershipsStatements) SelectHeroes(
	ctx context.Context, txn *sql.Tx, roomID, userID string, memberships []string,
) (heroes []string, err error) {
	stmt := sqlutil.TxStmt(txn, s.selectHeroesStmt)
	var rows *sql.Rows
	rows, err = stmt.QueryContext(ctx, roomID, userID, pq.StringArray(memberships))
	if err != nil {
		return
	}
	defer internal.CloseAndLogIfError(ctx, rows, "SelectHeroes: rows.close() failed")
	var hero string
	for rows.Next() {
		if err = rows.Scan(&hero); err != nil {
			return
		}
		heroes = append(heroes, hero)
	}
	return heroes, rows.Err()
}
<commit_msg>Fix #2390 (#2392)<commit_after>// Copyright 2021 The Matrix.org Foundation C.I.C.
//
// 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 postgres

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/lib/pq"
	"github.com/matrix-org/dendrite/internal"
	"github.com/matrix-org/dendrite/internal/sqlutil"
	"github.com/matrix-org/dendrite/syncapi/storage/tables"
	"github.com/matrix-org/dendrite/syncapi/types"
	"github.com/matrix-org/gomatrixserverlib"
)

// The memberships table is designed to track the last time that
// the user was a given state. This allows us to find out the
// most recent time that a user was invited to, joined or left
// a room, either by choice or otherwise. This is important for
// building history visibility.

const membershipsSchema = `
CREATE TABLE IF NOT EXISTS syncapi_memberships (
    -- The 'room_id' key for the state event.
    room_id TEXT NOT NULL,
    -- The state event ID
	user_id TEXT NOT NULL,
	-- The status of the membership
	membership TEXT NOT NULL,
	-- The event ID that last changed the membership
	event_id TEXT NOT NULL,
	-- The stream position of the change
	stream_pos BIGINT NOT NULL,
	-- The topological position of the change in the room
	topological_pos BIGINT NOT NULL,
	-- Unique index
	CONSTRAINT syncapi_memberships_unique UNIQUE (room_id, user_id, membership)
);
`

const upsertMembershipSQL = "" +
	"INSERT INTO syncapi_memberships (room_id, user_id, membership, event_id, stream_pos, topological_pos)" +
	" VALUES ($1, $2, $3, $4, $5, $6)" +
	" ON CONFLICT ON CONSTRAINT syncapi_memberships_unique" +
	" DO UPDATE SET event_id = $4, stream_pos = $5, topological_pos = $6"

const selectMembershipCountSQL = "" +
	"SELECT COUNT(*) FROM (" +
	" SELECT DISTINCT ON (room_id, user_id) room_id, user_id, membership FROM syncapi_memberships WHERE room_id = $1 AND stream_pos <= $2 ORDER BY room_id, user_id, stream_pos DESC" +
	") t WHERE t.membership = $3"

const selectHeroesSQL = "" +
	"SELECT DISTINCT user_id FROM syncapi_memberships WHERE room_id = $1 AND user_id != $2 AND membership = ANY($3) LIMIT 5"

type membershipsStatements struct {
	upsertMembershipStmt      *sql.Stmt
	selectMembershipCountStmt *sql.Stmt
	selectHeroesStmt          *sql.Stmt
}

func NewPostgresMembershipsTable(db *sql.DB) (tables.Memberships, error) {
	s := &membershipsStatements{}
	_, err := db.Exec(membershipsSchema)
	if err != nil {
		return nil, err
	}
	return s, sqlutil.StatementList{
		{&s.upsertMembershipStmt, upsertMembershipSQL},
		{&s.selectMembershipCountStmt, selectMembershipCountSQL},
		{&s.selectHeroesStmt, selectHeroesSQL},
	}.Prepare(db)
}

func (s *membershipsStatements) UpsertMembership(
	ctx context.Context, txn *sql.Tx, event *gomatrixserverlib.HeaderedEvent,
	streamPos, topologicalPos types.StreamPosition,
) error {
	membership, err := event.Membership()
	if err != nil {
		return fmt.Errorf("event.Membership: %w", err)
	}
	_, err = sqlutil.TxStmt(txn, s.upsertMembershipStmt).ExecContext(
		ctx,
		event.RoomID(),
		*event.StateKey(),
		membership,
		event.EventID(),
		streamPos,
		topologicalPos,
	)
	return err
}

func (s *membershipsStatements) SelectMembershipCount(
	ctx context.Context, txn *sql.Tx, roomID, membership string, pos types.StreamPosition,
) (count int, err error) {
	stmt := sqlutil.TxStmt(txn, s.selectMembershipCountStmt)
	err = stmt.QueryRowContext(ctx, roomID, pos, membership).Scan(&count)
	return
}

func (s *membershipsStatements) SelectHeroes(
	ctx context.Context, txn *sql.Tx, roomID, userID string, memberships []string,
) (heroes []string, err error) {
	stmt := sqlutil.TxStmt(txn, s.selectHeroesStmt)
	var rows *sql.Rows
	rows, err = stmt.QueryContext(ctx, roomID, userID, pq.StringArray(memberships))
	if err != nil {
		return
	}
	defer internal.CloseAndLogIfError(ctx, rows, "SelectHeroes: rows.close() failed")
	var hero string
	for rows.Next() {
		if err = rows.Scan(&hero); err != nil {
			return
		}
		heroes = append(heroes, hero)
	}
	return heroes, rows.Err()
}
<|endoftext|>


<commit_before>package main

// A comment

/* Multi
  line
  comment
*/

// To run type at terminal: go run language.go

import "fmt" // Input and output

// Program entry point
func main() {
	fmt.Println("Hello, World!"); // At terminal you can godoc fmt Println for more info

	// Data Types:
	// Integers: uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64
	// String: string
	// Booelan: bool

	// Variables (statically typed: defined and cannot be changed)

	// uint8 is 2^8 possible values, so 0 to 255

	// Declare, then initialize
	var aUint8 uint8
	aUint8 = 0

	// Declare and initialize
	var anInt int = 1

	// Type defined implicitly
	someNumber := 33
	someFloat := 100.01

	// String
	var someString string = "My String"  // We can use

	// Constant
	const HOURS_PER_DAY int = 24

	// Multiple variable declaration
	var (
		fingers = 5
		hands = 2
		lengthOfSomeString = len(someString)
	)

	// Math: +, -, *, /, %
	fingers = fingers + 5 - 5 / 1
	hands = (4 % hands) + 2

	// Printing to console
	fmt.Println(aUint8, " ", anInt, " ", someNumber, " ", someFloat, " ", someString, " ", lengthOfSomeString)
	// Output: 0 1 33 100.01 My String 9

	fmt.Println("Concatenating this string " + "and this other string.")

	fmt.Printf("Humans have %d fingers, %d hands, and %.2f average IQ\n", fingers, hands, someFloat)
	// Output: Humans have 5 fingers, 2 hands, and 100.01 average IQ

	// Output the data type
	fmt.Printf("aUint8 is of type %T\n", aUint8)

	// Output as binary
	fmt.Printf("someNumber in binary is %b \n", someNumber) // 100001
	fmt.Printf("The code 64 code is character %c \n", 64) // @

	// Arrays
	var cards[2] int
	cards[0] = 1
	cards[1] = 2

	cards2 := [2]int {1, 2} // Same as above

	// Going through the array, considering index
	for index, value := range cards {
		fmt.Println(value, index)
	}
	/* Output:
		1 0
		2 1
	*/

	for _, value := range cards2 {
		fmt.Println(value)
	}

	// Logical operators:
	// - And: &&
	// - Or: ||
	// - Not: !

	// Relational operators:
	// - Equality: ==
	// - Not Equal: !=
	// - Less Than: <
	// ... : >, <=, >=

	// For loops
	i:=0
	for i < 10 {
		i++
	}

	for j:=0; j<10; j++ {
		// Stuff to do
	}

	// If else
	if 5 >= 2 {
		// This line runs
	} else {
		// Does not run
	}

	// if else if
	if 4 > 4 {
		// This line doesn't run
	} else if ( 2 < 4) {
		// This line runs
	} else {
		// This line doesn't run
	}

	// switch
	switch(5) {
		case 1: // does not run
		case 5: // this line runs
		default: // does not run
	}

	// Slices: like an array, but no size specified
	slice := []int {0, 1, 2, 3, 4, 5}
	firstSlice := slice[0:3] // 0 up to but not including 3rd index, so length is 3 and has {0, 1, 2}
	fmt.Println(firstSlice[0], " ", firstSlice[2]) // 0 2
	fmt.Println(slice[:3]) // From 0 to but not including 3rd index.  Output: [0, 1, 2]
	fmt.Println(slice[2:]) // From 2 index to end. Output: [2, 3, 4, 5]

	zeroForFirst := 3
	size := 9
	notDefinedValuesSlice := make([]int, zeroForFirst, size)
	fmt.Println(notDefinedValuesSlice[0:]) // [0 0 0]

	copy(notDefinedValuesSlice, slice) // Copy values from slice to notDefinedValuesSlice
	fmt.Println(notDefinedValuesSlice[0:]) // [0 1 2]

	notDefinedValuesSlice = append(notDefinedValuesSlice, 4, 5, 6)
	fmt.Println(notDefinedValuesSlice[0:]) // [0 1 2 4 5 6]

	// Maps
	kidAges := make(map[string] int) // map[keyType] valueType
	kidAges["Tom"] = 12
	kidAges["Tim"] = 13
	fmt.Println(kidAges) // map[Tom:12 Tim:13]
	delete(kidAges, "Tom")
	fmt.Println(kidAges) // map[Tim:13]
	fmt.Println(len(kidAges)) // 1

	// Function call
	fmt.Println( sum(2, 3) ) // 5
}

// Declare a function
// func nameOfFunction(variableName variableType) returnType
func sum(a int, b int) int {
	return a + b
}<commit_msg>Added function with n arguments<commit_after>package main

// A comment

/* Multi
  line
  comment
*/

// To run type at terminal: go run language.go

import "fmt" // Input and output

// Program entry point
func main() {
	fmt.Println("Hello, World!"); // At terminal you can godoc fmt Println for more info

	// Data Types:
	// Integers: uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64
	// String: string
	// Booelan: bool

	// Variables (statically typed: defined and cannot be changed)

	// uint8 is 2^8 possible values, so 0 to 255

	// Declare, then initialize
	var aUint8 uint8
	aUint8 = 0

	// Declare and initialize
	var anInt int = 1

	// Type defined implicitly
	someNumber := 33
	someFloat := 100.01

	// String
	var someString string = "My String"  // We can use

	// Constant
	const HOURS_PER_DAY int = 24

	// Multiple variable declaration
	var (
		fingers = 5
		hands = 2
		lengthOfSomeString = len(someString)
	)

	// Math: +, -, *, /, %
	// Also: +=, -= for  a = a + x
	fingers = fingers + 5 - 5 / 1
	hands = (4 % hands) + 2

	// Printing to console
	fmt.Println(aUint8, " ", anInt, " ", someNumber, " ", someFloat, " ", someString, " ", lengthOfSomeString)
	// Output: 0 1 33 100.01 My String 9

	fmt.Println("Concatenating this string " + "and this other string.")

	fmt.Printf("Humans have %d fingers, %d hands, and %.2f average IQ\n", fingers, hands, someFloat)
	// Output: Humans have 5 fingers, 2 hands, and 100.01 average IQ

	// Output the data type
	fmt.Printf("aUint8 is of type %T\n", aUint8)

	// Output as binary
	fmt.Printf("someNumber in binary is %b \n", someNumber) // 100001
	fmt.Printf("The code 64 code is character %c \n", 64) // @

	// Arrays
	var cards[2] int
	cards[0] = 1
	cards[1] = 2

	cards2 := [2]int {1, 2} // Same as above

	// Going through the array, considering index
	for index, value := range cards {
		fmt.Println(value, index)
	}
	/* Output:
		1 0
		2 1
	*/

	for _, value := range cards2 {
		fmt.Println(value)
	}

	// Logical operators:
	// - And: &&
	// - Or: ||
	// - Not: !

	// Relational operators:
	// - Equality: ==
	// - Not Equal: !=
	// - Less Than: <
	// ... : >, <=, >=

	// For loops
	i:=0
	for i < 10 {
		i++
	}

	for j:=0; j<10; j++ {
		// Stuff to do
	}

	// If else
	if 5 >= 2 {
		// This line runs
	} else {
		// Does not run
	}

	// if else if
	if 4 > 4 {
		// This line doesn't run
	} else if ( 2 < 4) {
		// This line runs
	} else {
		// This line doesn't run
	}

	// switch
	switch(5) {
		case 1: // does not run
		case 5: // this line runs
		default: // does not run
	}

	// Slices: like an array, but no size specified
	slice := []int {0, 1, 2, 3, 4, 5}
	firstSlice := slice[0:3] // 0 up to but not including 3rd index, so length is 3 and has {0, 1, 2}
	fmt.Println(firstSlice[0], " ", firstSlice[2]) // 0 2
	fmt.Println(slice[:3]) // From 0 to but not including 3rd index.  Output: [0, 1, 2]
	fmt.Println(slice[2:]) // From 2 index to end. Output: [2, 3, 4, 5]

	zeroForFirst := 3
	size := 9
	notDefinedValuesSlice := make([]int, zeroForFirst, size)
	fmt.Println(notDefinedValuesSlice[0:]) // [0 0 0]

	copy(notDefinedValuesSlice, slice) // Copy values from slice to notDefinedValuesSlice
	fmt.Println(notDefinedValuesSlice[0:]) // [0 1 2]

	notDefinedValuesSlice = append(notDefinedValuesSlice, 4, 5, 6)
	fmt.Println(notDefinedValuesSlice[0:]) // [0 1 2 4 5 6]

	// Maps
	kidAges := make(map[string] int) // map[keyType] valueType
	kidAges["Tom"] = 12
	kidAges["Tim"] = 13
	fmt.Println(kidAges) // map[Tom:12 Tim:13]
	delete(kidAges, "Tom")
	fmt.Println(kidAges) // map[Tim:13]
	fmt.Println(len(kidAges)) // 1

	// Function calls
	fmt.Println( sum(2, 3) ) // 5

	{
		num1, num2 := returnTwoValues()
		fmt.Println(num1, " ", num2) // 2 3
	}

	fmt.Println( sumTheArguments(1, 2, 3, 4, 5, 6) ) // 21

}

// Declare a function
// func nameOfFunction(variableName variableType) returnType
func sum(a int, b int) int {
	return a + b
}

// Function with multiple return
// func nameOfFunction(arguments) (returnType1, returnType2, returnType...)
func returnTwoValues() (int, int) {
	return 2, 3
}
// Function with n arguments
func sumTheArguments(arguments ... int) int {
	theSum := 0
	for _, value := range arguments  {
		theSum += value
	}
	return theSum
}

// <|endoftext|>


<commit_before>package aero_test

import (
	"bufio"
	"os"
	"strings"
	"testing"

	"github.com/aerogo/aero"
	"github.com/akyoto/assert"
)

type routeDefinition struct {
	method string
	path   string
}

func TestRouterStatic(t *testing.T) {
	router := aero.Router{}
	page := func(aero.Context) error { return nil }
	assert.Nil(t, router.Find("GET", "/"))

	router.Add("GET", "/", page)
	assert.NotNil(t, router.Find("GET", "/"))
	assert.Nil(t, router.Find("GET", "/日本語"))

	router.Add("GET", "/日本語", page)
	assert.NotNil(t, router.Find("GET", "/"))
	assert.NotNil(t, router.Find("GET", "/日本語"))
}

func TestRouterParameters(t *testing.T) {
	router := aero.Router{}
	page := func(aero.Context) error { return nil }

	router.Add("GET", "/", page)
	router.Add("GET", "/user", page)
	router.Add("GET", "/user/:id", page)
	router.Add("GET", "/user/:id/profile", page)
	router.Add("GET", "/user/:id/profile/:theme", page)
	router.Add("GET", "/user/:id/:something", page)
	router.Add("GET", "/admin", page)

	assert.NotNil(t, router.Find("GET", "/"))
	assert.NotNil(t, router.Find("GET", "/user"))
	assert.NotNil(t, router.Find("GET", "/user/123"))
	assert.NotNil(t, router.Find("GET", "/user/123/profile"))
	assert.NotNil(t, router.Find("GET", "/user/123/profile/456"))
	assert.NotNil(t, router.Find("GET", "/user/123/456"))
	assert.NotNil(t, router.Find("GET", "/admin"))
}

func TestRouterWildcards(t *testing.T) {
	router := aero.Router{}
	page := func(aero.Context) error { return nil }

	router.Add("GET", "/", page)
	router.Add("GET", "/images", page)
	router.Add("GET", "/images/*file", page)
	router.Add("GET", "/videos/*file", page)
	router.Add("GET", "/*anything", page)

	assert.NotNil(t, router.Find("GET", "/"))
	assert.NotNil(t, router.Find("GET", "/images"))
	assert.NotNil(t, router.Find("GET", "/images/hello.webp"))
	assert.NotNil(t, router.Find("GET", "/videos/hello.webm"))
	assert.NotNil(t, router.Find("GET", "/documents/hello.txt"))
}

func TestRouterStaticData(t *testing.T) {
	router := aero.Router{}
	routes := loadRoutes("testdata/router/static.txt")
	page := func(aero.Context) error { return nil }

	for _, route := range routes {
		router.Add(route.method, route.path, page)
	}

	for _, route := range routes {
		if router.Find(route.method, route.path) == nil {
			t.Fatal(route.method + " " + route.path)
		}
	}
}

func TestRouterGitHubData(t *testing.T) {
	router := aero.Router{}
	routes := loadRoutes("testdata/router/github.txt")
	page := func(aero.Context) error { return nil }

	for _, route := range routes {
		router.Add(route.method, route.path, page)
	}

	for _, route := range routes {
		if router.Find(route.method, route.path) == nil {
			t.Fatal(route.method + " " + route.path)
		}
	}
}

func BenchmarkStaticRoutes(b *testing.B) {
	router := aero.Router{}
	routes := loadRoutes("testdata/router/static.txt")
	page := func(aero.Context) error { return nil }

	for _, route := range routes {
		router.Add(route.method, route.path, page)
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, route := range routes {
			if router.Find(route.method, route.path) == nil {
				b.Fatal(route.method + " " + route.path)
			}
		}
	}

	// b.RunParallel(func(pb *testing.PB) {
	// 	for pb.Next() {
	// 		for _, route := range routes {
	// 			if router.Find(route.method, route.path) == nil {
	// 				b.Fatal(route.method + " " + route.path)
	// 			}
	// 		}
	// 	}
	// })
}

func BenchmarkGitHubRoutes(b *testing.B) {
	router := aero.Router{}
	routes := loadRoutes("testdata/router/github.txt")
	page := func(aero.Context) error { return nil }

	for _, route := range routes {
		router.Add(route.method, route.path, page)
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, route := range routes {
			if router.Find(route.method, route.path) == nil {
				b.Fatal(route.method + " " + route.path)
			}
		}
	}

	// b.RunParallel(func(pb *testing.PB) {
	// 	for pb.Next() {
	// 		for _, route := range routes {
	// 			if router.Find(route.method, route.path) == nil {
	// 				b.Fatal(route.method + " " + route.path)
	// 			}
	// 		}
	// 	}
	// })
}

func loadRoutes(filePath string) []routeDefinition {
	routes := []routeDefinition{}
	f, err := os.Open(filePath)

	if err != nil {
		panic(err)
	}

	bufferedReader := bufio.NewReader(f)

	for {
		line, err := bufferedReader.ReadString('\n')

		if line != "" {
			line = strings.TrimSpace(line)
			parts := strings.Split(line, " ")
			routes = append(routes, routeDefinition{
				method: parts[0],
				path:   parts[1],
			})
		}

		if err != nil {
			break
		}
	}

	f.Close()
	return routes
}
<commit_msg>Improved tests<commit_after>package aero_test

import (
	"bufio"
	"os"
	"strings"
	"testing"

	"github.com/aerogo/aero"
	"github.com/akyoto/assert"
)

type routeDefinition struct {
	method string
	path   string
}

func TestRouterStatic(t *testing.T) {
	router := aero.Router{}
	page := func(aero.Context) error { return nil }
	assert.Nil(t, router.Find("GET", "/"))
	assert.Nil(t, router.Find("GET", "/日"))
	assert.Nil(t, router.Find("GET", "/日本"))
	assert.Nil(t, router.Find("GET", "/日本語"))

	router.Add("GET", "/", page)
	assert.NotNil(t, router.Find("GET", "/"))
	assert.Nil(t, router.Find("GET", "/日"))
	assert.Nil(t, router.Find("GET", "/日本"))
	assert.Nil(t, router.Find("GET", "/日本語"))

	router.Add("GET", "/日本語", page)
	assert.NotNil(t, router.Find("GET", "/"))
	assert.Nil(t, router.Find("GET", "/日"))
	assert.Nil(t, router.Find("GET", "/日本"))
	assert.NotNil(t, router.Find("GET", "/日本語"))
}

func TestRouterParameters(t *testing.T) {
	router := aero.Router{}
	page := func(aero.Context) error { return nil }

	router.Add("GET", "/", page)
	router.Add("GET", "/user", page)
	router.Add("GET", "/user/:id", page)
	router.Add("GET", "/user/:id/profile", page)
	router.Add("GET", "/user/:id/profile/:theme", page)
	router.Add("GET", "/user/:id/:something", page)
	router.Add("GET", "/admin", page)

	assert.NotNil(t, router.Find("GET", "/"))
	assert.NotNil(t, router.Find("GET", "/user"))
	assert.NotNil(t, router.Find("GET", "/user/123"))
	assert.NotNil(t, router.Find("GET", "/user/123/profile"))
	assert.NotNil(t, router.Find("GET", "/user/123/profile/456"))
	assert.NotNil(t, router.Find("GET", "/user/123/456"))
	assert.NotNil(t, router.Find("GET", "/admin"))

	assert.Nil(t, router.Find("GET", "/x"))
	assert.Nil(t, router.Find("GET", "/user/123/456/x"))
	assert.Nil(t, router.Find("GET", "/admin/x"))
}

func TestRouterWildcards(t *testing.T) {
	router := aero.Router{}
	page := func(aero.Context) error { return nil }

	router.Add("GET", "/", page)
	router.Add("GET", "/images", page)
	router.Add("GET", "/images/*file", page)
	router.Add("GET", "/videos/*file", page)
	router.Add("GET", "/*anything", page)

	assert.NotNil(t, router.Find("GET", "/"))
	assert.NotNil(t, router.Find("GET", "/images"))
	assert.NotNil(t, router.Find("GET", "/images/hello.webp"))
	assert.NotNil(t, router.Find("GET", "/videos/hello.webm"))
	assert.NotNil(t, router.Find("GET", "/documents/hello.txt"))
}

func TestRouterStaticData(t *testing.T) {
	router := aero.Router{}
	routes := loadRoutes("testdata/router/static.txt")
	page := func(aero.Context) error { return nil }

	for _, route := range routes {
		router.Add(route.method, route.path, page)
	}

	for _, route := range routes {
		if router.Find(route.method, route.path) == nil {
			t.Fatal(route.method + " " + route.path)
		}
	}
}

func TestRouterGitHubData(t *testing.T) {
	router := aero.Router{}
	routes := loadRoutes("testdata/router/github.txt")
	page := func(aero.Context) error { return nil }

	for _, route := range routes {
		router.Add(route.method, route.path, page)
	}

	for _, route := range routes {
		if router.Find(route.method, route.path) == nil {
			t.Fatal(route.method + " " + route.path)
		}
	}
}

func BenchmarkStaticRoutes(b *testing.B) {
	router := aero.Router{}
	routes := loadRoutes("testdata/router/static.txt")
	page := func(aero.Context) error { return nil }

	for _, route := range routes {
		router.Add(route.method, route.path, page)
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, route := range routes {
			if router.Find(route.method, route.path) == nil {
				b.Fatal(route.method + " " + route.path)
			}
		}
	}

	// b.RunParallel(func(pb *testing.PB) {
	// 	for pb.Next() {
	// 		for _, route := range routes {
	// 			if router.Find(route.method, route.path) == nil {
	// 				b.Fatal(route.method + " " + route.path)
	// 			}
	// 		}
	// 	}
	// })
}

func BenchmarkGitHubRoutes(b *testing.B) {
	router := aero.Router{}
	routes := loadRoutes("testdata/router/github.txt")
	page := func(aero.Context) error { return nil }

	for _, route := range routes {
		router.Add(route.method, route.path, page)
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, route := range routes {
			if router.Find(route.method, route.path) == nil {
				b.Fatal(route.method + " " + route.path)
			}
		}
	}

	// b.RunParallel(func(pb *testing.PB) {
	// 	for pb.Next() {
	// 		for _, route := range routes {
	// 			if router.Find(route.method, route.path) == nil {
	// 				b.Fatal(route.method + " " + route.path)
	// 			}
	// 		}
	// 	}
	// })
}

func loadRoutes(filePath string) []routeDefinition {
	routes := []routeDefinition{}
	f, err := os.Open(filePath)

	if err != nil {
		panic(err)
	}

	bufferedReader := bufio.NewReader(f)

	for {
		line, err := bufferedReader.ReadString('\n')

		if line != "" {
			line = strings.TrimSpace(line)
			parts := strings.Split(line, " ")
			routes = append(routes, routeDefinition{
				method: parts[0],
				path:   parts[1],
			})
		}

		if err != nil {
			break
		}
	}

	f.Close()
	return routes
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 framework

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"time"

	habv1beta1 "github.com/habitat-sh/habitat-operator/pkg/apis/habitat/v1beta1"

	appsv1beta1 "k8s.io/api/apps/v1beta1"
	"k8s.io/api/core/v1"
	apiv1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/util/yaml"
)

// CreateHabitat creates a Habitat.
func (f *Framework) CreateHabitat(habitat *habv1beta1.Habitat) error {
	return f.Client.Post().
		Namespace(TestNs).
		Resource(habv1beta1.HabitatResourcePlural).
		Body(habitat).
		Do().
		Error()
}

// WaitForResources waits until numPods are in the "Running" state.
// We wait for pods, because those take the longest to create.
// Waiting for anything else would be already testing.
func (f *Framework) WaitForResources(labelName, habitatName string, numPods int) error {
	return wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) {
		fs := fields.SelectorFromSet(fields.Set{
			"status.phase": string(apiv1.PodRunning),
		})

		ls := labels.SelectorFromSet(labels.Set{
			labelName: habitatName,
		})

		pods, err := f.KubeClient.CoreV1().Pods(TestNs).List(metav1.ListOptions{FieldSelector: fs.String(), LabelSelector: ls.String()})
		if err != nil {
			return false, err
		}

		if len(pods.Items) != numPods {
			return false, nil
		}

		return true, nil
	})
}

func (f *Framework) WaitForEndpoints(habitatName string) error {
	return wait.Poll(time.Second, time.Minute*5, func() (bool, error) {
		ep, err := f.KubeClient.CoreV1().Endpoints(TestNs).Get(habitatName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}

		if len(ep.Subsets) == 0 {
			return false, nil
		}

		if len(ep.Subsets[0].Addresses) == 0 {
			return false, nil
		}

		return true, nil
	})
}

// DeleteHabitat deletes a Habitat as a user would.
func (f *Framework) DeleteHabitat(habitatName string) error {
	return f.Client.Delete().
		Namespace(TestNs).
		Resource(habv1beta1.HabitatResourcePlural).
		Name(habitatName).
		Do().
		Error()
}

// DeleteService delete a Kubernetes service provided.
func (f *Framework) DeleteService(service string) error {
	return f.KubeClient.CoreV1().Services(TestNs).Delete(service, &metav1.DeleteOptions{})
}

func (f *Framework) createRBAC() error {
	// Create Service account.
	sa, err := convertServiceAccount("resources/operator/service-account.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.CoreV1().ServiceAccounts(TestNs).Create(sa)
	if err != nil {
		return err
	}

	// Create cluster role.
	cr, err := convertClusterRole("resources/operator/cluster-role.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.RbacV1().ClusterRoles().Create(cr)
	if err != nil {
		return err
	}

	// Create cluster role bindings.
	crb, err := convertClusterRoleBinding("resources/operator/cluster-role-binding.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.RbacV1().ClusterRoleBindings().Create(crb)
	if err != nil {
		return err
	}

	return nil
}

// convertServiceAccount takes in a path to the YAML file containing the manifest
// It converts it from that file to the ServiceAccount object.
func convertServiceAccount(pathToYaml string) (*apiv1.ServiceAccount, error) {
	sa := apiv1.ServiceAccount{}

	if err := convertToK8sResource(pathToYaml, &sa); err != nil {
		return nil, err
	}

	return &sa, nil
}

// convertClusterRole takes in a path to the YAML file containing the manifest.
// It converts the file to the ClusterRole object.
func convertClusterRole(pathToYaml string) (*rbacv1.ClusterRole, error) {
	cr := rbacv1.ClusterRole{}

	if err := convertToK8sResource(pathToYaml, &cr); err != nil {
		return nil, err
	}

	return &cr, nil
}

// convertClusterRoleBinding takes in a path to the YAML file containing the manifest.
// It converts the file to the ClusterRoleBinding object.
func convertClusterRoleBinding(pathToYaml string) (*rbacv1.ClusterRoleBinding, error) {
	crb := rbacv1.ClusterRoleBinding{}

	if err := convertToK8sResource(pathToYaml, &crb); err != nil {
		return nil, err
	}

	return &crb, nil
}

// ConvertDeployment takes in a path to the YAML file containing the manifest.
// It converts the file to the Deployment object.
func ConvertDeployment(pathToYaml string) (*appsv1beta1.Deployment, error) {
	d := appsv1beta1.Deployment{}

	if err := convertToK8sResource(pathToYaml, &d); err != nil {
		return nil, err
	}

	return &d, nil
}

// ConvertHabitat takes in a path to the YAML file containing the manifest.
// It converts the file to the Habitat object.
func ConvertHabitat(pathToYaml string) (*habv1beta1.Habitat, error) {
	hab := habv1beta1.Habitat{}

	if err := convertToK8sResource(pathToYaml, &hab); err != nil {
		return nil, err
	}

	return &hab, nil
}

// ConvertService takes in a path to the YAML file containing the manifest.
// It converts the file to the Service object.
func ConvertService(pathToYaml string) (*v1.Service, error) {
	s := v1.Service{}

	if err := convertToK8sResource(pathToYaml, &s); err != nil {
		return nil, err
	}

	return &s, nil
}

// ConvertSecret takes in a path to the YAML file containing the manifest.
// It converts the file to the Secret object.
func ConvertSecret(pathToYaml string) (*v1.Secret, error) {
	s := v1.Secret{}

	if err := convertToK8sResource(pathToYaml, &s); err != nil {
		return nil, err
	}

	return &s, nil
}

func convertToK8sResource(pathToYaml string, into interface{}) error {
	manifest, err := pathToOSFile(pathToYaml)
	if err != nil {
		return err
	}

	if err := yaml.NewYAMLToJSONDecoder(manifest).Decode(into); err != nil {
		return err
	}

	return nil
}

// pathToOSFile takes in a path and converts it to a File.
func pathToOSFile(relativePath string) (*os.File, error) {
	path, err := filepath.Abs(relativePath)
	if err != nil {
		return nil, err
	}

	manifest, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	return manifest, nil
}

// QueryService makes an HTTP GET request to `url` and returns the body.
func QueryService(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("Habitat Service did not start correctly.")
	}

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	return string(bodyBytes), nil
}
<commit_msg>Use runtime.Object in helper function<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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 framework

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"time"

	habv1beta1 "github.com/habitat-sh/habitat-operator/pkg/apis/habitat/v1beta1"

	appsv1beta1 "k8s.io/api/apps/v1beta1"
	"k8s.io/api/core/v1"
	apiv1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/util/yaml"
)

// CreateHabitat creates a Habitat.
func (f *Framework) CreateHabitat(habitat *habv1beta1.Habitat) error {
	return f.Client.Post().
		Namespace(TestNs).
		Resource(habv1beta1.HabitatResourcePlural).
		Body(habitat).
		Do().
		Error()
}

// WaitForResources waits until numPods are in the "Running" state.
// We wait for pods, because those take the longest to create.
// Waiting for anything else would be already testing.
func (f *Framework) WaitForResources(labelName, habitatName string, numPods int) error {
	return wait.Poll(2*time.Second, 5*time.Minute, func() (bool, error) {
		fs := fields.SelectorFromSet(fields.Set{
			"status.phase": string(apiv1.PodRunning),
		})

		ls := labels.SelectorFromSet(labels.Set{
			labelName: habitatName,
		})

		pods, err := f.KubeClient.CoreV1().Pods(TestNs).List(metav1.ListOptions{FieldSelector: fs.String(), LabelSelector: ls.String()})
		if err != nil {
			return false, err
		}

		if len(pods.Items) != numPods {
			return false, nil
		}

		return true, nil
	})
}

func (f *Framework) WaitForEndpoints(habitatName string) error {
	return wait.Poll(time.Second, time.Minute*5, func() (bool, error) {
		ep, err := f.KubeClient.CoreV1().Endpoints(TestNs).Get(habitatName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}

		if len(ep.Subsets) == 0 {
			return false, nil
		}

		if len(ep.Subsets[0].Addresses) == 0 {
			return false, nil
		}

		return true, nil
	})
}

// DeleteHabitat deletes a Habitat as a user would.
func (f *Framework) DeleteHabitat(habitatName string) error {
	return f.Client.Delete().
		Namespace(TestNs).
		Resource(habv1beta1.HabitatResourcePlural).
		Name(habitatName).
		Do().
		Error()
}

// DeleteService delete a Kubernetes service provided.
func (f *Framework) DeleteService(service string) error {
	return f.KubeClient.CoreV1().Services(TestNs).Delete(service, &metav1.DeleteOptions{})
}

func (f *Framework) createRBAC() error {
	// Create Service account.
	sa, err := convertServiceAccount("resources/operator/service-account.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.CoreV1().ServiceAccounts(TestNs).Create(sa)
	if err != nil {
		return err
	}

	// Create cluster role.
	cr, err := convertClusterRole("resources/operator/cluster-role.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.RbacV1().ClusterRoles().Create(cr)
	if err != nil {
		return err
	}

	// Create cluster role bindings.
	crb, err := convertClusterRoleBinding("resources/operator/cluster-role-binding.yml")
	if err != nil {
		return err
	}
	_, err = f.KubeClient.RbacV1().ClusterRoleBindings().Create(crb)
	if err != nil {
		return err
	}

	return nil
}

// convertServiceAccount takes in a path to the YAML file containing the manifest
// It converts it from that file to the ServiceAccount object.
func convertServiceAccount(pathToYaml string) (*apiv1.ServiceAccount, error) {
	sa := apiv1.ServiceAccount{}

	if err := convertToK8sResource(pathToYaml, &sa); err != nil {
		return nil, err
	}

	return &sa, nil
}

// convertClusterRole takes in a path to the YAML file containing the manifest.
// It converts the file to the ClusterRole object.
func convertClusterRole(pathToYaml string) (*rbacv1.ClusterRole, error) {
	cr := rbacv1.ClusterRole{}

	if err := convertToK8sResource(pathToYaml, &cr); err != nil {
		return nil, err
	}

	return &cr, nil
}

// convertClusterRoleBinding takes in a path to the YAML file containing the manifest.
// It converts the file to the ClusterRoleBinding object.
func convertClusterRoleBinding(pathToYaml string) (*rbacv1.ClusterRoleBinding, error) {
	crb := rbacv1.ClusterRoleBinding{}

	if err := convertToK8sResource(pathToYaml, &crb); err != nil {
		return nil, err
	}

	return &crb, nil
}

// ConvertDeployment takes in a path to the YAML file containing the manifest.
// It converts the file to the Deployment object.
func ConvertDeployment(pathToYaml string) (*appsv1beta1.Deployment, error) {
	d := appsv1beta1.Deployment{}

	if err := convertToK8sResource(pathToYaml, &d); err != nil {
		return nil, err
	}

	return &d, nil
}

// ConvertHabitat takes in a path to the YAML file containing the manifest.
// It converts the file to the Habitat object.
func ConvertHabitat(pathToYaml string) (*habv1beta1.Habitat, error) {
	hab := habv1beta1.Habitat{}

	if err := convertToK8sResource(pathToYaml, &hab); err != nil {
		return nil, err
	}

	return &hab, nil
}

// ConvertService takes in a path to the YAML file containing the manifest.
// It converts the file to the Service object.
func ConvertService(pathToYaml string) (*v1.Service, error) {
	s := v1.Service{}

	if err := convertToK8sResource(pathToYaml, &s); err != nil {
		return nil, err
	}

	return &s, nil
}

// ConvertSecret takes in a path to the YAML file containing the manifest.
// It converts the file to the Secret object.
func ConvertSecret(pathToYaml string) (*v1.Secret, error) {
	s := v1.Secret{}

	if err := convertToK8sResource(pathToYaml, &s); err != nil {
		return nil, err
	}

	return &s, nil
}

func convertToK8sResource(pathToYaml string, into runtime.Object) error {
	manifest, err := pathToOSFile(pathToYaml)
	if err != nil {
		return err
	}

	if err := yaml.NewYAMLToJSONDecoder(manifest).Decode(into); err != nil {
		return err
	}

	return nil
}

// pathToOSFile takes in a path and converts it to a File.
func pathToOSFile(relativePath string) (*os.File, error) {
	path, err := filepath.Abs(relativePath)
	if err != nil {
		return nil, err
	}

	manifest, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	return manifest, nil
}

// QueryService makes an HTTP GET request to `url` and returns the body.
func QueryService(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("Habitat Service did not start correctly.")
	}

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	return string(bodyBytes), nil
}
<|endoftext|>


<commit_before>package builds

import (
	"fmt"
	"sync"
	"time"

	"k8s.io/kubernetes/pkg/util/wait"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	exutil "github.com/openshift/origin/test/extended/util"
)

var _ = g.Describe("[builds][Slow] starting a build using CLI", func() {
	defer g.GinkgoRecover()
	var (
		buildFixture   = exutil.FixturePath("..", "extended", "testdata", "test-build.json")
		exampleGemfile = exutil.FixturePath("..", "extended", "testdata", "test-build-app", "Gemfile")
		exampleBuild   = exutil.FixturePath("..", "extended", "testdata", "test-build-app")
		oc             = exutil.NewCLI("cli-start-build", exutil.KubeConfigPath())
	)

	g.JustBeforeEach(func() {
		g.By("waiting for builder service account")
		err := exutil.WaitForBuilderAccount(oc.KubeREST().ServiceAccounts(oc.Namespace()))
		o.Expect(err).NotTo(o.HaveOccurred())
		oc.Run("create").Args("-f", buildFixture).Execute()
	})

	g.Describe("oc start-build --wait", func() {
		g.It("should start a build and wait for the build to complete", func() {
			g.By("starting the build with --wait flag")
			out, err := oc.Run("start-build").Args("sample-build", "--wait").Output()
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By(fmt.Sprintf("verifying the build %q status", out))
			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.It("should start a build and wait for the build to fail", func() {
			g.By("starting the build with --wait flag but wrong --commit")
			out, err := oc.Run("start-build").
				Args("sample-build", "--wait", "--commit", "fffffff").
				Output()
			o.Expect(err).To(o.HaveOccurred())
			o.Expect(out).Should(o.ContainSubstring(`status is "Failed"`))
		})
	})

	g.Describe("override environment", func() {
		g.It("should accept environment variables", func() {
			g.By("starting the build with -e FOO=bar")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", "-e", "FOO=bar,VAR=test").Output()
			o.Expect(err).NotTo(o.HaveOccurred())
			g.By(fmt.Sprintf("verifying the build output contains the env var"))
			o.Expect(out).To(o.ContainSubstring("FOO=bar"))
			// This variable is not set and thus inherited from the original build
			// config
			o.Expect(out).To(o.ContainSubstring("BAR=test"))
			o.Expect(out).To(o.ContainSubstring("VAR=test"))
			g.By(fmt.Sprintf("verifying the build %q status", out))
			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.It("should allow to change build log level", func() {
			g.By("starting the build with --build-loglevel=1")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", "--build-loglevel=1").Output()
			o.Expect(err).NotTo(o.HaveOccurred())
			g.By(fmt.Sprintf("verifying the build output is not verbose"))
			o.Expect(out).NotTo(o.ContainSubstring("Creating a new S2I builder"))
			g.By(fmt.Sprintf("verifying the build %q status", out))
			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})

	g.Describe("binary builds", func() {
		g.It("should accept --from-file as input", func() {
			g.By("starting the build with a Dockerfile")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", fmt.Sprintf("--from-file=%s", exampleGemfile)).Output()
			g.By(fmt.Sprintf("verifying the build %q status", out))
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(out).To(o.ContainSubstring("Uploading file"))
			o.Expect(out).To(o.ContainSubstring("as binary input for the build ..."))
			o.Expect(out).To(o.ContainSubstring("Your bundle is complete"))

			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
		})

		g.It("should accept --from-dir as input", func() {
			g.By("starting the build with a directory")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", fmt.Sprintf("--from-dir=%s", exampleBuild)).Output()
			g.By(fmt.Sprintf("verifying the build %q status", out))
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(out).To(o.ContainSubstring("Uploading directory"))
			o.Expect(out).To(o.ContainSubstring("as binary input for the build ..."))
			o.Expect(out).To(o.ContainSubstring("Your bundle is complete"))

			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.It("should accept --from-repo as input", func() {
			g.By("starting the build with a Git repository")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", fmt.Sprintf("--from-repo=%s", exampleBuild)).Output()
			g.By(fmt.Sprintf("verifying the build %q status", out))
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(out).To(o.ContainSubstring("Uploading"))
			o.Expect(out).To(o.ContainSubstring(`at commit "HEAD"`))
			o.Expect(out).To(o.ContainSubstring("as binary input for the build ..."))
			o.Expect(out).To(o.ContainSubstring("Your bundle is complete"))

			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.It("should accept --from-repo with --commit as input", func() {
			g.By("starting the build with a Git repository")
			// NOTE: This actually takes the commit from the origin repository. If the
			// test-build-app changes, this commit has to be bumped.
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--commit=f0f3834", "--wait", fmt.Sprintf("--from-repo=%s", exampleBuild)).Output()
			g.By(fmt.Sprintf("verifying the build %q status", out))
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(out).To(o.ContainSubstring("Uploading"))
			o.Expect(out).To(o.ContainSubstring(`at commit "f0f3834"`))
			o.Expect(out).To(o.ContainSubstring(`"commit":"f0f38342e53eac2a6995acca81d06bd9dd6d4964"`))
			o.Expect(out).To(o.ContainSubstring("as binary input for the build ..."))
			o.Expect(out).To(o.ContainSubstring("Your bundle is complete"))

			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})

	g.Describe("cancelling build started by oc start-build --wait", func() {
		g.It("should start a build and wait for the build to cancel", func() {
			g.By("starting the build with --wait flag")
			var wg sync.WaitGroup
			wg.Add(1)
			go func() {
				defer g.GinkgoRecover()
				out, err := oc.Run("start-build").Args("sample-build", "--wait").Output()
				defer wg.Done()
				o.Expect(err).To(o.HaveOccurred())
				o.Expect(out).Should(o.ContainSubstring(`status is "Cancelled"`))
			}()

			g.By("getting the build name")
			var buildName string
			wait.Poll(time.Duration(100*time.Millisecond), 1*time.Minute, func() (bool, error) {
				out, err := oc.Run("get").
					Args("build", "--template", "{{ (index .items 0).metadata.name }}").Output()
				// Give it second chance in case the build resource was not created yet
				if err != nil || len(out) == 0 {
					return false, nil
				}
				buildName = out
				return true, nil
			})

			o.Expect(buildName).ToNot(o.BeEmpty())

			g.By(fmt.Sprintf("cancelling the build %q", buildName))
			err := oc.Run("cancel-build").Args(buildName).Execute()
			o.Expect(err).ToNot(o.HaveOccurred())
			wg.Wait()
		})

	})

})
<commit_msg>Do not hard code build name<commit_after>package builds

import (
	"fmt"
	"sync"
	"time"

	"k8s.io/kubernetes/pkg/util/wait"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	exutil "github.com/openshift/origin/test/extended/util"
)

var _ = g.Describe("[builds][Slow] starting a build using CLI", func() {
	defer g.GinkgoRecover()
	var (
		buildFixture   = exutil.FixturePath("..", "extended", "testdata", "test-build.json")
		exampleGemfile = exutil.FixturePath("..", "extended", "testdata", "test-build-app", "Gemfile")
		exampleBuild   = exutil.FixturePath("..", "extended", "testdata", "test-build-app")
		oc             = exutil.NewCLI("cli-start-build", exutil.KubeConfigPath())
	)

	g.JustBeforeEach(func() {
		g.By("waiting for builder service account")
		err := exutil.WaitForBuilderAccount(oc.KubeREST().ServiceAccounts(oc.Namespace()))
		o.Expect(err).NotTo(o.HaveOccurred())
		oc.Run("create").Args("-f", buildFixture).Execute()
	})

	g.Describe("oc start-build --wait", func() {
		g.It("should start a build and wait for the build to complete", func() {
			g.By("starting the build with --wait flag")
			out, err := oc.Run("start-build").Args("sample-build", "--wait").Output()
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By(fmt.Sprintf("verifying the build %q status", out))
			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), out, exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.It("should start a build and wait for the build to fail", func() {
			g.By("starting the build with --wait flag but wrong --commit")
			out, err := oc.Run("start-build").
				Args("sample-build", "--wait", "--commit", "fffffff").
				Output()
			o.Expect(err).To(o.HaveOccurred())
			o.Expect(out).Should(o.ContainSubstring(`status is "Failed"`))
		})
	})

	g.Describe("override environment", func() {
		g.It("should accept environment variables", func() {
			g.By("starting the build with -e FOO=bar")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", "-e", "FOO=bar,VAR=test").Output()
			o.Expect(err).NotTo(o.HaveOccurred())
			g.By(fmt.Sprintf("verifying the build output contains the env var"))
			o.Expect(out).To(o.ContainSubstring("FOO=bar"))
			// This variable is not set and thus inherited from the original build
			// config
			o.Expect(out).To(o.ContainSubstring("BAR=test"))
			o.Expect(out).To(o.ContainSubstring("VAR=test"))
			g.By(fmt.Sprintf("verifying the build %q status", out))
			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.It("should allow to change build log level", func() {
			g.By("starting the build with --build-loglevel=1")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", "--build-loglevel=1").Output()
			o.Expect(err).NotTo(o.HaveOccurred())
			g.By(fmt.Sprintf("verifying the build output is not verbose"))
			o.Expect(out).NotTo(o.ContainSubstring("Creating a new S2I builder"))
			g.By(fmt.Sprintf("verifying the build %q status", out))
			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})

	g.Describe("binary builds", func() {
		g.It("should accept --from-file as input", func() {
			g.By("starting the build with a Dockerfile")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", fmt.Sprintf("--from-file=%s", exampleGemfile)).Output()
			g.By(fmt.Sprintf("verifying the build %q status", out))
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(out).To(o.ContainSubstring("Uploading file"))
			o.Expect(out).To(o.ContainSubstring("as binary input for the build ..."))
			o.Expect(out).To(o.ContainSubstring("Your bundle is complete"))

			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
		})

		g.It("should accept --from-dir as input", func() {
			g.By("starting the build with a directory")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", fmt.Sprintf("--from-dir=%s", exampleBuild)).Output()
			g.By(fmt.Sprintf("verifying the build %q status", out))
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(out).To(o.ContainSubstring("Uploading directory"))
			o.Expect(out).To(o.ContainSubstring("as binary input for the build ..."))
			o.Expect(out).To(o.ContainSubstring("Your bundle is complete"))

			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.It("should accept --from-repo as input", func() {
			g.By("starting the build with a Git repository")
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--wait", fmt.Sprintf("--from-repo=%s", exampleBuild)).Output()
			g.By(fmt.Sprintf("verifying the build %q status", out))
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(out).To(o.ContainSubstring("Uploading"))
			o.Expect(out).To(o.ContainSubstring(`at commit "HEAD"`))
			o.Expect(out).To(o.ContainSubstring("as binary input for the build ..."))
			o.Expect(out).To(o.ContainSubstring("Your bundle is complete"))

			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.It("should accept --from-repo with --commit as input", func() {
			g.By("starting the build with a Git repository")
			// NOTE: This actually takes the commit from the origin repository. If the
			// test-build-app changes, this commit has to be bumped.
			out, err := oc.Run("start-build").Args("sample-build", "--follow", "--commit=f0f3834", "--wait", fmt.Sprintf("--from-repo=%s", exampleBuild)).Output()
			g.By(fmt.Sprintf("verifying the build %q status", out))
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(out).To(o.ContainSubstring("Uploading"))
			o.Expect(out).To(o.ContainSubstring(`at commit "f0f3834"`))
			o.Expect(out).To(o.ContainSubstring(`"commit":"f0f38342e53eac2a6995acca81d06bd9dd6d4964"`))
			o.Expect(out).To(o.ContainSubstring("as binary input for the build ..."))
			o.Expect(out).To(o.ContainSubstring("Your bundle is complete"))

			err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "sample-build-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
			if err != nil {
				exutil.DumpBuildLogs("sample-build", oc)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
		})
	})

	g.Describe("cancelling build started by oc start-build --wait", func() {
		g.It("should start a build and wait for the build to cancel", func() {
			g.By("starting the build with --wait flag")
			var wg sync.WaitGroup
			wg.Add(1)
			go func() {
				defer g.GinkgoRecover()
				out, err := oc.Run("start-build").Args("sample-build", "--wait").Output()
				defer wg.Done()
				o.Expect(err).To(o.HaveOccurred())
				o.Expect(out).Should(o.ContainSubstring(`status is "Cancelled"`))
			}()

			g.By("getting the build name")
			var buildName string
			wait.Poll(time.Duration(100*time.Millisecond), 1*time.Minute, func() (bool, error) {
				out, err := oc.Run("get").
					Args("build", "--template", "{{ (index .items 0).metadata.name }}").Output()
				// Give it second chance in case the build resource was not created yet
				if err != nil || len(out) == 0 {
					return false, nil
				}
				buildName = out
				return true, nil
			})

			o.Expect(buildName).ToNot(o.BeEmpty())

			g.By(fmt.Sprintf("cancelling the build %q", buildName))
			err := oc.Run("cancel-build").Args(buildName).Execute()
			o.Expect(err).ToNot(o.HaveOccurred())
			wg.Wait()
		})

	})

})
<|endoftext|>


<commit_before>package collector

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/karimra/gnmic/outputs"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"google.golang.org/grpc"
)

// Config is the collector config
type Config struct {
	PrometheusAddress string
	Debug             bool
}

// Collector //
type Collector struct {
	Config        *Config
	Subscriptions map[string]*SubscriptionConfig
	Outputs       map[string][]outputs.Output
	DialOpts      []grpc.DialOption
	//
	m             *sync.Mutex
	Targets       map[string]*Target
	Logger        *log.Logger
	httpServer    *http.Server
	defaultOutput io.Writer
	ctx           context.Context
	cancelFn      context.CancelFunc
}

// NewCollector //
func NewCollector(ctx context.Context,
	config *Config,
	targetConfigs map[string]*TargetConfig,
	subscriptions map[string]*SubscriptionConfig,
	outputs map[string][]outputs.Output,
	dialOpts []grpc.DialOption,
	logger *log.Logger,
) *Collector {
	nctx, cancel := context.WithCancel(ctx)
	grpcMetrics := grpc_prometheus.NewClientMetrics()
	reg := prometheus.NewRegistry()
	reg.MustRegister(prometheus.NewGoCollector())
	grpcMetrics.EnableClientHandlingTimeHistogram()
	reg.MustRegister(grpcMetrics)
	httpServer := &http.Server{
		Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}),
		Addr:    config.PrometheusAddress,
	}
	c := &Collector{
		Config:        config,
		Subscriptions: subscriptions,
		Outputs:       outputs,
		DialOpts:      dialOpts,
		m:             new(sync.Mutex),
		Targets:       make(map[string]*Target),
		Logger:        logger,
		httpServer:    httpServer,
		defaultOutput: os.Stdout,
		ctx:           nctx,
		cancelFn:      cancel,
	}
	wg := new(sync.WaitGroup)
	wg.Add(len(targetConfigs))
	for _, tc := range targetConfigs {
		go func(tc *TargetConfig) {
			defer wg.Done()
			err := c.InitTarget(tc)
			if err != nil {
				c.Logger.Printf("failed to initialize target '%s': %v", tc.Name, err)
				return
			}
			c.Logger.Printf("target '%s' initialized", tc.Name)
		}(tc)
	}
	wg.Wait()
	return c
}

// InitTarget initializes a target based on *TargetConfig
func (c *Collector) InitTarget(tc *TargetConfig) error {
	t := NewTarget(tc)
	//
	t.Subscriptions = make([]*SubscriptionConfig, 0, len(tc.Subscriptions))
	for _, subName := range tc.Subscriptions {
		if sub, ok := c.Subscriptions[subName]; ok {
			t.Subscriptions = append(t.Subscriptions, sub)
		}
	}
	if len(t.Subscriptions) == 0 {
		t.Subscriptions = make([]*SubscriptionConfig, 0, len(c.Subscriptions))
		for _, sub := range c.Subscriptions {
			t.Subscriptions = append(t.Subscriptions, sub)
		}
	}
	//
	t.Outputs = make([]outputs.Output, 0, len(tc.Outputs))
	for _, outName := range tc.Outputs {
		if outs, ok := c.Outputs[outName]; ok {
			for _, o := range outs {
				t.Outputs = append(t.Outputs, o)
			}
		}
	}
	if len(t.Outputs) == 0 {
		t.Outputs = make([]outputs.Output, 0, len(c.Outputs))
		for _, o := range c.Outputs {
			t.Outputs = append(t.Outputs, o...)
		}
	}
	//
	err := t.CreateGNMIClient(c.ctx, c.DialOpts...)
	if err != nil {
		return err
	}
	t.ctx, t.cancelFn = context.WithCancel(c.ctx)
	c.m.Lock()
	defer c.m.Unlock()
	c.Targets[t.Config.Name] = t
	return nil
}

// Subscribe //
func (c *Collector) Subscribe(tName string) error {
	if t, ok := c.Targets[tName]; ok {
		for _, sc := range t.Subscriptions {
			req, err := sc.CreateSubscribeRequest()
			if err != nil {
				return err
			}
			go t.Subscribe(c.ctx, req, sc.Name)
		}
		return nil
	}
	return fmt.Errorf("unknown target name: %s", tName)
}

// Start start the prometheus server as well as a goroutine per target selecting on the response chan, the error chan and the ctx.Done() chan
func (c *Collector) Start() {
	go func() {
		if err := c.httpServer.ListenAndServe(); err != nil {
			c.Logger.Printf("Unable to start prometheus http server: %v", err)
			return
		}
	}()
	wg := new(sync.WaitGroup)
	wg.Add(len(c.Targets))
	for _, t := range c.Targets {
		go func(t *Target) {
			defer wg.Done()
			numOnceSubscriptions := t.numberOfOnceSubscriptions()
			remainingOnceSubscriptions := numOnceSubscriptions
			numSubscriptions := len(t.Subscriptions)
			for {
				select {
				case rsp := <-t.SubscribeResponses:
					m := make(map[string]interface{})
					m["subscription-name"] = rsp.SubscriptionName
					m["source"] = t.Config.Name
					b, err := c.FormatMsg(m, rsp.Response)
					if err != nil {
						c.Logger.Printf("failed formatting msg from target '%s': %v", t.Config.Name, err)
						continue
					}
					go t.Export(b, outputs.Meta{"source": t.Config.Name})
					if remainingOnceSubscriptions > 0 {
						if c.subscriptionMode(rsp.SubscriptionName) == "ONCE" {
							switch rsp.Response.Response.(type) {
							case *gnmi.SubscribeResponse_SyncResponse:
								remainingOnceSubscriptions--
							}
						}
					}
					if remainingOnceSubscriptions == 0 && numSubscriptions == numOnceSubscriptions {
						return
					}
				case err := <-t.Errors:
					if err == io.EOF {
						c.Logger.Printf("target '%s' closed stream(EOF)", t.Config.Name)
						return
					}
					c.Logger.Printf("target '%s' error: %v", t.Config.Name, err)
				case <-t.ctx.Done():
					return
				}
			}
		}(t)
	}
	wg.Wait()
}

// FormatMsg formats the gnmi.SubscribeResponse and returns a []byte and an error
func (c *Collector) FormatMsg(meta map[string]interface{}, rsp *gnmi.SubscribeResponse) ([]byte, error) {
	if rsp == nil {
		return nil, nil
	}
	switch rsp := rsp.Response.(type) {
	case *gnmi.SubscribeResponse_Update:
		msg := new(msg)
		msg.Timestamp = rsp.Update.Timestamp
		t := time.Unix(0, rsp.Update.Timestamp)
		msg.Time = &t
		if meta == nil {
			meta = make(map[string]interface{})
		}
		msg.Prefix = gnmiPathToXPath(rsp.Update.Prefix)
		var ok bool
		if _, ok = meta["source"]; ok {
			msg.Source = fmt.Sprintf("%s", meta["source"])
		}
		if _, ok = meta["system-name"]; ok {
			msg.SystemName = fmt.Sprintf("%s", meta["system-name"])
		}
		if _, ok = meta["subscription-name"]; ok {
			msg.SubscriptionName = fmt.Sprintf("%s", meta["subscription-name"])
		}
		for i, upd := range rsp.Update.Update {
			pathElems := make([]string, 0, len(upd.Path.Elem))
			for _, pElem := range upd.Path.Elem {
				pathElems = append(pathElems, pElem.GetName())
			}
			value, err := getValue(upd.Val)
			if err != nil {
				c.Logger.Println(err)
			}
			msg.Updates = append(msg.Updates,
				&update{
					Path:   gnmiPathToXPath(upd.Path),
					Values: make(map[string]interface{}),
				})
			msg.Updates[i].Values[strings.Join(pathElems, "/")] = value
		}
		for _, del := range rsp.Update.Delete {
			msg.Deletes = append(msg.Deletes, gnmiPathToXPath(del))
		}
		data, err := json.Marshal(msg)
		if err != nil {
			return nil, err
		}
		return data, nil
	}
	return nil, nil
}

// TargetPoll sends a gnmi.SubscribeRequest_Poll to targetName and returns the response and an error,
// it uses the targetName and the subscriptionName strings to find the gnmi.GNMI_SubscribeClient
func (c *Collector) TargetPoll(targetName, subscriptionName string) (*gnmi.SubscribeResponse, error) {
	if sub, ok := c.Subscriptions[subscriptionName]; ok {
		if strings.ToUpper(sub.Mode) != "POLL" {
			return nil, fmt.Errorf("subscription '%s' is not a POLL subscription", subscriptionName)
		}
		if t, ok := c.Targets[targetName]; ok {
			if subClient, ok := t.SubscribeClients[subscriptionName]; ok {
				err := subClient.Send(&gnmi.SubscribeRequest{
					Request: &gnmi.SubscribeRequest_Poll{
						Poll: &gnmi.Poll{},
					},
				})
				if err != nil {
					return nil, err
				}
				return subClient.Recv()
			}
		}
		return nil, fmt.Errorf("unknown target name '%s'", targetName)
	}
	return nil, fmt.Errorf("unknown subscription name '%s'", subscriptionName)
}

// PolledSubscriptionsTargets returns a map of target name to a list of subscription names that have Mode == POLL
func (c *Collector) PolledSubscriptionsTargets() map[string][]string {
	result := make(map[string][]string)
	for tn, target := range c.Targets {
		for _, sub := range target.Subscriptions {
			if strings.ToUpper(sub.Mode) == "POLL" {
				if result[tn] == nil {
					result[tn] = make([]string, 0)
				}
				result[tn] = append(result[tn], sub.Name)
			}
		}
	}
	return result
}

func (c *Collector) subscriptionMode(name string) string {
	if sub, ok := c.Subscriptions[name]; ok {
		return strings.ToUpper(sub.Mode)
	}
	return ""
}
<commit_msg>add back grpc prometheus metrics<commit_after>package collector

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/karimra/gnmic/outputs"
	"github.com/openconfig/gnmi/proto/gnmi"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"google.golang.org/grpc"
)

// Config is the collector config
type Config struct {
	PrometheusAddress string
	Debug             bool
}

// Collector //
type Collector struct {
	Config        *Config
	Subscriptions map[string]*SubscriptionConfig
	Outputs       map[string][]outputs.Output
	DialOpts      []grpc.DialOption
	//
	m             *sync.Mutex
	Targets       map[string]*Target
	Logger        *log.Logger
	httpServer    *http.Server
	defaultOutput io.Writer
	ctx           context.Context
	cancelFn      context.CancelFunc
}

// NewCollector //
func NewCollector(ctx context.Context,
	config *Config,
	targetConfigs map[string]*TargetConfig,
	subscriptions map[string]*SubscriptionConfig,
	outputs map[string][]outputs.Output,
	dialOpts []grpc.DialOption,
	logger *log.Logger,
) *Collector {
	nctx, cancel := context.WithCancel(ctx)
	grpcMetrics := grpc_prometheus.NewClientMetrics()
	reg := prometheus.NewRegistry()
	reg.MustRegister(prometheus.NewGoCollector())
	grpcMetrics.EnableClientHandlingTimeHistogram()
	reg.MustRegister(grpcMetrics)
	httpServer := &http.Server{
		Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}),
		Addr:    config.PrometheusAddress,
	}
	dialOpts = append(dialOpts, grpc.WithStreamInterceptor(grpcMetrics.StreamClientInterceptor()))
	c := &Collector{
		Config:        config,
		Subscriptions: subscriptions,
		Outputs:       outputs,
		DialOpts:      dialOpts,
		m:             new(sync.Mutex),
		Targets:       make(map[string]*Target),
		Logger:        logger,
		httpServer:    httpServer,
		defaultOutput: os.Stdout,
		ctx:           nctx,
		cancelFn:      cancel,
	}
	wg := new(sync.WaitGroup)
	wg.Add(len(targetConfigs))
	for _, tc := range targetConfigs {
		go func(tc *TargetConfig) {
			defer wg.Done()
			err := c.InitTarget(tc)
			if err != nil {
				c.Logger.Printf("failed to initialize target '%s': %v", tc.Name, err)
				return
			}
			c.Logger.Printf("target '%s' initialized", tc.Name)
		}(tc)
	}
	wg.Wait()
	return c
}

// InitTarget initializes a target based on *TargetConfig
func (c *Collector) InitTarget(tc *TargetConfig) error {
	t := NewTarget(tc)
	//
	t.Subscriptions = make([]*SubscriptionConfig, 0, len(tc.Subscriptions))
	for _, subName := range tc.Subscriptions {
		if sub, ok := c.Subscriptions[subName]; ok {
			t.Subscriptions = append(t.Subscriptions, sub)
		}
	}
	if len(t.Subscriptions) == 0 {
		t.Subscriptions = make([]*SubscriptionConfig, 0, len(c.Subscriptions))
		for _, sub := range c.Subscriptions {
			t.Subscriptions = append(t.Subscriptions, sub)
		}
	}
	//
	t.Outputs = make([]outputs.Output, 0, len(tc.Outputs))
	for _, outName := range tc.Outputs {
		if outs, ok := c.Outputs[outName]; ok {
			for _, o := range outs {
				t.Outputs = append(t.Outputs, o)
			}
		}
	}
	if len(t.Outputs) == 0 {
		t.Outputs = make([]outputs.Output, 0, len(c.Outputs))
		for _, o := range c.Outputs {
			t.Outputs = append(t.Outputs, o...)
		}
	}
	//
	err := t.CreateGNMIClient(c.ctx, c.DialOpts...)
	if err != nil {
		return err
	}
	t.ctx, t.cancelFn = context.WithCancel(c.ctx)
	c.m.Lock()
	defer c.m.Unlock()
	c.Targets[t.Config.Name] = t
	return nil
}

// Subscribe //
func (c *Collector) Subscribe(tName string) error {
	if t, ok := c.Targets[tName]; ok {
		for _, sc := range t.Subscriptions {
			req, err := sc.CreateSubscribeRequest()
			if err != nil {
				return err
			}
			go t.Subscribe(c.ctx, req, sc.Name)
		}
		return nil
	}
	return fmt.Errorf("unknown target name: %s", tName)
}

// Start start the prometheus server as well as a goroutine per target selecting on the response chan, the error chan and the ctx.Done() chan
func (c *Collector) Start() {
	go func() {
		if err := c.httpServer.ListenAndServe(); err != nil {
			c.Logger.Printf("Unable to start prometheus http server: %v", err)
			return
		}
	}()
	wg := new(sync.WaitGroup)
	wg.Add(len(c.Targets))
	for _, t := range c.Targets {
		go func(t *Target) {
			defer wg.Done()
			numOnceSubscriptions := t.numberOfOnceSubscriptions()
			remainingOnceSubscriptions := numOnceSubscriptions
			numSubscriptions := len(t.Subscriptions)
			for {
				select {
				case rsp := <-t.SubscribeResponses:
					m := make(map[string]interface{})
					m["subscription-name"] = rsp.SubscriptionName
					m["source"] = t.Config.Name
					b, err := c.FormatMsg(m, rsp.Response)
					if err != nil {
						c.Logger.Printf("failed formatting msg from target '%s': %v", t.Config.Name, err)
						continue
					}
					go t.Export(b, outputs.Meta{"source": t.Config.Name})
					if remainingOnceSubscriptions > 0 {
						if c.subscriptionMode(rsp.SubscriptionName) == "ONCE" {
							switch rsp.Response.Response.(type) {
							case *gnmi.SubscribeResponse_SyncResponse:
								remainingOnceSubscriptions--
							}
						}
					}
					if remainingOnceSubscriptions == 0 && numSubscriptions == numOnceSubscriptions {
						return
					}
				case err := <-t.Errors:
					if err == io.EOF {
						c.Logger.Printf("target '%s' closed stream(EOF)", t.Config.Name)
						return
					}
					c.Logger.Printf("target '%s' error: %v", t.Config.Name, err)
				case <-t.ctx.Done():
					return
				}
			}
		}(t)
	}
	wg.Wait()
}

// FormatMsg formats the gnmi.SubscribeResponse and returns a []byte and an error
func (c *Collector) FormatMsg(meta map[string]interface{}, rsp *gnmi.SubscribeResponse) ([]byte, error) {
	if rsp == nil {
		return nil, nil
	}
	switch rsp := rsp.Response.(type) {
	case *gnmi.SubscribeResponse_Update:
		msg := new(msg)
		msg.Timestamp = rsp.Update.Timestamp
		t := time.Unix(0, rsp.Update.Timestamp)
		msg.Time = &t
		if meta == nil {
			meta = make(map[string]interface{})
		}
		msg.Prefix = gnmiPathToXPath(rsp.Update.Prefix)
		var ok bool
		if _, ok = meta["source"]; ok {
			msg.Source = fmt.Sprintf("%s", meta["source"])
		}
		if _, ok = meta["system-name"]; ok {
			msg.SystemName = fmt.Sprintf("%s", meta["system-name"])
		}
		if _, ok = meta["subscription-name"]; ok {
			msg.SubscriptionName = fmt.Sprintf("%s", meta["subscription-name"])
		}
		for i, upd := range rsp.Update.Update {
			pathElems := make([]string, 0, len(upd.Path.Elem))
			for _, pElem := range upd.Path.Elem {
				pathElems = append(pathElems, pElem.GetName())
			}
			value, err := getValue(upd.Val)
			if err != nil {
				c.Logger.Println(err)
			}
			msg.Updates = append(msg.Updates,
				&update{
					Path:   gnmiPathToXPath(upd.Path),
					Values: make(map[string]interface{}),
				})
			msg.Updates[i].Values[strings.Join(pathElems, "/")] = value
		}
		for _, del := range rsp.Update.Delete {
			msg.Deletes = append(msg.Deletes, gnmiPathToXPath(del))
		}
		data, err := json.Marshal(msg)
		if err != nil {
			return nil, err
		}
		return data, nil
	}
	return nil, nil
}

// TargetPoll sends a gnmi.SubscribeRequest_Poll to targetName and returns the response and an error,
// it uses the targetName and the subscriptionName strings to find the gnmi.GNMI_SubscribeClient
func (c *Collector) TargetPoll(targetName, subscriptionName string) (*gnmi.SubscribeResponse, error) {
	if sub, ok := c.Subscriptions[subscriptionName]; ok {
		if strings.ToUpper(sub.Mode) != "POLL" {
			return nil, fmt.Errorf("subscription '%s' is not a POLL subscription", subscriptionName)
		}
		if t, ok := c.Targets[targetName]; ok {
			if subClient, ok := t.SubscribeClients[subscriptionName]; ok {
				err := subClient.Send(&gnmi.SubscribeRequest{
					Request: &gnmi.SubscribeRequest_Poll{
						Poll: &gnmi.Poll{},
					},
				})
				if err != nil {
					return nil, err
				}
				return subClient.Recv()
			}
		}
		return nil, fmt.Errorf("unknown target name '%s'", targetName)
	}
	return nil, fmt.Errorf("unknown subscription name '%s'", subscriptionName)
}

// PolledSubscriptionsTargets returns a map of target name to a list of subscription names that have Mode == POLL
func (c *Collector) PolledSubscriptionsTargets() map[string][]string {
	result := make(map[string][]string)
	for tn, target := range c.Targets {
		for _, sub := range target.Subscriptions {
			if strings.ToUpper(sub.Mode) == "POLL" {
				if result[tn] == nil {
					result[tn] = make([]string, 0)
				}
				result[tn] = append(result[tn], sub.Name)
			}
		}
	}
	return result
}

func (c *Collector) subscriptionMode(name string) string {
	if sub, ok := c.Subscriptions[name]; ok {
		return strings.ToUpper(sub.Mode)
	}
	return ""
}
<|endoftext|>


<commit_before>package collector

import (
	"log"
	"sync"

	"github.com/fsouza/go-dockerclient"
)

// Collector is responsible for discovering containers
// for monitoring and writing stats
type Collector struct {
	client     *docker.Client
	ch         chan Stats
	mutex      sync.Mutex
	registered map[string]struct{}
	interval   int
}

// NewCollector creates new Collector with specified docker client,
// collectd stats writer and stat updating interval
func NewCollector(client *docker.Client, w CollectdWriter, interval int) *Collector {
	ch := make(chan Stats)

	// TODO: this can be better, need to figure out how
	go func() {
		for s := range ch {
			w.Write(s)
		}
	}()

	return Collector{
		client:     client,
		ch:         ch,
		mutex:      sync.Mutex{},
		registered: map[string]struct{}{},
		interval:   interval,
	}
}

// Run stats loop that discovers containers and runs
// monitoring tasks for them
func (c *Collector) Run(interval int) error {
	ch := make(chan *docker.APIEvents)
	err := c.client.AddEventListener(ch)
	if err != nil {
		return err
	}

	defer c.client.RemoveEventListener(ch)

	containers, err := c.client.ListContainers(docker.ListContainersOptions{})
	if err != nil {
		return err
	}

	for _, container := range containers {
		go c.handle(container.ID)
	}

	for e := range ch {
		switch e.Status {
		case "start", "restart":
			go c.handle(e.ID)
		}
	}

	return nil
}

func (c *Collector) handle(id string) {
	m, err := NewMonitor(c.client, id, c.interval)
	if err != nil {
		if err == ErrNoNeedToMonitor {
			return
		}

		log.Printf("error handling %s: %s\n", id, err)

		return
	}

	go func() {
		if !c.register(id) {
			return
		}

		err := m.handle(c.ch)
		if err != nil {
			log.Printf("error handling container for app %s: %s\n", m.app, err)
		}

		c.unregister(id)
	}()
}

func (c *Collector) register(id string) bool {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if _, ok := c.registered[id]; ok {
		return false
	}

	c.registered[id] = struct{}{}
	return true
}

func (c *Collector) unregister(id string) {
	c.mutex.Lock()
	delete(c.registered, id)
	c.mutex.Unlock()
}
<commit_msg>Fixed compilation issue<commit_after>package collector

import (
	"log"
	"sync"

	"github.com/fsouza/go-dockerclient"
)

// Collector is responsible for discovering containers
// for monitoring and writing stats
type Collector struct {
	client     *docker.Client
	ch         chan Stats
	mutex      sync.Mutex
	registered map[string]struct{}
	interval   int
}

// NewCollector creates new Collector with specified docker client,
// collectd stats writer and stat updating interval
func NewCollector(client *docker.Client, w CollectdWriter, interval int) *Collector {
	ch := make(chan Stats)

	// TODO: this can be better, need to figure out how
	go func() {
		for s := range ch {
			w.Write(s)
		}
	}()

	return &Collector{
		client:     client,
		ch:         ch,
		mutex:      sync.Mutex{},
		registered: map[string]struct{}{},
		interval:   interval,
	}
}

// Run stats loop that discovers containers and runs
// monitoring tasks for them
func (c *Collector) Run(interval int) error {
	ch := make(chan *docker.APIEvents)
	err := c.client.AddEventListener(ch)
	if err != nil {
		return err
	}

	defer c.client.RemoveEventListener(ch)

	containers, err := c.client.ListContainers(docker.ListContainersOptions{})
	if err != nil {
		return err
	}

	for _, container := range containers {
		go c.handle(container.ID)
	}

	for e := range ch {
		switch e.Status {
		case "start", "restart":
			go c.handle(e.ID)
		}
	}

	return nil
}

func (c *Collector) handle(id string) {
	m, err := NewMonitor(c.client, id, c.interval)
	if err != nil {
		if err == ErrNoNeedToMonitor {
			return
		}

		log.Printf("error handling %s: %s\n", id, err)

		return
	}

	go func() {
		if !c.register(id) {
			return
		}

		err := m.handle(c.ch)
		if err != nil {
			log.Printf("error handling container for app %s: %s\n", m.app, err)
		}

		c.unregister(id)
	}()
}

func (c *Collector) register(id string) bool {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if _, ok := c.registered[id]; ok {
		return false
	}

	c.registered[id] = struct{}{}
	return true
}

func (c *Collector) unregister(id string) {
	c.mutex.Lock()
	delete(c.registered, id)
	c.mutex.Unlock()
}
<|endoftext|>


<commit_before>// This is free and unencumbered software released into the public
// domain.  For more information, see <http://unlicense.org> or the
// accompanying UNLICENSE file.

package command

import (
	"errors"
	"fmt"
	"os"

	"github.com/nelsam/gxui"
	"github.com/nelsam/gxui/themes/basic"
	"github.com/nelsam/vidar/command/focus"
	"github.com/nelsam/vidar/command/fs"
	"github.com/nelsam/vidar/commander/bind"
	"github.com/nelsam/vidar/plugin/status"
)

type Focuser interface {
	For(...focus.Opt) bind.Bindable
}

type FileOpener struct {
	status.General

	driver gxui.Driver
	theme  *basic.Theme

	file  *fs.Locator
	input <-chan gxui.Focusable

	focuser Focuser
	execer  Executor
}

func NewFileOpener(driver gxui.Driver, theme *basic.Theme) *FileOpener {
	o := &FileOpener{
		driver: driver,
		theme:  theme,
	}
	o.file = fs.NewLocator(driver, theme, fs.All)
	return o
}

func (f *FileOpener) Name() string {
	return "open-file"
}

func (f *FileOpener) Menu() string {
	return "File"
}

func (f *FileOpener) Defaults() []fmt.Stringer {
	return []fmt.Stringer{gxui.KeyboardEvent{
		Modifier: gxui.ModControl,
		Key:      gxui.KeyO,
	}}
}

func (f *FileOpener) Start(control gxui.Control) gxui.Control {
	f.file.LoadDir(control)
	input := make(chan gxui.Focusable, 1)
	f.input = input
	input <- f.file
	close(input)
	return nil
}

func (f *FileOpener) Next() gxui.Focusable {
	return <-f.input
}

func (f *FileOpener) Reset() {
	f.focuser = nil
	f.execer = nil
}

func (f *FileOpener) Store(elem interface{}) bind.Status {
	switch src := elem.(type) {
	case Focuser:
		f.focuser = src
	case Executor:
		f.execer = src
	}

	if f.focuser == nil || f.execer == nil {
		return bind.Waiting
	}
	return bind.Executing
}

func (f *FileOpener) Exec() error {
	path := f.file.Path()
	if path == "" {
		return errors.New("command.FileOpener: No file path provided")
	}

	if finfo, err := os.Stat(path); err == nil && finfo.IsDir() {
		return fmt.Errorf("command.FileOpener: You can't open directory %s as file", path)
	}

	f.execer.Execute(f.focuser.For(focus.Path(path)))
	return nil
}
<commit_msg>command.FileOpener: present errors to user<commit_after>// This is free and unencumbered software released into the public
// domain.  For more information, see <http://unlicense.org> or the
// accompanying UNLICENSE file.

package command

import (
	"fmt"
	"os"

	"github.com/nelsam/gxui"
	"github.com/nelsam/gxui/themes/basic"
	"github.com/nelsam/vidar/command/focus"
	"github.com/nelsam/vidar/command/fs"
	"github.com/nelsam/vidar/commander/bind"
	"github.com/nelsam/vidar/plugin/status"
)

type Focuser interface {
	For(...focus.Opt) bind.Bindable
}

type FileOpener struct {
	status.General

	driver gxui.Driver

	file  *fs.Locator
	input <-chan gxui.Focusable

	focuser Focuser
	execer  Executor
}

func NewFileOpener(driver gxui.Driver, theme *basic.Theme) *FileOpener {
	o := &FileOpener{
		General: status.General{Theme: theme},
		driver:  driver,
	}
	o.file = fs.NewLocator(driver, theme, fs.All)
	return o
}

func (f *FileOpener) Name() string {
	return "open-file"
}

func (f *FileOpener) Menu() string {
	return "File"
}

func (f *FileOpener) Defaults() []fmt.Stringer {
	return []fmt.Stringer{gxui.KeyboardEvent{
		Modifier: gxui.ModControl,
		Key:      gxui.KeyO,
	}}
}

func (f *FileOpener) Start(control gxui.Control) gxui.Control {
	f.file.LoadDir(control)
	input := make(chan gxui.Focusable, 1)
	f.input = input
	input <- f.file
	close(input)
	return nil
}

func (f *FileOpener) Next() gxui.Focusable {
	return <-f.input
}

func (f *FileOpener) Reset() {
	f.focuser = nil
	f.execer = nil
}

func (f *FileOpener) Store(elem interface{}) bind.Status {
	switch src := elem.(type) {
	case Focuser:
		f.focuser = src
	case Executor:
		f.execer = src
	}

	if f.focuser == nil || f.execer == nil {
		return bind.Waiting
	}
	return bind.Executing
}

func (f *FileOpener) Exec() error {
	path := f.file.Path()
	if path == "" {
		f.Err = "no file path provided"
		return fmt.Errorf("command.FileOpener: %s", f.Err)
	}

	if finfo, err := os.Stat(path); err == nil && finfo.IsDir() {
		f.Err = fmt.Sprintf("can't open directory %s as file", path)
		return fmt.Errorf("command.FileOpener: %s", f.Err)
	}

	f.execer.Execute(f.focuser.For(focus.Path(path)))
	return nil
}
<|endoftext|>


<commit_before>package common

import (
	"time"
)

/*
RepeatTimer repeatedly sends a struct{}{} to .Ch after each "dur" period.
It's good for keeping connections alive.
A RepeatTimer must be Stop()'d or it will keep a goroutine alive.
*/
type RepeatTimer struct {
	Name   string
	Ch     <-chan time.Time
	output chan<- time.Time
	input  chan repeatCommand

	dur     time.Duration
	ticker  *time.Ticker
	stopped bool
}

type repeatCommand int8

const (
	Reset repeatCommand = iota
	RQuit
)

func NewRepeatTimer(name string, dur time.Duration) *RepeatTimer {
	c := make(chan time.Time)
	var t = &RepeatTimer{
		Name:   name,
		Ch:     c,
		output: c,
		input:  make(chan repeatCommand),

		dur:    dur,
		ticker: time.NewTicker(dur),
	}
	go t.run()
	return t
}

// Wait the duration again before firing.
func (t *RepeatTimer) Reset() {
	t.input <- Reset
}

// For ease of .Stop()'ing services before .Start()'ing them,
// we ignore .Stop()'s on nil RepeatTimers.
func (t *RepeatTimer) Stop() bool {
	// use t.stopped to gracefully handle many Stop() without blocking
	if t == nil || t.stopped {
		return false
	}
	t.input <- RQuit
	t.stopped = true
	return true
}

func (t *RepeatTimer) run() {
	done := false
	for !done {
		select {
		case cmd := <-t.input:
			// stop goroutine if the input says so
			// don't close channels, as closed channels mess up select reads
			done = t.processInput(cmd)
		case tick := <-t.ticker.C:
			t.trySend(tick)
		}
	}
}

// trySend performs non-blocking send on t.Ch
func (t *RepeatTimer) trySend(tick time.Time) {
	// NOTE: this was blocking in previous version (t.Ch <- t_)
	// probably better not: https://golang.org/src/time/sleep.go#L132
	t.output <- tick
	// select {
	// case t.output <- tick:
	// default:
	// }
}

// all modifications of the internal state of ThrottleTimer
// happen in this method. It is only called from the run goroutine
// so we avoid any race conditions
func (t *RepeatTimer) processInput(cmd repeatCommand) (shutdown bool) {
	switch cmd {
	case Reset:
		t.ticker.Stop()
		t.ticker = time.NewTicker(t.dur)
	case RQuit:
		t.ticker.Stop()
		shutdown = true
	default:
		panic("unknown command!")
	}
	return shutdown
}
<commit_msg>rename trySend to send<commit_after>package common

import (
	"time"
)

/*
RepeatTimer repeatedly sends a struct{}{} to .Ch after each "dur" period.
It's good for keeping connections alive.
A RepeatTimer must be Stop()'d or it will keep a goroutine alive.
*/
type RepeatTimer struct {
	Name   string
	Ch     <-chan time.Time
	output chan<- time.Time
	input  chan repeatCommand

	dur     time.Duration
	ticker  *time.Ticker
	stopped bool
}

type repeatCommand int8

const (
	Reset repeatCommand = iota
	RQuit
)

func NewRepeatTimer(name string, dur time.Duration) *RepeatTimer {
	c := make(chan time.Time)
	var t = &RepeatTimer{
		Name:   name,
		Ch:     c,
		output: c,
		input:  make(chan repeatCommand),

		dur:    dur,
		ticker: time.NewTicker(dur),
	}
	go t.run()
	return t
}

// Wait the duration again before firing.
func (t *RepeatTimer) Reset() {
	t.input <- Reset
}

// For ease of .Stop()'ing services before .Start()'ing them,
// we ignore .Stop()'s on nil RepeatTimers.
func (t *RepeatTimer) Stop() bool {
	// use t.stopped to gracefully handle many Stop() without blocking
	if t == nil || t.stopped {
		return false
	}
	t.input <- RQuit
	t.stopped = true
	return true
}

func (t *RepeatTimer) run() {
	done := false
	for !done {
		select {
		case cmd := <-t.input:
			// stop goroutine if the input says so
			// don't close channels, as closed channels mess up select reads
			done = t.processInput(cmd)
		case tick := <-t.ticker.C:
			t.send(tick)
		}
	}
}

// send performs blocking send on t.Ch
func (t *RepeatTimer) send(tick time.Time) {
	// XXX: possibly it is better to not block:
	// https://golang.org/src/time/sleep.go#L132
	// select {
	// case t.output <- tick:
	// default:
	// }
	t.output <- tick
}

// all modifications of the internal state of ThrottleTimer
// happen in this method. It is only called from the run goroutine
// so we avoid any race conditions
func (t *RepeatTimer) processInput(cmd repeatCommand) (shutdown bool) {
	switch cmd {
	case Reset:
		t.ticker.Stop()
		t.ticker = time.NewTicker(t.dur)
	case RQuit:
		t.ticker.Stop()
		shutdown = true
	default:
		panic("unknown command!")
	}
	return shutdown
}
<|endoftext|>


<commit_before><commit_msg>Added pak name specification possiblity to spec sub-command<commit_after><|endoftext|>


<commit_before><commit_msg>Switching uuid lib to pborman instead of satori.<commit_after><|endoftext|>


<commit_before><commit_msg>omitempty correction<commit_after><|endoftext|>


<commit_before><commit_msg>redis driver<commit_after><|endoftext|>


<commit_before>// Copyright 2016 The Cockroach Authors.
//
// 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.
//
// Author: Matt Jibson (mjibson@cockroachlabs.com)

package acceptance

import (
	"strings"
	"testing"
)

func TestDockerJava(t *testing.T) {
	testDockerSuccess(t, "java", []string{"/bin/sh", "-c", strings.Replace(java, "%v", "Int(2, 3)", 1)})
	testDockerFail(t, "java", []string{"/bin/sh", "-c", strings.Replace(java, "%v", `String(2, "a")`, 1)})
}

const java = `
set -e
cat > main.java << 'EOF'
import java.sql.*;

public class main {
	public static void main(String[] args) throws Exception {
		Class.forName("org.postgresql.Driver");

		String DB_URL = "jdbc:postgresql://";
		DB_URL += System.getenv("PGHOST") + ":" + System.getenv("PGPORT");
		DB_URL += "/?ssl=true";
		DB_URL += "&sslcert=" + System.getenv("PGSSLCERT");
		DB_URL += "&sslkey=key.pk8";
		DB_URL += "&sslrootcert=/certs/ca.crt";
		DB_URL += "&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory";
		Connection conn = DriverManager.getConnection(DB_URL);

		PreparedStatement stmt = conn.prepareStatement("CREATE DATABASE test");
		int res = stmt.executeUpdate();
		if (res != 0) {
		    throw new Exception("unexpected: CREATE DATABASE reports " + res + " rows changed, expecting 0");
		}

		stmt = conn.prepareStatement("CREATE TABLE test.f (x INT, ts TIMESTAMP)");
		res = stmt.executeUpdate();
		if (res != 0) {
		    throw new Exception("unexpected: CREATE TABLE reports " + res + " rows changed, expecting 0");
		}

		stmt = conn.prepareStatement("INSERT INTO test.f VALUES (42, timestamp '2015-05-07 18:20:00')");
		res = stmt.executeUpdate();
		if (res != 1) {
		    throw new Exception("unexpected: INSERT reports " + res + " rows changed, expecting 1");
		}

		stmt = conn.prepareStatement("SELECT * FROM test.f");
		ResultSet rs = stmt.executeQuery();
		rs.next();
		int a = rs.getInt(1);
		if (a != 42) {
		    throw new Exception("unexpected: SELECT can't find inserted value: read " + a + ", expecting 42");
		}
		Timestamp ts = rs.getTimestamp(2);
		String tsStr = rs.getString(2);
		if (!tsStr.equals("2015-05-07 18:20:00")) {
			throw new Exception("unexpected value for ts: "+tsStr);
		}
		stmt = conn.prepareStatement("DROP TABLE test.f");
		res = stmt.executeUpdate();
		if (res != 0) {
		    throw new Exception("unexpected: DROP TABLE reports " + res + " rows changed, expecting 0");
		}

		stmt = conn.prepareStatement("SELECT 1, 2 > ?, ?::int, ?::string, ?::string, ?::string, ?::string, ?::string");
		stmt.setInt(1, 3);
		stmt.set%v;

		stmt.setBoolean(3, true);
		stmt.setLong(4, -4L);
		stmt.setFloat(5, 5.31f);
		stmt.setDouble(6, -6.21d);
		stmt.setShort(7, (short)7);

		rs = stmt.executeQuery();
		rs.next();
		a = rs.getInt(1);
		boolean b = rs.getBoolean(2);
		int c = rs.getInt(3);
		String d = rs.getString(4);
		String e = rs.getString(5);
		String f = rs.getString(6);
		String g = rs.getString(7);
		String h = rs.getString(8);
		if (a != 1 || b != false || c != 3 || !d.equals("true") || !e.equals("-4") || !f.startsWith("5.3") || !g.startsWith("-6.2") || !h.equals("7")) {
			throw new Exception("unexpected");
		}
	}
}
EOF
# See: https://basildoncoder.com/blog/postgresql-jdbc-client-certificates.html
openssl pkcs8 -topk8 -inform PEM -outform DER -in /certs/node.key -out key.pk8 -nocrypt

export PATH=$PATH:/usr/lib/jvm/java-1.8-openjdk/bin
javac main.java
java -cp /postgres.jar:. main
`
<commit_msg>acceptance: minor java test refactor<commit_after>// Copyright 2016 The Cockroach Authors.
//
// 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.
//
// Author: Matt Jibson (mjibson@cockroachlabs.com)

package acceptance

import (
	"strings"
	"testing"
)

func TestDockerJava(t *testing.T) {
	testDockerSuccess(t, "java", []string{"/bin/sh", "-c", strings.Replace(java, "%v", "Int(2, 3)", 1)})
	testDockerFail(t, "java", []string{"/bin/sh", "-c", strings.Replace(java, "%v", `String(2, "a")`, 1)})
}

const java = `
set -e
cat > main.java << 'EOF'
import java.sql.*;

public class main {
	public static void main(String[] args) throws Exception {
		Class.forName("org.postgresql.Driver");

		String DB_URL = "jdbc:postgresql://";
		DB_URL += System.getenv("PGHOST") + ":" + System.getenv("PGPORT");
		DB_URL += "/?ssl=true";
		DB_URL += "&sslcert=" + System.getenv("PGSSLCERT");
		DB_URL += "&sslkey=key.pk8";
		DB_URL += "&sslrootcert=/certs/ca.crt";
		DB_URL += "&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory";
		Connection conn = DriverManager.getConnection(DB_URL);

		PreparedStatement stmt = conn.prepareStatement("CREATE DATABASE test");
		int res = stmt.executeUpdate();
		if (res != 0) {
		    throw new Exception("unexpected: CREATE DATABASE reports " + res + " rows changed, expecting 0");
		}

		stmt = conn.prepareStatement("CREATE TABLE test.f (x INT, ts TIMESTAMP)");
		res = stmt.executeUpdate();
		if (res != 0) {
		    throw new Exception("unexpected: CREATE TABLE reports " + res + " rows changed, expecting 0");
		}

		stmt = conn.prepareStatement("INSERT INTO test.f VALUES (42, timestamp '2015-05-07 18:20:00')");
		res = stmt.executeUpdate();
		if (res != 1) {
		    throw new Exception("unexpected: INSERT reports " + res + " rows changed, expecting 1");
		}

		stmt = conn.prepareStatement("SELECT * FROM test.f");
		ResultSet rs = stmt.executeQuery();
		rs.next();
		int a = rs.getInt(1);
		if (a != 42) {
		    throw new Exception("unexpected: SELECT can't find inserted value: read " + a + ", expecting 42");
		}
		String tsStr = rs.getTimestamp(2).toString();
		if (!tsStr.equals("2015-05-07 18:20:00.0")) {
			throw new Exception("unexpected value for ts: "+tsStr);
		}
		stmt = conn.prepareStatement("DROP TABLE test.f");
		res = stmt.executeUpdate();
		if (res != 0) {
		    throw new Exception("unexpected: DROP TABLE reports " + res + " rows changed, expecting 0");
		}

		stmt = conn.prepareStatement("SELECT 1, 2 > ?, ?::int, ?::string, ?::string, ?::string, ?::string, ?::string");
		stmt.setInt(1, 3);
		stmt.set%v;

		stmt.setBoolean(3, true);
		stmt.setLong(4, -4L);
		stmt.setFloat(5, 5.31f);
		stmt.setDouble(6, -6.21d);
		stmt.setShort(7, (short)7);

		rs = stmt.executeQuery();
		rs.next();
		a = rs.getInt(1);
		boolean b = rs.getBoolean(2);
		int c = rs.getInt(3);
		String d = rs.getString(4);
		String e = rs.getString(5);
		String f = rs.getString(6);
		String g = rs.getString(7);
		String h = rs.getString(8);
		if (a != 1 || b != false || c != 3 || !d.equals("true") || !e.equals("-4") || !f.startsWith("5.3") || !g.startsWith("-6.2") || !h.equals("7")) {
			throw new Exception("unexpected");
		}
	}
}
EOF
# See: https://basildoncoder.com/blog/postgresql-jdbc-client-certificates.html
openssl pkcs8 -topk8 -inform PEM -outform DER -in /certs/node.key -out key.pk8 -nocrypt

export PATH=$PATH:/usr/lib/jvm/java-1.8-openjdk/bin
javac main.java
java -cp /postgres.jar:. main
`
<|endoftext|>