<commit_before>package config

import (
	"log"

	"github.com/aerokube/selenoid/session"
	"strings"
	"sync"
	"io/ioutil"
	"fmt"
	"encoding/json"
	"github.com/docker/docker/api/types/container"
)

// Quota - number of sessions for quota user
type Quota map[string]int

// Version - browser version for quota
type Version map[string]Quota

// Browsers - browser names for versions
type Browsers map[string]Version

// State - current state
type State struct {
	Total    int      `json:"total"`
	Used     int      `json:"used"`
	Queued   int      `json:"queued"`
	Pending  int      `json:"pending"`
	Browsers Browsers `json:"browsers"`
}

// Browser configuration
type Browser struct {
	Image interface{}       `json:"image"`
	Port  string            `json:"port"`
	Path  string            `json:"path"`
	Tmpfs map[string]string `json:"tmpfs"`
}

// Versions configuration
type Versions struct {
	Default  string              `json:"default"`
	Versions map[string]*Browser `json:"versions"`
}

// Config current configuration
type Config struct {
	lock               sync.RWMutex
	Browsers           map[string]Versions
	ContainerLogs *container.LogConfig
}

// NewConfig creates new config
func NewConfig() *Config {
	return &Config{Browsers: make(map[string]Versions), ContainerLogs: new(container.LogConfig)}
}

func loadJSON(filename string, v interface{}) error {
	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		return fmt.Errorf("read error: %v", err)
	}
	if err := json.Unmarshal(buf, v); err != nil {
		return fmt.Errorf("parse error: %v", err)
	}
	return nil
}

// Load loads config from file
func (config *Config) Load(browsers, containerLogs string) error {
	log.Println("Loading configuration files...")
	br := make(map[string]Versions)
	err := loadJSON(browsers, &br)
	if err != nil {
		return fmt.Errorf("browsers config: %v", err)
	}
	log.Printf("Loaded configuration from [%s]\n", browsers)
	var cl *container.LogConfig
	err = loadJSON(containerLogs, &cl)
	if err != nil {
		log.Printf("Using default containers log configuration because of: %v\n", err)
		cl = &container.LogConfig{}
	} else {
		log.Printf("Loaded configuration from [%s]\n", containerLogs)
	}
	config.lock.Lock()
	defer config.lock.Unlock()
	config.Browsers, config.ContainerLogs = br, cl
	return nil
}

// Find - find concrete browser
func (config *Config) Find(name string, version *string) (*Browser, bool) {
	config.lock.RLock()
	defer config.lock.RUnlock()
	browser, ok := config.Browsers[name]
	if !ok {
		return nil, false
	}
	if *version == "" {
		log.Println("Using default version:", browser.Default)
		*version = browser.Default
		if *version == "" {
			return nil, false
		}
	}
	for v, b := range browser.Versions {
		if strings.HasPrefix(v, *version) {
			*version = v
			return b, true
		}
	}
	return nil, false
}

// State - get current state
func (config *Config) State(sessions *session.Map, limit, queued, pending int) *State {
	config.lock.RLock()
	defer config.lock.RUnlock()
	state := &State{limit, 0, queued, pending, make(Browsers)}
	for n, b := range config.Browsers {
		state.Browsers[n] = make(Version)
		for v := range b.Versions {
			state.Browsers[n][v] = make(Quota)
		}
	}
	sessions.Each(func(id string, session *session.Session) {
		state.Used++
		_, ok := state.Browsers[session.Browser]
		if !ok {
			state.Browsers[session.Browser] = make(Version)
		}
		_, ok = state.Browsers[session.Browser][session.Version]
		if !ok {
			state.Browsers[session.Browser][session.Version] = make(Quota)
		}
		_, ok = state.Browsers[session.Browser][session.Version][session.Quota]
		if !ok {
			state.Browsers[session.Browser][session.Version][session.Quota] = 0
		}
		state.Browsers[session.Browser][session.Version][session.Quota]++
	})
	return state
}
<commit_msg>Fix empty tmpfs field<commit_after>package config

import (
	"log"

	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"
	"sync"

	"github.com/aerokube/selenoid/session"
	"github.com/docker/docker/api/types/container"
)

// Quota - number of sessions for quota user
type Quota map[string]int

// Version - browser version for quota
type Version map[string]Quota

// Browsers - browser names for versions
type Browsers map[string]Version

// State - current state
type State struct {
	Total    int      `json:"total"`
	Used     int      `json:"used"`
	Queued   int      `json:"queued"`
	Pending  int      `json:"pending"`
	Browsers Browsers `json:"browsers"`
}

// Browser configuration
type Browser struct {
	Image interface{}       `json:"image"`
	Port  string            `json:"port"`
	Path  string            `json:"path"`
	Tmpfs map[string]string `json:"tmpfs,omitempty"`
}

// Versions configuration
type Versions struct {
	Default  string              `json:"default"`
	Versions map[string]*Browser `json:"versions"`
}

// Config current configuration
type Config struct {
	lock          sync.RWMutex
	Browsers      map[string]Versions
	ContainerLogs *container.LogConfig
}

// NewConfig creates new config
func NewConfig() *Config {
	return &Config{Browsers: make(map[string]Versions), ContainerLogs: new(container.LogConfig)}
}

func loadJSON(filename string, v interface{}) error {
	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		return fmt.Errorf("read error: %v", err)
	}
	if err := json.Unmarshal(buf, v); err != nil {
		return fmt.Errorf("parse error: %v", err)
	}
	return nil
}

// Load loads config from file
func (config *Config) Load(browsers, containerLogs string) error {
	log.Println("Loading configuration files...")
	br := make(map[string]Versions)
	err := loadJSON(browsers, &br)
	if err != nil {
		return fmt.Errorf("browsers config: %v", err)
	}
	log.Printf("Loaded configuration from [%s]\n", browsers)
	var cl *container.LogConfig
	err = loadJSON(containerLogs, &cl)
	if err != nil {
		log.Printf("Using default containers log configuration because of: %v\n", err)
		cl = &container.LogConfig{}
	} else {
		log.Printf("Loaded configuration from [%s]\n", containerLogs)
	}
	config.lock.Lock()
	defer config.lock.Unlock()
	config.Browsers, config.ContainerLogs = br, cl
	return nil
}

// Find - find concrete browser
func (config *Config) Find(name string, version *string) (*Browser, bool) {
	config.lock.RLock()
	defer config.lock.RUnlock()
	browser, ok := config.Browsers[name]
	if !ok {
		return nil, false
	}
	if *version == "" {
		log.Println("Using default version:", browser.Default)
		*version = browser.Default
		if *version == "" {
			return nil, false
		}
	}
	for v, b := range browser.Versions {
		if strings.HasPrefix(v, *version) {
			*version = v
			return b, true
		}
	}
	return nil, false
}

// State - get current state
func (config *Config) State(sessions *session.Map, limit, queued, pending int) *State {
	config.lock.RLock()
	defer config.lock.RUnlock()
	state := &State{limit, 0, queued, pending, make(Browsers)}
	for n, b := range config.Browsers {
		state.Browsers[n] = make(Version)
		for v := range b.Versions {
			state.Browsers[n][v] = make(Quota)
		}
	}
	sessions.Each(func(id string, session *session.Session) {
		state.Used++
		_, ok := state.Browsers[session.Browser]
		if !ok {
			state.Browsers[session.Browser] = make(Version)
		}
		_, ok = state.Browsers[session.Browser][session.Version]
		if !ok {
			state.Browsers[session.Browser][session.Version] = make(Quota)
		}
		_, ok = state.Browsers[session.Browser][session.Version][session.Quota]
		if !ok {
			state.Browsers[session.Browser][session.Version][session.Quota] = 0
		}
		state.Browsers[session.Browser][session.Version][session.Quota]++
	})
	return state
}
<|endoftext|>


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

package core

import (
	"bytes"

	"github.com/google/btree"
	"github.com/pingcap/kvproto/pkg/metapb"
)

var _ btree.Item = &regionItem{}

type regionItem struct {
	region *metapb.Region
}

// Less returns true if the region start key is greater than the other.
// So we will sort the region with start key reversely.
func (r *regionItem) Less(other btree.Item) bool {
	left := r.region.GetStartKey()
	right := other.(*regionItem).region.GetStartKey()
	return bytes.Compare(left, right) > 0
}

func (r *regionItem) Contains(key []byte) bool {
	start, end := r.region.GetStartKey(), r.region.GetEndKey()
	return bytes.Compare(key, start) >= 0 && (len(end) == 0 || bytes.Compare(key, end) < 0)
}

const (
	defaultBTreeDegree = 64
)

type regionTree struct {
	tree *btree.BTree
}

func newRegionTree() *regionTree {
	return &regionTree{
		tree: btree.New(defaultBTreeDegree),
	}
}

func (t *regionTree) length() int {
	return t.tree.Len()
}

// update updates the tree with the region.
// It finds and deletes all the overlapped regions first, and then
// insert the region.
func (t *regionTree) update(region *metapb.Region) []*metapb.Region {
	item := &regionItem{region: region}

	result := t.find(region)
	if result == nil {
		result = item
	}

	var overlaps []*metapb.Region
	t.tree.DescendLessOrEqual(result, func(i btree.Item) bool {
		over := i.(*regionItem)
		if len(region.EndKey) > 0 && bytes.Compare(region.EndKey, over.region.StartKey) <= 0 {
			return false
		}
		overlaps = append(overlaps, over.region)
		return true
	})

	for _, item := range overlaps {
		t.tree.Delete(&regionItem{item})
	}

	t.tree.ReplaceOrInsert(item)

	return overlaps
}

// remove removes a region if the region is in the tree.
// It will do nothing if it cannot find the region or the found region
// is not the same with the region.
func (t *regionTree) remove(region *metapb.Region) {
	result := t.find(region)
	if result == nil || result.region.GetId() != region.GetId() {
		return
	}

	t.tree.Delete(result)
}

// search returns a region that contains the key.
func (t *regionTree) search(regionKey []byte) *metapb.Region {
	region := &metapb.Region{StartKey: regionKey}
	result := t.find(region)
	if result == nil {
		return nil
	}
	return result.region
}

// This is a helper function to find an item.
func (t *regionTree) find(region *metapb.Region) *regionItem {
	item := &regionItem{region: region}

	var result *regionItem
	t.tree.AscendGreaterOrEqual(item, func(i btree.Item) bool {
		result = i.(*regionItem)
		return false
	})

	if result == nil || !result.Contains(region.StartKey) {
		return nil
	}

	return result
}

func (t *regionTree) scanRange(startKey []byte, f func(*metapb.Region) bool) {
	startItem := &regionItem{region: &metapb.Region{StartKey: startKey}}
	t.tree.DescendLessOrEqual(startItem, func(item btree.Item) bool {
		return f(item.(*regionItem).region)
	})
}
<commit_msg>add comment (#997)<commit_after>// Copyright 2016 PingCAP, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package core

import (
	"bytes"

	"github.com/google/btree"
	"github.com/pingcap/kvproto/pkg/metapb"
)

var _ btree.Item = &regionItem{}

type regionItem struct {
	region *metapb.Region
}

// Less returns true if the region start key is greater than the other.
// So we will sort the region with start key reversely.
func (r *regionItem) Less(other btree.Item) bool {
	left := r.region.GetStartKey()
	right := other.(*regionItem).region.GetStartKey()
	return bytes.Compare(left, right) > 0
}

func (r *regionItem) Contains(key []byte) bool {
	start, end := r.region.GetStartKey(), r.region.GetEndKey()
	return bytes.Compare(key, start) >= 0 && (len(end) == 0 || bytes.Compare(key, end) < 0)
}

const (
	defaultBTreeDegree = 64
)

type regionTree struct {
	tree *btree.BTree
}

func newRegionTree() *regionTree {
	return &regionTree{
		tree: btree.New(defaultBTreeDegree),
	}
}

func (t *regionTree) length() int {
	return t.tree.Len()
}

// update updates the tree with the region.
// It finds and deletes all the overlapped regions first, and then
// insert the region.
func (t *regionTree) update(region *metapb.Region) []*metapb.Region {
	item := &regionItem{region: region}

	// note that find() gets the first item equals or greater to the region
	// in the case: |_______a_______|_____b_____|___c___|
	// new region is     |______d______|
	// find() will return regionItem of region_a
	// and both startKey of region_a and region_b are less than endKey of region_d,
	// thus they are regarded as overlapped regions.
	result := t.find(region)
	if result == nil {
		result = item
	}

	var overlaps []*metapb.Region
	t.tree.DescendLessOrEqual(result, func(i btree.Item) bool {
		over := i.(*regionItem)
		if len(region.EndKey) > 0 && bytes.Compare(region.EndKey, over.region.StartKey) <= 0 {
			return false
		}
		overlaps = append(overlaps, over.region)
		return true
	})

	for _, item := range overlaps {
		t.tree.Delete(&regionItem{item})
	}

	t.tree.ReplaceOrInsert(item)

	return overlaps
}

// remove removes a region if the region is in the tree.
// It will do nothing if it cannot find the region or the found region
// is not the same with the region.
func (t *regionTree) remove(region *metapb.Region) {
	result := t.find(region)
	if result == nil || result.region.GetId() != region.GetId() {
		return
	}

	t.tree.Delete(result)
}

// search returns a region that contains the key.
func (t *regionTree) search(regionKey []byte) *metapb.Region {
	region := &metapb.Region{StartKey: regionKey}
	result := t.find(region)
	if result == nil {
		return nil
	}
	return result.region
}

// This is a helper function to find an item.
func (t *regionTree) find(region *metapb.Region) *regionItem {
	item := &regionItem{region: region}

	var result *regionItem
	t.tree.AscendGreaterOrEqual(item, func(i btree.Item) bool {
		result = i.(*regionItem)
		return false
	})

	if result == nil || !result.Contains(region.StartKey) {
		return nil
	}

	return result
}

func (t *regionTree) scanRange(startKey []byte, f func(*metapb.Region) bool) {
	startItem := &regionItem{region: &metapb.Region{StartKey: startKey}}
	t.tree.DescendLessOrEqual(startItem, func(item btree.Item) bool {
		return f(item.(*regionItem).region)
	})
}
<|endoftext|>


<commit_before>package config

import (
	"io/ioutil"
	"path/filepath"

	"gopkg.in/yaml.v2"
)

// Config is the root cfg object
type Config struct {
	File             string             `yaml:",omitempty"`
	WorkingDirectory string             `yaml:"workdir,omitempty"`
	Excludes         []string           `yaml:"excludes"`
	Pipeline         []string           `yaml:"pipeline"`
	Growl            bool               `yaml:"growl,omitempty"`
	Server           string             `yaml:"server,omitempty"`
	Sidecars         map[string]Sidecar `yaml:"sidecars,omitempty"`
}

// Sidecar defines a background ("sidecar") service that is kept running
type Sidecar struct {
	Image       string            `yaml:"image"`
	Environment map[string]string `yaml:"env,omitempty"`
	Script      string            `yaml:"script,omitempty"`
	Volumes     map[string]string `yaml:"volumes,omitempty"`
}

var (
	cfg  *Config
	File string
)

// Load bootstraps the cfg object with the cfg file.
func Load() *Config {
	if cfg != nil {
		return cfg
	}
	configFile, err := filepath.Abs(File)
	if err != nil {
		panic(err)
	}
	cfg, err := ParseConfig(configFile)
	if err != nil {
		panic(err)
	}
	return cfg
}

// Returns the loaded cfg file name.
func (c *Config) GetConfigFile() string {
	return c.file
}

// Reloads this configuration. It will panic if an error occurs.
func (c *Config) Reload() {
	var err error
	_, err = ParseConfig(c.file)
	if err != nil {
		panic(err)
	}
}

// ParseConfig reads and parses a cfg file.
func ParseConfig(cfgFile string) (*Config, error) {
	source, err := ioutil.ReadFile(cfgFile)
	if err != nil {
		return nil, err
	}
	if err := yaml.Unmarshal(source, &cfg); err != nil {
		return nil, err
	}
	cfg.file = cfgFile
	return cfg, nil
}

func SerializeConfig(cfg *Config) ([]byte, error) {
	return yaml.Marshal(cfg)
}
<commit_msg>fixes config<commit_after>package config

import (
	"io/ioutil"
	"path/filepath"

	"gopkg.in/yaml.v2"
)

// Config is the root cfg object
type Config struct {
	File             string             `yaml:",omitempty"`
	WorkingDirectory string             `yaml:"workdir,omitempty"`
	Excludes         []string           `yaml:"excludes"`
	Pipeline         []string           `yaml:"pipeline"`
	Growl            bool               `yaml:"growl,omitempty"`
	Server           string             `yaml:"server,omitempty"`
	Sidecars         map[string]Sidecar `yaml:"sidecars,omitempty"`
}

// Sidecar defines a background ("sidecar") service that is kept running
type Sidecar struct {
	Image       string            `yaml:"image"`
	Environment map[string]string `yaml:"env,omitempty"`
	Script      string            `yaml:"script,omitempty"`
	Volumes     map[string]string `yaml:"volumes,omitempty"`
}

var (
	cfg  *Config
	File string
)

// Load bootstraps the cfg object with the cfg file.
func Load() *Config {
	if cfg != nil {
		return cfg
	}
	configFile, err := filepath.Abs(File)
	if err != nil {
		panic(err)
	}
	cfg, err := ParseConfig(configFile)
	if err != nil {
		panic(err)
	}
	return cfg
}

// Returns the loaded cfg file name.
func (c *Config) GetConfigFile() string {
	return c.File
}

// Reloads this configuration. It will panic if an error occurs.
func (c *Config) Reload() {
	var err error
	_, err = ParseConfig(c.File)
	if err != nil {
		panic(err)
	}
}

// ParseConfig reads and parses a cfg file.
func ParseConfig(cfgFile string) (*Config, error) {
	source, err := ioutil.ReadFile(cfgFile)
	if err != nil {
		return nil, err
	}
	if err := yaml.Unmarshal(source, &cfg); err != nil {
		return nil, err
	}
	cfg.File = cfgFile
	return cfg, nil
}

func SerializeConfig(cfg *Config) ([]byte, error) {
	return yaml.Marshal(cfg)
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package config

import (
	"errors"
	"fmt"
	"math/rand"
	"net/url"
	"os"
	"strings"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	// Viper remote client need to be internally initialized
	_ "github.com/spf13/viper/remote"

	"github.com/skydive-project/skydive/common"
)

// ErrNoAnalyzerSpecified error no analyzer section is specified in the configuration file
var ErrNoAnalyzerSpecified = errors.New("No analyzer specified in the configuration file")

var (
	cfg           *viper.Viper
	relocationMap = map[string]string{
		"auth.keystone.auth_url":               "openstack.auth_url",
		"auth.keystone.tenant_name":            "openstack.tenant_name",
		"auth.keystone.domain_name":            "openstack.domain_name",
		"agent.topology.neutron.auth_url":      "openstack.auth_url",
		"agent.topology.neutron.domain_name":   "openstack.domain_name",
		"agent.topology.neutron.endpoint_type": "openstack.endpoint_type",
		"agent.topology.neutron.password":      "openstack.password",
		"agent.topology.neutron.region_name":   "openstack.region_name",
		"agent.topology.neutron.tenant_name":   "openstack.tenant_name",
		"agent.topology.neutron.username":      "openstack.username",
	}
)

func init() {
	host, err := os.Hostname()
	if err != nil {
		panic(err)
	}

	cfg = viper.New()

	cfg.SetDefault("agent.flow.probes", []string{"gopacket", "pcapsocket"})
	cfg.SetDefault("agent.flow.pcapsocket.bind_address", "127.0.0.1")
	cfg.SetDefault("agent.flow.pcapsocket.min_port", 8100)
	cfg.SetDefault("agent.flow.pcapsocket.max_port", 8132)
	cfg.SetDefault("agent.flow.stats_update", 1)
	cfg.SetDefault("agent.listen", "127.0.0.1:8081")
	cfg.SetDefault("agent.topology.probes", []string{"ovsdb"})
	cfg.SetDefault("agent.topology.netlink.metrics_update", 30)
	cfg.SetDefault("agent.topology.neutron.endpoint_type", "public")
	cfg.SetDefault("agent.topology.socketinfo.host_update", 10)
	cfg.SetDefault("agent.X509_servername", "")

	cfg.SetDefault("analyzer.listen", "127.0.0.1:8082")
	cfg.SetDefault("analyzer.replication.debug", false)
	cfg.SetDefault("analyzer.ssh_enabled", false)
	cfg.SetDefault("analyzer.storage.bulk_insert", 100)
	cfg.SetDefault("analyzer.storage.bulk_insert_deadline", 5)
	cfg.SetDefault("analyzer.storage.max_flow_buffer_size", 100000)
	cfg.SetDefault("analyzer.topology.probes", []string{})

	cfg.SetDefault("auth.type", "noauth")
	cfg.SetDefault("auth.keystone.tenant", "admin")

	cfg.SetDefault("cache.expire", 300)
	cfg.SetDefault("cache.cleanup", 30)

	cfg.SetDefault("docker.url", "unix:///var/run/docker.sock")
	cfg.SetDefault("docker.netns.run_path", "/var/run/docker/netns")

	cfg.SetDefault("etcd.data_dir", "/var/lib/skydive/etcd")
	cfg.SetDefault("etcd.embedded", true)
	cfg.SetDefault("etcd.name", host)
	cfg.SetDefault("etcd.listen", "127.0.0.1:12379")

	cfg.SetDefault("flow.expire", 600)
	cfg.SetDefault("flow.update", 60)
	cfg.SetDefault("flow.protocol", "udp")

	cfg.SetDefault("graph.backend", "memory")

	cfg.SetDefault("http.rest.debug", false)
	cfg.SetDefault("http.ws.ping_delay", 2)
	cfg.SetDefault("http.ws.pong_timeout", 5)
	cfg.SetDefault("http.ws.bulk_maxmsgs", 100)
	cfg.SetDefault("http.ws.bulk_maxdelay", 1)
	cfg.SetDefault("http.ws.queue_size", 10000)
	cfg.SetDefault("http.ws.enable_write_compression", true)

	cfg.SetDefault("host_id", host)

	cfg.SetDefault("http.rest.debug", false)
	cfg.SetDefault("http.ws.ping_delay", 2)
	cfg.SetDefault("http.ws.pong_timeout", 5)
	cfg.SetDefault("http.ws.bulk_maxmsgs", 100)
	cfg.SetDefault("http.ws.bulk_maxdelay", 1)
	cfg.SetDefault("http.ws.queue_size", 10000)
	cfg.SetDefault("http.ws.enable_write_compression", true)

	cfg.SetDefault("k8s.config_file", "/etc/skydive/kubeconfig")
	cfg.SetDefault("k8s.probes", []string{"networkpolicy", "pod", "container", "node", "namespace"})

	cfg.SetDefault("logging.backends", []string{"stderr"})
	cfg.SetDefault("logging.color", true)
	cfg.SetDefault("logging.encoder", "")
	cfg.SetDefault("logging.file.path", "/var/log/skydive.log")
	cfg.SetDefault("logging.level", "INFO")
	cfg.SetDefault("logging.syslog.tag", "skydive")

	cfg.SetDefault("netns.run_path", "/var/run/netns")

	cfg.SetDefault("opencontrail.mpls_udp_port", 51234)

	cfg.SetDefault("ovs.ovsdb", "unix:///var/run/openvswitch/db.sock")
	cfg.SetDefault("ovs.oflow.enable", false)

	cfg.SetDefault("sflow.port_min", 6345)
	cfg.SetDefault("sflow.port_max", 6355)

	cfg.SetDefault("storage.elasticsearch.host", "127.0.0.1:9200")
	cfg.SetDefault("storage.elasticsearch.maxconns", 10)
	cfg.SetDefault("storage.elasticsearch.retry", 60)
	cfg.SetDefault("storage.elasticsearch.bulk_maxdocs", 100)
	cfg.SetDefault("storage.elasticsearch.bulk_maxdelay", 5)
	cfg.SetDefault("storage.orientdb.addr", "http://localhost:2480")
	cfg.SetDefault("storage.orientdb.database", "Skydive")
	cfg.SetDefault("storage.orientdb.username", "root")
	cfg.SetDefault("storage.orientdb.password", "root")

	cfg.SetDefault("ui.bandwidth_absolute_active", 1)
	cfg.SetDefault("ui.bandwidth_absolute_alert", 1000)
	cfg.SetDefault("ui.bandwidth_absolute_warning", 100)
	cfg.SetDefault("ui.bandwidth_relative_active", 0.1)
	cfg.SetDefault("ui.bandwidth_relative_alert", 0.8)
	cfg.SetDefault("ui.bandwidth_relative_warning", 0.5)
	cfg.SetDefault("ui.bandwidth_threshold", "relative")
	cfg.SetDefault("ui.bandwidth_update_rate", 5)

	replacer := strings.NewReplacer(".", "_", "-", "_")
	cfg.SetEnvPrefix("SKYDIVE")
	cfg.SetEnvKeyReplacer(replacer)
	cfg.AutomaticEnv()
}

func checkStrictPositiveInt(key string) error {
	if value := cfg.GetInt(key); value <= 0 {
		return fmt.Errorf("invalid value for %s (%d)", key, value)
	}

	return nil
}

func checkPositiveInt(key string) error {
	if value := cfg.GetInt(key); value < 0 {
		return fmt.Errorf("invalid value for %s (%d)", key, value)
	}

	return nil
}

func checkStrictRangeFloat(key string, min, max float64) error {
	if value := cfg.GetFloat64(key); value <= min || value > max {
		return fmt.Errorf("invalid value for %s (%f)", key, value)
	}

	return nil
}

func checkConfig() error {
	if err := checkStrictPositiveInt("flow.expire"); err != nil {
		return err
	}

	if err := checkStrictPositiveInt("flow.update"); err != nil {
		return err
	}

	if err := checkPositiveInt("etcd.max_wal_files"); err != nil {
		return err
	}

	if err := checkPositiveInt("etcd.max_snap_files"); err != nil {
		return err
	}

	return nil
}

func checkViperSupportedExts(ext string) bool {
	for _, e := range viper.SupportedExts {
		if e == ext {
			return true
		}
	}
	return false
}

// InitConfig with a backend
func InitConfig(backend string, paths []string) error {
	if len(paths) == 0 {
		return fmt.Errorf("Empty configuration path")
	}

	cfg.SetConfigType("yaml")

	switch backend {
	case "file":
		for _, path := range paths {
			configFile, err := os.Open(path)
			if err != nil {
				return err
			}
			if err := cfg.MergeConfig(configFile); err != nil {
				return err
			}
		}
	case "etcd":
		if len(paths) != 1 {
			return fmt.Errorf("You can specify only one etcd endpoint for configuration")
		}
		path := paths[0]
		u, err := url.Parse(path)
		if err != nil {
			return err
		}
		if err := cfg.AddRemoteProvider("etcd", fmt.Sprintf("%s://%s", u.Scheme, u.Host), u.Path); err != nil {
			return err
		}
		if err := cfg.ReadRemoteConfig(); err != nil {
			return err
		}
	default:
		return fmt.Errorf("Invalid backend: %s", backend)
	}

	return checkConfig()
}

// GetConfig get current config
func GetConfig() *viper.Viper {
	return cfg
}

// SetDefault set default configuration key the value
func SetDefault(key string, value interface{}) {
	cfg.SetDefault(key, value)
}

// GetAnalyzerServiceAddresses returns a list of connectable Analyzers
func GetAnalyzerServiceAddresses() ([]common.ServiceAddress, error) {
	var addresses []common.ServiceAddress
	for _, a := range GetStringSlice("analyzers") {
		sa, err := common.ServiceAddressFromString(a)
		if err != nil {
			return nil, err
		}
		addresses = append(addresses, sa)
	}

	// shuffle in order to load balance connections
	for i := range addresses {
		j := rand.Intn(i + 1)
		addresses[i], addresses[j] = addresses[j], addresses[i]
	}

	return addresses, nil
}

// GetOneAnalyzerServiceAddress returns a random connectable Analyzer
func GetOneAnalyzerServiceAddress() (common.ServiceAddress, error) {
	addresses, err := GetAnalyzerServiceAddresses()
	if err != nil {
		return common.ServiceAddress{}, err
	}

	if len(addresses) == 0 {
		return common.ServiceAddress{}, ErrNoAnalyzerSpecified
	}

	return addresses[rand.Intn(len(addresses))], nil
}

// GetEtcdServerAddrs returns the ETCD server address specified in the configuration file or embedded
func GetEtcdServerAddrs() []string {
	etcdServers := GetStringSlice("etcd.servers")
	if len(etcdServers) > 0 {
		return etcdServers
	}
	if address, err := GetOneAnalyzerServiceAddress(); err == nil {
		return []string{"http://" + address.Addr + ":12379"}
	}
	return []string{"http://localhost:12379"}
}

// IsTLSenabled returns true is the analyzer certificates are set
func IsTLSenabled() bool {
	certPEM := GetString("analyzer.X509_cert")
	keyPEM := GetString("analyzer.X509_key")
	if len(certPEM) > 0 && len(keyPEM) > 0 {
		return true
	}
	return false
}

func realKey(key string) string {
	for {
		if cfg.IsSet(key) {
			return key
		}
		newKey, found := relocationMap[key]
		if !found {
			return key
		}
		fmt.Fprintf(os.Stderr, "Config value '%s' is now deprecated. Please use '%s' instead\n", key, newKey)
		key = newKey
	}
}

// Get returns a value of the configuration as in interface
func Get(key string) interface{} {
	return cfg.Get(realKey(key))
}

// Set a value of the configuration
func Set(key string, value interface{}) {
	cfg.Set(key, value)
}

// GetBool returns a boolean from the configuration
func GetBool(key string) bool {
	return cfg.GetBool(realKey(key))
}

// GetInt returns an interger from the configuration
func GetInt(key string) int {
	return cfg.GetInt(realKey(key))
}

// GetString returns a string from the configuration
func GetString(key string) string {
	return cfg.GetString(realKey(key))
}

// GetStringSlice returns a slice of strings from the configuration
func GetStringSlice(key string) []string {
	return cfg.GetStringSlice(realKey(key))
}

// GetStringMapString returns a map of strings from the configuration
func GetStringMapString(key string) map[string]string {
	return cfg.GetStringMapString(realKey(key))
}

// BindPFlag binds a command line flag to a configuration value
func BindPFlag(key string, flag *pflag.Flag) error {
	return cfg.BindPFlag(key, flag)
}

// GetURL constructs a URL from a tuple of protocol, address, port and path
// If TLS is enabled, it will return the https (or wss) version of the URL.
func GetURL(protocol string, addr string, port int, path string) *url.URL {
	u, _ := url.Parse(fmt.Sprintf("%s://%s:%d%s", protocol, addr, port, path))

	if (protocol == "http" || protocol == "ws") && IsTLSenabled() == true {
		u.Scheme += "s"
	}

	return u
}
<commit_msg>api: config api use default value for type inference<commit_after>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package config

import (
	"errors"
	"fmt"
	"math/rand"
	"net/url"
	"os"
	"strings"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	// Viper remote client need to be internally initialized
	_ "github.com/spf13/viper/remote"

	"github.com/skydive-project/skydive/common"
)

// ErrNoAnalyzerSpecified error no analyzer section is specified in the configuration file
var ErrNoAnalyzerSpecified = errors.New("No analyzer specified in the configuration file")

var (
	cfg           *viper.Viper
	relocationMap = map[string]string{
		"auth.keystone.auth_url":               "openstack.auth_url",
		"auth.keystone.tenant_name":            "openstack.tenant_name",
		"auth.keystone.domain_name":            "openstack.domain_name",
		"agent.topology.neutron.auth_url":      "openstack.auth_url",
		"agent.topology.neutron.domain_name":   "openstack.domain_name",
		"agent.topology.neutron.endpoint_type": "openstack.endpoint_type",
		"agent.topology.neutron.password":      "openstack.password",
		"agent.topology.neutron.region_name":   "openstack.region_name",
		"agent.topology.neutron.tenant_name":   "openstack.tenant_name",
		"agent.topology.neutron.username":      "openstack.username",
	}
)

func init() {
	host, err := os.Hostname()
	if err != nil {
		panic(err)
	}

	cfg = viper.New()

	cfg.SetDefault("agent.flow.probes", []string{"gopacket", "pcapsocket"})
	cfg.SetDefault("agent.flow.pcapsocket.bind_address", "127.0.0.1")
	cfg.SetDefault("agent.flow.pcapsocket.min_port", 8100)
	cfg.SetDefault("agent.flow.pcapsocket.max_port", 8132)
	cfg.SetDefault("agent.flow.stats_update", 1)
	cfg.SetDefault("agent.listen", "127.0.0.1:8081")
	cfg.SetDefault("agent.topology.probes", []string{"ovsdb"})
	cfg.SetDefault("agent.topology.netlink.metrics_update", 30)
	cfg.SetDefault("agent.topology.neutron.endpoint_type", "public")
	cfg.SetDefault("agent.topology.socketinfo.host_update", 10)
	cfg.SetDefault("agent.X509_servername", "")

	cfg.SetDefault("analyzer.listen", "127.0.0.1:8082")
	cfg.SetDefault("analyzer.replication.debug", false)
	cfg.SetDefault("analyzer.ssh_enabled", false)
	cfg.SetDefault("analyzer.storage.bulk_insert", 100)
	cfg.SetDefault("analyzer.storage.bulk_insert_deadline", 5)
	cfg.SetDefault("analyzer.storage.max_flow_buffer_size", 100000)
	cfg.SetDefault("analyzer.topology.probes", []string{})

	cfg.SetDefault("auth.type", "noauth")
	cfg.SetDefault("auth.keystone.tenant", "admin")

	cfg.SetDefault("cache.expire", 300)
	cfg.SetDefault("cache.cleanup", 30)

	cfg.SetDefault("docker.url", "unix:///var/run/docker.sock")
	cfg.SetDefault("docker.netns.run_path", "/var/run/docker/netns")

	cfg.SetDefault("etcd.data_dir", "/var/lib/skydive/etcd")
	cfg.SetDefault("etcd.embedded", true)
	cfg.SetDefault("etcd.name", host)
	cfg.SetDefault("etcd.listen", "127.0.0.1:12379")

	cfg.SetDefault("flow.expire", 600)
	cfg.SetDefault("flow.update", 60)
	cfg.SetDefault("flow.protocol", "udp")

	cfg.SetDefault("graph.backend", "memory")

	cfg.SetDefault("http.rest.debug", false)
	cfg.SetDefault("http.ws.ping_delay", 2)
	cfg.SetDefault("http.ws.pong_timeout", 5)
	cfg.SetDefault("http.ws.bulk_maxmsgs", 100)
	cfg.SetDefault("http.ws.bulk_maxdelay", 1)
	cfg.SetDefault("http.ws.queue_size", 10000)
	cfg.SetDefault("http.ws.enable_write_compression", true)

	cfg.SetDefault("host_id", host)

	cfg.SetDefault("http.rest.debug", false)
	cfg.SetDefault("http.ws.ping_delay", 2)
	cfg.SetDefault("http.ws.pong_timeout", 5)
	cfg.SetDefault("http.ws.bulk_maxmsgs", 100)
	cfg.SetDefault("http.ws.bulk_maxdelay", 1)
	cfg.SetDefault("http.ws.queue_size", 10000)
	cfg.SetDefault("http.ws.enable_write_compression", true)

	cfg.SetDefault("k8s.config_file", "/etc/skydive/kubeconfig")
	cfg.SetDefault("k8s.probes", []string{"networkpolicy", "pod", "container", "node", "namespace"})

	cfg.SetDefault("logging.backends", []string{"stderr"})
	cfg.SetDefault("logging.color", true)
	cfg.SetDefault("logging.encoder", "")
	cfg.SetDefault("logging.file.path", "/var/log/skydive.log")
	cfg.SetDefault("logging.level", "INFO")
	cfg.SetDefault("logging.syslog.tag", "skydive")

	cfg.SetDefault("netns.run_path", "/var/run/netns")

	cfg.SetDefault("opencontrail.mpls_udp_port", 51234)

	cfg.SetDefault("ovs.ovsdb", "unix:///var/run/openvswitch/db.sock")
	cfg.SetDefault("ovs.oflow.enable", false)

	cfg.SetDefault("sflow.port_min", 6345)
	cfg.SetDefault("sflow.port_max", 6355)

	cfg.SetDefault("storage.elasticsearch.host", "127.0.0.1:9200")
	cfg.SetDefault("storage.elasticsearch.maxconns", 10)
	cfg.SetDefault("storage.elasticsearch.retry", 60)
	cfg.SetDefault("storage.elasticsearch.bulk_maxdocs", 100)
	cfg.SetDefault("storage.elasticsearch.bulk_maxdelay", 5)
	cfg.SetDefault("storage.orientdb.addr", "http://localhost:2480")
	cfg.SetDefault("storage.orientdb.database", "Skydive")
	cfg.SetDefault("storage.orientdb.username", "root")
	cfg.SetDefault("storage.orientdb.password", "root")

	cfg.SetDefault("ui.bandwidth_absolute_active", 1)
	cfg.SetDefault("ui.bandwidth_absolute_alert", 1000)
	cfg.SetDefault("ui.bandwidth_absolute_warning", 100)
	cfg.SetDefault("ui.bandwidth_relative_active", 0.1)
	cfg.SetDefault("ui.bandwidth_relative_alert", 0.8)
	cfg.SetDefault("ui.bandwidth_relative_warning", 0.5)
	cfg.SetDefault("ui.bandwidth_threshold", "relative")
	cfg.SetDefault("ui.bandwidth_update_rate", 5)
	cfg.SetDefault("ui.topology.favorites", map[string]string{})

	replacer := strings.NewReplacer(".", "_", "-", "_")
	cfg.SetEnvPrefix("SKYDIVE")
	cfg.SetEnvKeyReplacer(replacer)
	cfg.AutomaticEnv()
	cfg.SetTypeByDefaultValue(true)
}

func checkStrictPositiveInt(key string) error {
	if value := cfg.GetInt(key); value <= 0 {
		return fmt.Errorf("invalid value for %s (%d)", key, value)
	}

	return nil
}

func checkPositiveInt(key string) error {
	if value := cfg.GetInt(key); value < 0 {
		return fmt.Errorf("invalid value for %s (%d)", key, value)
	}

	return nil
}

func checkStrictRangeFloat(key string, min, max float64) error {
	if value := cfg.GetFloat64(key); value <= min || value > max {
		return fmt.Errorf("invalid value for %s (%f)", key, value)
	}

	return nil
}

func checkConfig() error {
	if err := checkStrictPositiveInt("flow.expire"); err != nil {
		return err
	}

	if err := checkStrictPositiveInt("flow.update"); err != nil {
		return err
	}

	if err := checkPositiveInt("etcd.max_wal_files"); err != nil {
		return err
	}

	if err := checkPositiveInt("etcd.max_snap_files"); err != nil {
		return err
	}

	return nil
}

func checkViperSupportedExts(ext string) bool {
	for _, e := range viper.SupportedExts {
		if e == ext {
			return true
		}
	}
	return false
}

// InitConfig with a backend
func InitConfig(backend string, paths []string) error {
	if len(paths) == 0 {
		return fmt.Errorf("Empty configuration path")
	}

	cfg.SetConfigType("yaml")

	switch backend {
	case "file":
		for _, path := range paths {
			configFile, err := os.Open(path)
			if err != nil {
				return err
			}
			if err := cfg.MergeConfig(configFile); err != nil {
				return err
			}
		}
	case "etcd":
		if len(paths) != 1 {
			return fmt.Errorf("You can specify only one etcd endpoint for configuration")
		}
		path := paths[0]
		u, err := url.Parse(path)
		if err != nil {
			return err
		}
		if err := cfg.AddRemoteProvider("etcd", fmt.Sprintf("%s://%s", u.Scheme, u.Host), u.Path); err != nil {
			return err
		}
		if err := cfg.ReadRemoteConfig(); err != nil {
			return err
		}
	default:
		return fmt.Errorf("Invalid backend: %s", backend)
	}

	return checkConfig()
}

// GetConfig get current config
func GetConfig() *viper.Viper {
	return cfg
}

// SetDefault set default configuration key the value
func SetDefault(key string, value interface{}) {
	cfg.SetDefault(key, value)
}

// GetAnalyzerServiceAddresses returns a list of connectable Analyzers
func GetAnalyzerServiceAddresses() ([]common.ServiceAddress, error) {
	var addresses []common.ServiceAddress
	for _, a := range GetStringSlice("analyzers") {
		sa, err := common.ServiceAddressFromString(a)
		if err != nil {
			return nil, err
		}
		addresses = append(addresses, sa)
	}

	// shuffle in order to load balance connections
	for i := range addresses {
		j := rand.Intn(i + 1)
		addresses[i], addresses[j] = addresses[j], addresses[i]
	}

	return addresses, nil
}

// GetOneAnalyzerServiceAddress returns a random connectable Analyzer
func GetOneAnalyzerServiceAddress() (common.ServiceAddress, error) {
	addresses, err := GetAnalyzerServiceAddresses()
	if err != nil {
		return common.ServiceAddress{}, err
	}

	if len(addresses) == 0 {
		return common.ServiceAddress{}, ErrNoAnalyzerSpecified
	}

	return addresses[rand.Intn(len(addresses))], nil
}

// GetEtcdServerAddrs returns the ETCD server address specified in the configuration file or embedded
func GetEtcdServerAddrs() []string {
	etcdServers := GetStringSlice("etcd.servers")
	if len(etcdServers) > 0 {
		return etcdServers
	}
	if address, err := GetOneAnalyzerServiceAddress(); err == nil {
		return []string{"http://" + address.Addr + ":12379"}
	}
	return []string{"http://localhost:12379"}
}

// IsTLSenabled returns true is the analyzer certificates are set
func IsTLSenabled() bool {
	certPEM := GetString("analyzer.X509_cert")
	keyPEM := GetString("analyzer.X509_key")
	if len(certPEM) > 0 && len(keyPEM) > 0 {
		return true
	}
	return false
}

func realKey(key string) string {
	for {
		if cfg.IsSet(key) {
			return key
		}
		newKey, found := relocationMap[key]
		if !found {
			return key
		}
		fmt.Fprintf(os.Stderr, "Config value '%s' is now deprecated. Please use '%s' instead\n", key, newKey)
		key = newKey
	}
}

// Get returns a value of the configuration as in interface
func Get(key string) interface{} {
	return cfg.Get(realKey(key))
}

// Set a value of the configuration
func Set(key string, value interface{}) {
	cfg.Set(key, value)
}

// GetBool returns a boolean from the configuration
func GetBool(key string) bool {
	return cfg.GetBool(realKey(key))
}

// GetInt returns an interger from the configuration
func GetInt(key string) int {
	return cfg.GetInt(realKey(key))
}

// GetString returns a string from the configuration
func GetString(key string) string {
	return cfg.GetString(realKey(key))
}

// GetStringSlice returns a slice of strings from the configuration
func GetStringSlice(key string) []string {
	return cfg.GetStringSlice(realKey(key))
}

// GetStringMapString returns a map of strings from the configuration
func GetStringMapString(key string) map[string]string {
	return cfg.GetStringMapString(realKey(key))
}

// BindPFlag binds a command line flag to a configuration value
func BindPFlag(key string, flag *pflag.Flag) error {
	return cfg.BindPFlag(key, flag)
}

// GetURL constructs a URL from a tuple of protocol, address, port and path
// If TLS is enabled, it will return the https (or wss) version of the URL.
func GetURL(protocol string, addr string, port int, path string) *url.URL {
	u, _ := url.Parse(fmt.Sprintf("%s://%s:%d%s", protocol, addr, port, path))

	if (protocol == "http" || protocol == "ws") && IsTLSenabled() == true {
		u.Scheme += "s"
	}

	return u
}
<|endoftext|>


<commit_before>package config

import (
	"net"

	"github.com/kelseyhightower/envconfig"
)

const prefix = "cctldstats"

// CCTLDStats global project configuration.
var CCTLDStats *cctldstatsConfig

// config contains all configuration parameters for running the statistic services.
type cctldstatsConfig struct {
	// Database stores all the database information to connect to the back-end.
	Database struct {
		// King type of the database, possible values are: mysql or postgres.
		Kind string `envconfig:"kind"`

		// Name name of the database.
		Name string `envconfig:"name"`

		// Username user used to connect to the desired database.
		Username string `envconfig:"username"`

		// Password password used to connect to the desired database.
		Password string `envconfig:"password"`

		// Host address of the database
		Host string `envconfig:"host"`
	} `envconfig:"database"`

	// DomainTableName name of the table in the database that stores the domains
	DomainTableName string `envconfig:"domain_table_name"`

	// ACL IP addresses that are allowed to retrieve information from the services.
	ACL []net.IP `envconfig:"acl"`
}

// Load fill the global configuration variable using default values and environment variables.
func Load() error {
	CCTLDStats = new(cctldstatsConfig)
	CCTLDStats.Database.Kind = "mysql"
	CCTLDStats.Database.Host = "localhost"
	CCTLDStats.DomainTableName = "domain"
	CCTLDStats.ACL = []net.IP{net.ParseIP("127.0.0.1")}
	return envconfig.Process(prefix, CCTLDStats)
}
<commit_msg>Add safety check<commit_after>package config

import (
	"errors"
	"net"
	"strings"

	"github.com/kelseyhightower/envconfig"
)

const prefix = "cctldstats"

// CCTLDStats global project configuration.
var CCTLDStats *cctldstatsConfig

// config contains all configuration parameters for running the statistic services.
type cctldstatsConfig struct {
	// Database stores all the database information to connect to the back-end.
	Database struct {
		// King type of the database, possible values are: mysql or postgres.
		Kind string `envconfig:"kind"`

		// Name name of the database.
		Name string `envconfig:"name"`

		// Username user used to connect to the desired database.
		Username string `envconfig:"username"`

		// Password password used to connect to the desired database.
		Password string `envconfig:"password"`

		// Host address of the database
		Host string `envconfig:"host"`
	} `envconfig:"database"`

	// DomainTableName name of the table in the database that stores the domains
	DomainTableName string `envconfig:"domain_table_name"`

	// ACL IP addresses that are allowed to retrieve information from the services.
	ACL []net.IP `envconfig:"acl"`
}

// Load fill the global configuration variable using default values and environment variables.
func Load() error {
	CCTLDStats = new(cctldstatsConfig)
	CCTLDStats.Database.Kind = "mysql"
	CCTLDStats.Database.Host = "localhost"
	CCTLDStats.DomainTableName = "domain"
	CCTLDStats.ACL = []net.IP{net.ParseIP("127.0.0.1")}

	if err := envconfig.Process(prefix, CCTLDStats); err != nil {
		return err
	}

	// safety check to avoid wrongly setting the domain table name (SQL injection)
	CCTLDStats.DomainTableName = strings.TrimSpace(CCTLDStats.DomainTableName)
	if CCTLDStats.DomainTableName == "" || len(strings.Split(CCTLDStats.DomainTableName, " ")) != 1 {
		return errors.New("Did you configured the domain table name correctly?")
	}

	return nil
}
<|endoftext|>


<commit_before>package config

import (
    "fmt"
    "strings"
)

// A single Config may contain Nodes from different Sources
// TODO: tag each Config object with a Node/Source, such that
//		 sources override, and recursive updates are scoped to that source only
type Source interface {
    // uniquely identifying
    String()    string
}

/* Config objects */
type Meta struct {
	node		Node	// origin node
}

func (meta Meta) Source() string {
	if meta.node.Source != nil {
		return meta.node.Source.String()
	} else {
		// some nodes are implicitly created, and thus not directly applicable..
		return ""
	}
}

func (meta Meta) Path() string {
	return meta.node.Path
}

type ServiceFrontend struct {
	Meta			`json:"-"`

    IPv4    string  `json:"ipv4,omitempty"`
    IPv6    string  `json:"ipv6,omitempty"`
    TCP     uint16  `json:"tcp,omitempty"`
    UDP     uint16  `json:"udp,omitempty"`
}

type ServiceBackend struct {
	Meta			`json:"-"`

    IPv4    string  `json:"ipv4,omitempty"`
    IPv6    string  `json:"ipv6,omitempty"`
    TCP     uint16  `json:"tcp,omitempty"`
    UDP     uint16  `json:"udp,omitempty"`

    Weight  uint    `json:"weight,omitempty"`   // default: 10
}

const ServiceBackendWeight uint = 10

type Service struct {
	Meta			`json:"-"`

    Frontend        ServiceFrontend
    Backends        map[string]ServiceBackend
}

// Copy-on-write update for .Backends
func (service *Service) setBackend(backendName string, serviceBackend ServiceBackend, remove bool) {
    serviceBackends := make(map[string]ServiceBackend)

    for backendName, backend := range service.Backends {
        serviceBackends[backendName] = backend
    }

    if remove {
        delete(serviceBackends, backendName)
    } else {
        serviceBackends[backendName] = serviceBackend
    }

    service.Backends = serviceBackends
}

// Apply a recursive remove from source
func (service *Service) removeBackends(source Source) {
    serviceBackends := make(map[string]ServiceBackend)

    for backendName, backend := range service.Backends {
		if backend.Source() == source.String() {
			//log.Printf("service %s drop source=%s backends: %s source=%s\n", service.Path(), source.String(), backendName, backend.Source())

			// remove all from this source
			continue
		} else {
			//log.Printf("service %s keep source=%s backends: %s source=%s\n", service.Path(), source.String(), backendName, backend.Source())
		}

        serviceBackends[backendName] = backend
    }

    service.Backends = serviceBackends
}

type Route struct {
	Meta			`json:"-"`

    // IPv4 prefix to match
    // empty for default match
    Prefix4     string

    // Override backend IPv4 address for ipvs
    Gateway4    string

    // Configure IPVS fwd-method to use for destination
    //  droute tunnel masq
    // Filter out backend if set to
    //  filter
    IpvsMethod  string
}

// Top-level config object
type Config struct {
    Routes      map[string]Route
    Services    map[string]Service
}

// Copy-on-write update for .Services
func (config *Config) setService(serviceName string, service Service, remove bool) {
    services := make(map[string]Service)

    for serviceName, service := range config.Services {
        services[serviceName] = service
    }

    if remove {
        delete(services, serviceName)
    } else {
        services[serviceName] = service
    }

    config.Services = services
}

func (config *Config) updateServices(node Node) error {
	if node.Remove {
		services := make(map[string]Service)

		for serviceName, service := range config.Services {
			if service.Source() == node.Source.String() {
				// remove service from this source
				continue
			} else {
				// remove any backends from this source
				service.removeBackends(node.Source)
			}

			services[serviceName] = service
		}

		config.Services = services
	}

    return nil
}

func (config *Config) updateService(node Node, serviceName string) error {
    service := config.Services[serviceName]

    config.setService(serviceName, service, node.Remove)

    return nil
}

func (config *Config) updateServiceFrontend(node Node, serviceName string, serviceFrontend ServiceFrontend) error {
    service := config.Services[serviceName]

	// service is owned by whatever source has its Frontend
	service.node = node
    service.Frontend = serviceFrontend

    config.setService(serviceName, service, node.Remove)

    return nil
}

func (config *Config) updateServiceBackends(node Node, serviceName string) error {
    service := config.Services[serviceName]

	if node.Remove {
		service.removeBackends(node.Source)
	}

    config.setService(serviceName, service, false)

    return nil
}

func (config *Config) updateServiceBackend(node Node, serviceName string, backendName string, serviceBackend ServiceBackend) error {
    service := config.Services[serviceName]

    service.setBackend(backendName, serviceBackend, node.Remove)
    config.setService(serviceName, service, false)

    return nil
}

func (config *Config) updateRoutes(node Node) error {
	if node.Remove {
		routes := make(map[string]Route)

		for routeName, route := range config.Routes {
			if route.Source() == node.Source.String() {
				// remove from this source
				continue
			}

			routes[routeName] = route
		}

		config.Routes = routes
	}

    return nil
}

func (config *Config) updateRoute(node Node, routeName string, route Route) error {
    routes := make(map[string]Route)

    for name, route := range config.Routes {
        routes[name] = route
    }

    if node.Remove {
        // clear
        delete(routes, routeName)
    } else {
        routes[routeName] = route
    }

    config.Routes = routes

    return nil
}

// Update config from Node
func (config *Config) update(node Node) error {
    nodePath := strings.Split(node.Path, "/")

    if len(node.Path) == 0 {
        // Split("", "/") would give [""]
        nodePath = nil
    }

    // match config tree path
    if len(nodePath) == 0 && node.IsDir {
        // XXX: just ignore? Undefined if it makes sense to do anything here
        return nil

    } else if len(nodePath) == 1 && nodePath[0] == "services" && node.IsDir {
        // recursive on all services
        return config.updateServices(node)

    } else if len(nodePath) >= 2 && nodePath[0] == "services" {
        serviceName := nodePath[1]

        if len(nodePath) == 2 && node.IsDir {
            return config.updateService(node, serviceName)

        } else if len(nodePath) == 3 && nodePath[2] == "frontend" && !node.IsDir {
            var serviceFrontend = ServiceFrontend{
				Meta:       Meta{node:node},
			}

            if err := node.unmarshal(&serviceFrontend); err != nil {
                return fmt.Errorf("service %s frontend: %s", serviceName, err)
            }

            return config.updateServiceFrontend(node, serviceName, serviceFrontend)

        } else if len(nodePath) == 3 && nodePath[2] == "backends" && node.IsDir {
            // recursive on all backends
            return config.updateServiceBackends(node, serviceName)

        } else if len(nodePath) >= 4 && nodePath[2] == "backends" {
            backendName := nodePath[3]

            if len(nodePath) == 4 && !node.IsDir {
                var serviceBackend = ServiceBackend{
					Meta:       Meta{node:node},
                    Weight:     ServiceBackendWeight,
                }

                if err := node.unmarshal(&serviceBackend); err != nil {
                    return fmt.Errorf("service %s backend %s: %s", serviceName, backendName, err)
                }

                return config.updateServiceBackend(node, serviceName, backendName, serviceBackend)

            } else {
                return fmt.Errorf("Ignore unknown service %s backends node", serviceName)
            }

        } else {
            return fmt.Errorf("Ignore unknown service %s node", serviceName)
        }

    } else if len(nodePath) == 1 && nodePath[0] == "routes" && node.IsDir {
        return config.updateRoutes(node)

    } else if len(nodePath) >= 2 && nodePath[0] == "routes" {
        routeName := nodePath[1]

        if len(nodePath) == 2 && !node.IsDir {
            var route = Route{
				Meta:       Meta{node:node},
			}

            if err := node.unmarshal(&route); err != nil {
                return fmt.Errorf("route %s: %s", routeName, err)
            }

            return config.updateRoute(node, routeName, route)

        } else {
            return fmt.Errorf("Ignore unknown route node")
        }
    } else {
        return fmt.Errorf("Ignore unknown node")
    }
}

/*
func (self ConfigService) Path() string {
    return makePath("services", self.ServiceName)
}

func (self ConfigServiceFrontend) Path() string {
    return makePath("services", self.ServiceName, "frontend")
}

func (self ConfigServiceBackend) Path() string {
    return makePath("services", self.ServiceName, "backends", self.BackendName)
}

func (self ConfigRoute) Path() string {
    return makePath("routes", self.RouteName)
}
*/
<commit_msg>config: fix Config.update() with nil Node.Source<commit_after>package config

import (
    "fmt"
    "strings"
)

// A single Config may contain Nodes from different Sources
// TODO: tag each Config object with a Node/Source, such that
//		 sources override, and recursive updates are scoped to that source only
type Source interface {
    // uniquely identifying
    String()    string
}

/* Config objects */
type Meta struct {
	node		Node	// origin node
}

func (meta Meta) Source() string {
	if meta.node.Source != nil {
		return meta.node.Source.String()
	} else {
		// some nodes are implicitly created, and thus not directly applicable..
		return ""
	}
}

func (meta Meta) Path() string {
	return meta.node.Path
}

type ServiceFrontend struct {
	Meta			`json:"-"`

    IPv4    string  `json:"ipv4,omitempty"`
    IPv6    string  `json:"ipv6,omitempty"`
    TCP     uint16  `json:"tcp,omitempty"`
    UDP     uint16  `json:"udp,omitempty"`
}

type ServiceBackend struct {
	Meta			`json:"-"`

    IPv4    string  `json:"ipv4,omitempty"`
    IPv6    string  `json:"ipv6,omitempty"`
    TCP     uint16  `json:"tcp,omitempty"`
    UDP     uint16  `json:"udp,omitempty"`

    Weight  uint    `json:"weight,omitempty"`   // default: 10
}

const ServiceBackendWeight uint = 10

type Service struct {
	Meta			`json:"-"`

    Frontend        ServiceFrontend
    Backends        map[string]ServiceBackend
}

// Copy-on-write update for .Backends
func (service *Service) setBackend(backendName string, serviceBackend ServiceBackend, remove bool) {
    serviceBackends := make(map[string]ServiceBackend)

    for backendName, backend := range service.Backends {
        serviceBackends[backendName] = backend
    }

    if remove {
        delete(serviceBackends, backendName)
    } else {
        serviceBackends[backendName] = serviceBackend
    }

    service.Backends = serviceBackends
}

// Apply a recursive remove from source
func (service *Service) removeBackends(source Source) {
    serviceBackends := make(map[string]ServiceBackend)

    for backendName, backend := range service.Backends {
		if source == nil || backend.Source() == source.String() {
			//log.Printf("service %s drop source=%s backends: %s source=%s\n", service.Path(), source.String(), backendName, backend.Source())

			// remove all from this source
			continue
		} else {
			//log.Printf("service %s keep source=%s backends: %s source=%s\n", service.Path(), source.String(), backendName, backend.Source())
		}

        serviceBackends[backendName] = backend
    }

    service.Backends = serviceBackends
}

type Route struct {
	Meta			`json:"-"`

    // IPv4 prefix to match
    // empty for default match
    Prefix4     string

    // Override backend IPv4 address for ipvs
    Gateway4    string

    // Configure IPVS fwd-method to use for destination
    //  droute tunnel masq
    // Filter out backend if set to
    //  filter
    IpvsMethod  string
}

// Top-level config object
type Config struct {
    Routes      map[string]Route
    Services    map[string]Service
}

// Copy-on-write update for .Services
func (config *Config) setService(serviceName string, service Service, remove bool) {
    services := make(map[string]Service)

    for serviceName, service := range config.Services {
        services[serviceName] = service
    }

    if remove {
        delete(services, serviceName)
    } else {
        services[serviceName] = service
    }

    config.Services = services
}

func (config *Config) updateServices(node Node) error {
	if node.Remove {
		services := make(map[string]Service)

		for serviceName, service := range config.Services {
			if node.Source == nil || service.Source() == node.Source.String() {
				// remove service from this source
				continue
			} else {
				// remove any backends from this source
				service.removeBackends(node.Source)
			}

			services[serviceName] = service
		}

		config.Services = services
	}

    return nil
}

func (config *Config) updateService(node Node, serviceName string) error {
    service := config.Services[serviceName]

    config.setService(serviceName, service, node.Remove)

    return nil
}

func (config *Config) updateServiceFrontend(node Node, serviceName string, serviceFrontend ServiceFrontend) error {
    service := config.Services[serviceName]

	// service is owned by whatever source has its Frontend
	service.node = node
    service.Frontend = serviceFrontend

    config.setService(serviceName, service, node.Remove)

    return nil
}

func (config *Config) updateServiceBackends(node Node, serviceName string) error {
    service := config.Services[serviceName]

	if node.Remove {
		service.removeBackends(node.Source)
	}

    config.setService(serviceName, service, false)

    return nil
}

func (config *Config) updateServiceBackend(node Node, serviceName string, backendName string, serviceBackend ServiceBackend) error {
    service := config.Services[serviceName]

    service.setBackend(backendName, serviceBackend, node.Remove)
    config.setService(serviceName, service, false)

    return nil
}

func (config *Config) updateRoutes(node Node) error {
	if node.Remove {
		routes := make(map[string]Route)

		for routeName, route := range config.Routes {
			if route.Source() == node.Source.String() {
				// remove from this source
				continue
			}

			routes[routeName] = route
		}

		config.Routes = routes
	}

    return nil
}

func (config *Config) updateRoute(node Node, routeName string, route Route) error {
    routes := make(map[string]Route)

    for name, route := range config.Routes {
        routes[name] = route
    }

    if node.Remove {
        // clear
        delete(routes, routeName)
    } else {
        routes[routeName] = route
    }

    config.Routes = routes

    return nil
}

// Update config from Node
func (config *Config) update(node Node) error {
    nodePath := strings.Split(node.Path, "/")

    if len(node.Path) == 0 {
        // Split("", "/") would give [""]
        nodePath = nil
    }

    // match config tree path
    if len(nodePath) == 0 && node.IsDir {
        // XXX: just ignore? Undefined if it makes sense to do anything here
        return nil

    } else if len(nodePath) == 1 && nodePath[0] == "services" && node.IsDir {
        // recursive on all services
        return config.updateServices(node)

    } else if len(nodePath) >= 2 && nodePath[0] == "services" {
        serviceName := nodePath[1]

        if len(nodePath) == 2 && node.IsDir {
            return config.updateService(node, serviceName)

        } else if len(nodePath) == 3 && nodePath[2] == "frontend" && !node.IsDir {
            var serviceFrontend = ServiceFrontend{
				Meta:       Meta{node:node},
			}

            if err := node.unmarshal(&serviceFrontend); err != nil {
                return fmt.Errorf("service %s frontend: %s", serviceName, err)
            }

            return config.updateServiceFrontend(node, serviceName, serviceFrontend)

        } else if len(nodePath) == 3 && nodePath[2] == "backends" && node.IsDir {
            // recursive on all backends
            return config.updateServiceBackends(node, serviceName)

        } else if len(nodePath) >= 4 && nodePath[2] == "backends" {
            backendName := nodePath[3]

            if len(nodePath) == 4 && !node.IsDir {
                var serviceBackend = ServiceBackend{
					Meta:       Meta{node:node},
                    Weight:     ServiceBackendWeight,
                }

                if err := node.unmarshal(&serviceBackend); err != nil {
                    return fmt.Errorf("service %s backend %s: %s", serviceName, backendName, err)
                }

                return config.updateServiceBackend(node, serviceName, backendName, serviceBackend)

            } else {
                return fmt.Errorf("Ignore unknown service %s backends node", serviceName)
            }

        } else {
            return fmt.Errorf("Ignore unknown service %s node", serviceName)
        }

    } else if len(nodePath) == 1 && nodePath[0] == "routes" && node.IsDir {
        return config.updateRoutes(node)

    } else if len(nodePath) >= 2 && nodePath[0] == "routes" {
        routeName := nodePath[1]

        if len(nodePath) == 2 && !node.IsDir {
            var route = Route{
				Meta:       Meta{node:node},
			}

            if err := node.unmarshal(&route); err != nil {
                return fmt.Errorf("route %s: %s", routeName, err)
            }

            return config.updateRoute(node, routeName, route)

        } else {
            return fmt.Errorf("Ignore unknown route node")
        }
    } else {
        return fmt.Errorf("Ignore unknown node")
    }
}

/*
func (self ConfigService) Path() string {
    return makePath("services", self.ServiceName)
}

func (self ConfigServiceFrontend) Path() string {
    return makePath("services", self.ServiceName, "frontend")
}

func (self ConfigServiceBackend) Path() string {
    return makePath("services", self.ServiceName, "backends", self.BackendName)
}

func (self ConfigRoute) Path() string {
    return makePath("routes", self.RouteName)
}
*/
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
)

var Settings *Config

type Config struct {
	Get struct {
		// Settings for daemon
		Address string
		Port    uint
	}

	// sites for CORS
	CORS struct {
		Sites []string
	}

	Database struct {
		// Database connection settings
		User           string
		Password       string
		Proto          string
		Host           string
		Database       string
		MaxIdle        int
		MaxConnections int
	}

	Redis struct {
		// Redis address and max pool connections
		Protocol       string
		Address        string
		MaxIdle        int
		MaxConnections int
	}
}

func Print() {

	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n", "Local Config")
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Server")
	fmt.Printf("%-20v%40v\n", "Type", "GET")
	fmt.Printf("%-20v%40v\n", "Address", Settings.Post.Address)
	fmt.Printf("%-20v%40v\n", "Port", Settings.Post.Port)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "CORS")
	fmt.Printf("%-20v%40v\n", "Domains", strings.Join(Settings.CORS.Sites, ", "))
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Database")
	fmt.Printf("%-20v%40v\n", "User", Settings.Database.User)
	fmt.Printf("%-20v%40v\n", "Password", Settings.Database.Password)
	fmt.Printf("%-20v%40v\n", "Protocol", Settings.Database.Proto)
	fmt.Printf("%-20v%40v\n", "Host", Settings.Database.Host)
	fmt.Printf("%-20v%40v\n", "Database", Settings.Database.Database)
	fmt.Printf("%-20v%40v\n", "Max Idle", Settings.Database.MaxIdle)
	fmt.Printf("%-20v%40v\n", "Max Connections", Settings.Database.MaxConnections)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Redis")
	fmt.Printf("%-20v%40v\n", "Protocol", Settings.Redis.Protocol)
	fmt.Printf("%-20v%40v\n", "Address", Settings.Redis.Address)
	fmt.Printf("%-20v%40v\n", "Max Idle", Settings.Redis.MaxIdle)
	fmt.Printf("%-20v%40v\n", "Max Connections", Settings.Redis.MaxConnections)
	fmt.Println(strings.Repeat("*", 60))

}

func init() {
	file, err := os.Open("/etc/pram/pram.conf")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	Settings = &Config{}

	decoder := json.NewDecoder(file)

	err = decoder.Decode(&Settings)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

}
<commit_msg>migrate to shared libs<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
)

var Settings *Config

type Config struct {
	Get struct {
		// Settings for daemon
		Address string
		Port    uint
	}

	// sites for CORS
	CORS struct {
		Sites []string
	}

	Database struct {
		// Database connection settings
		User           string
		Password       string
		Proto          string
		Host           string
		Database       string
		MaxIdle        int
		MaxConnections int
	}

	Redis struct {
		// Redis address and max pool connections
		Protocol       string
		Address        string
		MaxIdle        int
		MaxConnections int
	}
}

func Print() {

	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n", "Local Config")
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Server")
	fmt.Printf("%-20v%40v\n", "Type", "GET")
	fmt.Printf("%-20v%40v\n", "Address", Settings.Get.Address)
	fmt.Printf("%-20v%40v\n", "Port", Settings.Get.Port)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "CORS")
	fmt.Printf("%-20v%40v\n", "Domains", strings.Join(Settings.CORS.Sites, ", "))
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Database")
	fmt.Printf("%-20v%40v\n", "User", Settings.Database.User)
	fmt.Printf("%-20v%40v\n", "Password", Settings.Database.Password)
	fmt.Printf("%-20v%40v\n", "Protocol", Settings.Database.Proto)
	fmt.Printf("%-20v%40v\n", "Host", Settings.Database.Host)
	fmt.Printf("%-20v%40v\n", "Database", Settings.Database.Database)
	fmt.Printf("%-20v%40v\n", "Max Idle", Settings.Database.MaxIdle)
	fmt.Printf("%-20v%40v\n", "Max Connections", Settings.Database.MaxConnections)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Redis")
	fmt.Printf("%-20v%40v\n", "Protocol", Settings.Redis.Protocol)
	fmt.Printf("%-20v%40v\n", "Address", Settings.Redis.Address)
	fmt.Printf("%-20v%40v\n", "Max Idle", Settings.Redis.MaxIdle)
	fmt.Printf("%-20v%40v\n", "Max Connections", Settings.Redis.MaxConnections)
	fmt.Println(strings.Repeat("*", 60))

}

func init() {
	file, err := os.Open("/etc/pram/pram.conf")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	Settings = &Config{}

	decoder := json.NewDecoder(file)

	err = decoder.Decode(&Settings)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

}
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"bufio"
	"log"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"text/template"
)

const path = "tlds.go"

var tldsTmpl = template.Must(template.New("tlds").Parse(`// Generated by tldsgen

package xurls

// TLDs is a sorted list of all public top-level domains
//
// Sources:
//  * https://data.iana.org/TLD/tlds-alpha-by-domain.txt
//  * https://publicsuffix.org/list/effective_tld_names.dat
var TLDs = []string{
{{range $_, $value := .TLDs}}` + "\t`" + `{{$value}}` + "`" + `,
{{end}}}
`))

func addFromIana(addTld func(tld string)) error {
	url := "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
	log.Printf("Fetching %s", url)
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	scanner := bufio.NewScanner(resp.Body)
	re := regexp.MustCompile(`^[^#]+$`)
	for scanner.Scan() {
		line := scanner.Text()
		tld := re.FindString(line)
		addTld(tld)
	}
	return nil
}

func addFromPublicSuffix(addTld func(tld string)) error {
	url := "https://publicsuffix.org/list/effective_tld_names.dat"
	log.Printf("Fetching %s", url)
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	scanner := bufio.NewScanner(resp.Body)
	re := regexp.MustCompile(`^[^/.]+$`)
	for scanner.Scan() {
		line := scanner.Text()
		tld := re.FindString(line)
		addTld(tld)
	}
	return nil
}

func tldList() ([]string, error) {
	tlds := make(map[string]struct{})
	addTld := func(tld string) {
		if tld == "" {
			return
		}
		tld = strings.ToLower(tld)
		if strings.HasPrefix(tld, "xn--") {
			return
		}
		tlds[tld] = struct{}{}
	}
	if err := addFromIana(addTld); err != nil {
		return nil, err
	}
	if err := addFromPublicSuffix(addTld); err != nil {
		return nil, err
	}
	list := make([]string, 0, len(tlds))
	for tld := range tlds {
		list = append(list, tld)
	}
	sort.Strings(list)
	return list, nil
}

func writeTlds(tlds []string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	return tldsTmpl.Execute(f, struct {
		TLDs []string
	}{
		TLDs: tlds,
	})
}

func main() {
	log.Printf("Downloading TLD list...")
	tlds, err := tldList()
	if err != nil {
		log.Fatalf("Could not get TLD list: %s", err)
	}
	log.Printf("Generating %s...", path)
	if err := writeTlds(tlds); err != nil {
		log.Fatalf("Could not write path: %s", err)
	}
}
<commit_msg>Remove redundant log in tldsgen<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"bufio"
	"log"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"text/template"
)

const path = "tlds.go"

var tldsTmpl = template.Must(template.New("tlds").Parse(`// Generated by tldsgen

package xurls

// TLDs is a sorted list of all public top-level domains
//
// Sources:
//  * https://data.iana.org/TLD/tlds-alpha-by-domain.txt
//  * https://publicsuffix.org/list/effective_tld_names.dat
var TLDs = []string{
{{range $_, $value := .TLDs}}` + "\t`" + `{{$value}}` + "`" + `,
{{end}}}
`))

func addFromIana(addTld func(tld string)) error {
	url := "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
	log.Printf("Fetching %s", url)
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	scanner := bufio.NewScanner(resp.Body)
	re := regexp.MustCompile(`^[^#]+$`)
	for scanner.Scan() {
		line := scanner.Text()
		tld := re.FindString(line)
		addTld(tld)
	}
	return nil
}

func addFromPublicSuffix(addTld func(tld string)) error {
	url := "https://publicsuffix.org/list/effective_tld_names.dat"
	log.Printf("Fetching %s", url)
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	scanner := bufio.NewScanner(resp.Body)
	re := regexp.MustCompile(`^[^/.]+$`)
	for scanner.Scan() {
		line := scanner.Text()
		tld := re.FindString(line)
		addTld(tld)
	}
	return nil
}

func tldList() ([]string, error) {
	tlds := make(map[string]struct{})
	addTld := func(tld string) {
		if tld == "" {
			return
		}
		tld = strings.ToLower(tld)
		if strings.HasPrefix(tld, "xn--") {
			return
		}
		tlds[tld] = struct{}{}
	}
	if err := addFromIana(addTld); err != nil {
		return nil, err
	}
	if err := addFromPublicSuffix(addTld); err != nil {
		return nil, err
	}
	list := make([]string, 0, len(tlds))
	for tld := range tlds {
		list = append(list, tld)
	}
	sort.Strings(list)
	return list, nil
}

func writeTlds(tlds []string) error {
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	return tldsTmpl.Execute(f, struct {
		TLDs []string
	}{
		TLDs: tlds,
	})
}

func main() {
	tlds, err := tldList()
	if err != nil {
		log.Fatalf("Could not get TLD list: %s", err)
	}
	log.Printf("Generating %s...", path)
	if err := writeTlds(tlds); err != nil {
		log.Fatalf("Could not write path: %s", err)
	}
}
<|endoftext|>


<commit_before>// Package topics is an experimental package that provides additional tooling
// around Kafka Topics. This package does not make any promises around
// backwards compatibility.
package topics

import (
	"context"
	"errors"
	"regexp"

	"github.com/segmentio/kafka-go"
)

// List returns a slice of all the Topics
func List(ctx context.Context, client *kafka.Client) (topics []kafka.Topic, err error) {
	if client == nil {
		return nil, errors.New("client is required")
	}
	response, err := client.Metadata(ctx, &kafka.MetadataRequest{
		Addr: client.Addr,
	})
	if err != nil {
		return nil, err
	}

	return response.Topics, nil
}

// ListRe returns a slice of Topics that match a regex
func ListRe(ctx context.Context, cli *kafka.Client, re *regexp.Regexp) (topics []string, err error) {
	alltopics, err := List(ctx, cli)
	if err != nil {
		return nil, err
	}

	for _, val := range alltopics {
		if re.MatchString(val.Name) {
			topics = append(topics, val.Name)
		}
	}
	return topics, nil
}
<commit_msg>topics: Make return types consistent<commit_after>// Package topics is an experimental package that provides additional tooling
// around Kafka Topics. This package does not make any promises around
// backwards compatibility.
package topics

import (
	"context"
	"errors"
	"regexp"

	"github.com/segmentio/kafka-go"
)

// List returns a slice of all the Topics
func List(ctx context.Context, client *kafka.Client) (topics []kafka.Topic, err error) {
	if client == nil {
		return nil, errors.New("client is required")
	}
	response, err := client.Metadata(ctx, &kafka.MetadataRequest{
		Addr: client.Addr,
	})
	if err != nil {
		return nil, err
	}

	return response.Topics, nil
}

// ListRe returns a slice of Topics that match a regex
func ListRe(ctx context.Context, cli *kafka.Client, re *regexp.Regexp) (topics []kafka.Topic, err error) {
	alltopics, err := List(ctx, cli)
	if err != nil {
		return nil, err
	}

	for _, val := range alltopics {
		if re.MatchString(val.Name) {
			topics = append(topics, val)
		}
	}
	return topics, nil
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 scraperhelper

import (
	"context"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/component/componenthelper"
	"go.opentelemetry.io/collector/consumer/pdata"
	"go.opentelemetry.io/collector/obsreport"
)

// ScrapeMetrics scrapes metrics.
type ScrapeMetrics func(context.Context) (pdata.MetricSlice, error)

// ScrapeResourceMetrics scrapes resource metrics.
type ScrapeResourceMetrics func(context.Context) (pdata.ResourceMetricsSlice, error)

type baseSettings struct {
	componentOptions []componenthelper.Option
}

// ScraperOption apply changes to internal options.
type ScraperOption func(*baseSettings)

// BaseScraper is the base interface for scrapers.
type BaseScraper interface {
	component.Component

	// Name returns the scraper name
	Name() string
}

// MetricsScraper is an interface for scrapers that scrape metrics.
type MetricsScraper interface {
	BaseScraper
	Scrape(context.Context, string) (pdata.MetricSlice, error)
}

// ResourceMetricsScraper is an interface for scrapers that scrape resource metrics.
type ResourceMetricsScraper interface {
	BaseScraper
	Scrape(context.Context, string) (pdata.ResourceMetricsSlice, error)
}

var _ BaseScraper = (*baseScraper)(nil)

type baseScraper struct {
	component.Component
	name string
}

func (b baseScraper) Name() string {
	return b.name
}

// WithStart sets the function that will be called on startup.
func WithStart(start componenthelper.Start) ScraperOption {
	return func(o *baseSettings) {
		o.componentOptions = append(o.componentOptions, componenthelper.WithStart(start))
	}
}

// WithShutdown sets the function that will be called on shutdown.
func WithShutdown(shutdown componenthelper.Shutdown) ScraperOption {
	return func(o *baseSettings) {
		o.componentOptions = append(o.componentOptions, componenthelper.WithShutdown(shutdown))
	}
}

type metricsScraper struct {
	baseScraper
	ScrapeMetrics
}

var _ MetricsScraper = (*metricsScraper)(nil)

// NewMetricsScraper creates a Scraper that calls Scrape at the specified
// collection interval, reports observability information, and passes the
// scraped metrics to the next consumer.
func NewMetricsScraper(
	name string,
	scrape ScrapeMetrics,
	options ...ScraperOption,
) MetricsScraper {
	set := &baseSettings{}
	for _, op := range options {
		op(set)
	}

	ms := &metricsScraper{
		baseScraper: baseScraper{
			Component: componenthelper.New(set.componentOptions...),
			name:      name,
		},
		ScrapeMetrics: scrape,
	}

	return ms
}

func (ms metricsScraper) Scrape(ctx context.Context, receiverName string) (pdata.MetricSlice, error) {
	ctx = obsreport.ScraperContext(ctx, receiverName, ms.Name())
	ctx = obsreport.StartMetricsScrapeOp(ctx, receiverName, ms.Name())
	metrics, err := ms.ScrapeMetrics(ctx)
	obsreport.EndMetricsScrapeOp(ctx, metrics.Len(), err)
	return metrics, err
}

type resourceMetricsScraper struct {
	baseScraper
	ScrapeResourceMetrics
}

var _ ResourceMetricsScraper = (*resourceMetricsScraper)(nil)

// NewResourceMetricsScraper creates a Scraper that calls Scrape at the
// specified collection interval, reports observability information, and
// passes the scraped resource metrics to the next consumer.
func NewResourceMetricsScraper(
	name string,
	scrape ScrapeResourceMetrics,
	options ...ScraperOption,
) ResourceMetricsScraper {
	set := &baseSettings{}
	for _, op := range options {
		op(set)
	}

	rms := &resourceMetricsScraper{
		baseScraper: baseScraper{
			Component: componenthelper.New(set.componentOptions...),
			name:      name,
		},
		ScrapeResourceMetrics: scrape,
	}

	return rms
}

func (rms resourceMetricsScraper) Scrape(ctx context.Context, receiverName string) (pdata.ResourceMetricsSlice, error) {
	ctx = obsreport.ScraperContext(ctx, receiverName, rms.Name())
	ctx = obsreport.StartMetricsScrapeOp(ctx, receiverName, rms.Name())
	resourceMetrics, err := rms.ScrapeResourceMetrics(ctx)
	obsreport.EndMetricsScrapeOp(ctx, metricCount(resourceMetrics), err)
	return resourceMetrics, err
}

func metricCount(resourceMetrics pdata.ResourceMetricsSlice) int {
	count := 0

	for i := 0; i < resourceMetrics.Len(); i++ {
		ilm := resourceMetrics.At(i).InstrumentationLibraryMetrics()
		for j := 0; j < ilm.Len(); j++ {
			count += ilm.At(j).Metrics().Len()
		}
	}

	return count
}
<commit_msg>Make sure we don't try to count metrics if scraper returns an error (#2708) (#2902)<commit_after>// Copyright The OpenTelemetry 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 scraperhelper

import (
	"context"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/component/componenthelper"
	"go.opentelemetry.io/collector/consumer/pdata"
	"go.opentelemetry.io/collector/obsreport"
)

// ScrapeMetrics scrapes metrics.
type ScrapeMetrics func(context.Context) (pdata.MetricSlice, error)

// ScrapeResourceMetrics scrapes resource metrics.
type ScrapeResourceMetrics func(context.Context) (pdata.ResourceMetricsSlice, error)

type baseSettings struct {
	componentOptions []componenthelper.Option
}

// ScraperOption apply changes to internal options.
type ScraperOption func(*baseSettings)

// BaseScraper is the base interface for scrapers.
type BaseScraper interface {
	component.Component

	// Name returns the scraper name
	Name() string
}

// MetricsScraper is an interface for scrapers that scrape metrics.
type MetricsScraper interface {
	BaseScraper
	Scrape(context.Context, string) (pdata.MetricSlice, error)
}

// ResourceMetricsScraper is an interface for scrapers that scrape resource metrics.
type ResourceMetricsScraper interface {
	BaseScraper
	Scrape(context.Context, string) (pdata.ResourceMetricsSlice, error)
}

var _ BaseScraper = (*baseScraper)(nil)

type baseScraper struct {
	component.Component
	name string
}

func (b baseScraper) Name() string {
	return b.name
}

// WithStart sets the function that will be called on startup.
func WithStart(start componenthelper.Start) ScraperOption {
	return func(o *baseSettings) {
		o.componentOptions = append(o.componentOptions, componenthelper.WithStart(start))
	}
}

// WithShutdown sets the function that will be called on shutdown.
func WithShutdown(shutdown componenthelper.Shutdown) ScraperOption {
	return func(o *baseSettings) {
		o.componentOptions = append(o.componentOptions, componenthelper.WithShutdown(shutdown))
	}
}

type metricsScraper struct {
	baseScraper
	ScrapeMetrics
}

var _ MetricsScraper = (*metricsScraper)(nil)

// NewMetricsScraper creates a Scraper that calls Scrape at the specified
// collection interval, reports observability information, and passes the
// scraped metrics to the next consumer.
func NewMetricsScraper(
	name string,
	scrape ScrapeMetrics,
	options ...ScraperOption,
) MetricsScraper {
	set := &baseSettings{}
	for _, op := range options {
		op(set)
	}

	ms := &metricsScraper{
		baseScraper: baseScraper{
			Component: componenthelper.New(set.componentOptions...),
			name:      name,
		},
		ScrapeMetrics: scrape,
	}

	return ms
}

func (ms metricsScraper) Scrape(ctx context.Context, receiverName string) (pdata.MetricSlice, error) {
	ctx = obsreport.ScraperContext(ctx, receiverName, ms.Name())
	ctx = obsreport.StartMetricsScrapeOp(ctx, receiverName, ms.Name())
	metrics, err := ms.ScrapeMetrics(ctx)
	count := 0
	if err == nil {
		count = metrics.Len()
	}
	obsreport.EndMetricsScrapeOp(ctx, count, err)
	return metrics, err
}

type resourceMetricsScraper struct {
	baseScraper
	ScrapeResourceMetrics
}

var _ ResourceMetricsScraper = (*resourceMetricsScraper)(nil)

// NewResourceMetricsScraper creates a Scraper that calls Scrape at the
// specified collection interval, reports observability information, and
// passes the scraped resource metrics to the next consumer.
func NewResourceMetricsScraper(
	name string,
	scrape ScrapeResourceMetrics,
	options ...ScraperOption,
) ResourceMetricsScraper {
	set := &baseSettings{}
	for _, op := range options {
		op(set)
	}

	rms := &resourceMetricsScraper{
		baseScraper: baseScraper{
			Component: componenthelper.New(set.componentOptions...),
			name:      name,
		},
		ScrapeResourceMetrics: scrape,
	}

	return rms
}

func (rms resourceMetricsScraper) Scrape(ctx context.Context, receiverName string) (pdata.ResourceMetricsSlice, error) {
	ctx = obsreport.ScraperContext(ctx, receiverName, rms.Name())
	ctx = obsreport.StartMetricsScrapeOp(ctx, receiverName, rms.Name())
	resourceMetrics, err := rms.ScrapeResourceMetrics(ctx)
	count := 0
	if err == nil {
		count = metricCount(resourceMetrics)
	}
	obsreport.EndMetricsScrapeOp(ctx, count, err)
	return resourceMetrics, err
}

func metricCount(resourceMetrics pdata.ResourceMetricsSlice) int {
	count := 0

	for i := 0; i < resourceMetrics.Len(); i++ {
		ilm := resourceMetrics.At(i).InstrumentationLibraryMetrics()
		for j := 0; j < ilm.Len(); j++ {
			count += ilm.At(j).Metrics().Len()
		}
	}

	return count
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

type APIClient struct {
	BaseUrl string
	Token   string
}

type AuthorizationRequest struct {
	ApiKey    string `json:"apiKey"`
	ApiSecret string `json:"apiSecret"`
}

type AuthorizationResponse struct {
	Token string `json:"token"`
}

type ActivitiesResponse struct {
	Activities []Activity `json:"activities"`
}

type CurrentTrackingResponse struct {
	CurrentTracking *CurrentTracking `json:"currentTracking"`
}

type StartActivityRequest struct {
	StartedAt TimeularTime `json:"startedAt"`
}

type StartActivityResponse struct {
	CurrentTracking CurrentTracking `json:"currentTracking"`
}

type StopActivityRequest struct {
	StoppedAt TimeularTime `json:"stoppedAt"`
}

type StopActivityResponse struct {
	CreatedTimeEntry TimeEntry `json:"createdTimeEntry"`
}

type ErrorResponse struct {
	Timestamp int    `json:"timestamp"`
	Status    int    `json:"status"`
	Error     string `json:"error"`
	Exception string `json:"exception"`
	Message   string `json:"message"`
	Path      string `json:"path"`
}

func (client *APIClient) Authenticate() error {
	authString, _ := ioutil.ReadFile("./config.json")
	request := &AuthorizationRequest{}
	json.Unmarshal(authString, request)

	response := &AuthorizationResponse{}

	if err := client.doPost("/developer/sign-in", request, response); err != nil {
		return err
	}

	client.Token = "Bearer " + response.Token

	return nil
}

func (client *APIClient) GetCurrentTracking() (*CurrentTracking, error) {

	response := &CurrentTrackingResponse{}

	if err := client.doGet("/tracking", response); err != nil {
		return nil, err
	}

	return response.CurrentTracking, nil
}

func (client *APIClient) GetActivities() ([]Activity, error) {

	response := &ActivitiesResponse{}

	if err := client.doGet("/activities", response); err != nil {
		return []Activity{}, err
	}

	return response.Activities, nil
}

func (client *APIClient) StartActivity(a Activity) *CurrentTracking {

	requestBody := &StartActivityRequest{
		StartedAt: TimeularTime{time.Now()},
	}

	response := &StartActivityResponse{}

	if err := client.doPost(fmt.Sprintf("/tracking/%s/start", a.ID), requestBody, response); err != nil {
		return nil
	}

	return &response.CurrentTracking
}

func (client *APIClient) StopActivity(a Activity) {

	requestBody := &StopActivityRequest{
		StoppedAt: TimeularTime{time.Now()},
	}

	response := &StopActivityResponse{}

	if err := client.doPost(fmt.Sprintf("/tracking/%s/stop", a.ID), requestBody, response); err != nil {
		return
	}
}

func (client *APIClient) doPost(path string, requestObject interface{}, responseObject interface{}) error {
	requestBody, _ := json.Marshal(requestObject)

	request, _ := http.NewRequest("POST", client.BaseUrl+path, bytes.NewBuffer(requestBody))
	request.Header.Set("Authorization", client.Token)
	request.Header.Set("Accept", "application/json;charset:UTF-8")
	request.Header.Set("Content-Type", "application/json;charset:UTF-8")

	res, err := (&http.Client{}).Do(request)
	defer res.Body.Close()

	body, _ := ioutil.ReadAll(res.Body)

	if err != nil {
		log.Println("Request failed. Err: ", err)
		return err
	}

	if res.StatusCode != 200 {
		response := ErrorResponse{}
		err = json.Unmarshal(body, &response)
		log.Println()
		log.Println("========== POST call failed ==========")
		log.Printf("Path:        %s\n", response.Path)
		log.Printf("Status Code: %d\n", response.Status)
		log.Printf("Error:       %s\n", response.Error)
		log.Printf("Message:     %s\n", response.Message)
		log.Println()
		return errors.New(response.Message)
	}

	return json.Unmarshal(body, responseObject)
}

func (client *APIClient) doGet(path string, response interface{}) error {
	req, _ := http.NewRequest("GET", client.BaseUrl+path, nil)
	req.Header.Set("Accept", "application/json;charset:UTF-8")
	req.Header.Set("Authorization", client.Token)

	res, err := (&http.Client{}).Do(req)
	defer res.Body.Close()

	if err != nil {
		return err
	}

	body, _ := ioutil.ReadAll(res.Body)

	if res.StatusCode != 200 {
		response := ErrorResponse{}
		err = json.Unmarshal(body, &response)
		log.Println()
		log.Println("========== GET call failed ==========")
		log.Printf("Path:        %s\n", response.Path)
		log.Printf("Status Code: %d\n", response.Status)
		log.Printf("Error:       %s\n", response.Error)
		log.Printf("Message:     %s\n", response.Message)
		log.Println()
		return errors.New(response.Message)
	}

	return json.Unmarshal(body, &response)
}
<commit_msg>Exit the application when the config file can not be read<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

type APIClient struct {
	BaseUrl string
	Token   string
}

type AuthorizationRequest struct {
	ApiKey    string `json:"apiKey"`
	ApiSecret string `json:"apiSecret"`
}

type AuthorizationResponse struct {
	Token string `json:"token"`
}

type ActivitiesResponse struct {
	Activities []Activity `json:"activities"`
}

type CurrentTrackingResponse struct {
	CurrentTracking *CurrentTracking `json:"currentTracking"`
}

type StartActivityRequest struct {
	StartedAt TimeularTime `json:"startedAt"`
}

type StartActivityResponse struct {
	CurrentTracking CurrentTracking `json:"currentTracking"`
}

type StopActivityRequest struct {
	StoppedAt TimeularTime `json:"stoppedAt"`
}

type StopActivityResponse struct {
	CreatedTimeEntry TimeEntry `json:"createdTimeEntry"`
}

type ErrorResponse struct {
	Timestamp int    `json:"timestamp"`
	Status    int    `json:"status"`
	Error     string `json:"error"`
	Exception string `json:"exception"`
	Message   string `json:"message"`
	Path      string `json:"path"`
}

func (client *APIClient) Authenticate() error {
	authString, err := ioutil.ReadFile("./config.json")
	if err != nil {
		return err
	}
	request := &AuthorizationRequest{}
	json.Unmarshal(authString, request)

	response := &AuthorizationResponse{}

	if err := client.doPost("/developer/sign-in", request, response); err != nil {
		return err
	}

	client.Token = "Bearer " + response.Token

	return nil
}

func (client *APIClient) GetCurrentTracking() (*CurrentTracking, error) {

	response := &CurrentTrackingResponse{}

	if err := client.doGet("/tracking", response); err != nil {
		return nil, err
	}

	return response.CurrentTracking, nil
}

func (client *APIClient) GetActivities() ([]Activity, error) {

	response := &ActivitiesResponse{}

	if err := client.doGet("/activities", response); err != nil {
		return []Activity{}, err
	}

	return response.Activities, nil
}

func (client *APIClient) StartActivity(a Activity) *CurrentTracking {

	requestBody := &StartActivityRequest{
		StartedAt: TimeularTime{time.Now()},
	}

	response := &StartActivityResponse{}

	if err := client.doPost(fmt.Sprintf("/tracking/%s/start", a.ID), requestBody, response); err != nil {
		return nil
	}

	return &response.CurrentTracking
}

func (client *APIClient) StopActivity(a Activity) {

	requestBody := &StopActivityRequest{
		StoppedAt: TimeularTime{time.Now()},
	}

	response := &StopActivityResponse{}

	if err := client.doPost(fmt.Sprintf("/tracking/%s/stop", a.ID), requestBody, response); err != nil {
		return
	}
}

func (client *APIClient) doPost(path string, requestObject interface{}, responseObject interface{}) error {
	requestBody, _ := json.Marshal(requestObject)

	request, _ := http.NewRequest("POST", client.BaseUrl+path, bytes.NewBuffer(requestBody))
	request.Header.Set("Authorization", client.Token)
	request.Header.Set("Accept", "application/json;charset:UTF-8")
	request.Header.Set("Content-Type", "application/json;charset:UTF-8")

	res, err := (&http.Client{}).Do(request)
	defer res.Body.Close()

	body, _ := ioutil.ReadAll(res.Body)

	if err != nil {
		log.Println("Request failed. Err: ", err)
		return err
	}

	if res.StatusCode != 200 {
		response := ErrorResponse{}
		err = json.Unmarshal(body, &response)
		log.Println()
		log.Println("========== POST call failed ==========")
		log.Printf("Path:        %s\n", response.Path)
		log.Printf("Status Code: %d\n", response.Status)
		log.Printf("Error:       %s\n", response.Error)
		log.Printf("Message:     %s\n", response.Message)
		log.Println()
		return errors.New(response.Message)
	}

	return json.Unmarshal(body, responseObject)
}

func (client *APIClient) doGet(path string, response interface{}) error {
	req, _ := http.NewRequest("GET", client.BaseUrl+path, nil)
	req.Header.Set("Accept", "application/json;charset:UTF-8")
	req.Header.Set("Authorization", client.Token)

	res, err := (&http.Client{}).Do(req)
	defer res.Body.Close()

	if err != nil {
		return err
	}

	body, _ := ioutil.ReadAll(res.Body)

	if res.StatusCode != 200 {
		response := ErrorResponse{}
		err = json.Unmarshal(body, &response)
		log.Println()
		log.Println("========== GET call failed ==========")
		log.Printf("Path:        %s\n", response.Path)
		log.Printf("Status Code: %d\n", response.Status)
		log.Printf("Error:       %s\n", response.Error)
		log.Printf("Message:     %s\n", response.Message)
		log.Println()
		return errors.New(response.Message)
	}

	return json.Unmarshal(body, &response)
}
<|endoftext|>