<commit_before>package db

import (
	"errors"
	"github.com/garyburd/redigo/redis"
	"github.com/sad0vnikov/radish/config"
	"github.com/sad0vnikov/radish/logger"
	"github.com/sad0vnikov/radish/helpers"
	rd "github.com/sad0vnikov/radish/redis"
	"regexp"
	"strconv"
	"strings"
)

//Connections is an interface for objects storing Redis connections
type Connections interface {
	GetByName(serverName string, dbNum uint8) (redis.Conn, error)
	GetMaxDbNumsForServer(serverName string) (uint8, error)
	GetServerKeyspaceStat(serverName string) (map[string]rd.ServerKeyspaceStat, error)
	GetServerStat(serverName string) (rd.ServerStat, error)
}

//RedisConnections is a struct storing redis connection
type RedisConnections struct {
	pool *redis.Pool
}

//GetByName function returns redigo Redis connection instance by server name
func (connections RedisConnections) GetByName(serverName string, dbNum uint8) (redis.Conn, error) {
	server, prs := config.Get().Servers[serverName]
	if !prs {
		return nil, errors.New("no server with name " + serverName + " found")
	}

	if connections.pool == nil {
		connections.pool = &redis.Pool{
			MaxIdle: 3,
			Dial: func() (redis.Conn, error) {
				serverAddr := server.Host + ":" + strconv.Itoa(server.Port)
				logger.Info("connecting to Redis server " + serverAddr)
				conn, err := redis.Dial("tcp", serverAddr)
				logger.Info("connected to Redis server " + serverAddr)
				return conn, err
			},
		}
	}

	var c redis.Conn
	c = connections.pool.Get()
	c.Do("SELECT", dbNum)

	return c, nil
}

//GetMaxDbNumsForServer returns a maxium db number for given Redis server
func (connections RedisConnections) GetMaxDbNumsForServer(serverName string) (uint8, error) {
	conn, err := connections.GetByName(serverName, 0)
	if err != nil {
		return 0, err
	}

	r, err := conn.Do("CONFIG", "GET", "databases")
	cfgValues, err := redis.Strings(r, err)
	if err != nil {
		return 0, err
	}

	cnt, err := strconv.ParseUint(cfgValues[1], 10, 8)

	if err != nil {
		return 0, err
	}

	return uint8(cnt), nil
}

func (connections RedisConnections) GetServerStat(serverName string) (rd.ServerStat, error) {
	conn, err := connections.GetByName(serverName, 0)
	if err != nil {
		return rd.ServerStat{}, err
	}

	r, err := conn.Do("INFO")
	info, err := redis.String(r, err)
	if err != nil {
		return rd.ServerStat{}, err
	}

	c := parseInfoStrings(strings.Split(info, "\r\n"))

	maxMemory, err := connections.getServerConfigParam(serverName, "maxmemory")
	if err != nil {
		return rd.ServerStat{}, err
	}
	maxMemoryBytes, err := strconv.Atoi(maxMemory)
	c.MaxMemoryBytes =  int64(maxMemoryBytes) * 1024
	c.MaxMemoryHuman = helpers.SizeInBytesToHumanReadable(c.MaxMemoryBytes)
	return c, err
}

func (connections RedisConnections) GetServerKeyspaceStat(serverName string) (map[string]rd.ServerKeyspaceStat, error) {
	conn, err := connections.GetByName(serverName, 0)
	if err != nil {
		return nil, err
	}

	r, err := conn.Do("INFO", "keyspace")
	rs, err := redis.String(r, err)
	info := strings.Split(rs, "\r\n")

	databaseStatRowsCount := len(info) - 1
	stat := make(map[string]rd.ServerKeyspaceStat, databaseStatRowsCount)
	dbNum := 0
	for i := 1; i < len(info); i++ {
		dbName := "db" + strconv.Itoa(dbNum)
		statString := info[i]

		dbKeyspaceStat := parseKeyspaceStatString(statString)
		dbNum++

		stat[dbName] = dbKeyspaceStat
	}
	return stat, nil
}

func (connections RedisConnections) getServerConfigParam(serverName, paramName string)  (string, error) {
	conn, err := connections.GetByName(serverName, 0)
	if err != nil {
		return "", err
	}

	r, err := conn.Do("CONFIG", "GET", paramName)
	rs, err := redis.Strings(r, err)
	return rs[1], err
}

func parseKeyspaceStatString(statString string) rd.ServerKeyspaceStat {
	parseKeyspaceInfoRegexp := "[,:]?([a-z_]+=[a-z0-9]+)"
	r := regexp.MustCompile(parseKeyspaceInfoRegexp)
	stats := r.FindAllString(statString, -1)
	dbKeyspaceStat := rd.ServerKeyspaceStat{}
	for _, statString := range stats {
		splittedStat := strings.Split(statString, "=")
		statName := splittedStat[0][1:]
		statValue := splittedStat[1]
		switch statName {
		case "keys":
			intValue, _ := strconv.ParseInt(statValue, 10, 64)
			dbKeyspaceStat.KeysCount = intValue
		}
	}
	return dbKeyspaceStat
}

func parseInfoStrings(info []string) rd.ServerStat {
	c := rd.ServerStat{}

	for _, s := range info {
		if !strings.Contains(s, ":") {
			continue
		}

		splittedRow := strings.Split(s, ":")
		statName := splittedRow[0]
		statValue := splittedRow[1]
		intValue, _ := strconv.ParseInt(statValue, 10, 64)
		switch statName {
		case "used_memory_human":
			c.UsedMemoryHuman = statValue
		case "used_memory":
			c.UsedMemoryBytes = intValue
		case "redis_version":
			c.RedisVersion = statValue
		case "uptime_in_seconds":
			c.UptimeInSeconds = intValue
		case "connected_clients":
			c.ConnectedClientsCount = intValue
		}
	}
	return c
}

//MockedConnections is a struct storing mocked redis connections
type MockedConnections struct {
	ConnectionMock redis.Conn
}

//GetByName returns mocked connection
func (connections MockedConnections) GetByName(serverName string, dbNum uint8) (redis.Conn, error) {
	if connections.ConnectionMock == nil {
		return nil, errors.New("mock is not set, put your connection mock at ConectionMock struct field")
	}
	return connections.ConnectionMock, nil
}

//GetMaxDbNumsForServer returns max db nums for a mocked connection
func (connections MockedConnections) GetMaxDbNumsForServer(serverName string) (uint8, error) {
	if connections.ConnectionMock == nil {
		return 0, errors.New("mock is not set, put your connection mock at ConectionMock struct field")
	}
	return uint8(8), nil
}

func (connections MockedConnections) GetServerStat(serverName string) (rd.ServerStat, error) {
	return rd.ServerStat{}, nil
}

func (connections MockedConnections) GetServerKeyspaceStat(serverName string) (map[string]rd.ServerKeyspaceStat, error) {
	return map[string]rd.ServerKeyspaceStat{}, nil
}

//GetServersWithConnectionData returns servers list from config with connection data like databases count, connection status, etc.
func GetServersWithConnectionData() map[string]rd.Server {
	configServers := config.Get().Servers
	result := make(map[string]rd.Server)
	for _, srv := range configServers {
		dbsCount, err := GetMaxDbNumsForServer(srv.Name)
		if err == nil {
			srv.ConnectionCheckPassed = true
		}
		srv.DatabasesCount = dbsCount
		if srv.ConnectionCheckPassed {
			serverStat, err := connector.GetServerStat(srv.Name)
			if err != nil {
				logger.Error(err)
			}
			keyspaceStat, err := connector.GetServerKeyspaceStat(srv.Name)
			if err != nil {
				logger.Error(err)
			}
			srv.ServerStat = serverStat
			srv.KeyspaceStat = keyspaceStat
		}

		result[srv.Name] = srv
	}

	return result
}
<commit_msg>* getting db stat<commit_after>package db

import (
	"errors"
	"github.com/garyburd/redigo/redis"
	"github.com/sad0vnikov/radish/config"
	"github.com/sad0vnikov/radish/logger"
	"github.com/sad0vnikov/radish/helpers"
	rd "github.com/sad0vnikov/radish/redis"
	"regexp"
	"strconv"
	"strings"
)

//Connections is an interface for objects storing Redis connections
type Connections interface {
	GetByName(serverName string, dbNum uint8) (redis.Conn, error)
	GetMaxDbNumsForServer(serverName string) (uint8, error)
	GetServerKeyspaceStat(serverName string) (map[string]rd.ServerKeyspaceStat, error)
	GetServerStat(serverName string) (rd.ServerStat, error)
}

//RedisConnections is a struct storing redis connection
type RedisConnections struct {
	pool *redis.Pool
}

//GetByName function returns redigo Redis connection instance by server name
func (connections RedisConnections) GetByName(serverName string, dbNum uint8) (redis.Conn, error) {
	server, prs := config.Get().Servers[serverName]
	if !prs {
		return nil, errors.New("no server with name " + serverName + " found")
	}

	if connections.pool == nil {
		connections.pool = &redis.Pool{
			MaxIdle: 3,
			Dial: func() (redis.Conn, error) {
				serverAddr := server.Host + ":" + strconv.Itoa(server.Port)
				logger.Info("connecting to Redis server " + serverAddr)
				conn, err := redis.Dial("tcp", serverAddr)
				logger.Info("connected to Redis server " + serverAddr)
				return conn, err
			},
		}
	}

	var c redis.Conn
	c = connections.pool.Get()
	c.Do("SELECT", dbNum)

	return c, nil
}

//GetMaxDbNumsForServer returns a maxium db number for given Redis server
func (connections RedisConnections) GetMaxDbNumsForServer(serverName string) (uint8, error) {
	conn, err := connections.GetByName(serverName, 0)
	if err != nil {
		return 0, err
	}

	r, err := conn.Do("CONFIG", "GET", "databases")
	cfgValues, err := redis.Strings(r, err)
	if err != nil {
		return 0, err
	}

	cnt, err := strconv.ParseUint(cfgValues[1], 10, 8)

	if err != nil {
		return 0, err
	}

	return uint8(cnt), nil
}

func (connections RedisConnections) GetServerStat(serverName string) (rd.ServerStat, error) {
	conn, err := connections.GetByName(serverName, 0)
	if err != nil {
		return rd.ServerStat{}, err
	}

	r, err := conn.Do("INFO")
	info, err := redis.String(r, err)
	if err != nil {
		return rd.ServerStat{}, err
	}

	c := parseInfoStrings(strings.Split(info, "\r\n"))

	maxMemory, err := connections.getServerConfigParam(serverName, "maxmemory")
	if err != nil {
		return rd.ServerStat{}, err
	}
	maxMemoryBytes, err := strconv.Atoi(maxMemory)
	c.MaxMemoryBytes =  int64(maxMemoryBytes) * 1024
	c.MaxMemoryHuman = helpers.SizeInBytesToHumanReadable(c.MaxMemoryBytes)
	return c, err
}

func (connections RedisConnections) GetServerKeyspaceStat(serverName string) (map[string]rd.ServerKeyspaceStat, error) {
	conn, err := connections.GetByName(serverName, 0)
	if err != nil {
		return nil, err
	}

	r, err := conn.Do("INFO", "keyspace")
	rs, err := redis.String(r, err)
	info := strings.Split(rs, "\r\n")

	databaseStatRowsCount := len(info) - 1
	stat := make(map[string]rd.ServerKeyspaceStat, databaseStatRowsCount)
	dbNum := 0
	for i := 1; i < len(info); i++ {
		dbName := "db" + strconv.Itoa(dbNum)
		statString := info[i]

		if len(statString) > 0 {
			dbKeyspaceStat := parseKeyspaceStatString(statString)
			dbNum++

			stat[dbName] = dbKeyspaceStat
		}

	}
	return stat, nil
}

func (connections RedisConnections) getServerConfigParam(serverName, paramName string)  (string, error) {
	conn, err := connections.GetByName(serverName, 0)
	if err != nil {
		return "", err
	}

	r, err := conn.Do("CONFIG", "GET", paramName)
	rs, err := redis.Strings(r, err)
	return rs[1], err
}

func parseKeyspaceStatString(statString string) rd.ServerKeyspaceStat {
	parseKeyspaceInfoRegexp := "[,:]?([a-z_]+=[a-z0-9]+)"
	r := regexp.MustCompile(parseKeyspaceInfoRegexp)
	stats := r.FindAllString(statString, -1)
	dbKeyspaceStat := rd.ServerKeyspaceStat{}
	for _, statString := range stats {
		splittedStat := strings.Split(statString, "=")
		statName := splittedStat[0][1:]
		statValue := splittedStat[1]
		switch statName {
		case "keys":
			intValue, _ := strconv.ParseInt(statValue, 10, 64)
			dbKeyspaceStat.KeysCount = intValue
		}
	}
	return dbKeyspaceStat
}

func parseInfoStrings(info []string) rd.ServerStat {
	c := rd.ServerStat{}

	for _, s := range info {
		if !strings.Contains(s, ":") {
			continue
		}

		splittedRow := strings.Split(s, ":")
		statName := splittedRow[0]
		statValue := splittedRow[1]
		intValue, _ := strconv.ParseInt(statValue, 10, 64)
		switch statName {
		case "used_memory_human":
			c.UsedMemoryHuman = statValue
		case "used_memory":
			c.UsedMemoryBytes = intValue
		case "redis_version":
			c.RedisVersion = statValue
		case "uptime_in_seconds":
			c.UptimeInSeconds = intValue
		case "connected_clients":
			c.ConnectedClientsCount = intValue
		}
	}
	return c
}

//MockedConnections is a struct storing mocked redis connections
type MockedConnections struct {
	ConnectionMock redis.Conn
}

//GetByName returns mocked connection
func (connections MockedConnections) GetByName(serverName string, dbNum uint8) (redis.Conn, error) {
	if connections.ConnectionMock == nil {
		return nil, errors.New("mock is not set, put your connection mock at ConectionMock struct field")
	}
	return connections.ConnectionMock, nil
}

//GetMaxDbNumsForServer returns max db nums for a mocked connection
func (connections MockedConnections) GetMaxDbNumsForServer(serverName string) (uint8, error) {
	if connections.ConnectionMock == nil {
		return 0, errors.New("mock is not set, put your connection mock at ConectionMock struct field")
	}
	return uint8(8), nil
}

func (connections MockedConnections) GetServerStat(serverName string) (rd.ServerStat, error) {
	return rd.ServerStat{}, nil
}

func (connections MockedConnections) GetServerKeyspaceStat(serverName string) (map[string]rd.ServerKeyspaceStat, error) {
	return map[string]rd.ServerKeyspaceStat{}, nil
}

//GetServersWithConnectionData returns servers list from config with connection data like databases count, connection status, etc.
func GetServersWithConnectionData() map[string]rd.Server {
	configServers := config.Get().Servers
	result := make(map[string]rd.Server)
	for _, srv := range configServers {
		dbsCount, err := GetMaxDbNumsForServer(srv.Name)
		if err == nil {
			srv.ConnectionCheckPassed = true
		}
		srv.DatabasesCount = dbsCount
		if srv.ConnectionCheckPassed {
			serverStat, err := connector.GetServerStat(srv.Name)
			if err != nil {
				logger.Error(err)
			}
			keyspaceStat, err := connector.GetServerKeyspaceStat(srv.Name)
			if err != nil {
				logger.Error(err)
			}
			srv.ServerStat = serverStat
			srv.KeyspaceStat = keyspaceStat
		}

		result[srv.Name] = srv
	}

	return result
}
<|endoftext|>


<commit_before>// Copyright 2015 The Prometheus 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.

package relabel

import (
	"reflect"
	"testing"

	"github.com/prometheus/common/model"

	"github.com/prometheus/prometheus/config"
)

func TestRelabel(t *testing.T) {
	tests := []struct {
		input   model.LabelSet
		relabel []*config.RelabelConfig
		output  model.LabelSet
	}{
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "ch${1}-ch${1}",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
				"d": "choo-choo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a", "b"},
					Regex:        config.MustNewRegexp("f(.*);(.*)r"),
					TargetLabel:  model.LabelName("a"),
					Separator:    ";",
					Replacement:  "b${1}${2}m", // boobam
					Action:       config.RelabelReplace,
				},
				{
					SourceLabels: model.LabelNames{"c", "a"},
					Regex:        config.MustNewRegexp("(b).*b(.*)ba(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "$1$2$2$3",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "boobam",
				"b": "bar",
				"c": "baz",
				"d": "boooom",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*o.*"),
					Action:       config.RelabelDrop,
				}, {
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "ch$1-ch$1",
					Action:       config.RelabelReplace,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*o.*"),
					Action:       config.RelabelDrop,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "abc",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*(b).*"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "$1",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "abc",
				"d": "b",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("no-match"),
					Action:       config.RelabelDrop,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f|o"),
					Action:       config.RelabelDrop,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("no-match"),
					Action:       config.RelabelKeep,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f.*"),
					Action:       config.RelabelKeep,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			// No replacement must be applied if there is no match.
			input: model.LabelSet{
				"a": "boo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f"),
					TargetLabel:  model.LabelName("b"),
					Replacement:  "bar",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "boo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"c"},
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Action:       config.RelabelHashMod,
					Modulus:      1000,
				},
			},
			output: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
				"d": "976",
			},
		},
		{
			input: model.LabelSet{
				"a":  "foo",
				"b1": "bar",
				"b2": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					Regex:       config.MustNewRegexp("(b.*)"),
					Replacement: "bar_${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"a":      "foo",
				"b1":     "bar",
				"b2":     "baz",
				"bar_b1": "bar",
				"bar_b2": "baz",
			},
		},
		{
			input: model.LabelSet{
				"a":             "foo",
				"__meta_my_bar": "aaa",
				"__meta_my_baz": "bbb",
				"__meta_other":  "ccc",
			},
			relabel: []*config.RelabelConfig{
				{
					Regex:       config.MustNewRegexp("__meta_(my.*)"),
					Replacement: "${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"a":             "foo",
				"__meta_my_bar": "aaa",
				"__meta_my_baz": "bbb",
				"__meta_other":  "ccc",
				"my_bar":        "aaa",
				"my_baz":        "bbb",
			},
		},
		{ // valid case
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${2}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"a":    "some-name-value",
				"name": "value",
			},
		},
		{ // invalid replacement ""
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${3}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"a": "some-name-value",
			},
		},
		{ // invalid target_labels
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("${3}"),
				},
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("0${3}"),
				},
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("-${3}"),
				},
			},
			output: model.LabelSet{
				"a": "some-name-value",
			},
		},
		{ // more complex real-life like usecase
			input: model.LabelSet{
				"__meta_sd_tags": "path:/secret,job:some-job,label:foo=bar",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)path:(/[^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("__metrics_path__"),
				},
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)job:([^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("job"),
				},
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp(".*?(?:,|^)label:([^=]+)=([^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${2}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"__meta_sd_tags":   "path:/secret,job:some-job,label:foo=bar",
				"__metrics_path__": "/secret",
				"job":              "some-job",
				"foo":              "bar",
			},
		},
	}

	for i, test := range tests {
		res := Process(test.input, test.relabel...)

		if !reflect.DeepEqual(res, test.output) {
			t.Errorf("Test %d: relabel output mismatch: expected %#v, got %#v", i+1, test.output, res)
		}
	}
}
<commit_msg>simplify regex<commit_after>// Copyright 2015 The Prometheus 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.

package relabel

import (
	"reflect"
	"testing"

	"github.com/prometheus/common/model"

	"github.com/prometheus/prometheus/config"
)

func TestRelabel(t *testing.T) {
	tests := []struct {
		input   model.LabelSet
		relabel []*config.RelabelConfig
		output  model.LabelSet
	}{
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "ch${1}-ch${1}",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
				"d": "choo-choo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a", "b"},
					Regex:        config.MustNewRegexp("f(.*);(.*)r"),
					TargetLabel:  model.LabelName("a"),
					Separator:    ";",
					Replacement:  "b${1}${2}m", // boobam
					Action:       config.RelabelReplace,
				},
				{
					SourceLabels: model.LabelNames{"c", "a"},
					Regex:        config.MustNewRegexp("(b).*b(.*)ba(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "$1$2$2$3",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "boobam",
				"b": "bar",
				"c": "baz",
				"d": "boooom",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*o.*"),
					Action:       config.RelabelDrop,
				}, {
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f(.*)"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "ch$1-ch$1",
					Action:       config.RelabelReplace,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*o.*"),
					Action:       config.RelabelDrop,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "abc",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp(".*(b).*"),
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Replacement:  "$1",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "abc",
				"d": "b",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("no-match"),
					Action:       config.RelabelDrop,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f|o"),
					Action:       config.RelabelDrop,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("no-match"),
					Action:       config.RelabelKeep,
				},
			},
			output: nil,
		},
		{
			input: model.LabelSet{
				"a": "foo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f.*"),
					Action:       config.RelabelKeep,
				},
			},
			output: model.LabelSet{
				"a": "foo",
			},
		},
		{
			// No replacement must be applied if there is no match.
			input: model.LabelSet{
				"a": "boo",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("f"),
					TargetLabel:  model.LabelName("b"),
					Replacement:  "bar",
					Action:       config.RelabelReplace,
				},
			},
			output: model.LabelSet{
				"a": "boo",
			},
		},
		{
			input: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"c"},
					TargetLabel:  model.LabelName("d"),
					Separator:    ";",
					Action:       config.RelabelHashMod,
					Modulus:      1000,
				},
			},
			output: model.LabelSet{
				"a": "foo",
				"b": "bar",
				"c": "baz",
				"d": "976",
			},
		},
		{
			input: model.LabelSet{
				"a":  "foo",
				"b1": "bar",
				"b2": "baz",
			},
			relabel: []*config.RelabelConfig{
				{
					Regex:       config.MustNewRegexp("(b.*)"),
					Replacement: "bar_${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"a":      "foo",
				"b1":     "bar",
				"b2":     "baz",
				"bar_b1": "bar",
				"bar_b2": "baz",
			},
		},
		{
			input: model.LabelSet{
				"a":             "foo",
				"__meta_my_bar": "aaa",
				"__meta_my_baz": "bbb",
				"__meta_other":  "ccc",
			},
			relabel: []*config.RelabelConfig{
				{
					Regex:       config.MustNewRegexp("__meta_(my.*)"),
					Replacement: "${1}",
					Action:      config.RelabelLabelMap,
				},
			},
			output: model.LabelSet{
				"a":             "foo",
				"__meta_my_bar": "aaa",
				"__meta_my_baz": "bbb",
				"__meta_other":  "ccc",
				"my_bar":        "aaa",
				"my_baz":        "bbb",
			},
		},
		{ // valid case
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${2}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"a":    "some-name-value",
				"name": "value",
			},
		},
		{ // invalid replacement ""
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${3}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"a": "some-name-value",
			},
		},
		{ // invalid target_labels
			input: model.LabelSet{
				"a": "some-name-value",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("${3}"),
				},
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("0${3}"),
				},
				{
					SourceLabels: model.LabelNames{"a"},
					Regex:        config.MustNewRegexp("some-([^-]+)-([^,]+)"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("-${3}"),
				},
			},
			output: model.LabelSet{
				"a": "some-name-value",
			},
		},
		{ // more complex real-life like usecase
			input: model.LabelSet{
				"__meta_sd_tags": "path:/secret,job:some-job,label:foo=bar",
			},
			relabel: []*config.RelabelConfig{
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp("(?:.+,|^)path:(/[^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("__metrics_path__"),
				},
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp("(?:.+,|^)job:([^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${1}",
					TargetLabel:  model.LabelName("job"),
				},
				{
					SourceLabels: model.LabelNames{"__meta_sd_tags"},
					Regex:        config.MustNewRegexp("(?:.+,|^)label:([^=]+)=([^,]+).*"),
					Action:       config.RelabelReplace,
					Replacement:  "${2}",
					TargetLabel:  model.LabelName("${1}"),
				},
			},
			output: model.LabelSet{
				"__meta_sd_tags":   "path:/secret,job:some-job,label:foo=bar",
				"__metrics_path__": "/secret",
				"job":              "some-job",
				"foo":              "bar",
			},
		},
	}

	for i, test := range tests {
		res := Process(test.input, test.relabel...)

		if !reflect.DeepEqual(res, test.output) {
			t.Errorf("Test %d: relabel output mismatch: expected %#v, got %#v", i+1, test.output, res)
		}
	}
}
<|endoftext|>


<commit_before>package releaseman

import (
	"bytes"
	"fmt"
	"text/template"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/go-utils/fileutil"
	"github.com/bitrise-tools/releaseman/git"
	version "github.com/hashicorp/go-version"
)

//=======================================
// Consts
//=======================================

// ChangelogTemplate ...
const ChangelogTemplate = `
{{range .Sections}}### {{.EndTaggedCommit.Tag}} ({{.EndTaggedCommit.Date.Format "2006 Jan 02"}})
---
{{range $idx, $commit := .Commits}} * {{$commit.Message}}{{ "\n" }}{{end}}
{{end}}`

//=======================================
// Models
//=======================================

// ChangelogSectionModel ...
type ChangelogSectionModel struct {
	StartTaggedCommit git.CommitModel
	EndTaggedCommit   git.CommitModel
	Commits           []git.CommitModel
}

// ChangelogModel ...
type ChangelogModel struct {
	Version  string
	Sections []ChangelogSectionModel
}

//=======================================
// Utility
//=======================================

func commitsBetween(startDate *time.Time, endDate *time.Time, commits []git.CommitModel) []git.CommitModel {
	relevantCommits := []git.CommitModel{}
	isRelevantCommit := false

	for _, commit := range commits {
		if !isRelevantCommit && (startDate == nil || (*startDate).Sub(commit.Date) <= 0) {
			isRelevantCommit = true
		}

		if isRelevantCommit && endDate != nil && (*endDate).Sub(commit.Date) <= 0 {
			return relevantCommits
		}

		if isRelevantCommit {
			relevantCommits = append(relevantCommits, commit)
		}
	}

	return relevantCommits
}

func reversedSections(sections []ChangelogSectionModel) []ChangelogSectionModel {
	reversed := []ChangelogSectionModel{}
	for i := len(sections) - 1; i >= 0; i-- {
		reversed = append(reversed, sections[i])
	}
	return reversed
}

func generateChangelog(commits, taggedCommits []git.CommitModel, version string) ChangelogModel {
	changelog := ChangelogModel{
		Version:  version,
		Sections: []ChangelogSectionModel{},
	}

	if len(taggedCommits) > 0 {
		if len(taggedCommits) > 1 {
			// Commits between tags
			for i := 0; i < len(taggedCommits)-1; i++ {
				startTaggedCommit := taggedCommits[i]
				endTaggedCommit := taggedCommits[i+1]

				relevantCommits := commitsBetween(&(startTaggedCommit.Date), &(endTaggedCommit.Date), commits)

				section := ChangelogSectionModel{
					StartTaggedCommit: startTaggedCommit,
					EndTaggedCommit:   endTaggedCommit,
					Commits:           relevantCommits,
				}
				changelog.Sections = append(changelog.Sections, section)
			}
		}

		// Commits between last tag and current state
		relevantCommits := commitsBetween(&(taggedCommits[len(taggedCommits)-1].Date), nil, commits)

		section := ChangelogSectionModel{
			StartTaggedCommit: taggedCommits[len(taggedCommits)-1],
			EndTaggedCommit: git.CommitModel{
				Tag:  version,
				Date: time.Now(),
			},
			Commits: relevantCommits,
		}
		changelog.Sections = append(changelog.Sections, section)
	} else {
		relevantCommits := commitsBetween(nil, nil, commits)

		section := ChangelogSectionModel{
			StartTaggedCommit: git.CommitModel{},
			EndTaggedCommit: git.CommitModel{
				Tag:  version,
				Date: time.Now(),
			},
			Commits: relevantCommits,
		}
		changelog.Sections = append(changelog.Sections, section)
	}

	changelog.Sections = reversedSections(changelog.Sections)

	return changelog
}

//=======================================
// Main
//=======================================

// BumpedVersion ...
func BumpedVersion(versionStr string, segmentIdx int) (string, error) {
	version, err := version.NewVersion(versionStr)
	if err != nil {
		return "", err
	}
	if len(version.Segments()) < segmentIdx-1 {
		return "", fmt.Errorf("Version segments length (%d), increment segemnt at idx (%d)", len(version.Segments()), segmentIdx)
	}
	version.Segments()[segmentIdx] = version.Segments()[segmentIdx] + 1
	return version.String(), nil
}

// WriteChangelog ...
func WriteChangelog(commits, taggedCommits []git.CommitModel, config Config, append bool) error {
	changelog := generateChangelog(commits, taggedCommits, config.Release.Version)
	log.Debugf("Changelog: %#v", changelog)

	changelogItemTemplateStr := ChangelogTemplate
	if config.Changelog.ItemTemplate != "" {
		changelogItemTemplateStr = config.Changelog.ItemTemplate
	}

	tmpl, err := template.New("changelog").Parse(changelogItemTemplateStr)
	if err != nil {
		log.Fatalf("Failed to parse template, error: %#v", err)
	}

	var changelogBytes bytes.Buffer
	err = tmpl.Execute(&changelogBytes, changelog)
	if err != nil {
		log.Fatalf("Failed to execute template, error: %#v", err)
	}
	changelogStr := changelogBytes.String()

	if append {
		log.Debugln("Changelog exist, append new version")
		previosChangelogStr, err := fileutil.ReadStringFromFile(config.Changelog.Path)
		if err != nil {
			return err
		}

		changelogStr = changelogStr + previosChangelogStr
	}

	return fileutil.WriteStringToFile(config.Changelog.Path, changelogStr)
}
<commit_msg>Reverse collected commits order<commit_after>package releaseman

import (
	"bytes"
	"fmt"
	"text/template"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/go-utils/fileutil"
	"github.com/bitrise-tools/releaseman/git"
	version "github.com/hashicorp/go-version"
)

//=======================================
// Consts
//=======================================

// ChangelogTemplate ...
const ChangelogTemplate = `
{{range .Sections}}### {{.EndTaggedCommit.Tag}} ({{.EndTaggedCommit.Date.Format "2006 Jan 02"}})
---
{{range $idx, $commit := .Commits}} * {{$commit.Message}}{{ "\n" }}{{end}}
{{end}}`

//=======================================
// Models
//=======================================

// ChangelogSectionModel ...
type ChangelogSectionModel struct {
	StartTaggedCommit git.CommitModel
	EndTaggedCommit   git.CommitModel
	Commits           []git.CommitModel
}

// ChangelogModel ...
type ChangelogModel struct {
	Version  string
	Sections []ChangelogSectionModel
}

//=======================================
// Utility
//=======================================

func commitsBetween(startDate *time.Time, endDate *time.Time, commits []git.CommitModel) []git.CommitModel {
	relevantCommits := []git.CommitModel{}
	isRelevantCommit := false

	for _, commit := range commits {
		if !isRelevantCommit && (startDate == nil || (*startDate).Sub(commit.Date) <= 0) {
			isRelevantCommit = true
		}

		if isRelevantCommit && endDate != nil && (*endDate).Sub(commit.Date) <= 0 {
			return relevantCommits
		}

		if isRelevantCommit {
			relevantCommits = append([]git.CommitModel{commit}, relevantCommits...)
		}
	}

	return relevantCommits
}

func reversedSections(sections []ChangelogSectionModel) []ChangelogSectionModel {
	reversed := []ChangelogSectionModel{}
	for i := len(sections) - 1; i >= 0; i-- {
		reversed = append(reversed, sections[i])
	}
	return reversed
}

func generateChangelog(commits, taggedCommits []git.CommitModel, version string) ChangelogModel {
	changelog := ChangelogModel{
		Version:  version,
		Sections: []ChangelogSectionModel{},
	}

	if len(taggedCommits) > 0 {
		if len(taggedCommits) > 1 {
			// Commits between tags
			for i := 0; i < len(taggedCommits)-1; i++ {
				startTaggedCommit := taggedCommits[i]
				endTaggedCommit := taggedCommits[i+1]

				relevantCommits := commitsBetween(&(startTaggedCommit.Date), &(endTaggedCommit.Date), commits)

				section := ChangelogSectionModel{
					StartTaggedCommit: startTaggedCommit,
					EndTaggedCommit:   endTaggedCommit,
					Commits:           relevantCommits,
				}
				changelog.Sections = append(changelog.Sections, section)
			}
		}

		// Commits between last tag and current state
		relevantCommits := commitsBetween(&(taggedCommits[len(taggedCommits)-1].Date), nil, commits)

		section := ChangelogSectionModel{
			StartTaggedCommit: taggedCommits[len(taggedCommits)-1],
			EndTaggedCommit: git.CommitModel{
				Tag:  version,
				Date: time.Now(),
			},
			Commits: relevantCommits,
		}
		changelog.Sections = append(changelog.Sections, section)
	} else {
		relevantCommits := commitsBetween(nil, nil, commits)

		section := ChangelogSectionModel{
			StartTaggedCommit: git.CommitModel{},
			EndTaggedCommit: git.CommitModel{
				Tag:  version,
				Date: time.Now(),
			},
			Commits: relevantCommits,
		}
		changelog.Sections = append(changelog.Sections, section)
	}

	changelog.Sections = reversedSections(changelog.Sections)

	return changelog
}

//=======================================
// Main
//=======================================

// BumpedVersion ...
func BumpedVersion(versionStr string, segmentIdx int) (string, error) {
	version, err := version.NewVersion(versionStr)
	if err != nil {
		return "", err
	}
	if len(version.Segments()) < segmentIdx-1 {
		return "", fmt.Errorf("Version segments length (%d), increment segemnt at idx (%d)", len(version.Segments()), segmentIdx)
	}
	version.Segments()[segmentIdx] = version.Segments()[segmentIdx] + 1
	return version.String(), nil
}

// WriteChangelog ...
func WriteChangelog(commits, taggedCommits []git.CommitModel, config Config, append bool) error {
	changelog := generateChangelog(commits, taggedCommits, config.Release.Version)
	log.Debugf("Changelog: %#v", changelog)

	changelogItemTemplateStr := ChangelogTemplate
	if config.Changelog.ItemTemplate != "" {
		changelogItemTemplateStr = config.Changelog.ItemTemplate
	}

	tmpl, err := template.New("changelog").Parse(changelogItemTemplateStr)
	if err != nil {
		log.Fatalf("Failed to parse template, error: %#v", err)
	}

	var changelogBytes bytes.Buffer
	err = tmpl.Execute(&changelogBytes, changelog)
	if err != nil {
		log.Fatalf("Failed to execute template, error: %#v", err)
	}
	changelogStr := changelogBytes.String()

	if append {
		log.Debugln("Changelog exist, append new version")
		previosChangelogStr, err := fileutil.ReadStringFromFile(config.Changelog.Path)
		if err != nil {
			return err
		}

		changelogStr = changelogStr + previosChangelogStr
	}

	return fileutil.WriteStringToFile(config.Changelog.Path, changelogStr)
}
<|endoftext|>


<commit_before>package kubernetes

import (
	gversion "github.com/hashicorp/go-version"
	"github.com/hashicorp/terraform/helper/schema"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/version"
)

// Flatteners

func flattenServicePort(in []v1.ServicePort) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, n := range in {
		m := make(map[string]interface{})
		m["name"] = n.Name
		m["protocol"] = string(n.Protocol)
		m["port"] = int(n.Port)
		m["target_port"] = n.TargetPort.String()
		m["node_port"] = int(n.NodePort)

		att[i] = m
	}
	return att
}

func flattenServiceSpec(in v1.ServiceSpec) []interface{} {
	att := make(map[string]interface{})
	if len(in.Ports) > 0 {
		att["port"] = flattenServicePort(in.Ports)
	}
	if len(in.Selector) > 0 {
		att["selector"] = in.Selector
	}
	if in.ClusterIP != "" {
		att["cluster_ip"] = in.ClusterIP
	}
	if in.Type != "" {
		att["type"] = string(in.Type)
	}
	if len(in.ExternalIPs) > 0 {
		att["external_ips"] = newStringSet(schema.HashString, in.ExternalIPs)
	}
	if in.SessionAffinity != "" {
		att["session_affinity"] = string(in.SessionAffinity)
	}
	if in.LoadBalancerIP != "" {
		att["load_balancer_ip"] = in.LoadBalancerIP
	}
	if len(in.LoadBalancerSourceRanges) > 0 {
		att["load_balancer_source_ranges"] = newStringSet(schema.HashString, in.LoadBalancerSourceRanges)
	}
	if in.ExternalName != "" {
		att["external_name"] = in.ExternalName
	}
	att["publish_not_ready_addresses"] = in.PublishNotReadyAddresses

	return []interface{}{att}
}

func flattenLoadBalancerIngress(in []v1.LoadBalancerIngress) []interface{} {
	out := make([]interface{}, len(in), len(in))
	for i, ingress := range in {
		att := make(map[string]interface{})

		att["ip"] = ingress.IP
		att["hostname"] = ingress.Hostname

		out[i] = att
	}
	return out
}

// Expanders

func expandServicePort(l []interface{}) []v1.ServicePort {
	if len(l) == 0 || l[0] == nil {
		return []v1.ServicePort{}
	}
	obj := make([]v1.ServicePort, len(l), len(l))
	for i, n := range l {
		cfg := n.(map[string]interface{})
		obj[i] = v1.ServicePort{
			Port:       int32(cfg["port"].(int)),
			TargetPort: intstr.Parse(cfg["target_port"].(string)),
		}
		if v, ok := cfg["name"].(string); ok {
			obj[i].Name = v
		}
		if v, ok := cfg["protocol"].(string); ok {
			obj[i].Protocol = v1.Protocol(v)
		}
		if v, ok := cfg["node_port"].(int); ok {
			obj[i].NodePort = int32(v)
		}
	}
	return obj
}

func expandServiceSpec(l []interface{}) v1.ServiceSpec {
	if len(l) == 0 || l[0] == nil {
		return v1.ServiceSpec{}
	}
	in := l[0].(map[string]interface{})
	obj := v1.ServiceSpec{}

	if v, ok := in["port"].([]interface{}); ok && len(v) > 0 {
		obj.Ports = expandServicePort(v)
	}
	if v, ok := in["selector"].(map[string]interface{}); ok && len(v) > 0 {
		obj.Selector = expandStringMap(v)
	}
	if v, ok := in["cluster_ip"].(string); ok {
		obj.ClusterIP = v
	}
	if v, ok := in["type"].(string); ok {
		obj.Type = v1.ServiceType(v)
	}
	if v, ok := in["external_ips"].(*schema.Set); ok && v.Len() > 0 {
		obj.ExternalIPs = sliceOfString(v.List())
	}
	if v, ok := in["session_affinity"].(string); ok {
		obj.SessionAffinity = v1.ServiceAffinity(v)
	}
	if v, ok := in["load_balancer_ip"].(string); ok {
		obj.LoadBalancerIP = v
	}
	if v, ok := in["load_balancer_source_ranges"].(*schema.Set); ok && v.Len() > 0 {
		obj.LoadBalancerSourceRanges = sliceOfString(v.List())
	}
	if v, ok := in["external_name"].(string); ok {
		obj.ExternalName = v
	}
	if v, ok := in["publish_not_ready_addresses"].(bool); ok {
		obj.PublishNotReadyAddresses = v
	}
	if v, ok := in["external_traffic_policy"].(string); ok {
		obj.ExternalName = v
	}
	return obj
}

// Patch Ops

func patchServiceSpec(keyPrefix, pathPrefix string, d *schema.ResourceData, v *version.Info) (PatchOperations, error) {
	ops := make([]PatchOperation, 0, 0)
	if d.HasChange(keyPrefix + "selector") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "selector",
			Value: d.Get(keyPrefix + "selector").(map[string]interface{}),
		})
	}
	if d.HasChange(keyPrefix + "type") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "type",
			Value: d.Get(keyPrefix + "type").(string),
		})
	}
	if d.HasChange(keyPrefix + "session_affinity") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "sessionAffinity",
			Value: d.Get(keyPrefix + "session_affinity").(string),
		})
	}
	if d.HasChange(keyPrefix + "load_balancer_ip") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "loadBalancerIP",
			Value: d.Get(keyPrefix + "load_balancer_ip").(string),
		})
	}
	if d.HasChange(keyPrefix + "load_balancer_source_ranges") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "loadBalancerSourceRanges",
			Value: d.Get(keyPrefix + "load_balancer_source_ranges").(*schema.Set).List(),
		})
	}
	if d.HasChange(keyPrefix + "port") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "ports",
			Value: expandServicePort(d.Get(keyPrefix + "port").([]interface{})),
		})
	}
	if d.HasChange(keyPrefix + "external_ips") {
		k8sVersion, err := gversion.NewVersion(v.String())
		if err != nil {
			return nil, err
		}
		v1_8_0, _ := gversion.NewVersion("1.8.0")
		if k8sVersion.LessThan(v1_8_0) {
			// If we haven't done this the deprecated field would have priority
			ops = append(ops, &ReplaceOperation{
				Path:  pathPrefix + "deprecatedPublicIPs",
				Value: nil,
			})
		}

		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "externalIPs",
			Value: d.Get(keyPrefix + "external_ips").(*schema.Set).List(),
		})
	}
	if d.HasChange(keyPrefix + "external_name") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "externalName",
			Value: d.Get(keyPrefix + "external_name").(string),
		})
	}
	if d.HasChange(keyPrefix + "publish_not_ready_addresses") {
		p := pathPrefix + "publishNotReadyAddresses"
		v := d.Get(keyPrefix + "publish_not_ready_addresses").(bool)
		if v {
			ops = append(ops, &AddOperation{
				Path:  p,
				Value: v,
			})
		} else {
			ops = append(ops, &RemoveOperation{
				Path: p,
			})
		}
	}
	return ops, nil
}
<commit_msg>Update/add references to ExternalTrafficPolicy in structure_service_spec<commit_after>package kubernetes

import (
	gversion "github.com/hashicorp/go-version"
	"github.com/hashicorp/terraform/helper/schema"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/version"
)

// Flatteners

func flattenServicePort(in []v1.ServicePort) []interface{} {
	att := make([]interface{}, len(in), len(in))
	for i, n := range in {
		m := make(map[string]interface{})
		m["name"] = n.Name
		m["protocol"] = string(n.Protocol)
		m["port"] = int(n.Port)
		m["target_port"] = n.TargetPort.String()
		m["node_port"] = int(n.NodePort)

		att[i] = m
	}
	return att
}

func flattenServiceSpec(in v1.ServiceSpec) []interface{} {
	att := make(map[string]interface{})
	if len(in.Ports) > 0 {
		att["port"] = flattenServicePort(in.Ports)
	}
	if len(in.Selector) > 0 {
		att["selector"] = in.Selector
	}
	if in.ClusterIP != "" {
		att["cluster_ip"] = in.ClusterIP
	}
	if in.Type != "" {
		att["type"] = string(in.Type)
	}
	if len(in.ExternalIPs) > 0 {
		att["external_ips"] = newStringSet(schema.HashString, in.ExternalIPs)
	}
	if in.SessionAffinity != "" {
		att["session_affinity"] = string(in.SessionAffinity)
	}
	if in.LoadBalancerIP != "" {
		att["load_balancer_ip"] = in.LoadBalancerIP
	}
	if len(in.LoadBalancerSourceRanges) > 0 {
		att["load_balancer_source_ranges"] = newStringSet(schema.HashString, in.LoadBalancerSourceRanges)
	}
	if in.ExternalName != "" {
		att["external_name"] = in.ExternalName
	}
	att["publish_not_ready_addresses"] = in.PublishNotReadyAddresses

	if in.ExternalTrafficPolicy != "" {
		att["external_traffic_policy"] = string(in.ExternalTrafficPolicy)
	}
	return []interface{}{att}
}

func flattenLoadBalancerIngress(in []v1.LoadBalancerIngress) []interface{} {
	out := make([]interface{}, len(in), len(in))
	for i, ingress := range in {
		att := make(map[string]interface{})

		att["ip"] = ingress.IP
		att["hostname"] = ingress.Hostname

		out[i] = att
	}
	return out
}

// Expanders

func expandServicePort(l []interface{}) []v1.ServicePort {
	if len(l) == 0 || l[0] == nil {
		return []v1.ServicePort{}
	}
	obj := make([]v1.ServicePort, len(l), len(l))
	for i, n := range l {
		cfg := n.(map[string]interface{})
		obj[i] = v1.ServicePort{
			Port:       int32(cfg["port"].(int)),
			TargetPort: intstr.Parse(cfg["target_port"].(string)),
		}
		if v, ok := cfg["name"].(string); ok {
			obj[i].Name = v
		}
		if v, ok := cfg["protocol"].(string); ok {
			obj[i].Protocol = v1.Protocol(v)
		}
		if v, ok := cfg["node_port"].(int); ok {
			obj[i].NodePort = int32(v)
		}
	}
	return obj
}

func expandServiceSpec(l []interface{}) v1.ServiceSpec {
	if len(l) == 0 || l[0] == nil {
		return v1.ServiceSpec{}
	}
	in := l[0].(map[string]interface{})
	obj := v1.ServiceSpec{}

	if v, ok := in["port"].([]interface{}); ok && len(v) > 0 {
		obj.Ports = expandServicePort(v)
	}
	if v, ok := in["selector"].(map[string]interface{}); ok && len(v) > 0 {
		obj.Selector = expandStringMap(v)
	}
	if v, ok := in["cluster_ip"].(string); ok {
		obj.ClusterIP = v
	}
	if v, ok := in["type"].(string); ok {
		obj.Type = v1.ServiceType(v)
	}
	if v, ok := in["external_ips"].(*schema.Set); ok && v.Len() > 0 {
		obj.ExternalIPs = sliceOfString(v.List())
	}
	if v, ok := in["session_affinity"].(string); ok {
		obj.SessionAffinity = v1.ServiceAffinity(v)
	}
	if v, ok := in["load_balancer_ip"].(string); ok {
		obj.LoadBalancerIP = v
	}
	if v, ok := in["load_balancer_source_ranges"].(*schema.Set); ok && v.Len() > 0 {
		obj.LoadBalancerSourceRanges = sliceOfString(v.List())
	}
	if v, ok := in["external_name"].(string); ok {
		obj.ExternalName = v
	}
	if v, ok := in["publish_not_ready_addresses"].(bool); ok {
		obj.PublishNotReadyAddresses = v
	}
	if v, ok := in["external_traffic_policy"].(string); ok {
		obj.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyType(v)
	}
	return obj
}

// Patch Ops

func patchServiceSpec(keyPrefix, pathPrefix string, d *schema.ResourceData, v *version.Info) (PatchOperations, error) {
	ops := make([]PatchOperation, 0, 0)
	if d.HasChange(keyPrefix + "selector") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "selector",
			Value: d.Get(keyPrefix + "selector").(map[string]interface{}),
		})
	}
	if d.HasChange(keyPrefix + "type") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "type",
			Value: d.Get(keyPrefix + "type").(string),
		})
	}
	if d.HasChange(keyPrefix + "session_affinity") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "sessionAffinity",
			Value: d.Get(keyPrefix + "session_affinity").(string),
		})
	}
	if d.HasChange(keyPrefix + "load_balancer_ip") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "loadBalancerIP",
			Value: d.Get(keyPrefix + "load_balancer_ip").(string),
		})
	}
	if d.HasChange(keyPrefix + "load_balancer_source_ranges") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "loadBalancerSourceRanges",
			Value: d.Get(keyPrefix + "load_balancer_source_ranges").(*schema.Set).List(),
		})
	}
	if d.HasChange(keyPrefix + "port") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "ports",
			Value: expandServicePort(d.Get(keyPrefix + "port").([]interface{})),
		})
	}
	if d.HasChange(keyPrefix + "external_ips") {
		k8sVersion, err := gversion.NewVersion(v.String())
		if err != nil {
			return nil, err
		}
		v1_8_0, _ := gversion.NewVersion("1.8.0")
		if k8sVersion.LessThan(v1_8_0) {
			// If we haven't done this the deprecated field would have priority
			ops = append(ops, &ReplaceOperation{
				Path:  pathPrefix + "deprecatedPublicIPs",
				Value: nil,
			})
		}

		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "externalIPs",
			Value: d.Get(keyPrefix + "external_ips").(*schema.Set).List(),
		})
	}
	if d.HasChange(keyPrefix + "external_name") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "externalName",
			Value: d.Get(keyPrefix + "external_name").(string),
		})
	}
	if d.HasChange(keyPrefix + "publish_not_ready_addresses") {
		p := pathPrefix + "publishNotReadyAddresses"
		v := d.Get(keyPrefix + "publish_not_ready_addresses").(bool)
		if v {
			ops = append(ops, &AddOperation{
				Path:  p,
				Value: v,
			})
		} else {
			ops = append(ops, &RemoveOperation{
				Path: p,
			})
		}
	}
	if d.HasChange(keyPrefix + "external_traffic_policy") {
		ops = append(ops, &ReplaceOperation{
			Path:  pathPrefix + "externalTrafficPolicy",
			Value: d.Get(keyPrefix + "external_traffic_policy").(string),
		})
	}
	return ops, nil
}
<|endoftext|>


<commit_before>// Package server provides a sample HTTP/Websocket server which registers
// itself in consul using one or more url prefixes to demonstrate and
// test the automatic fabio routing table update.
//
// During startup the server performs the following steps:
//
// * Add a handler for each prefix which provides a unique
//   response for that instance and endpoint
// * Add a `/health` handler for the consul health check
// * Register the service in consul with the listen address,
//   a health check under the given name and with one `urlprefix-`
//   tag per prefix
// * Install a signal handler to deregister the service on exit
//
// If the protocol is set to "ws" the registered endpoints function
// as websocket echo servers.
//
// Example:
//
//   # http server
//   ./server -addr 127.0.0.1:5000 -name svc-a -prefix /foo,/bar
//   ./server -addr 127.0.0.1:5001 -name svc-b -prefix /baz,/bar
//
//   # websocket server
//   ./server -addr 127.0.0.1:6000 -name ws-a -prefix /echo1,/echo2 -proto ws
//
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/hashicorp/consul/api"
	"golang.org/x/net/websocket"
)

func main() {
	var addr, consul, name, prefix, proto, token string
	var certFile, keyFile string
	flag.StringVar(&addr, "addr", "127.0.0.1:5000", "host:port of the service")
	flag.StringVar(&consul, "consul", "127.0.0.1:8500", "host:port of the consul agent")
	flag.StringVar(&name, "name", filepath.Base(os.Args[0]), "name of the service")
	flag.StringVar(&prefix, "prefix", "", "comma-sep list of host/path prefixes to register")
	flag.StringVar(&proto, "proto", "http", "protocol for endpoints: http or ws")
	flag.StringVar(&token, "token", "", "consul ACL token")
	flag.StringVar(&certFile, "cert", "", "path to cert file")
	flag.StringVar(&keyFile, "key", "", "path to key file")
	flag.Parse()

	if prefix == "" {
		flag.Usage()
		os.Exit(1)
	}

	// register prefixes
	prefixes := strings.Split(prefix, ",")
	for _, p := range prefixes {
		switch proto {
		case "http":
			http.HandleFunc(p, func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintf(w, "Serving %s from %s on %s\n", r.RequestURI, name, addr)
			})
		case "ws":
			http.Handle(p, websocket.Handler(EchoServer))
		default:
			log.Fatal("Invalid protocol ", proto)
		}
	}

	// register consul health check endpoint
	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "OK")
	})

	// start http server
	go func() {
		log.Printf("Listening on %s serving %s", addr, prefix)

		var err error
		if certFile != "" {
			err = http.ListenAndServeTLS(addr, certFile, keyFile, nil)
		} else {
			err = http.ListenAndServe(addr, nil)
		}
		if err != nil {
			log.Fatal(err)
		}
	}()

	// build urlprefix-host/path tag list
	// e.g. urlprefix-/foo, urlprefix-/bar, ...
	var tags []string
	for _, p := range prefixes {
		tags = append(tags, "urlprefix-"+p)
	}

	// get host and port as string/int
	host, portstr, err := net.SplitHostPort(addr)
	if err != nil {
		log.Fatal(err)
	}
	port, err := strconv.Atoi(portstr)
	if err != nil {
		log.Fatal(err)
	}

	var check *api.AgentServiceCheck
	if certFile != "" {
		check = &api.AgentServiceCheck{
			TCP:      addr,
			Interval: "2s",
			Timeout:  "1s",
		}
	} else {
		check = &api.AgentServiceCheck{
			HTTP:     "http://" + addr + "/health",
			Interval: "1s",
			Timeout:  "1s",
		}
	}

	// register service with health check
	serviceID := name + "-" + addr
	service := &api.AgentServiceRegistration{
		ID:      serviceID,
		Name:    name,
		Port:    port,
		Address: host,
		Tags:    tags,
		Check:   check,
	}

	config := &api.Config{Address: consul, Scheme: "http", Token: token}
	client, err := api.NewClient(config)
	if err != nil {
		log.Fatal(err)
	}

	if err := client.Agent().ServiceRegister(service); err != nil {
		log.Fatal(err)
	}
	log.Printf("Registered service %q in consul with tags %q", name, strings.Join(tags, ","))

	// run until we get a signal
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, os.Kill)
	<-quit

	// deregister service
	if err := client.Agent().ServiceDeregister(serviceID); err != nil {
		log.Fatal(err)
	}
	log.Printf("Deregistered service %q in consul", name)
}

func EchoServer(ws *websocket.Conn) {
	addr := ws.LocalAddr().String()
	pfx := []byte("[" + addr + "] ")

	log.Printf("ws connect on %s", addr)

	// the following could be done with io.Copy(ws, ws)
	// but I want to add some meta data
	var msg = make([]byte, 1024)
	for {
		n, err := ws.Read(msg)
		if err != nil && err != io.EOF {
			log.Printf("ws error on %s. %s", addr, err)
			break
		}
		_, err = ws.Write(append(pfx, msg[:n]...))
		if err != nil && err != io.EOF {
			log.Printf("ws error on %s. %s", addr, err)
			break
		}
	}
	log.Printf("ws disconnect on %s", addr)
}
<commit_msg>Issue #206: Add support for custom status code to demo server<commit_after>// Package server provides a sample HTTP/Websocket server which registers
// itself in consul using one or more url prefixes to demonstrate and
// test the automatic fabio routing table update.
//
// During startup the server performs the following steps:
//
// * Add a handler for each prefix which provides a unique
//   response for that instance and endpoint
// * Add a `/health` handler for the consul health check
// * Register the service in consul with the listen address,
//   a health check under the given name and with one `urlprefix-`
//   tag per prefix
// * Install a signal handler to deregister the service on exit
//
// If the protocol is set to "ws" the registered endpoints function
// as websocket echo servers.
//
// Example:
//
//   # http server
//   ./server -addr 127.0.0.1:5000 -name svc-a -prefix /foo,/bar
//   ./server -addr 127.0.0.1:5001 -name svc-b -prefix /baz,/bar
//
//   # websocket server
//   ./server -addr 127.0.0.1:6000 -name ws-a -prefix /echo1,/echo2 -proto ws
//
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/hashicorp/consul/api"
	"golang.org/x/net/websocket"
)

func main() {
	var addr, consul, name, prefix, proto, token string
	var certFile, keyFile string
	var status int
	flag.StringVar(&addr, "addr", "127.0.0.1:5000", "host:port of the service")
	flag.StringVar(&consul, "consul", "127.0.0.1:8500", "host:port of the consul agent")
	flag.StringVar(&name, "name", filepath.Base(os.Args[0]), "name of the service")
	flag.StringVar(&prefix, "prefix", "", "comma-sep list of host/path prefixes to register")
	flag.StringVar(&proto, "proto", "http", "protocol for endpoints: http or ws")
	flag.StringVar(&token, "token", "", "consul ACL token")
	flag.StringVar(&certFile, "cert", "", "path to cert file")
	flag.StringVar(&keyFile, "key", "", "path to key file")
	flag.IntVar(&status, "status", http.StatusOK, "http status code")
	flag.Parse()

	if prefix == "" {
		flag.Usage()
		os.Exit(1)
	}

	// register prefixes
	prefixes := strings.Split(prefix, ",")
	for _, p := range prefixes {
		switch proto {
		case "http":
			http.HandleFunc(p, func(w http.ResponseWriter, r *http.Request) {
				w.WriteHeader(status)
				fmt.Fprintf(w, "Serving %s from %s on %s\n", r.RequestURI, name, addr)
			})
		case "ws":
			http.Handle(p, websocket.Handler(EchoServer))
		default:
			log.Fatal("Invalid protocol ", proto)
		}
	}

	// register consul health check endpoint
	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "OK")
	})

	// start http server
	go func() {
		log.Printf("Listening on %s serving %s", addr, prefix)

		var err error
		if certFile != "" {
			err = http.ListenAndServeTLS(addr, certFile, keyFile, nil)
		} else {
			err = http.ListenAndServe(addr, nil)
		}
		if err != nil {
			log.Fatal(err)
		}
	}()

	// build urlprefix-host/path tag list
	// e.g. urlprefix-/foo, urlprefix-/bar, ...
	var tags []string
	for _, p := range prefixes {
		tags = append(tags, "urlprefix-"+p)
	}

	// get host and port as string/int
	host, portstr, err := net.SplitHostPort(addr)
	if err != nil {
		log.Fatal(err)
	}
	port, err := strconv.Atoi(portstr)
	if err != nil {
		log.Fatal(err)
	}

	var check *api.AgentServiceCheck
	if certFile != "" {
		check = &api.AgentServiceCheck{
			TCP:      addr,
			Interval: "2s",
			Timeout:  "1s",
		}
	} else {
		check = &api.AgentServiceCheck{
			HTTP:     "http://" + addr + "/health",
			Interval: "1s",
			Timeout:  "1s",
		}
	}

	// register service with health check
	serviceID := name + "-" + addr
	service := &api.AgentServiceRegistration{
		ID:      serviceID,
		Name:    name,
		Port:    port,
		Address: host,
		Tags:    tags,
		Check:   check,
	}

	config := &api.Config{Address: consul, Scheme: "http", Token: token}
	client, err := api.NewClient(config)
	if err != nil {
		log.Fatal(err)
	}

	if err := client.Agent().ServiceRegister(service); err != nil {
		log.Fatal(err)
	}
	log.Printf("Registered service %q in consul with tags %q", name, strings.Join(tags, ","))

	// run until we get a signal
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, os.Kill)
	<-quit

	// deregister service
	if err := client.Agent().ServiceDeregister(serviceID); err != nil {
		log.Fatal(err)
	}
	log.Printf("Deregistered service %q in consul", name)
}

func EchoServer(ws *websocket.Conn) {
	addr := ws.LocalAddr().String()
	pfx := []byte("[" + addr + "] ")

	log.Printf("ws connect on %s", addr)

	// the following could be done with io.Copy(ws, ws)
	// but I want to add some meta data
	var msg = make([]byte, 1024)
	for {
		n, err := ws.Read(msg)
		if err != nil && err != io.EOF {
			log.Printf("ws error on %s. %s", addr, err)
			break
		}
		_, err = ws.Write(append(pfx, msg[:n]...))
		if err != nil && err != io.EOF {
			log.Printf("ws error on %s. %s", addr, err)
			break
		}
	}
	log.Printf("ws disconnect on %s", addr)
}
<|endoftext|>


<commit_before>package postgres

import (
	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

const postgresSchemaCollectionName = "_SCHEMA"

const postgresRelationDoesNotExistError = "42P01"
const postgresDuplicateRelationError = "42P07"
const postgresDuplicateColumnError = "42701"
const postgresUniqueIndexViolationError = "23505"
const postgresTransactionAbortedError = "25P02"

// PostgresAdapter postgres 数据库适配器
type PostgresAdapter struct {
	collectionPrefix string
	collectionList   []string
}

// NewPostgresAdapter ...
func NewPostgresAdapter(collectionPrefix string) *PostgresAdapter {
	return &PostgresAdapter{
		collectionPrefix: collectionPrefix,
		collectionList:   []string{},
	}
}

// ClassExists ...
func (p *PostgresAdapter) ClassExists(name string) bool {
	return false
}

// SetClassLevelPermissions ...
func (p *PostgresAdapter) SetClassLevelPermissions(className string, CLPs types.M) error {
	return nil
}

// CreateClass ...
func (p *PostgresAdapter) CreateClass(className string, schema types.M) (types.M, error) {
	return nil, nil
}

// AddFieldIfNotExists ...
func (p *PostgresAdapter) AddFieldIfNotExists(className, fieldName string, fieldType types.M) error {
	return nil
}

// DeleteClass ...
func (p *PostgresAdapter) DeleteClass(className string) (types.M, error) {
	return nil, nil
}

// DeleteAllClasses ...
func (p *PostgresAdapter) DeleteAllClasses() error {
	return nil
}

// DeleteFields ...
func (p *PostgresAdapter) DeleteFields(className string, schema types.M, fieldNames []string) error {
	return nil
}

// CreateObject ...
func (p *PostgresAdapter) CreateObject(className string, schema, object types.M) error {
	return nil
}

// GetAllClasses ...
func (p *PostgresAdapter) GetAllClasses() ([]types.M, error) {
	return nil, nil
}

// GetClass ...
func (p *PostgresAdapter) GetClass(className string) (types.M, error) {
	return nil, nil
}

// DeleteObjectsByQuery ...
func (p *PostgresAdapter) DeleteObjectsByQuery(className string, schema, query types.M) error {
	return nil
}

// Find ...
func (p *PostgresAdapter) Find(className string, schema, query, options types.M) ([]types.M, error) {
	return nil, nil
}

// Count ...
func (p *PostgresAdapter) Count(className string, schema, query types.M) (int, error) {
	return 0, nil
}

// UpdateObjectsByQuery ...
func (p *PostgresAdapter) UpdateObjectsByQuery(className string, schema, query, update types.M) error {
	return nil
}

// FindOneAndUpdate ...
func (p *PostgresAdapter) FindOneAndUpdate(className string, schema, query, update types.M) (types.M, error) {
	return nil, nil
}

// UpsertOneObject ...
func (p *PostgresAdapter) UpsertOneObject(className string, schema, query, update types.M) error {
	return nil
}

// EnsureUniqueness ...
func (p *PostgresAdapter) EnsureUniqueness(className string, schema types.M, fieldNames []string) error {
	return nil
}

// PerformInitialization ...
func (p *PostgresAdapter) PerformInitialization(options types.M) error {
	return nil
}

var parseToPosgresComparator = map[string]string{
	"$gt":  ">",
	"$lt":  "<",
	"$gte": ">=",
	"$lte": "<=",
}

func parseTypeToPostgresType(t types.M) (string, error) {
	if t == nil {
		return "", nil
	}
	tp := utils.S(t["type"])
	switch tp {
	case "String":
		return "text", nil
	case "Date":
		return "timestamp with time zone", nil
	case "Object":
		return "jsonb", nil
	case "File":
		return "text", nil
	case "Boolean":
		return "boolean", nil
	case "Pointer":
		return "char(10)", nil
	case "Number":
		return "double precision", nil
	case "GeoPoint":
		return "point", nil
	case "Array":
		if contents := utils.M(t["contents"]); contents != nil {
			if utils.S(contents["type"]) == "String" {
				return "text[]", nil
			}
		}
		return "jsonb", nil
	default:
		return "", errs.E(errs.IncorrectType, "no type for "+tp+" yet")
	}
}

func toPostgresValue(value interface{}) interface{} {
	if v := utils.M(value); v != nil {
		if utils.S(v["__type"]) == "Date" {
			return v["iso"]
		}
		if utils.S(v["__type"]) == "File" {
			return v["name"]
		}
	}
	return value
}

func transformValue(value interface{}) interface{} {
	if v := utils.M(value); v != nil {
		if utils.S(v["__type"]) == "Pointer" {
			return v["objectId"]
		}
	}
	return value
}

var emptyCLPS = types.M{
	"find":     types.M{},
	"get":      types.M{},
	"create":   types.M{},
	"update":   types.M{},
	"delete":   types.M{},
	"addField": types.M{},
}

var defaultCLPS = types.M{
	"find":     types.M{"*": true},
	"get":      types.M{"*": true},
	"create":   types.M{"*": true},
	"update":   types.M{"*": true},
	"delete":   types.M{"*": true},
	"addField": types.M{"*": true},
}

func toParseSchema(schema types.M) types.M {
	if schema == nil {
		return nil
	}

	var fields types.M
	if fields = utils.M(schema["fields"]); fields == nil {
		fields = types.M{}
	}

	if utils.S(schema["className"]) == "_User" {
		if _, ok := fields["_hashed_password"]; ok {
			delete(fields, "_hashed_password")
		}
	}

	if _, ok := fields["_wperm"]; ok {
		delete(fields, "_wperm")
	}
	if _, ok := fields["_rperm"]; ok {
		delete(fields, "_rperm")
	}

	var clps types.M
	clps = utils.CopyMap(defaultCLPS)
	if classLevelPermissions := utils.M(schema["classLevelPermissions"]); classLevelPermissions != nil {
		// clps = utils.CopyMap(emptyCLPS)
		// 不存在的 action 默认为公共权限
		for k, v := range classLevelPermissions {
			clps[k] = v
		}
	}

	return types.M{
		"className":             schema["className"],
		"fields":                fields,
		"classLevelPermissions": clps,
	}
}

func toPostgresSchema(schema types.M) types.M {
	// TODO
	return nil
}

func handleDotFields(object types.M) types.M {
	// TODO
	return nil
}

func validateKeys(object interface{}) error {
	// TODO
	return nil
}

func joinTablesForSchema(schema types.M) []string {
	// TODO
	return nil
}

func buildWhereClause(schema, query types.M, index int) (types.M, error) {
	// TODO
	// toPostgresSchema
	// removeWhiteSpace
	// processRegexPattern
	return nil, nil
}

func removeWhiteSpace(s string) string {
	// TODO
	return ""
}

func processRegexPattern(s string) string {
	// TODO
	// literalizeRegexPart
	return ""
}

func createLiteralRegex(s string) string {
	// TODO
	return ""
}

func literalizeRegexPart(s string) string {
	// TODO
	// createLiteralRegex
	return ""
}
<commit_msg>完成 toPostgresSchema<commit_after>package postgres

import (
	"github.com/lfq7413/tomato/errs"
	"github.com/lfq7413/tomato/types"
	"github.com/lfq7413/tomato/utils"
)

const postgresSchemaCollectionName = "_SCHEMA"

const postgresRelationDoesNotExistError = "42P01"
const postgresDuplicateRelationError = "42P07"
const postgresDuplicateColumnError = "42701"
const postgresUniqueIndexViolationError = "23505"
const postgresTransactionAbortedError = "25P02"

// PostgresAdapter postgres 数据库适配器
type PostgresAdapter struct {
	collectionPrefix string
	collectionList   []string
}

// NewPostgresAdapter ...
func NewPostgresAdapter(collectionPrefix string) *PostgresAdapter {
	return &PostgresAdapter{
		collectionPrefix: collectionPrefix,
		collectionList:   []string{},
	}
}

// ClassExists ...
func (p *PostgresAdapter) ClassExists(name string) bool {
	return false
}

// SetClassLevelPermissions ...
func (p *PostgresAdapter) SetClassLevelPermissions(className string, CLPs types.M) error {
	return nil
}

// CreateClass ...
func (p *PostgresAdapter) CreateClass(className string, schema types.M) (types.M, error) {
	return nil, nil
}

// AddFieldIfNotExists ...
func (p *PostgresAdapter) AddFieldIfNotExists(className, fieldName string, fieldType types.M) error {
	return nil
}

// DeleteClass ...
func (p *PostgresAdapter) DeleteClass(className string) (types.M, error) {
	return nil, nil
}

// DeleteAllClasses ...
func (p *PostgresAdapter) DeleteAllClasses() error {
	return nil
}

// DeleteFields ...
func (p *PostgresAdapter) DeleteFields(className string, schema types.M, fieldNames []string) error {
	return nil
}

// CreateObject ...
func (p *PostgresAdapter) CreateObject(className string, schema, object types.M) error {
	return nil
}

// GetAllClasses ...
func (p *PostgresAdapter) GetAllClasses() ([]types.M, error) {
	return nil, nil
}

// GetClass ...
func (p *PostgresAdapter) GetClass(className string) (types.M, error) {
	return nil, nil
}

// DeleteObjectsByQuery ...
func (p *PostgresAdapter) DeleteObjectsByQuery(className string, schema, query types.M) error {
	return nil
}

// Find ...
func (p *PostgresAdapter) Find(className string, schema, query, options types.M) ([]types.M, error) {
	return nil, nil
}

// Count ...
func (p *PostgresAdapter) Count(className string, schema, query types.M) (int, error) {
	return 0, nil
}

// UpdateObjectsByQuery ...
func (p *PostgresAdapter) UpdateObjectsByQuery(className string, schema, query, update types.M) error {
	return nil
}

// FindOneAndUpdate ...
func (p *PostgresAdapter) FindOneAndUpdate(className string, schema, query, update types.M) (types.M, error) {
	return nil, nil
}

// UpsertOneObject ...
func (p *PostgresAdapter) UpsertOneObject(className string, schema, query, update types.M) error {
	return nil
}

// EnsureUniqueness ...
func (p *PostgresAdapter) EnsureUniqueness(className string, schema types.M, fieldNames []string) error {
	return nil
}

// PerformInitialization ...
func (p *PostgresAdapter) PerformInitialization(options types.M) error {
	return nil
}

var parseToPosgresComparator = map[string]string{
	"$gt":  ">",
	"$lt":  "<",
	"$gte": ">=",
	"$lte": "<=",
}

func parseTypeToPostgresType(t types.M) (string, error) {
	if t == nil {
		return "", nil
	}
	tp := utils.S(t["type"])
	switch tp {
	case "String":
		return "text", nil
	case "Date":
		return "timestamp with time zone", nil
	case "Object":
		return "jsonb", nil
	case "File":
		return "text", nil
	case "Boolean":
		return "boolean", nil
	case "Pointer":
		return "char(10)", nil
	case "Number":
		return "double precision", nil
	case "GeoPoint":
		return "point", nil
	case "Array":
		if contents := utils.M(t["contents"]); contents != nil {
			if utils.S(contents["type"]) == "String" {
				return "text[]", nil
			}
		}
		return "jsonb", nil
	default:
		return "", errs.E(errs.IncorrectType, "no type for "+tp+" yet")
	}
}

func toPostgresValue(value interface{}) interface{} {
	if v := utils.M(value); v != nil {
		if utils.S(v["__type"]) == "Date" {
			return v["iso"]
		}
		if utils.S(v["__type"]) == "File" {
			return v["name"]
		}
	}
	return value
}

func transformValue(value interface{}) interface{} {
	if v := utils.M(value); v != nil {
		if utils.S(v["__type"]) == "Pointer" {
			return v["objectId"]
		}
	}
	return value
}

var emptyCLPS = types.M{
	"find":     types.M{},
	"get":      types.M{},
	"create":   types.M{},
	"update":   types.M{},
	"delete":   types.M{},
	"addField": types.M{},
}

var defaultCLPS = types.M{
	"find":     types.M{"*": true},
	"get":      types.M{"*": true},
	"create":   types.M{"*": true},
	"update":   types.M{"*": true},
	"delete":   types.M{"*": true},
	"addField": types.M{"*": true},
}

func toParseSchema(schema types.M) types.M {
	if schema == nil {
		return nil
	}

	var fields types.M
	if fields = utils.M(schema["fields"]); fields == nil {
		fields = types.M{}
	}

	if utils.S(schema["className"]) == "_User" {
		if _, ok := fields["_hashed_password"]; ok {
			delete(fields, "_hashed_password")
		}
	}

	if _, ok := fields["_wperm"]; ok {
		delete(fields, "_wperm")
	}
	if _, ok := fields["_rperm"]; ok {
		delete(fields, "_rperm")
	}

	var clps types.M
	clps = utils.CopyMap(defaultCLPS)
	if classLevelPermissions := utils.M(schema["classLevelPermissions"]); classLevelPermissions != nil {
		// clps = utils.CopyMap(emptyCLPS)
		// 不存在的 action 默认为公共权限
		for k, v := range classLevelPermissions {
			clps[k] = v
		}
	}

	return types.M{
		"className":             schema["className"],
		"fields":                fields,
		"classLevelPermissions": clps,
	}
}

func toPostgresSchema(schema types.M) types.M {
	if schema == nil {
		return nil
	}

	var fields types.M
	if fields = utils.M(schema["fields"]); fields == nil {
		fields = types.M{}
	}

	fields["_wperm"] = types.M{
		"type":     "Array",
		"contents": types.M{"type": "String"},
	}
	fields["_rperm"] = types.M{
		"type":     "Array",
		"contents": types.M{"type": "String"},
	}

	if utils.S(schema["className"]) == "_User" {
		fields["_hashed_password"] = types.M{"type": "String"}
		fields["_password_history"] = types.M{"type": "Array"}
	}

	schema["fields"] = fields

	return schema
}

func handleDotFields(object types.M) types.M {
	// TODO
	return nil
}

func validateKeys(object interface{}) error {
	// TODO
	return nil
}

func joinTablesForSchema(schema types.M) []string {
	// TODO
	return nil
}

func buildWhereClause(schema, query types.M, index int) (types.M, error) {
	// TODO
	// removeWhiteSpace
	// processRegexPattern
	return nil, nil
}

func removeWhiteSpace(s string) string {
	// TODO
	return ""
}

func processRegexPattern(s string) string {
	// TODO
	// literalizeRegexPart
	return ""
}

func createLiteralRegex(s string) string {
	// TODO
	return ""
}

func literalizeRegexPart(s string) string {
	// TODO
	// createLiteralRegex
	return ""
}
<|endoftext|>


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

// Package main contains a simple command line tool for Places API Text Search
// Documentation: https://developers.google.com/places/web-service/search#TextSearchRequests
package main

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

	"github.com/kr/pretty"
	"golang.org/x/net/context"
	"googlemaps.github.io/maps"
)

var (
	apiKey    = flag.String("key", "", "API Key for using Google Maps API.")
	clientID  = flag.String("client_id", "", "ClientID for Maps for Work API access.")
	signature = flag.String("signature", "", "Signature for Maps for Work API access.")
	location  = flag.String("location", "", "The latitude/longitude around which to retrieve place information. This must be specified as latitude,longitude.")
	radius    = flag.Uint("radius", 0, "Defines the distance (in meters) within which to bias place results. The maximum allowed radius is 50,000 meters.")
	keyword   = flag.String("keyword", "", "Specifies the language in which to return results. Optional.")
	language  = flag.String("language", "", "The language in which to return results.")
	minPrice  = flag.String("minprice", "", "Restricts results to only those places within the specified price level.")
	maxPrice  = flag.String("maxprice", "", "Restricts results to only those places within the specified price level.")
	name      = flag.String("name", "", "One or more terms to be matched against the names of places, separated with a space character.")
	openNow   = flag.Bool("open_now", false, "Restricts results to only those places that are open for business at the time the query is sent.")
	rankBy    = flag.String("rankby", "", "Specifies the order in which results are listed. Valid values are prominence or distance.")
	placeType = flag.String("type", "", "Restricts the results to places matching the specified type.")
	pageToken = flag.String("pagetoken", "", "Set to retrieve the next page of results.")
)

func usageAndExit(msg string) {
	fmt.Fprintln(os.Stderr, msg)
	fmt.Println("Flags:")
	flag.PrintDefaults()
	os.Exit(2)
}

func check(err error) {
	if err != nil {
		log.Fatalf("fatal error: %s", err)
	}
}

func main() {
	flag.Parse()

	var client *maps.Client
	var err error
	if *apiKey != "" {
		client, err = maps.NewClient(maps.WithAPIKey(*apiKey))
	} else if *clientID != "" || *signature != "" {
		client, err = maps.NewClient(maps.WithClientIDAndSignature(*clientID, *signature))
	} else {
		usageAndExit("Please specify an API Key, or Client ID and Signature.")
	}
	check(err)

	r := &maps.NearbySearchRequest{
		Radius:    *radius,
		Keyword:   *keyword,
		Language:  *language,
		Name:      *name,
		OpenNow:   *openNow,
		PageToken: *pageToken,
	}

	parseLocation(*location, r)
	parsePriceLevels(*minPrice, *maxPrice, r)
	parseRankBy(*rankBy, r)
	parsePlaceType(*placeType, r)

	resp, err := client.NearbySearch(context.Background(), r)
	check(err)

	pretty.Println(resp)
}

func parseLocation(location string, r *maps.NearbySearchRequest) {
	if location != "" {
		l, err := maps.ParseLatLng(location)
		check(err)
		r.Location = &l
	}
}

func parsePriceLevel(priceLevel string) maps.PriceLevel {
	switch priceLevel {
	case "0":
		return maps.PriceLevelFree
	case "1":
		return maps.PriceLevelInexpensive
	case "2":
		return maps.PriceLevelModerate
	case "3":
		return maps.PriceLevelExpensive
	case "4":
		return maps.PriceLevelVeryExpensive
	default:
		usageAndExit(fmt.Sprintf("Unknown price level: '%s'", priceLevel))
	}
	return maps.PriceLevelFree
}

func parsePriceLevels(minPrice string, maxPrice string, r *maps.NearbySearchRequest) {
	if minPrice != "" {
		r.MinPrice = parsePriceLevel(minPrice)
	}

	if maxPrice != "" {
		r.MaxPrice = parsePriceLevel(minPrice)
	}
}

func parseRankBy(rankBy string, r *maps.NearbySearchRequest) {
	switch rankBy {
	case "prominence":
		r.RankBy = maps.RankByProminence
		return
	case "distance":
		r.RankBy = maps.RankByDistance
		return
	case "":
		return
	default:
		usageAndExit(fmt.Sprintf("Unknown rank by: \"%v\"", rankBy))
	}
}

func parsePlaceType(placeType string, r *maps.NearbySearchRequest) {
	if placeType != "" {
		t, err := maps.ParsePlaceType(placeType)
		if err != nil {
			usageAndExit(fmt.Sprintf("Unknown place type \"%v\"", placeType))
		}

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

// Package main contains a simple command line tool for Places API Text Search
// Documentation: https://developers.google.com/places/web-service/search#TextSearchRequests
package main

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

	"github.com/kr/pretty"
	"golang.org/x/net/context"
	"googlemaps.github.io/maps"
)

var (
	apiKey    = flag.String("key", "", "API Key for using Google Maps API.")
	clientID  = flag.String("client_id", "", "ClientID for Maps for Work API access.")
	signature = flag.String("signature", "", "Signature for Maps for Work API access.")
	location  = flag.String("location", "", "The latitude/longitude around which to retrieve place information. This must be specified as latitude,longitude.")
	radius    = flag.Uint("radius", 0, "Defines the distance (in meters) within which to bias place results. The maximum allowed radius is 50,000 meters.")
	keyword   = flag.String("keyword", "", "A term to be matched against all content that Google has indexed for this place, including but not limited to name, type, and address, as well as customer reviews and other third-party content.")
	language  = flag.String("language", "", "The language in which to return results.")
	minPrice  = flag.String("minprice", "", "Restricts results to only those places within the specified price level.")
	maxPrice  = flag.String("maxprice", "", "Restricts results to only those places within the specified price level.")
	name      = flag.String("name", "", "One or more terms to be matched against the names of places, separated with a space character.")
	openNow   = flag.Bool("open_now", false, "Restricts results to only those places that are open for business at the time the query is sent.")
	rankBy    = flag.String("rankby", "", "Specifies the order in which results are listed. Valid values are prominence or distance.")
	placeType = flag.String("type", "", "Restricts the results to places matching the specified type.")
	pageToken = flag.String("pagetoken", "", "Set to retrieve the next page of results.")
)

func usageAndExit(msg string) {
	fmt.Fprintln(os.Stderr, msg)
	fmt.Println("Flags:")
	flag.PrintDefaults()
	os.Exit(2)
}

func check(err error) {
	if err != nil {
		log.Fatalf("fatal error: %s", err)
	}
}

func main() {
	flag.Parse()

	var client *maps.Client
	var err error
	if *apiKey != "" {
		client, err = maps.NewClient(maps.WithAPIKey(*apiKey))
	} else if *clientID != "" || *signature != "" {
		client, err = maps.NewClient(maps.WithClientIDAndSignature(*clientID, *signature))
	} else {
		usageAndExit("Please specify an API Key, or Client ID and Signature.")
	}
	check(err)

	r := &maps.NearbySearchRequest{
		Radius:    *radius,
		Keyword:   *keyword,
		Language:  *language,
		Name:      *name,
		OpenNow:   *openNow,
		PageToken: *pageToken,
	}

	parseLocation(*location, r)
	parsePriceLevels(*minPrice, *maxPrice, r)
	parseRankBy(*rankBy, r)
	parsePlaceType(*placeType, r)

	resp, err := client.NearbySearch(context.Background(), r)
	check(err)

	pretty.Println(resp)
}

func parseLocation(location string, r *maps.NearbySearchRequest) {
	if location != "" {
		l, err := maps.ParseLatLng(location)
		check(err)
		r.Location = &l
	}
}

func parsePriceLevel(priceLevel string) maps.PriceLevel {
	switch priceLevel {
	case "0":
		return maps.PriceLevelFree
	case "1":
		return maps.PriceLevelInexpensive
	case "2":
		return maps.PriceLevelModerate
	case "3":
		return maps.PriceLevelExpensive
	case "4":
		return maps.PriceLevelVeryExpensive
	default:
		usageAndExit(fmt.Sprintf("Unknown price level: '%s'", priceLevel))
	}
	return maps.PriceLevelFree
}

func parsePriceLevels(minPrice string, maxPrice string, r *maps.NearbySearchRequest) {
	if minPrice != "" {
		r.MinPrice = parsePriceLevel(minPrice)
	}

	if maxPrice != "" {
		r.MaxPrice = parsePriceLevel(minPrice)
	}
}

func parseRankBy(rankBy string, r *maps.NearbySearchRequest) {
	switch rankBy {
	case "prominence":
		r.RankBy = maps.RankByProminence
		return
	case "distance":
		r.RankBy = maps.RankByDistance
		return
	case "":
		return
	default:
		usageAndExit(fmt.Sprintf("Unknown rank by: \"%v\"", rankBy))
	}
}

func parsePlaceType(placeType string, r *maps.NearbySearchRequest) {
	if placeType != "" {
		t, err := maps.ParsePlaceType(placeType)
		if err != nil {
			usageAndExit(fmt.Sprintf("Unknown place type \"%v\"", placeType))
		}

		r.Type = t
	}
}
<|endoftext|>


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

package gitiles

import (
	"archive/tar"
	"compress/gzip"
	"io"
	"testing"
)

func TestProductionArchive(t *testing.T) {
	gs, err := NewService(Options{
		Address: "https://go.googlesource.com",
	})
	if err != nil {
		t.Fatalf("NewService: %v", err)
	}

	repo := gs.NewRepoService("crypto")
	if err != nil {
		t.Fatalf("NewRepoService: %v", err)
	}

	stream, err := repo.GetArchive("master", "ssh", ArchiveTgz)
	if err != nil {
		t.Fatalf("GetArchive: %v", err)
	}
	defer stream.Close()

	gz, err := gzip.NewReader(stream)
	if err != nil {
		t.Fatalf("gzip.NewReader: %v", err)
	}
	r := tar.NewReader(gz)

	names := map[string]bool{}
	for {
		hdr, err := r.Next()
		if err == io.EOF {
			break
		}
		names[hdr.Name] = true
	}

	if !names["mux.go"] {
		t.Fatal("did not find 'mux.go', got %v", names)
	}
}

func TestProductionDescribe(t *testing.T) {
	gs, err := NewService(Options{
		Address: "https://gerrit.googlesource.com",
	})
	if err != nil {
		t.Fatal(err)
	}

	repo := gs.NewRepoService("gitiles")
	if err != nil {
		t.Fatal(err)
	}
	got, err := repo.Describe("9de65953ec", DescribeContains)
	if err != nil {
		t.Fatal(err)
	}

	if want := "v0.1-6~361"; want != got {
		t.Fatalf("got %v, want %v", got, want)
	}
}

func TestProductionRefs(t *testing.T) {
	gs, err := NewService(Options{
		Address: "https://gerrit.googlesource.com",
	})
	if err != nil {
		t.Fatal(err)
	}

	repo := gs.NewRepoService("gitiles")
	if err != nil {
		t.Fatal(err)
	}
	got, err := repo.Refs("refs/heads")
	if err != nil {
		t.Fatal(err)
	}

	if got["master"] == nil {
		t.Errorf("got %v, want key 'master'", got)
	}
}
<commit_msg>gitiles: fix vet error<commit_after>// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gitiles

import (
	"archive/tar"
	"compress/gzip"
	"io"
	"testing"
)

func TestProductionArchive(t *testing.T) {
	gs, err := NewService(Options{
		Address: "https://go.googlesource.com",
	})
	if err != nil {
		t.Fatalf("NewService: %v", err)
	}

	repo := gs.NewRepoService("crypto")
	if err != nil {
		t.Fatalf("NewRepoService: %v", err)
	}

	stream, err := repo.GetArchive("master", "ssh", ArchiveTgz)
	if err != nil {
		t.Fatalf("GetArchive: %v", err)
	}
	defer stream.Close()

	gz, err := gzip.NewReader(stream)
	if err != nil {
		t.Fatalf("gzip.NewReader: %v", err)
	}
	r := tar.NewReader(gz)

	names := map[string]bool{}
	for {
		hdr, err := r.Next()
		if err == io.EOF {
			break
		}
		names[hdr.Name] = true
	}

	if !names["mux.go"] {
		t.Fatalf("did not find 'mux.go', got %v", names)
	}
}

func TestProductionDescribe(t *testing.T) {
	gs, err := NewService(Options{
		Address: "https://gerrit.googlesource.com",
	})
	if err != nil {
		t.Fatal(err)
	}

	repo := gs.NewRepoService("gitiles")
	if err != nil {
		t.Fatal(err)
	}
	got, err := repo.Describe("9de65953ec", DescribeContains)
	if err != nil {
		t.Fatal(err)
	}

	if want := "v0.1-6~361"; want != got {
		t.Fatalf("got %v, want %v", got, want)
	}
}

func TestProductionRefs(t *testing.T) {
	gs, err := NewService(Options{
		Address: "https://gerrit.googlesource.com",
	})
	if err != nil {
		t.Fatal(err)
	}

	repo := gs.NewRepoService("gitiles")
	if err != nil {
		t.Fatal(err)
	}
	got, err := repo.Refs("refs/heads")
	if err != nil {
		t.Fatal(err)
	}

	if got["master"] == nil {
		t.Errorf("got %v, want key 'master'", got)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

// Package log provides some handy types and method to activate and deactivate specific log
// behavior within files in a transparent way.
package log

import (
	"fmt"
	"testing"
)

// Logger is a minimalist interface to represent logger
type Logger interface {
	Log(format string, a ...interface{})
}

// DebugLogger can be used in development to display loglines in the console
type DebugLogger struct {
	Tag string
}

// Log implements the Logger interface
func (l DebugLogger) Log(format string, a ...interface{}) {
	fmt.Printf("\033[33m[ %s ]\033[0m ", l.Tag) // Tag printed in yellow
	fmt.Printf(format, a...)
	fmt.Print("\n")
}

// TestLogger can be used in a test environnement to display log only on failure
type TestLogger struct {
	Tag string
	T   *testing.T
}

// Log implements the Logger interface
func (l TestLogger) Log(format string, a ...interface{}) {
	l.T.Logf("\033[33m[ %s ]\033[0m %s", l.Tag, fmt.Sprintf(format, a...)) // Tag printed in yellow
}

// VoidLogger can be used to deactivate logs by displaying nothing
type VoidLogger struct{}

// Log implements the Logger interface
func (l VoidLogger) Log(format string, a ...interface{}) {}
<commit_msg>[broker] Add MultiLogger to utils/loggers<commit_after>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

// Package log provides some handy types and method to activate and deactivate specific log
// behavior within files in a transparent way.
package log

import (
	"fmt"
	"testing"
)

// Logger is a minimalist interface to represent logger
type Logger interface {
	Log(format string, a ...interface{})
}

// DebugLogger can be used in development to display loglines in the console
type DebugLogger struct {
	Tag string
}

// Log implements the Logger interface
func (l DebugLogger) Log(format string, a ...interface{}) {
	fmt.Printf("\033[33m[ %s ]\033[0m ", l.Tag) // Tag printed in yellow
	fmt.Printf(format, a...)
	fmt.Print("\n")
}

// TestLogger can be used in a test environnement to display log only on failure
type TestLogger struct {
	Tag string
	T   *testing.T
}

// Log implements the Logger interface
func (l TestLogger) Log(format string, a ...interface{}) {
	l.T.Logf("\033[33m[ %s ]\033[0m %s", l.Tag, fmt.Sprintf(format, a...)) // Tag printed in yellow
}

// MultiLogger aggregates several loggers log to each of them
type MultiLogger struct {
	loggers []Logger
}

// Log implements the Logger interface
func (l MultiLogger) Log(format string, a ...interface{}) {
	for _, logger := range l.loggers {
		logger.Log(format, a...)
	}
}
<|endoftext|>