<commit_before>package internal

import (
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry-incubator/cf-test-helpers/commandstarter"
	"github.com/cloudfoundry-incubator/cf-test-helpers/internal"
)

type TestSpace struct {
	QuotaDefinitionName                  string
	organizationName                     string
	spaceName                            string
	isPersistent                         bool
	isExistingOrganization               bool
	QuotaDefinitionTotalMemoryLimit      string
	QuotaDefinitionInstanceMemoryLimit   string
	QuotaDefinitionRoutesLimit           string
	QuotaDefinitionAppInstanceLimit      string
	QuotaDefinitionServiceInstanceLimit  string
	QuotaDefinitionAllowPaidServicesFlag string
	CommandStarter                       internal.Starter
	Timeout                              time.Duration
}

type spaceConfig interface {
	GetScaledTimeout(time.Duration) time.Duration
	GetPersistentAppSpace() string
	GetPersistentAppOrg() string
	GetPersistentAppQuotaName() string
	GetNamePrefix() string
	GetUseExistingOrganization() bool
	GetExistingOrganization() string
}

type Space interface {
	Create()
	Destroy()
	ShouldRemain() bool
	OrganizationName() string
}

func NewRegularTestSpace(cfg spaceConfig, quotaLimit string) *TestSpace {
	organizationName, isExistingOrganization := organizationName(cfg)
	return NewBaseTestSpace(
		generator.PrefixedRandomName(cfg.GetNamePrefix(), "SPACE"),
		organizationName,
		generator.PrefixedRandomName(cfg.GetNamePrefix(), "QUOTA"),
		quotaLimit,
		false,
		isExistingOrganization,
		cfg.GetScaledTimeout(1*time.Minute),
		commandstarter.NewCommandStarter(),
	)
}

func NewPersistentAppTestSpace(cfg spaceConfig) *TestSpace {
	baseTestSpace := NewBaseTestSpace(
		cfg.GetPersistentAppSpace(),
		cfg.GetPersistentAppOrg(),
		cfg.GetPersistentAppQuotaName(),
		"10G",
		true,
		true,
		cfg.GetScaledTimeout(1*time.Minute),
		commandstarter.NewCommandStarter(),
	)
	return baseTestSpace
}

func NewBaseTestSpace(spaceName, organizationName, quotaDefinitionName, quotaDefinitionTotalMemoryLimit string, isPersistent bool, isExistingOrganization bool, timeout time.Duration, cmdStarter internal.Starter) *TestSpace {
	testSpace := &TestSpace{
		QuotaDefinitionName:                  quotaDefinitionName,
		QuotaDefinitionTotalMemoryLimit:      quotaDefinitionTotalMemoryLimit,
		QuotaDefinitionInstanceMemoryLimit:   "-1",
		QuotaDefinitionRoutesLimit:           "1000",
		QuotaDefinitionAppInstanceLimit:      "-1",
		QuotaDefinitionServiceInstanceLimit:  "100",
		QuotaDefinitionAllowPaidServicesFlag: "--allow-paid-service-plans",
		organizationName:                     organizationName,
		spaceName:                            spaceName,
		CommandStarter:                       cmdStarter,
		Timeout:                              timeout,
		isPersistent:                         isPersistent,
		isExistingOrganization:               isExistingOrganization,
	}
	return testSpace
}

func (ts *TestSpace) Create() {
	args := []string{
		"create-quota",
		ts.QuotaDefinitionName,
		"-m", ts.QuotaDefinitionTotalMemoryLimit,
		"-i", ts.QuotaDefinitionInstanceMemoryLimit,
		"-r", ts.QuotaDefinitionRoutesLimit,
		"-a", ts.QuotaDefinitionAppInstanceLimit,
		"-s", ts.QuotaDefinitionServiceInstanceLimit,
		ts.QuotaDefinitionAllowPaidServicesFlag,
	}

	if !ts.isExistingOrganization {
		createQuota := internal.Cf(ts.CommandStarter, args...)
		EventuallyWithOffset(1, createQuota, ts.Timeout).Should(Exit(0))

		createOrg := internal.Cf(ts.CommandStarter, "create-org", ts.organizationName)
		EventuallyWithOffset(1, createOrg, ts.Timeout).Should(Exit(0))

		setQuota := internal.Cf(ts.CommandStarter, "set-quota", ts.organizationName, ts.QuotaDefinitionName)
		EventuallyWithOffset(1, setQuota, ts.Timeout).Should(Exit(0))
	}

	createSpace := internal.Cf(ts.CommandStarter, "create-space", "-o", ts.organizationName, ts.spaceName)
	EventuallyWithOffset(1, createSpace, ts.Timeout).Should(Exit(0))
}

func (ts *TestSpace) Destroy() {
	if ts.isExistingOrganization {
		deleteSpace := internal.Cf(ts.CommandStarter, "delete-space", "-f", "-o", ts.organizationName, ts.spaceName)
		EventuallyWithOffset(1, deleteSpace, ts.Timeout).Should(Exit(0))
	} else {
		deleteOrg := internal.Cf(ts.CommandStarter, "delete-org", "-f", ts.organizationName)
		EventuallyWithOffset(1, deleteOrg, ts.Timeout).Should(Exit(0))

		deleteQuota := internal.Cf(ts.CommandStarter, "delete-quota", "-f", ts.QuotaDefinitionName)
		EventuallyWithOffset(1, deleteQuota, ts.Timeout).Should(Exit(0))
	}
}

func (ts *TestSpace) OrganizationName() string {
	if ts == nil {
		return ""
	}
	return ts.organizationName
}

func (ts *TestSpace) SpaceName() string {
	if ts == nil {
		return ""
	}
	return ts.spaceName
}

func (ts *TestSpace) ShouldRemain() bool {
	return ts.isPersistent
}

func organizationName(cfg spaceConfig) (string, bool) {
	if cfg.GetUseExistingOrganization() {
		Expect(cfg.GetExistingOrganization()).To(Not(BeEmpty()), "existing_organization must be specified")
		return cfg.GetExistingOrganization(), true
	}
	return generator.PrefixedRandomName(cfg.GetNamePrefix(), "ORG"), false
}
<commit_msg>[#143286743] Use the config flag when creating the persistent org<commit_after>package internal

import (
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry-incubator/cf-test-helpers/commandstarter"
	"github.com/cloudfoundry-incubator/cf-test-helpers/internal"
)

type TestSpace struct {
	QuotaDefinitionName                  string
	organizationName                     string
	spaceName                            string
	isPersistent                         bool
	isExistingOrganization               bool
	QuotaDefinitionTotalMemoryLimit      string
	QuotaDefinitionInstanceMemoryLimit   string
	QuotaDefinitionRoutesLimit           string
	QuotaDefinitionAppInstanceLimit      string
	QuotaDefinitionServiceInstanceLimit  string
	QuotaDefinitionAllowPaidServicesFlag string
	CommandStarter                       internal.Starter
	Timeout                              time.Duration
}

type spaceConfig interface {
	GetScaledTimeout(time.Duration) time.Duration
	GetPersistentAppSpace() string
	GetPersistentAppOrg() string
	GetPersistentAppQuotaName() string
	GetNamePrefix() string
	GetUseExistingOrganization() bool
	GetExistingOrganization() string
}

type Space interface {
	Create()
	Destroy()
	ShouldRemain() bool
	OrganizationName() string
}

func NewRegularTestSpace(cfg spaceConfig, quotaLimit string) *TestSpace {
	organizationName, isExistingOrganization := organizationName(cfg)
	return NewBaseTestSpace(
		generator.PrefixedRandomName(cfg.GetNamePrefix(), "SPACE"),
		organizationName,
		generator.PrefixedRandomName(cfg.GetNamePrefix(), "QUOTA"),
		quotaLimit,
		false,
		isExistingOrganization,
		cfg.GetScaledTimeout(1*time.Minute),
		commandstarter.NewCommandStarter(),
	)
}

func NewPersistentAppTestSpace(cfg spaceConfig) *TestSpace {
	baseTestSpace := NewBaseTestSpace(
		cfg.GetPersistentAppSpace(),
		cfg.GetPersistentAppOrg(),
		cfg.GetPersistentAppQuotaName(),
		"10G",
		true,
		cfg.GetUseExistingOrganization(),
		cfg.GetScaledTimeout(1*time.Minute),
		commandstarter.NewCommandStarter(),
	)
	return baseTestSpace
}

func NewBaseTestSpace(spaceName, organizationName, quotaDefinitionName, quotaDefinitionTotalMemoryLimit string, isPersistent bool, isExistingOrganization bool, timeout time.Duration, cmdStarter internal.Starter) *TestSpace {
	testSpace := &TestSpace{
		QuotaDefinitionName:                  quotaDefinitionName,
		QuotaDefinitionTotalMemoryLimit:      quotaDefinitionTotalMemoryLimit,
		QuotaDefinitionInstanceMemoryLimit:   "-1",
		QuotaDefinitionRoutesLimit:           "1000",
		QuotaDefinitionAppInstanceLimit:      "-1",
		QuotaDefinitionServiceInstanceLimit:  "100",
		QuotaDefinitionAllowPaidServicesFlag: "--allow-paid-service-plans",
		organizationName:                     organizationName,
		spaceName:                            spaceName,
		CommandStarter:                       cmdStarter,
		Timeout:                              timeout,
		isPersistent:                         isPersistent,
		isExistingOrganization:               isExistingOrganization,
	}
	return testSpace
}

func (ts *TestSpace) Create() {
	args := []string{
		"create-quota",
		ts.QuotaDefinitionName,
		"-m", ts.QuotaDefinitionTotalMemoryLimit,
		"-i", ts.QuotaDefinitionInstanceMemoryLimit,
		"-r", ts.QuotaDefinitionRoutesLimit,
		"-a", ts.QuotaDefinitionAppInstanceLimit,
		"-s", ts.QuotaDefinitionServiceInstanceLimit,
		ts.QuotaDefinitionAllowPaidServicesFlag,
	}

	if !ts.isExistingOrganization {
		createQuota := internal.Cf(ts.CommandStarter, args...)
		EventuallyWithOffset(1, createQuota, ts.Timeout).Should(Exit(0))

		createOrg := internal.Cf(ts.CommandStarter, "create-org", ts.organizationName)
		EventuallyWithOffset(1, createOrg, ts.Timeout).Should(Exit(0))

		setQuota := internal.Cf(ts.CommandStarter, "set-quota", ts.organizationName, ts.QuotaDefinitionName)
		EventuallyWithOffset(1, setQuota, ts.Timeout).Should(Exit(0))
	}

	createSpace := internal.Cf(ts.CommandStarter, "create-space", "-o", ts.organizationName, ts.spaceName)
	EventuallyWithOffset(1, createSpace, ts.Timeout).Should(Exit(0))
}

func (ts *TestSpace) Destroy() {
	if ts.isExistingOrganization {
		deleteSpace := internal.Cf(ts.CommandStarter, "delete-space", "-f", "-o", ts.organizationName, ts.spaceName)
		EventuallyWithOffset(1, deleteSpace, ts.Timeout).Should(Exit(0))
	} else {
		deleteOrg := internal.Cf(ts.CommandStarter, "delete-org", "-f", ts.organizationName)
		EventuallyWithOffset(1, deleteOrg, ts.Timeout).Should(Exit(0))

		deleteQuota := internal.Cf(ts.CommandStarter, "delete-quota", "-f", ts.QuotaDefinitionName)
		EventuallyWithOffset(1, deleteQuota, ts.Timeout).Should(Exit(0))
	}
}

func (ts *TestSpace) OrganizationName() string {
	if ts == nil {
		return ""
	}
	return ts.organizationName
}

func (ts *TestSpace) SpaceName() string {
	if ts == nil {
		return ""
	}
	return ts.spaceName
}

func (ts *TestSpace) ShouldRemain() bool {
	return ts.isPersistent
}

func organizationName(cfg spaceConfig) (string, bool) {
	if cfg.GetUseExistingOrganization() {
		Expect(cfg.GetExistingOrganization()).To(Not(BeEmpty()), "existing_organization must be specified")
		return cfg.GetExistingOrganization(), true
	}
	return generator.PrefixedRandomName(cfg.GetNamePrefix(), "ORG"), false
}
<|endoftext|>


<commit_before>package config

import (
	"os"
	"path/filepath"
	"testing"
)

func TestIsAbs(t *testing.T) {

	a := []string{"/map",
		"data/dd",
		"c:\\windows",
		"sdf\\ss",
		"logs"}

	for _, item := range a {
		t.Logf("%s is abs: %v", item, filepath.IsAbs(item))
	}
}<commit_msg>去掉无用测试<commit_after><|endoftext|>


<commit_before>package config

import (
	"bytes"
	"os"
	"testing"

	"github.com/sahib/brig/util/testutil"
	"github.com/stretchr/testify/require"
)

func getTypeOfDefaultKey(key string, defaults DefaultMapping) string {
	defaultEntry := getDefaultByKey(key, defaults)
	if defaultEntry == nil {
		return ""
	}

	return getTypeOf(defaultEntry.Default)
}

func TestDefaults(t *testing.T) {
	require.Equal(t, getDefaultByKey("daemon.port", Defaults).Default, 6666)
	require.Nil(t, getDefaultByKey("daemon.port.sub", Defaults))
	require.Nil(t, getDefaultByKey("daemon.xxx", Defaults))
	require.Nil(t, getDefaultByKey("daemon", Defaults))
}

func TestDefaultsType(t *testing.T) {
	require.Equal(t, "int", getTypeOfDefaultKey("daemon.port", Defaults))
	require.Equal(t, "string", getTypeOfDefaultKey("data.ipfs.path", Defaults))
	require.Equal(t, "", getTypeOfDefaultKey("not.yet.there", Defaults))
}

var testConfig = `daemon:
  port: 6667
data:
  ipfs:
    path: x
`

func TestGetDefault(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	require.Equal(t, int64(6667), cfg.Int("daemon.port"))
}

func TestGetNonExisting(t *testing.T) {
	defer func() { require.NotNil(t, recover()) }()

	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	// should panic.
	require.Nil(t, cfg.SetFloat("not.existing", 23))
}

func TestSet(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	require.Nil(t, cfg.SetInt("daemon.port", 6666))
	require.Equal(t, int64(6666), cfg.Int("daemon.port"))
}

func TestSetBucketKey(t *testing.T) {
	defer func() { require.NotNil(t, recover()) }()

	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	// should panic.
	require.Nil(t, cfg.SetString("daemon", "oh oh"))
}

func TestAddChangeSignal(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	callCount := 0
	cbID := cfg.AddChangedKeyEvent("data.ipfs.path", func(key string) {
		require.Equal(t, "new-value", cfg.String("data.ipfs.path"))
		callCount++
	})

	require.Equal(t, 0, callCount)
	require.Nil(t, cfg.SetInt("daemon.port", 42))
	require.Equal(t, 0, callCount)
	require.Nil(t, cfg.SetString("data.ipfs.path", "new-value"))
	require.Equal(t, 1, callCount)
	require.Nil(t, cfg.SetString("data.ipfs.path", "new-value"))
	require.Equal(t, 1, callCount)

	require.Nil(t, cfg.RemoveChangedKeyEvent(cbID))
	require.Nil(t, cfg.SetString("data.ipfs.path", "newer-value"))
	require.Equal(t, 1, callCount)
}

func TestAddChangeSignalAll(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	callCount := 0
	cbID := cfg.AddChangedKeyEvent("", func(key string) {
		callCount++
	})

	require.Equal(t, 0, callCount)
	require.Nil(t, cfg.SetInt("daemon.port", 42))
	require.Equal(t, 1, callCount)
	require.Nil(t, cfg.SetString("data.ipfs.path", "new-value"))
	require.Equal(t, 2, callCount)

	require.Nil(t, cfg.RemoveChangedKeyEvent(cbID))
	require.Nil(t, cfg.SetString("data.ipfs.path", "newer-value"))
	require.Equal(t, 2, callCount)
}

func TestOpenSave(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	require.Nil(t, cfg.SetInt("daemon.port", 6666))
	require.Nil(t, cfg.SetString("data.ipfs.path", "y"))

	buf := &bytes.Buffer{}
	require.Nil(t, cfg.Save(buf))

	newCfg, err := Open(buf, Defaults)
	require.Nil(t, err)

	require.Equal(t, int64(6666), newCfg.Int("daemon.port"))
	require.Equal(t, "y", newCfg.String("data.ipfs.path"))
}

func TestKeys(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	keys := cfg.Keys()
	require.Equal(t, []string{
		"daemon.port",
		"data.ipfs.path",
		"fs.compress.default_algo",
		"fs.sync.conflict_strategy",
		"fs.sync.ignore_moved",
		"fs.sync.ignore_removed",
		"repo.current_user",
	}, keys)
}

func TestAddExtraKeys(t *testing.T) {
	// There is no default for "a: 1" -> fail.
	_, err := Open(bytes.NewReader([]byte(`a: 1`)), Defaults)
	require.NotNil(t, err)
}

func TestSection(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	fsSec := cfg.Section("fs")
	require.Equal(t, "snappy", fsSec.String("compress.default_algo"))
	require.Equal(t, "snappy", cfg.String("fs.compress.default_algo"))

	require.Nil(t, fsSec.SetString("compress.default_algo", "lz4"))
	require.Equal(t, "lz4", fsSec.String("compress.default_algo"))
	require.Equal(t, "lz4", cfg.String("fs.compress.default_algo"))

	require.Nil(t, cfg.SetString("fs.compress.default_algo", "none"))
	require.Equal(t, "none", fsSec.String("compress.default_algo"))
	require.Equal(t, "none", cfg.String("fs.compress.default_algo"))

	childKeys := fsSec.Keys()
	require.Equal(t, []string{
		"fs.compress.default_algo",
		"fs.sync.conflict_strategy",
		"fs.sync.ignore_moved",
		"fs.sync.ignore_removed",
	}, childKeys)
}

func TestSectionSignals(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	parentCallCount := 0
	parentID := cfg.AddChangedKeyEvent("fs.compress.default_algo", func(key string) {
		require.Equal(t, "fs.compress.default_algo", key)
		parentCallCount++
	})

	fsSec := cfg.Section("fs")

	childCallCount := 0
	childID := fsSec.AddChangedKeyEvent("compress.default_algo", func(key string) {
		require.Equal(t, "compress.default_algo", key)
		childCallCount++
	})

	require.Nil(t, cfg.SetString("fs.compress.default_algo", "none"))
	require.Nil(t, fsSec.SetString("compress.default_algo", "lz4"))

	require.Equal(t, 2, parentCallCount)
	require.Equal(t, 1, childCallCount)

	require.Nil(t, fsSec.RemoveChangedKeyEvent(childID))
	require.Nil(t, cfg.RemoveChangedKeyEvent(parentID))

}

func TestIsValidKey(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), Defaults)
	require.Nil(t, err)

	require.True(t, cfg.IsValidKey("daemon.port"))
	require.False(t, cfg.IsValidKey("data.port"))
}

func TestCast(t *testing.T) {
	defaults := DefaultMapping{
		"string": DefaultEntry{
			Default: "a",
		},
		"int": DefaultEntry{
			Default: 2,
		},
		"float": DefaultEntry{
			Default: 3.0,
		},
		"bool": DefaultEntry{
			Default: false,
		},
	}

	cfg, err := Open(bytes.NewReader(nil), defaults)
	require.Nil(t, err)

	// Same string cast:
	strCast, err := cfg.Cast("string", "test")
	require.Nil(t, err)
	require.Equal(t, "test", strCast)

	// Int cast:
	intCast, err := cfg.Cast("int", "123")
	require.Nil(t, err)
	require.Equal(t, int64(123), intCast)

	// Float cast:
	floatCast, err := cfg.Cast("float", "5.0")
	require.Nil(t, err)
	require.Equal(t, float64(5.0), floatCast)

	// Bool cast:
	boolCast, err := cfg.Cast("bool", "true")
	require.Nil(t, err)
	require.Equal(t, true, boolCast)

	// Wrong cast types:
	_, err = cfg.Cast("int", "im a string")
	require.NotNil(t, err)

	_, err = cfg.Cast("int", "2.0")
	require.NotNil(t, err)
}

func TestValidation(t *testing.T) {
	defaults := DefaultMapping{
		"enum-val": DefaultEntry{
			Default:      "a",
			NeedsRestart: false,
			Validator:    EnumValidator("a", "b", "c"),
		},
	}

	// Check initial validation:
	_, err := Open(bytes.NewReader([]byte("enum-val: d")), defaults)
	require.NotNil(t, err)

	cfg, err := Open(bytes.NewReader([]byte("enum-val: c")), defaults)
	require.Nil(t, err)
	require.Equal(t, cfg.String("enum-val"), "c")

	// Set an invalid enum value:
	require.NotNil(t, cfg.SetString("enum-val", "C"))
	require.Nil(t, cfg.SetString("enum-val", "a"))
	require.Equal(t, cfg.String("enum-val"), "a")
}

func TestIntvalidator(t *testing.T) {
	vdt := IntRangeValidator(10, 100)
	require.Contains(t, vdt("x").Error(), "is not an integer")
	require.Contains(t, vdt(int64(9)).Error(), "may not be less than 10")
	require.Contains(t, vdt(int64(101)).Error(), "may not be more than 100")

	require.Nil(t, vdt(int64(10)))
	require.Nil(t, vdt(int64(100)))
	require.Nil(t, vdt(int64(50)))
}

func configMustEquals(t *testing.T, aCfg, bCfg *Config) {
	require.Equal(t, aCfg.Keys(), bCfg.Keys())
	for _, key := range aCfg.Keys() {
		require.Equal(t, aCfg.Get(key), bCfg.Get(key), key)
	}
}

func TestToFileFromFile(t *testing.T) {
	cfg, err := Open(bytes.NewReader(nil), Defaults)
	require.Nil(t, err)

	path := "/tmp/brig-test-config.yml"
	require.Nil(t, ToFile(path, cfg))

	defer os.Remove(path)

	loadCfg, err := FromFile(path, Defaults)
	require.Nil(t, err)

	configMustEquals(t, cfg, loadCfg)
}

func TestSetIncompatibleType(t *testing.T) {
	cfg, err := Open(bytes.NewReader(nil), Defaults)
	require.Nil(t, err)

	require.NotNil(t, cfg.SetString("daemon.port", "xxx"))
}

func TestVersionPersisting(t *testing.T) {
	cfg, err := Open(bytes.NewReader(nil), Defaults)
	require.Nil(t, err)

	require.Equal(t, Version(0), cfg.Version())
	cfg.version = Version(1)

	buf := &bytes.Buffer{}
	require.Nil(t, cfg.Save(buf))

	cfg, err = Open(bytes.NewReader(buf.Bytes()), Defaults)
	require.Nil(t, err)

	require.Equal(t, Version(1), cfg.Version())
}

func TestOpenMalformed(t *testing.T) {
	malformed := testutil.CreateDummyBuf(1024)

	// Not panicking here is okay for now as test.
	// Later one might want to add something like a fuzzer for this.
	_, err := Open(bytes.NewReader(malformed), Defaults)
	require.NotNil(t, err)
}
<commit_msg>config: test: bring back defaults<commit_after>package config

import (
	"bytes"
	"os"
	"testing"

	"github.com/sahib/brig/util/testutil"
	"github.com/stretchr/testify/require"
)

var TestDefaults = DefaultMapping{
	"daemon": DefaultMapping{
		"port": DefaultEntry{
			Default:      6666,
			NeedsRestart: true,
			Docs:         "Port of the daemon process",
			Validator:    IntRangeValidator(1, 655356),
		},
	},
	"fs": DefaultMapping{
		"sync": DefaultMapping{
			"ignore_removed": DefaultEntry{
				Default:      false,
				NeedsRestart: false,
				Docs:         "Do not remove what the remote removed",
			},
			"ignore_moved": DefaultEntry{
				Default:      false,
				NeedsRestart: false,
				Docs:         "Do not move what the remote moved",
			},
			"conflict_strategy": DefaultEntry{
				Default:      "marker",
				NeedsRestart: false,
				Validator: EnumValidator(
					"marker", "ignore",
				),
			},
		},
		"compress": DefaultMapping{
			"default_algo": DefaultEntry{
				Default:      "snappy",
				NeedsRestart: false,
				Docs:         "What compression algorithm to use by default",
				Validator: EnumValidator(
					"snappy", "lz4", "none",
				),
			},
		},
	},
	"repo": DefaultMapping{
		"current_user": DefaultEntry{
			Default:      "",
			NeedsRestart: true,
			Docs:         "The repository owner that is published to the outside",
		},
	},
	"data": DefaultMapping{
		"ipfs": DefaultMapping{
			"path": DefaultEntry{
				Default:      "",
				NeedsRestart: true,
				Docs:         "Root directory of the ipfs repository",
			},
		},
	},
}

func getTypeOfDefaultKey(key string, defaults DefaultMapping) string {
	defaultEntry := getDefaultByKey(key, defaults)
	if defaultEntry == nil {
		return ""
	}

	return getTypeOf(defaultEntry.Default)
}

func TestGetDefaults(t *testing.T) {
	require.Equal(t, getDefaultByKey("daemon.port", TestDefaults).Default, 6666)
	require.Nil(t, getDefaultByKey("daemon.port.sub", TestDefaults))
	require.Nil(t, getDefaultByKey("daemon.xxx", TestDefaults))
	require.Nil(t, getDefaultByKey("daemon", TestDefaults))
}

func TestDefaultsType(t *testing.T) {
	require.Equal(t, "int", getTypeOfDefaultKey("daemon.port", TestDefaults))
	require.Equal(t, "string", getTypeOfDefaultKey("data.ipfs.path", TestDefaults))
	require.Equal(t, "", getTypeOfDefaultKey("not.yet.there", TestDefaults))
}

var testConfig = `daemon:
  port: 6667
data:
  ipfs:
    path: x
`

func TestGetDefault(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	require.Equal(t, int64(6667), cfg.Int("daemon.port"))
}

func TestGetNonExisting(t *testing.T) {
	defer func() { require.NotNil(t, recover()) }()

	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	// should panic.
	require.Nil(t, cfg.SetFloat("not.existing", 23))
}

func TestSet(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	require.Nil(t, cfg.SetInt("daemon.port", 6666))
	require.Equal(t, int64(6666), cfg.Int("daemon.port"))
}

func TestSetBucketKey(t *testing.T) {
	defer func() { require.NotNil(t, recover()) }()

	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	// should panic.
	require.Nil(t, cfg.SetString("daemon", "oh oh"))
}

func TestAddChangeSignal(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	callCount := 0
	cbID := cfg.AddChangedKeyEvent("data.ipfs.path", func(key string) {
		require.Equal(t, "new-value", cfg.String("data.ipfs.path"))
		callCount++
	})

	require.Equal(t, 0, callCount)
	require.Nil(t, cfg.SetInt("daemon.port", 42))
	require.Equal(t, 0, callCount)
	require.Nil(t, cfg.SetString("data.ipfs.path", "new-value"))
	require.Equal(t, 1, callCount)
	require.Nil(t, cfg.SetString("data.ipfs.path", "new-value"))
	require.Equal(t, 1, callCount)

	require.Nil(t, cfg.RemoveChangedKeyEvent(cbID))
	require.Nil(t, cfg.SetString("data.ipfs.path", "newer-value"))
	require.Equal(t, 1, callCount)
}

func TestAddChangeSignalAll(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	callCount := 0
	cbID := cfg.AddChangedKeyEvent("", func(key string) {
		callCount++
	})

	require.Equal(t, 0, callCount)
	require.Nil(t, cfg.SetInt("daemon.port", 42))
	require.Equal(t, 1, callCount)
	require.Nil(t, cfg.SetString("data.ipfs.path", "new-value"))
	require.Equal(t, 2, callCount)

	require.Nil(t, cfg.RemoveChangedKeyEvent(cbID))
	require.Nil(t, cfg.SetString("data.ipfs.path", "newer-value"))
	require.Equal(t, 2, callCount)
}

func TestOpenSave(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	require.Nil(t, cfg.SetInt("daemon.port", 6666))
	require.Nil(t, cfg.SetString("data.ipfs.path", "y"))

	buf := &bytes.Buffer{}
	require.Nil(t, cfg.Save(buf))

	newCfg, err := Open(buf, TestDefaults)
	require.Nil(t, err)

	require.Equal(t, int64(6666), newCfg.Int("daemon.port"))
	require.Equal(t, "y", newCfg.String("data.ipfs.path"))
}

func TestKeys(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	keys := cfg.Keys()
	require.Equal(t, []string{
		"daemon.port",
		"data.ipfs.path",
		"fs.compress.default_algo",
		"fs.sync.conflict_strategy",
		"fs.sync.ignore_moved",
		"fs.sync.ignore_removed",
		"repo.current_user",
	}, keys)
}

func TestAddExtraKeys(t *testing.T) {
	// There is no default for "a: 1" -> fail.
	_, err := Open(bytes.NewReader([]byte(`a: 1`)), TestDefaults)
	require.NotNil(t, err)
}

func TestSection(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	fsSec := cfg.Section("fs")
	require.Equal(t, "snappy", fsSec.String("compress.default_algo"))
	require.Equal(t, "snappy", cfg.String("fs.compress.default_algo"))

	require.Nil(t, fsSec.SetString("compress.default_algo", "lz4"))
	require.Equal(t, "lz4", fsSec.String("compress.default_algo"))
	require.Equal(t, "lz4", cfg.String("fs.compress.default_algo"))

	require.Nil(t, cfg.SetString("fs.compress.default_algo", "none"))
	require.Equal(t, "none", fsSec.String("compress.default_algo"))
	require.Equal(t, "none", cfg.String("fs.compress.default_algo"))

	childKeys := fsSec.Keys()
	require.Equal(t, []string{
		"fs.compress.default_algo",
		"fs.sync.conflict_strategy",
		"fs.sync.ignore_moved",
		"fs.sync.ignore_removed",
	}, childKeys)
}

func TestSectionSignals(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	parentCallCount := 0
	parentID := cfg.AddChangedKeyEvent("fs.compress.default_algo", func(key string) {
		require.Equal(t, "fs.compress.default_algo", key)
		parentCallCount++
	})

	fsSec := cfg.Section("fs")

	childCallCount := 0
	childID := fsSec.AddChangedKeyEvent("compress.default_algo", func(key string) {
		require.Equal(t, "compress.default_algo", key)
		childCallCount++
	})

	require.Nil(t, cfg.SetString("fs.compress.default_algo", "none"))
	require.Nil(t, fsSec.SetString("compress.default_algo", "lz4"))

	require.Equal(t, 2, parentCallCount)
	require.Equal(t, 1, childCallCount)

	require.Nil(t, fsSec.RemoveChangedKeyEvent(childID))
	require.Nil(t, cfg.RemoveChangedKeyEvent(parentID))

}

func TestIsValidKey(t *testing.T) {
	cfg, err := Open(bytes.NewReader([]byte(testConfig)), TestDefaults)
	require.Nil(t, err)

	require.True(t, cfg.IsValidKey("daemon.port"))
	require.False(t, cfg.IsValidKey("data.port"))
}

func TestCast(t *testing.T) {
	defaults := DefaultMapping{
		"string": DefaultEntry{
			Default: "a",
		},
		"int": DefaultEntry{
			Default: 2,
		},
		"float": DefaultEntry{
			Default: 3.0,
		},
		"bool": DefaultEntry{
			Default: false,
		},
	}

	cfg, err := Open(bytes.NewReader(nil), defaults)
	require.Nil(t, err)

	// Same string cast:
	strCast, err := cfg.Cast("string", "test")
	require.Nil(t, err)
	require.Equal(t, "test", strCast)

	// Int cast:
	intCast, err := cfg.Cast("int", "123")
	require.Nil(t, err)
	require.Equal(t, int64(123), intCast)

	// Float cast:
	floatCast, err := cfg.Cast("float", "5.0")
	require.Nil(t, err)
	require.Equal(t, float64(5.0), floatCast)

	// Bool cast:
	boolCast, err := cfg.Cast("bool", "true")
	require.Nil(t, err)
	require.Equal(t, true, boolCast)

	// Wrong cast types:
	_, err = cfg.Cast("int", "im a string")
	require.NotNil(t, err)

	_, err = cfg.Cast("int", "2.0")
	require.NotNil(t, err)
}

func TestValidation(t *testing.T) {
	defaults := DefaultMapping{
		"enum-val": DefaultEntry{
			Default:      "a",
			NeedsRestart: false,
			Validator:    EnumValidator("a", "b", "c"),
		},
	}

	// Check initial validation:
	_, err := Open(bytes.NewReader([]byte("enum-val: d")), defaults)
	require.NotNil(t, err)

	cfg, err := Open(bytes.NewReader([]byte("enum-val: c")), defaults)
	require.Nil(t, err)
	require.Equal(t, cfg.String("enum-val"), "c")

	// Set an invalid enum value:
	require.NotNil(t, cfg.SetString("enum-val", "C"))
	require.Nil(t, cfg.SetString("enum-val", "a"))
	require.Equal(t, cfg.String("enum-val"), "a")
}

func TestIntvalidator(t *testing.T) {
	vdt := IntRangeValidator(10, 100)
	require.Contains(t, vdt("x").Error(), "is not an integer")
	require.Contains(t, vdt(int64(9)).Error(), "may not be less than 10")
	require.Contains(t, vdt(int64(101)).Error(), "may not be more than 100")

	require.Nil(t, vdt(int64(10)))
	require.Nil(t, vdt(int64(100)))
	require.Nil(t, vdt(int64(50)))
}

func configMustEquals(t *testing.T, aCfg, bCfg *Config) {
	require.Equal(t, aCfg.Keys(), bCfg.Keys())
	for _, key := range aCfg.Keys() {
		require.Equal(t, aCfg.Get(key), bCfg.Get(key), key)
	}
}

func TestToFileFromFile(t *testing.T) {
	cfg, err := Open(bytes.NewReader(nil), TestDefaults)
	require.Nil(t, err)

	path := "/tmp/brig-test-config.yml"
	require.Nil(t, ToFile(path, cfg))

	defer os.Remove(path)

	loadCfg, err := FromFile(path, TestDefaults)
	require.Nil(t, err)

	configMustEquals(t, cfg, loadCfg)
}

func TestSetIncompatibleType(t *testing.T) {
	cfg, err := Open(bytes.NewReader(nil), TestDefaults)
	require.Nil(t, err)

	require.NotNil(t, cfg.SetString("daemon.port", "xxx"))
}

func TestVersionPersisting(t *testing.T) {
	cfg, err := Open(bytes.NewReader(nil), TestDefaults)
	require.Nil(t, err)

	require.Equal(t, Version(0), cfg.Version())
	cfg.version = Version(1)

	buf := &bytes.Buffer{}
	require.Nil(t, cfg.Save(buf))

	cfg, err = Open(bytes.NewReader(buf.Bytes()), TestDefaults)
	require.Nil(t, err)

	require.Equal(t, Version(1), cfg.Version())
}

func TestOpenMalformed(t *testing.T) {
	malformed := testutil.CreateDummyBuf(1024)

	// Not panicking here is okay for now as test.
	// Later one might want to add something like a fuzzer for this.
	_, err := Open(bytes.NewReader(malformed), TestDefaults)
	require.NotNil(t, err)
}
<|endoftext|>


<commit_before>package executor

import (
	"log"
	"sync"

	"github.com/smartystreets/goconvey/web/server/contract"
)

type concurrentCoordinator struct {
	batchSize int
	queue     chan *contract.Package
	folders   []*contract.Package
	shell     contract.Shell
	waiter    sync.WaitGroup
}

func (self *concurrentCoordinator) ExecuteConcurrently() {
	self.enlistWorkers()
	self.scheduleTasks()
	self.awaitCompletion()
	self.checkForErrors()
}

func (self *concurrentCoordinator) enlistWorkers() {
	for i := 0; i < self.batchSize; i++ {
		self.waiter.Add(1)
		go self.worker(i)
	}
}
func (self *concurrentCoordinator) worker(id int) {
	for folder := range self.queue {
		if !folder.Active {
			log.Printf("Skipping concurrent execution: %s\n", folder.Name)
			continue
		}
		log.Printf("Executing concurrent tests: %s\n", folder.Name)
		folder.Output, folder.Error = self.shell.GoTest(folder.Path, folder.Name)
	}
	self.waiter.Done()
}

func (self *concurrentCoordinator) scheduleTasks() {
	for _, folder := range self.folders {
		self.queue <- folder
	}
}

func (self *concurrentCoordinator) awaitCompletion() {
	close(self.queue)
	self.waiter.Wait()
}

func (self *concurrentCoordinator) checkForErrors() {
	for _, folder := range self.folders {
		if hasUnexpectedError(folder) {
			log.Println("Unexpected error at", folder.Path)
			panic(folder.Error)
		}
	}
}
func hasUnexpectedError(folder *contract.Package) bool {
	return folder.Error != nil && folder.Output == ""
}

func newCuncurrentCoordinator(folders []*contract.Package, batchSize int, shell contract.Shell) *concurrentCoordinator {
	self := new(concurrentCoordinator)
	self.queue = make(chan *contract.Package)
	self.folders = folders
	self.batchSize = batchSize
	self.shell = shell
	return self
}
<commit_msg>create packageName with '/' instead of '\' for windows<commit_after>package executor

import (
	"log"
	"strings"
	"sync"

	"github.com/smartystreets/goconvey/web/server/contract"
)

type concurrentCoordinator struct {
	batchSize int
	queue     chan *contract.Package
	folders   []*contract.Package
	shell     contract.Shell
	waiter    sync.WaitGroup
}

func (self *concurrentCoordinator) ExecuteConcurrently() {
	self.enlistWorkers()
	self.scheduleTasks()
	self.awaitCompletion()
	self.checkForErrors()
}

func (self *concurrentCoordinator) enlistWorkers() {
	for i := 0; i < self.batchSize; i++ {
		self.waiter.Add(1)
		go self.worker(i)
	}
}
func (self *concurrentCoordinator) worker(id int) {
	for folder := range self.queue {
		packageName := strings.Replace(folder.Name, "\\", "/", -1)
		if !folder.Active {
			log.Printf("Skipping concurrent execution: %s\n", packageName)
			continue
		}
		log.Printf("Executing concurrent tests: %s\n", packageName)
		folder.Output, folder.Error = self.shell.GoTest(folder.Path, packageName)
	}
	self.waiter.Done()
}

func (self *concurrentCoordinator) scheduleTasks() {
	for _, folder := range self.folders {
		self.queue <- folder
	}
}

func (self *concurrentCoordinator) awaitCompletion() {
	close(self.queue)
	self.waiter.Wait()
}

func (self *concurrentCoordinator) checkForErrors() {
	for _, folder := range self.folders {
		if hasUnexpectedError(folder) {
			log.Println("Unexpected error at", folder.Path)
			panic(folder.Error)
		}
	}
}
func hasUnexpectedError(folder *contract.Package) bool {
	return folder.Error != nil && folder.Output == ""
}

func newCuncurrentCoordinator(folders []*contract.Package, batchSize int, shell contract.Shell) *concurrentCoordinator {
	self := new(concurrentCoordinator)
	self.queue = make(chan *contract.Package)
	self.folders = folders
	self.batchSize = batchSize
	self.shell = shell
	return self
}
<|endoftext|>


<commit_before>// Copyright 2017 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/gt/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package zlog

import (
	// "errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

type Zlog struct {
	// log.Logger
}

var (
	logger, errLogger *zap.Logger
	sugar, errSugar   *zap.SugaredLogger
	zErr              error
	zlogTime          zapcore.Field = zap.String("time", time.Now().Format("2006-01-02 15:04:05"))
)

type logConfig struct {
	Mode    string
	Path    string
	Name    string
	MaxDays int64
	// Srv  Server     `toml:"server"`
}

var config logConfig

func Init(tpath string) {
	if _, err := toml.DecodeFile(tpath, &config); err != nil {
		fmt.Println(err)
		return
	}

	go deleteOldLog()

	if config.Mode == "dev" {
		InitDev()
		zlogTime = zap.Error(nil)
	} else {
		InitLog()
		// InitErrLog()
		go InitErrLog()
	}
}

func deleteOldLog() {
	fileDir, _ := conf()
	var maxDays int64 = 28

	if config.MaxDays != 0 {
		maxDays = config.MaxDays
	}

	filepath.Walk(fileDir, func(path string, info os.FileInfo, err error) (returnErr error) {
		defer func() {
			if r := recover(); r != nil {
				returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
			}
		}()

		if info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*maxDays) {

			if strings.HasPrefix(filepath.Base(path), filepath.Base(fileDir)) {
				// if err := os.Remove(path); err != nil {
				if err := os.RemoveAll(path); err != nil {
					returnErr = fmt.Errorf("Failed to remove %s: %v", path, err)
				}
			}
		}
		return returnErr
	})
}

func InitDev() {
	// logger, _ = zap.NewProduction()
	logCfg := zap.NewDevelopmentConfig()
	logCfg.Sampling = nil
	logger, zErr = logCfg.Build()
	if zErr != nil {
		log.Fatal("NewDevelopmentConfig ERR:", zErr)
	}

	errLogger = logger

	defer logger.Sync() // flushes buffer, if any
	sugar = logger.Sugar()
	errSugar = sugar
}

func conf() (string, string) {
	// var lpath, name string
	var lpath, name string = "./log", "log"

	if config.Path != "" {
		lpath = config.Path
	}

	if config.Name != "" {
		name = config.Name
	}

	return lpath, name
}

func InitLog() {
	lpath, name := conf()

	logTime := time.Now().Format("2006-01-02")
	logPath := lpath + "/" + logTime + "/" + name + ".json"
	ws := zapcore.AddSync(&lumberjack.Logger{
		Filename:   logPath,
		MaxSize:    500, // megabytes
		MaxBackups: 3,
		MaxAge:     28, // days
	})
	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		ws,
		zap.InfoLevel,
	)
	// logger = zap.New(core).WithOptions(zap.AddCaller())
	logger = zap.New(core).WithOptions(zap.AddStacktrace(zap.InfoLevel))

	defer logger.Sync() // flushes buffer, if any
	sugar = logger.Sugar()
}

func InitErrLog() {
	// lumberjack.Logger is already safe for concurrent use, so we don't need to
	// lock it.
	lpath, name := conf()

	logTime := time.Now().Format("2006-01-02")
	logPath := lpath + "/" + logTime + "/" + name + "_err.json"
	ws := zapcore.AddSync(&lumberjack.Logger{
		Filename:   logPath,
		MaxSize:    500, // megabytes
		MaxBackups: 3,
		MaxAge:     28, // days
	})

	highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.ErrorLevel
	})

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		ws,
		// zap.ErrorLevel,
		highPriority,
	)

	errLogger = zap.New(core).WithOptions(zap.AddStacktrace(zap.ErrorLevel))
	defer logger.Sync() // flushes buffer, if any
	errSugar = errLogger.Sugar()
}

func Print(args ...interface{}) string {
	if len(args) == 0 {
		return ""
	}
	return fmt.Sprintf("%v ", args[0])
}

func Printf(args ...interface{}) string {
	if len(args) < 5 {
		return ""
	}

	return fmt.Sprintf(
		"method: %v, statusCode: %v, req: %s, ip: %s, time: %fs",
		args[0],
		args[1],
		args[2],
		args[3],
		args[4])
}

func (z *Zlog) Error(msg string, err error) {
	errLogger.Error(msg,
		zlogTime,
		zap.Error(err),
	)
}

func LogInfo(msg string, info ...string) {
	var logInfo string = ""
	if len(info) > 0 {
		logInfo = info[0]
	}

	errLogger.Info(msg,
		zlogTime,
		zap.String("info", logInfo),
	)
}

func Error(msg string, err ...error) {
	var logErr error = nil
	if len(err) > 0 {
		logErr = err[0]
	}
	errLogger.Error(msg,
		zlogTime,
		zap.Error(logErr),
	)
}

func Fatal(msg string, err ...error) {
	var logErr error = nil
	if len(err) > 0 {
		logErr = err[0]
	}
	errLogger.Fatal(msg,
		zlogTime,
		zap.Error(logErr),
	)
}

func Panic(msg string, err ...error) {
	var logErr error = nil
	if len(err) > 0 {
		logErr = err[0]
	}
	errLogger.Panic(msg,
		zlogTime,
		zap.Error(logErr),
	)
}

func LogsError(msg string, err error) {
	errSugar.Error(msg,
		zlogTime,
		zap.Error(err),
	)
}

func SugarError(msg string, err error) {
	errSugar.Error(msg,
		zlogTime,
		zap.Error(err),
	)
}

func SugarFatal(msg string, err error) {
	errSugar.Fatal(msg,
		zlogTime,
		zap.Error(err),
	)
}

func SugarPanic(msg string, err error) {
	errSugar.Panic(msg,
		zlogTime,
		zap.Error(err),
	)
}

func Info(msg string, info ...string) {
	var logInfo string = ""
	if len(info) > 0 {
		logInfo = info[0]
	}
	logger.Info(msg,
		zlogTime,
		zap.String("info", logInfo),
	// fields,
	)
}

func Warn(msg string, warn ...string) {
	var logWarn string = ""
	if len(warn) > 0 {
		logWarn = warn[0]
	}
	logger.Warn(msg,
		zlogTime,
		zap.String("warn", logWarn),
	)
}

func Debug(msg string, debug ...string) {
	var logDebug string = ""
	if len(debug) > 0 {
		logDebug = debug[0]
	}
	logger.Debug(msg,
		zlogTime,
		zap.String("debug", logDebug),
	)
}

func Infoff(msg string, fields ...zapcore.Field) {
	logger.Info(msg,
		zlogTime,
		fields[0],
	)
}

func LogError(msg string, err error) {
	logger.Error(msg,
		zlogTime,
		zap.Error(err),
	)
}

func LogPanic(msg string, err error) {
	logger.Panic(msg,
		zlogTime,
		zap.Error(err),
	)
}

func LogFatal(msg string, err error) {
	logger.Fatal(msg,
		zlogTime,
		zap.Error(err),
	)
}

func Infof(msg, info string) {
	sugar.Infof(msg,
		zlogTime,
		zap.String("info", info),
	)
}

func InfoW(msg, info string) {
	sugar.Infow(msg,
		zlogTime,
		"info", info,
	)
}

func Errorf(msg string, err error) {
	sugar.Errorf(msg,
		zlogTime,
		zap.Error(err),
	)
}

func Warnf(msg, warn string) {
	sugar.Warnf(msg,
		zlogTime,
		zap.String("warn", warn),
	)
}
<commit_msg>Update log<commit_after>// Copyright 2017 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/gt/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package zlog

import (
	// "errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

type Zlog struct {
	// log.Logger
}

var (
	logger, errLogger *zap.Logger
	sugar, errSugar   *zap.SugaredLogger
	zErr              error
	zlogTime          zapcore.Field = zap.String("time", time.Now().Format("2006-01-02 15:04:05"))
)

type logConfig struct {
	Mode    string
	Path    string
	Name    string
	MaxDays int64 `toml:"max_days"`
	// Srv  Server     `toml:"server"`
}

var config logConfig

func Init(tpath string) {
	if _, err := toml.DecodeFile(tpath, &config); err != nil {
		fmt.Println(err)
		return
	}

	go deleteOldLog()

	if config.Mode == "dev" {
		InitDev()
		zlogTime = zap.Error(nil)
	} else {
		InitLog()
		// InitErrLog()
		go InitErrLog()
	}
}

func deleteOldLog() {
	fileDir, _ := conf()
	var maxDays int64 = 28

	if config.MaxDays != 0 {
		maxDays = config.MaxDays
	}

	filepath.Walk(fileDir, func(path string, info os.FileInfo, err error) (returnErr error) {
		defer func() {
			if r := recover(); r != nil {
				returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
			}
		}()

		if info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*maxDays) {

			if strings.HasPrefix(filepath.Base(path), filepath.Base(fileDir)) {
				// if err := os.Remove(path); err != nil {
				if err := os.RemoveAll(path); err != nil {
					returnErr = fmt.Errorf("Failed to remove %s: %v", path, err)
				}
			}
		}
		return returnErr
	})
}

func InitDev() {
	// logger, _ = zap.NewProduction()
	logCfg := zap.NewDevelopmentConfig()
	logCfg.Sampling = nil
	logger, zErr = logCfg.Build()
	if zErr != nil {
		log.Fatal("NewDevelopmentConfig ERR:", zErr)
	}

	errLogger = logger

	defer logger.Sync() // flushes buffer, if any
	sugar = logger.Sugar()
	errSugar = sugar
}

func conf() (string, string) {
	// var lpath, name string
	var lpath, name string = "./log", "log"

	if config.Path != "" {
		lpath = config.Path
	}

	if config.Name != "" {
		name = config.Name
	}

	return lpath, name
}

func InitLog() {
	lpath, name := conf()

	logTime := time.Now().Format("2006-01-02")
	logPath := lpath + "/" + logTime + "/" + name + ".json"
	ws := zapcore.AddSync(&lumberjack.Logger{
		Filename:   logPath,
		MaxSize:    500, // megabytes
		MaxBackups: 3,
		MaxAge:     28, // days
	})
	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		ws,
		zap.InfoLevel,
	)
	// logger = zap.New(core).WithOptions(zap.AddCaller())
	logger = zap.New(core).WithOptions(zap.AddStacktrace(zap.InfoLevel))

	defer logger.Sync() // flushes buffer, if any
	sugar = logger.Sugar()
}

func InitErrLog() {
	// lumberjack.Logger is already safe for concurrent use, so we don't need to
	// lock it.
	lpath, name := conf()

	logTime := time.Now().Format("2006-01-02")
	logPath := lpath + "/" + logTime + "/" + name + "_err.json"
	ws := zapcore.AddSync(&lumberjack.Logger{
		Filename:   logPath,
		MaxSize:    500, // megabytes
		MaxBackups: 3,
		MaxAge:     28, // days
	})

	highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.ErrorLevel
	})

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		ws,
		// zap.ErrorLevel,
		highPriority,
	)

	errLogger = zap.New(core).WithOptions(zap.AddStacktrace(zap.ErrorLevel))
	defer logger.Sync() // flushes buffer, if any
	errSugar = errLogger.Sugar()
}

func Print(args ...interface{}) string {
	if len(args) == 0 {
		return ""
	}
	return fmt.Sprintf("%v ", args[0])
}

func Printf(args ...interface{}) string {
	if len(args) < 5 {
		return ""
	}

	return fmt.Sprintf(
		"method: %v, statusCode: %v, req: %s, ip: %s, time: %fs",
		args[0],
		args[1],
		args[2],
		args[3],
		args[4])
}

func (z *Zlog) Error(msg string, err error) {
	errLogger.Error(msg,
		zlogTime,
		zap.Error(err),
	)
}

func LogInfo(msg string, info ...string) {
	var logInfo string = ""
	if len(info) > 0 {
		logInfo = info[0]
	}

	errLogger.Info(msg,
		zlogTime,
		zap.String("info", logInfo),
	)
}

func Error(msg string, err ...error) {
	var logErr error = nil
	if len(err) > 0 {
		logErr = err[0]
	}
	errLogger.Error(msg,
		zlogTime,
		zap.Error(logErr),
	)
}

func Fatal(msg string, err ...error) {
	var logErr error = nil
	if len(err) > 0 {
		logErr = err[0]
	}
	errLogger.Fatal(msg,
		zlogTime,
		zap.Error(logErr),
	)
}

func Panic(msg string, err ...error) {
	var logErr error = nil
	if len(err) > 0 {
		logErr = err[0]
	}
	errLogger.Panic(msg,
		zlogTime,
		zap.Error(logErr),
	)
}

func LogsError(msg string, err error) {
	errSugar.Error(msg,
		zlogTime,
		zap.Error(err),
	)
}

func SugarError(msg string, err error) {
	errSugar.Error(msg,
		zlogTime,
		zap.Error(err),
	)
}

func SugarFatal(msg string, err error) {
	errSugar.Fatal(msg,
		zlogTime,
		zap.Error(err),
	)
}

func SugarPanic(msg string, err error) {
	errSugar.Panic(msg,
		zlogTime,
		zap.Error(err),
	)
}

func Info(msg string, info ...string) {
	var logInfo string = ""
	if len(info) > 0 {
		logInfo = info[0]
	}
	logger.Info(msg,
		zlogTime,
		zap.String("info", logInfo),
	// fields,
	)
}

func Warn(msg string, warn ...string) {
	var logWarn string = ""
	if len(warn) > 0 {
		logWarn = warn[0]
	}
	logger.Warn(msg,
		zlogTime,
		zap.String("warn", logWarn),
	)
}

func Debug(msg string, debug ...string) {
	var logDebug string = ""
	if len(debug) > 0 {
		logDebug = debug[0]
	}
	logger.Debug(msg,
		zlogTime,
		zap.String("debug", logDebug),
	)
}

func Infoff(msg string, fields ...zapcore.Field) {
	logger.Info(msg,
		zlogTime,
		fields[0],
	)
}

func LogError(msg string, err error) {
	logger.Error(msg,
		zlogTime,
		zap.Error(err),
	)
}

func LogPanic(msg string, err error) {
	logger.Panic(msg,
		zlogTime,
		zap.Error(err),
	)
}

func LogFatal(msg string, err error) {
	logger.Fatal(msg,
		zlogTime,
		zap.Error(err),
	)
}

func Infof(msg, info string) {
	sugar.Infof(msg,
		zlogTime,
		zap.String("info", info),
	)
}

func InfoW(msg, info string) {
	sugar.Infow(msg,
		zlogTime,
		"info", info,
	)
}

func Errorf(msg string, err error) {
	sugar.Errorf(msg,
		zlogTime,
		zap.Error(err),
	)
}

func Warnf(msg, warn string) {
	sugar.Warnf(msg,
		zlogTime,
		zap.String("warn", warn),
	)
}
<|endoftext|>


<commit_before>package agent

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/hashicorp/go-checkpoint"
	"github.com/hashicorp/go-hclog"
	mcli "github.com/mitchellh/cli"

	"github.com/hashicorp/consul/agent"
	"github.com/hashicorp/consul/agent/config"
	"github.com/hashicorp/consul/command/cli"
	"github.com/hashicorp/consul/command/flags"
	"github.com/hashicorp/consul/lib"
	"github.com/hashicorp/consul/logging"
	"github.com/hashicorp/consul/service_os"
	consulversion "github.com/hashicorp/consul/version"
)

func New(ui cli.Ui) *cmd {
	buildDate, err := time.Parse(time.RFC3339, consulversion.BuildDate)
	if err != nil {
		ui.Error(fmt.Sprintf("Fatal error with internal time set; check makefile for build date %v %v \n", buildDate, err))
		return nil
	}

	c := &cmd{
		ui:                ui,
		revision:          consulversion.GitCommit,
		version:           consulversion.Version,
		versionPrerelease: consulversion.VersionPrerelease,
		versionHuman:      consulversion.GetHumanVersion(),
		buildDate:         buildDate,
		flags:             flag.NewFlagSet("", flag.ContinueOnError),
	}
	config.AddFlags(c.flags, &c.configLoadOpts)
	c.help = flags.Usage(help, c.flags)
	return c
}

// AgentCommand is a Command implementation that runs a Consul agent.
// The command will not end unless a shutdown message is sent on the
// ShutdownCh. If two messages are sent on the ShutdownCh it will forcibly
// exit.
type cmd struct {
	ui                cli.Ui
	flags             *flag.FlagSet
	http              *flags.HTTPFlags
	help              string
	revision          string
	version           string
	versionPrerelease string
	versionHuman      string
	buildDate         time.Time
	configLoadOpts    config.LoadOpts
	logger            hclog.InterceptLogger
}

func (c *cmd) Run(args []string) int {
	code := c.run(args)
	if c.logger != nil {
		c.logger.Info("Exit code", "code", code)
	}
	return code
}

// checkpointResults is used to handler periodic results from our update checker
func (c *cmd) checkpointResults(results *checkpoint.CheckResponse, err error) {
	if err != nil {
		c.logger.Error("Failed to check for updates", "error", err)
		return
	}
	if results.Outdated {
		c.logger.Info("Newer Consul version available", "new_version", results.CurrentVersion, "current_version", c.version)
	}
	for _, alert := range results.Alerts {
		switch alert.Level {
		case "info":
			c.logger.Info("Bulletin", "alert_level", alert.Level, "alert_message", alert.Message, "alert_URL", alert.URL)
		default:
			c.logger.Error("Bulletin", "alert_level", alert.Level, "alert_message", alert.Message, "alert_URL", alert.URL)
		}
	}
}

func (c *cmd) startupUpdateCheck(config *config.RuntimeConfig) {
	version := config.Version
	if config.VersionPrerelease != "" {
		version += fmt.Sprintf("-%s", config.VersionPrerelease)
	}
	updateParams := &checkpoint.CheckParams{
		Product: "consul",
		Version: version,
	}
	if !config.DisableAnonymousSignature {
		updateParams.SignatureFile = filepath.Join(config.DataDir, "checkpoint-signature")
	}

	// Schedule a periodic check with expected interval of 24 hours
	checkpoint.CheckInterval(updateParams, 24*time.Hour, c.checkpointResults)

	// Do an immediate check within the next 30 seconds
	go func() {
		time.Sleep(lib.RandomStagger(30 * time.Second))
		c.checkpointResults(checkpoint.Check(updateParams))
	}()
}

// startupJoin is invoked to handle any joins specified to take place at start time
func (c *cmd) startupJoin(agent *agent.Agent, cfg *config.RuntimeConfig) error {
	if len(cfg.StartJoinAddrsLAN) == 0 {
		return nil
	}

	c.logger.Info("Joining cluster")
	// NOTE: For partitioned servers you are only capable of using start join
	// to join nodes in the default partition.
	n, err := agent.JoinLAN(cfg.StartJoinAddrsLAN, agent.AgentEnterpriseMeta())
	if err != nil {
		return err
	}

	c.logger.Info("Join completed. Initial agents synced with", "agent_count", n)
	return nil
}

// startupJoinWan is invoked to handle any joins -wan specified to take place at start time
func (c *cmd) startupJoinWan(agent *agent.Agent, cfg *config.RuntimeConfig) error {
	if len(cfg.StartJoinAddrsWAN) == 0 {
		return nil
	}

	c.logger.Info("Joining wan cluster")
	n, err := agent.JoinWAN(cfg.StartJoinAddrsWAN)
	if err != nil {
		return err
	}

	c.logger.Info("Join wan completed. Initial agents synced with", "agent_count", n)
	return nil
}

func (c *cmd) run(args []string) int {
	ui := &mcli.PrefixedUi{
		OutputPrefix: "==> ",
		InfoPrefix:   "    ",
		ErrorPrefix:  "==> ",
		Ui:           c.ui,
	}

	if err := c.flags.Parse(args); err != nil {
		if !strings.Contains(err.Error(), "help requested") {
			ui.Error(fmt.Sprintf("error parsing flags: %v", err))
		}
		return 1
	}
	if len(c.flags.Args()) > 0 {
		ui.Error(fmt.Sprintf("Unexpected extra arguments: %v", c.flags.Args()))
		return 1
	}

	// FIXME: logs should always go to stderr, but previously they were sent to
	// stdout, so continue to use Stdout for now, and fix this in a future release.
	logGate := &logging.GatedWriter{Writer: c.ui.Stdout()}
	loader := func(source config.Source) (config.LoadResult, error) {
		c.configLoadOpts.DefaultConfig = source
		return config.Load(c.configLoadOpts)
	}
	bd, err := agent.NewBaseDeps(loader, logGate)
	if err != nil {
		ui.Error(err.Error())
		return 1
	}
	c.logger = bd.Logger
	agent, err := agent.New(bd)
	if err != nil {
		ui.Error(err.Error())
		return 1
	}

	config := bd.RuntimeConfig
	if config.Logging.LogJSON {
		// Hide all non-error output when JSON logging is enabled.
		ui.Ui = &cli.BasicUI{
			BasicUi: mcli.BasicUi{ErrorWriter: c.ui.Stderr(), Writer: ioutil.Discard},
		}
	}

	ui.Output("Starting Consul agent...")

	segment := config.SegmentName
	if config.ServerMode {
		segment = "<all>"
	}
	ui.Info(fmt.Sprintf("       Version: '%s'", c.versionHuman))
	if strings.Contains(c.versionHuman, "dev") {
		ui.Info(fmt.Sprintf("      Revision: '%s'", c.revision))
	}
	ui.Info(fmt.Sprintf("    Build Date: '%s'", c.buildDate))
	ui.Info(fmt.Sprintf("       Node ID: '%s'", config.NodeID))
	ui.Info(fmt.Sprintf("     Node name: '%s'", config.NodeName))
	if ap := config.PartitionOrEmpty(); ap != "" {
		ui.Info(fmt.Sprintf("     Partition: '%s'", ap))
	}
	ui.Info(fmt.Sprintf("    Datacenter: '%s' (Segment: '%s')", config.Datacenter, segment))
	ui.Info(fmt.Sprintf("        Server: %v (Bootstrap: %v)", config.ServerMode, config.Bootstrap))
	ui.Info(fmt.Sprintf("   Client Addr: %v (HTTP: %d, HTTPS: %d, gRPC: %d, DNS: %d)", config.ClientAddrs,
		config.HTTPPort, config.HTTPSPort, config.GRPCPort, config.DNSPort))
	ui.Info(fmt.Sprintf("  Cluster Addr: %v (LAN: %d, WAN: %d)", config.AdvertiseAddrLAN,
		config.SerfPortLAN, config.SerfPortWAN))
	ui.Info(fmt.Sprintf("       Encrypt: Gossip: %v, TLS-Outgoing: %v, TLS-Incoming: %v, Auto-Encrypt-TLS: %t",
		config.EncryptKey != "", config.TLS.InternalRPC.VerifyOutgoing, config.TLS.InternalRPC.VerifyIncoming, config.AutoEncryptTLS || config.AutoEncryptAllowTLS))
	// Enable log streaming
	ui.Output("")
	ui.Output("Log data will now stream in as it occurs:\n")
	logGate.Flush()

	// wait for signal
	signalCh := make(chan os.Signal, 10)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE)

	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		for {
			var sig os.Signal
			select {
			case s := <-signalCh:
				sig = s
			case <-ctx.Done():
				return
			}

			switch sig {
			case syscall.SIGPIPE:
				continue

			case syscall.SIGHUP:
				err := fmt.Errorf("cannot reload before agent started")
				c.logger.Error("Caught", "signal", sig, "error", err)

			default:
				c.logger.Info("Caught", "signal", sig)
				cancel()
				return
			}
		}
	}()

	err = agent.Start(ctx)
	signal.Stop(signalCh)
	cancel()

	if err != nil {
		c.logger.Error("Error starting agent", "error", err)
		return 1
	}

	// shutdown agent before endpoints
	defer agent.ShutdownEndpoints()
	defer agent.ShutdownAgent()

	if !config.DisableUpdateCheck && !config.DevMode {
		c.startupUpdateCheck(config)
	}

	if err := c.startupJoin(agent, config); err != nil {
		c.logger.Error(err.Error())
		return 1
	}

	if err := c.startupJoinWan(agent, config); err != nil {
		c.logger.Error(err.Error())
		return 1
	}

	// Let the agent know we've finished registration
	agent.StartSync()

	c.logger.Info("Consul agent running!")

	// wait for signal
	signalCh = make(chan os.Signal, 10)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE)

	for {
		var sig os.Signal
		select {
		case s := <-signalCh:
			sig = s
		case <-service_os.Shutdown_Channel():
			sig = os.Interrupt
		case err := <-agent.RetryJoinCh():
			c.logger.Error("Retry join failed", "error", err)
			return 1
		case <-agent.Failed():
			// The deferred Shutdown method will log the appropriate error
			return 1
		case <-agent.ShutdownCh():
			// agent is already down!
			return 0
		}

		switch sig {
		case syscall.SIGPIPE:
			continue

		case syscall.SIGHUP:
			c.logger.Info("Caught", "signal", sig)

			err := agent.ReloadConfig()
			if err != nil {
				c.logger.Error("Reload config failed", "error", err)
			}
			config = agent.GetConfig()
		default:
			c.logger.Info("Caught", "signal", sig)

			graceful := (sig == os.Interrupt && !(config.SkipLeaveOnInt)) || (sig == syscall.SIGTERM && (config.LeaveOnTerm))
			if !graceful {
				c.logger.Info("Graceful shutdown disabled. Exiting")
				return 1
			}

			c.logger.Info("Gracefully shutting down agent...")
			gracefulCh := make(chan struct{})
			go func() {
				if err := agent.Leave(); err != nil {
					c.logger.Error("Error on leave", "error", err)
					return
				}
				close(gracefulCh)
			}()

			gracefulTimeout := 15 * time.Second
			select {
			case <-signalCh:
				c.logger.Info("Caught second signal, Exiting", "signal", sig)
				return 1
			case <-time.After(gracefulTimeout):
				c.logger.Info("Timeout on graceful leave. Exiting")
				return 1
			case <-gracefulCh:
				c.logger.Info("Graceful exit completed")
				return 0
			}
		}
	}
}

func (c *cmd) Synopsis() string {
	return synopsis
}

func (c *cmd) Help() string {
	return c.help
}

const synopsis = "Runs a Consul agent"
const help = `
Usage: consul agent [options]

  Starts the Consul agent and runs until an interrupt is received. The
  agent represents a single node in a cluster.
`
<commit_msg>cli: update agent log preamble to reflect per-listener TLS config<commit_after>package agent

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/hashicorp/go-checkpoint"
	"github.com/hashicorp/go-hclog"
	mcli "github.com/mitchellh/cli"

	"github.com/hashicorp/consul/agent"
	"github.com/hashicorp/consul/agent/config"
	"github.com/hashicorp/consul/command/cli"
	"github.com/hashicorp/consul/command/flags"
	"github.com/hashicorp/consul/lib"
	"github.com/hashicorp/consul/logging"
	"github.com/hashicorp/consul/service_os"
	consulversion "github.com/hashicorp/consul/version"
)

func New(ui cli.Ui) *cmd {
	buildDate, err := time.Parse(time.RFC3339, consulversion.BuildDate)
	if err != nil {
		ui.Error(fmt.Sprintf("Fatal error with internal time set; check makefile for build date %v %v \n", buildDate, err))
		return nil
	}

	c := &cmd{
		ui:                ui,
		revision:          consulversion.GitCommit,
		version:           consulversion.Version,
		versionPrerelease: consulversion.VersionPrerelease,
		versionHuman:      consulversion.GetHumanVersion(),
		buildDate:         buildDate,
		flags:             flag.NewFlagSet("", flag.ContinueOnError),
	}
	config.AddFlags(c.flags, &c.configLoadOpts)
	c.help = flags.Usage(help, c.flags)
	return c
}

// AgentCommand is a Command implementation that runs a Consul agent.
// The command will not end unless a shutdown message is sent on the
// ShutdownCh. If two messages are sent on the ShutdownCh it will forcibly
// exit.
type cmd struct {
	ui                cli.Ui
	flags             *flag.FlagSet
	http              *flags.HTTPFlags
	help              string
	revision          string
	version           string
	versionPrerelease string
	versionHuman      string
	buildDate         time.Time
	configLoadOpts    config.LoadOpts
	logger            hclog.InterceptLogger
}

func (c *cmd) Run(args []string) int {
	code := c.run(args)
	if c.logger != nil {
		c.logger.Info("Exit code", "code", code)
	}
	return code
}

// checkpointResults is used to handler periodic results from our update checker
func (c *cmd) checkpointResults(results *checkpoint.CheckResponse, err error) {
	if err != nil {
		c.logger.Error("Failed to check for updates", "error", err)
		return
	}
	if results.Outdated {
		c.logger.Info("Newer Consul version available", "new_version", results.CurrentVersion, "current_version", c.version)
	}
	for _, alert := range results.Alerts {
		switch alert.Level {
		case "info":
			c.logger.Info("Bulletin", "alert_level", alert.Level, "alert_message", alert.Message, "alert_URL", alert.URL)
		default:
			c.logger.Error("Bulletin", "alert_level", alert.Level, "alert_message", alert.Message, "alert_URL", alert.URL)
		}
	}
}

func (c *cmd) startupUpdateCheck(config *config.RuntimeConfig) {
	version := config.Version
	if config.VersionPrerelease != "" {
		version += fmt.Sprintf("-%s", config.VersionPrerelease)
	}
	updateParams := &checkpoint.CheckParams{
		Product: "consul",
		Version: version,
	}
	if !config.DisableAnonymousSignature {
		updateParams.SignatureFile = filepath.Join(config.DataDir, "checkpoint-signature")
	}

	// Schedule a periodic check with expected interval of 24 hours
	checkpoint.CheckInterval(updateParams, 24*time.Hour, c.checkpointResults)

	// Do an immediate check within the next 30 seconds
	go func() {
		time.Sleep(lib.RandomStagger(30 * time.Second))
		c.checkpointResults(checkpoint.Check(updateParams))
	}()
}

// startupJoin is invoked to handle any joins specified to take place at start time
func (c *cmd) startupJoin(agent *agent.Agent, cfg *config.RuntimeConfig) error {
	if len(cfg.StartJoinAddrsLAN) == 0 {
		return nil
	}

	c.logger.Info("Joining cluster")
	// NOTE: For partitioned servers you are only capable of using start join
	// to join nodes in the default partition.
	n, err := agent.JoinLAN(cfg.StartJoinAddrsLAN, agent.AgentEnterpriseMeta())
	if err != nil {
		return err
	}

	c.logger.Info("Join completed. Initial agents synced with", "agent_count", n)
	return nil
}

// startupJoinWan is invoked to handle any joins -wan specified to take place at start time
func (c *cmd) startupJoinWan(agent *agent.Agent, cfg *config.RuntimeConfig) error {
	if len(cfg.StartJoinAddrsWAN) == 0 {
		return nil
	}

	c.logger.Info("Joining wan cluster")
	n, err := agent.JoinWAN(cfg.StartJoinAddrsWAN)
	if err != nil {
		return err
	}

	c.logger.Info("Join wan completed. Initial agents synced with", "agent_count", n)
	return nil
}

func (c *cmd) run(args []string) int {
	ui := &mcli.PrefixedUi{
		OutputPrefix: "==> ",
		InfoPrefix:   "    ",
		ErrorPrefix:  "==> ",
		Ui:           c.ui,
	}

	if err := c.flags.Parse(args); err != nil {
		if !strings.Contains(err.Error(), "help requested") {
			ui.Error(fmt.Sprintf("error parsing flags: %v", err))
		}
		return 1
	}
	if len(c.flags.Args()) > 0 {
		ui.Error(fmt.Sprintf("Unexpected extra arguments: %v", c.flags.Args()))
		return 1
	}

	// FIXME: logs should always go to stderr, but previously they were sent to
	// stdout, so continue to use Stdout for now, and fix this in a future release.
	logGate := &logging.GatedWriter{Writer: c.ui.Stdout()}
	loader := func(source config.Source) (config.LoadResult, error) {
		c.configLoadOpts.DefaultConfig = source
		return config.Load(c.configLoadOpts)
	}
	bd, err := agent.NewBaseDeps(loader, logGate)
	if err != nil {
		ui.Error(err.Error())
		return 1
	}
	c.logger = bd.Logger
	agent, err := agent.New(bd)
	if err != nil {
		ui.Error(err.Error())
		return 1
	}

	config := bd.RuntimeConfig
	if config.Logging.LogJSON {
		// Hide all non-error output when JSON logging is enabled.
		ui.Ui = &cli.BasicUI{
			BasicUi: mcli.BasicUi{ErrorWriter: c.ui.Stderr(), Writer: ioutil.Discard},
		}
	}

	ui.Output("Starting Consul agent...")

	segment := config.SegmentName
	if config.ServerMode {
		segment = "<all>"
	}
	ui.Info(fmt.Sprintf("          Version: '%s'", c.versionHuman))
	if strings.Contains(c.versionHuman, "dev") {
		ui.Info(fmt.Sprintf("         Revision: '%s'", c.revision))
	}
	ui.Info(fmt.Sprintf("       Build Date: '%s'", c.buildDate))
	ui.Info(fmt.Sprintf("          Node ID: '%s'", config.NodeID))
	ui.Info(fmt.Sprintf("        Node name: '%s'", config.NodeName))
	if ap := config.PartitionOrEmpty(); ap != "" {
		ui.Info(fmt.Sprintf("        Partition: '%s'", ap))
	}
	ui.Info(fmt.Sprintf("       Datacenter: '%s' (Segment: '%s')", config.Datacenter, segment))
	ui.Info(fmt.Sprintf("           Server: %v (Bootstrap: %v)", config.ServerMode, config.Bootstrap))
	ui.Info(fmt.Sprintf("      Client Addr: %v (HTTP: %d, HTTPS: %d, gRPC: %d, DNS: %d)", config.ClientAddrs,
		config.HTTPPort, config.HTTPSPort, config.GRPCPort, config.DNSPort))
	ui.Info(fmt.Sprintf("     Cluster Addr: %v (LAN: %d, WAN: %d)", config.AdvertiseAddrLAN,
		config.SerfPortLAN, config.SerfPortWAN))
	ui.Info(fmt.Sprintf("Gossip Encryption: %t", config.EncryptKey != ""))
	ui.Info(fmt.Sprintf(" Auto-Encrypt-TLS: %t", config.AutoEncryptTLS || config.AutoEncryptAllowTLS))
	ui.Info(fmt.Sprintf("        HTTPS TLS: Verify Incoming: %t, Verify Outgoing: %t, Min Version: %s",
		config.TLS.HTTPS.VerifyIncoming, config.TLS.HTTPS.VerifyOutgoing, config.TLS.HTTPS.TLSMinVersion))
	ui.Info(fmt.Sprintf("         gRPC TLS: Verify Incoming: %t, Min Version: %s", config.TLS.GRPC.VerifyIncoming, config.TLS.GRPC.TLSMinVersion))
	ui.Info(fmt.Sprintf(" Internal RPC TLS: Verify Incoming: %t, Verify Outgoing: %t (Verify Hostname: %t), Min Version: %s",
		config.TLS.InternalRPC.VerifyIncoming, config.TLS.InternalRPC.VerifyOutgoing, config.TLS.InternalRPC.VerifyServerHostname, config.TLS.InternalRPC.TLSMinVersion))
	// Enable log streaming
	ui.Output("")
	ui.Output("Log data will now stream in as it occurs:\n")
	logGate.Flush()

	// wait for signal
	signalCh := make(chan os.Signal, 10)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE)

	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		for {
			var sig os.Signal
			select {
			case s := <-signalCh:
				sig = s
			case <-ctx.Done():
				return
			}

			switch sig {
			case syscall.SIGPIPE:
				continue

			case syscall.SIGHUP:
				err := fmt.Errorf("cannot reload before agent started")
				c.logger.Error("Caught", "signal", sig, "error", err)

			default:
				c.logger.Info("Caught", "signal", sig)
				cancel()
				return
			}
		}
	}()

	err = agent.Start(ctx)
	signal.Stop(signalCh)
	cancel()

	if err != nil {
		c.logger.Error("Error starting agent", "error", err)
		return 1
	}

	// shutdown agent before endpoints
	defer agent.ShutdownEndpoints()
	defer agent.ShutdownAgent()

	if !config.DisableUpdateCheck && !config.DevMode {
		c.startupUpdateCheck(config)
	}

	if err := c.startupJoin(agent, config); err != nil {
		c.logger.Error(err.Error())
		return 1
	}

	if err := c.startupJoinWan(agent, config); err != nil {
		c.logger.Error(err.Error())
		return 1
	}

	// Let the agent know we've finished registration
	agent.StartSync()

	c.logger.Info("Consul agent running!")

	// wait for signal
	signalCh = make(chan os.Signal, 10)
	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE)

	for {
		var sig os.Signal
		select {
		case s := <-signalCh:
			sig = s
		case <-service_os.Shutdown_Channel():
			sig = os.Interrupt
		case err := <-agent.RetryJoinCh():
			c.logger.Error("Retry join failed", "error", err)
			return 1
		case <-agent.Failed():
			// The deferred Shutdown method will log the appropriate error
			return 1
		case <-agent.ShutdownCh():
			// agent is already down!
			return 0
		}

		switch sig {
		case syscall.SIGPIPE:
			continue

		case syscall.SIGHUP:
			c.logger.Info("Caught", "signal", sig)

			err := agent.ReloadConfig()
			if err != nil {
				c.logger.Error("Reload config failed", "error", err)
			}
			config = agent.GetConfig()
		default:
			c.logger.Info("Caught", "signal", sig)

			graceful := (sig == os.Interrupt && !(config.SkipLeaveOnInt)) || (sig == syscall.SIGTERM && (config.LeaveOnTerm))
			if !graceful {
				c.logger.Info("Graceful shutdown disabled. Exiting")
				return 1
			}

			c.logger.Info("Gracefully shutting down agent...")
			gracefulCh := make(chan struct{})
			go func() {
				if err := agent.Leave(); err != nil {
					c.logger.Error("Error on leave", "error", err)
					return
				}
				close(gracefulCh)
			}()

			gracefulTimeout := 15 * time.Second
			select {
			case <-signalCh:
				c.logger.Info("Caught second signal, Exiting", "signal", sig)
				return 1
			case <-time.After(gracefulTimeout):
				c.logger.Info("Timeout on graceful leave. Exiting")
				return 1
			case <-gracefulCh:
				c.logger.Info("Graceful exit completed")
				return 0
			}
		}
	}
}

func (c *cmd) Synopsis() string {
	return synopsis
}

func (c *cmd) Help() string {
	return c.help
}

const synopsis = "Runs a Consul agent"
const help = `
Usage: consul agent [options]

  Starts the Consul agent and runs until an interrupt is received. The
  agent represents a single node in a cluster.
`
<|endoftext|>


<commit_before>package sequtil

import "testing"

func getFakeOligoText() string {
	result := "barcode\tATCGTACGTC\tTAGAATAAAC\tsample1\n"
	result += "linker\tTCGGCAGCGTCAGAT\tGACTGTGGCAACACC\n"
	result += "primer\tGTGTAT\tATCAAT\tlocus1\n"
	return result
}

func getInvalidOligosText() string {
	result := "barcode\tATCGTACGTC\tTAGAATAAAC\tsample1\n"
	result += "linker\tTCGGCAGCGTCAGAT\tGACTGTGGCAACACC\n"
	result += "primer\tGTGTAT\tATCAAT\t\n"	//missing primer id value
	return result
}
func TestOligoTextToMapsAndLinkerSlice(t *testing.T) {
	text := getFakeOligoText()
	primerMap, barcodeMap, linkers := OligoTextToMapsAndLinkerSlice(text)
	primerinput := [2]string{"GTGTAT", "ATCAAT"}
	primeroutput := primerMap[primerinput]

	if (primeroutput != "locus1") {
		t.Errorf("OligoTextToMap returned a primerMap with m[%s] = %s, wanted 'locus1'", primerinput, primeroutput)
	}
	barcodeinput := [2]string{"ATCGTACGTC", "TAGAATAAAC"}
	barcodeoutput := barcodeMap[barcodeinput]
	if (barcodeoutput != "sample1") {
		t.Errorf("OligoTextToMap returned a barcodeMap with m[%s] = %s, wanted 'sample1'", barcodeinput, barcodeoutput)
	}
	linkerinput := [2]string{"TCGGCAGCGTCAGAT", "GACTGTGGCAACACC"}
	linkeroutput := linkers[0]
	if (linkerinput != linkeroutput) {
		t.Errorf("OligoTextToMap returned linkers with [0] = %s, wanted '%s'", linkeroutput, linkerinput)
	}
}

func TestValidateOligosTextTrue(t *testing.T) {
	text := getFakeOligoText()
	if _, ok := ValidateOligosText(text); !ok {
		t.Errorf("ValidateOligosText failed on valid text:\n%s", text)
	}
}

func TestValidateOligosTextFalse(t *testing.T) {
	text := getInvalidOligosText()
	if _, ok := ValidateOligosText(text); ok {
		t.Errorf("ValidateOligosText returned 'ok' on bad input:\n%s", text)
	}
}

func TestValidateOligosTextNumberOfLinkers(t *testing.T) {
	text := getFakeOligoText()
	if numLinkers, _ := ValidateOligosText(text); numLinkers != 1 {
		t.Errorf("ValidateOligosText returned numLinkers = %d, expected 1", numLinkers)
	}
}

func TestValidateOligoLine(t *testing.T) {
	line := "barcode\tATCGTACGTC\tTAGAATAAAC\tsample1\n"
	oligotype := ValidateOligoLine(line)
	if (oligotype != "barcode") {
		t.Errorf("ValidateOligoLine returned type=%s; wanted 'barcode'", oligotype)
	}
}

func TestValidateOligoBadLine(t *testing.T) {
	line := "acme_oligo_much_sequence\tGATTACA\tGATTACA\tmany_sample\n"
	oligotype := ValidateOligoLine(line)
	if oligotype != "" {
		t.Errorf("ValidateOligoLine returned type=%s; expected empty string for invalid input", oligotype)
	}
}

//func TestOligoTextToMapLinkerEntry(t *testing.T) {
//	text := getFakeOligoText()
//	resultmap := OligoTextToMap(text)
//	mapinput := [2]string{"AAA", "TTT"}
//	mapoutput := resultmap[mapinput]
//	if (mapoutput != "") {
//		t.Errorf("OligoTextToMap returned a map with m[%s] = %s, wanted empty string", mapinput, mapoutput)
//	}
//}

<commit_msg>cleaned up unit tests<commit_after>package sequtil

import (
	"fmt"
	"testing"
)

func getFakeOligoText() string {
	result := "barcode\tATCGTACGTC\tTAGAATAAAC\tsample1\n"
	result += "linker\tTCGGCAGCGTCAGAT\tGACTGTGGCAACACC\n"
	result += "primer\tGTGTAT\tATCAAT\tlocus1\n"
	return result
}

func getInvalidOligosText() string {
	result := "barcode\tATCGTACGTC\tTAGAATAAAC\tsample1\n"
	result += "linker\tTCGGCAGCGTCAGAT\tGACTGTGGCAACACC\n"
	result += "primer\tGTGTAT\tATCAAT\t\n"	//missing primer id value
	return result
}
func TestOligoTextToMapsAndLinkerSlice(t *testing.T) {
	text := getFakeOligoText()
	primerMap, barcodeMap, linkers := OligoTextToMapsAndLinkerSlice(text)
	primerinput := [2]string{"GTGTAT", "ATCAAT"}
	primeroutput := primerMap[primerinput]

	if (primeroutput != "locus1") {
		t.Errorf("OligoTextToMap returned a primerMap with m[%s] = %s, wanted 'locus1'", primerinput, primeroutput)
	}
	barcodeinput := [2]string{"ATCGTACGTC", "TAGAATAAAC"}
	barcodeoutput := barcodeMap[barcodeinput]
	if (barcodeoutput != "sample1") {
		t.Errorf("OligoTextToMap returned a barcodeMap with m[%s] = %s, wanted 'sample1'", barcodeinput, barcodeoutput)
	}
	linkerinput := [2]string{"TCGGCAGCGTCAGAT", "GACTGTGGCAACACC"}
	linkeroutput := linkers[0]
	if (linkerinput != linkeroutput) {
		t.Errorf("OligoTextToMap returned linkers with [0] = %s, wanted '%s'", linkeroutput, linkerinput)
	}
}

func TestValidateOligosTextTrue(t *testing.T) {
	text := getFakeOligoText()
	if _, ok := ValidateOligosText(text); !ok {
		t.Errorf("ValidateOligosText failed on valid text:\n%s", text)
	}
}

func TestValidateOligosTextFalse(t *testing.T) {
	text := getInvalidOligosText()
	fmt.Printf("Unit test should print error message: ")
	if _, ok := ValidateOligosText(text); ok {
		t.Errorf("ValidateOligosText returned 'ok' on bad input:\n%s", text)
	}
}

func TestValidateOligosTextNumberOfLinkers(t *testing.T) {
	text := getFakeOligoText()
	if numLinkers, _ := ValidateOligosText(text); numLinkers != 1 {
		t.Errorf("ValidateOligosText returned numLinkers = %d, expected 1", numLinkers)
	}
}

func TestValidateOligoLine(t *testing.T) {
	line := "barcode\tATCGTACGTC\tTAGAATAAAC\tsample1\n"
	oligotype := ValidateOligoLine(line)
	if (oligotype != "barcode") {
		t.Errorf("ValidateOligoLine returned type=%s; wanted 'barcode'", oligotype)
	}
}

func TestValidateOligoBadLine(t *testing.T) {
	line := "acme_oligo_much_sequence\tGATTACA\tGATTACA\tmany_sample\n"
	fmt.Printf("Unit test should print error message: ")
	oligotype := ValidateOligoLine(line)
	if oligotype != "" {
		t.Errorf("ValidateOligoLine returned type=%s; expected empty string for invalid input", oligotype)
	}
}
<|endoftext|>


<commit_before>package enproxy

import (
	"fmt"
	"net"
	"sync"

	"github.com/getlantern/idletiming"
)

// lazyConn is a lazily initializing conn that makes sure it is only initialized
// once.  Using these allows us to ensure that we only create one connection per
// connection id, but to still support doing the Dial calls concurrently.
type lazyConn struct {
	p       *Proxy
	id      string
	addr    string
	hitEOF  bool
	connOut net.Conn
	err     error
	mutex   sync.Mutex
}

func (p *Proxy) newLazyConn(id string, addr string) *lazyConn {
	return &lazyConn{
		p:    p,
		id:   id,
		addr: addr,
	}
}

func (l *lazyConn) get() (conn net.Conn, err error) {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	if l.err != nil {
		// If dial already resulted in an error, return that
		return nil, err
	}
	if l.connOut == nil {
		// Lazily dial out
		conn, err := l.p.Dial(l.addr)
		if err != nil {
			l.err = fmt.Errorf("Unable to dial out to %s: %s", l.addr, err)
			return nil, l.err
		}

		// Wrap the connection in an idle timing one
		l.connOut = idletiming.Conn(conn, l.p.IdleTimeout, func() {
			l.mutex.Lock()
			defer l.mutex.Unlock()
			delete(l.p.connMap, l.id)
		})
	}

	return l.connOut, l.err
}
<commit_msg>Synchronized access to connMap<commit_after>package enproxy

import (
	"fmt"
	"net"
	"sync"

	"github.com/getlantern/idletiming"
)

// lazyConn is a lazily initializing conn that makes sure it is only initialized
// once.  Using these allows us to ensure that we only create one connection per
// connection id, but to still support doing the Dial calls concurrently.
type lazyConn struct {
	p       *Proxy
	id      string
	addr    string
	hitEOF  bool
	connOut net.Conn
	err     error
	mutex   sync.Mutex
}

func (p *Proxy) newLazyConn(id string, addr string) *lazyConn {
	return &lazyConn{
		p:    p,
		id:   id,
		addr: addr,
	}
}

func (l *lazyConn) get() (conn net.Conn, err error) {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	if l.err != nil {
		// If dial already resulted in an error, return that
		return nil, err
	}
	if l.connOut == nil {
		// Lazily dial out
		conn, err := l.p.Dial(l.addr)
		if err != nil {
			l.err = fmt.Errorf("Unable to dial out to %s: %s", l.addr, err)
			return nil, l.err
		}

		// Wrap the connection in an idle timing one
		l.connOut = idletiming.Conn(conn, l.p.IdleTimeout, func() {
			l.p.connMapMutex.Lock()
			defer l.p.connMapMutex.Unlock()
			delete(l.p.connMap, l.id)
		})
	}

	return l.connOut, l.err
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	"net"
	"net/http"
	"net/url"
	"path/filepath"
	"runtime"
	"time"

	"github.com/getlantern/golog"
	"github.com/getlantern/tarfs"
	"github.com/getlantern/waitforserver"
	"github.com/skratchdot/open-golang/open"
)

const (
	LocalUIDir = "../../../lantern-ui/app"
)

var (
	log = golog.LoggerFor("flashlight.ui")

	l            net.Listener
	fs           *tarfs.FileSystem
	Translations *tarfs.FileSystem
	server       *http.Server
	uiaddr       string

	externalUrl    = "https://www.facebook.com/manototv" // this string is going to be changed by Makefile
	openedExternal = false
	r              = http.NewServeMux()
)

func init() {
	// Assume the default directory containing UI assets is
	// a sibling directory to this file's directory.
	localResourcesPath := ""
	_, curDir, _, ok := runtime.Caller(1)
	if !ok {
		log.Errorf("Unable to determine caller directory")
	} else {
		localResourcesPath = filepath.Join(curDir, LocalUIDir)
		absLocalResourcesPath, err := filepath.Abs(localResourcesPath)
		if err != nil {
			absLocalResourcesPath = localResourcesPath
		}
		log.Debugf("Creating tarfs filesystem that prefers local resources at %v", absLocalResourcesPath)
	}

	var err error
	fs, err = tarfs.New(Resources, localResourcesPath)
	if err != nil {
		// Panicking here because this shouldn't happen at runtime unless the
		// resources were incorrectly embedded.
		panic(fmt.Errorf("Unable to open tarfs filesystem: %v", err))
	}
	Translations = fs.SubDir("locale")
}

func Handle(p string, handler http.Handler) string {
	r.Handle(p, handler)
	return uiaddr + p
}

func Start(addr string) error {
	var err error
	l, err = net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("Unable to listen at %v: %v", addr, l)
	}

	r.Handle("/", http.FileServer(fs))

	server = &http.Server{
		Handler:  r,
		ErrorLog: log.AsStdLogger(),
	}
	go func() {
		err := server.Serve(l)
		if err != nil {
			log.Errorf("Error serving: %v", err)
		}
	}()
	uiaddr = fmt.Sprintf("http://%v", l.Addr().String())
	log.Debugf("UI available at %v", uiaddr)

	return nil
}

// Show opens the UI in a browser. It will wait for the UI addr come up for at most 3 seconds
func Show() {
	go func() {
		addr, er := url.Parse(uiaddr)
		if er != nil {
			log.Errorf("Could not parse url `%v` with error `%v`", uiaddr, er)
			return
		}
		if err := waitforserver.WaitForServer("tcp", addr.Host, 10*time.Second); err != nil {
			log.Errorf("Error waiting for server: %v", err)
			return
		}
		err := open.Run(uiaddr)
		if err != nil {
			log.Errorf("Error opening page to `%v`: %v", uiaddr, err)
		}
		if externalUrl != "NO"+"_URL" && !openedExternal {
			time.Sleep(4 * time.Second)
			err = open.Run(externalUrl)
			if err != nil {
				log.Errorf("Error opening external page to `%v`: %v", uiaddr, err)
			}
			openedExternal = true
		}
	}()
}
<commit_msg>cleaned up error naming<commit_after>package ui

import (
	"fmt"
	"net"
	"net/http"
	"net/url"
	"path/filepath"
	"runtime"
	"time"

	"github.com/getlantern/golog"
	"github.com/getlantern/tarfs"
	"github.com/getlantern/waitforserver"
	"github.com/skratchdot/open-golang/open"
)

const (
	LocalUIDir = "../../../lantern-ui/app"
)

var (
	log = golog.LoggerFor("flashlight.ui")

	l            net.Listener
	fs           *tarfs.FileSystem
	Translations *tarfs.FileSystem
	server       *http.Server
	uiaddr       string

	externalUrl    = "https://www.facebook.com/manototv" // this string is going to be changed by Makefile
	openedExternal = false
	r              = http.NewServeMux()
)

func init() {
	// Assume the default directory containing UI assets is
	// a sibling directory to this file's directory.
	localResourcesPath := ""
	_, curDir, _, ok := runtime.Caller(1)
	if !ok {
		log.Errorf("Unable to determine caller directory")
	} else {
		localResourcesPath = filepath.Join(curDir, LocalUIDir)
		absLocalResourcesPath, err := filepath.Abs(localResourcesPath)
		if err != nil {
			absLocalResourcesPath = localResourcesPath
		}
		log.Debugf("Creating tarfs filesystem that prefers local resources at %v", absLocalResourcesPath)
	}

	var err error
	fs, err = tarfs.New(Resources, localResourcesPath)
	if err != nil {
		// Panicking here because this shouldn't happen at runtime unless the
		// resources were incorrectly embedded.
		panic(fmt.Errorf("Unable to open tarfs filesystem: %v", err))
	}
	Translations = fs.SubDir("locale")
}

func Handle(p string, handler http.Handler) string {
	r.Handle(p, handler)
	return uiaddr + p
}

func Start(addr string) error {
	var err error
	l, err = net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("Unable to listen at %v: %v", addr, l)
	}

	r.Handle("/", http.FileServer(fs))

	server = &http.Server{
		Handler:  r,
		ErrorLog: log.AsStdLogger(),
	}
	go func() {
		err := server.Serve(l)
		if err != nil {
			log.Errorf("Error serving: %v", err)
		}
	}()
	uiaddr = fmt.Sprintf("http://%v", l.Addr().String())
	log.Debugf("UI available at %v", uiaddr)

	return nil
}

// Show opens the UI in a browser. It will wait for the UI addr come up for at most 3 seconds
func Show() {
	go func() {
		addr, err := url.Parse(uiaddr)
		if err != nil {
			log.Errorf("Could not parse url `%v` with error `%v`", uiaddr, err)
			return
		}
		if err := waitforserver.WaitForServer("tcp", addr.Host, 10*time.Second); err != nil {
			log.Errorf("Error waiting for server: %v", err)
			return
		}
		err = open.Run(uiaddr)
		if err != nil {
			log.Errorf("Error opening page to `%v`: %v", uiaddr, err)
		}
		if externalUrl != "NO"+"_URL" && !openedExternal {
			time.Sleep(4 * time.Second)
			err = open.Run(externalUrl)
			if err != nil {
				log.Errorf("Error opening external page to `%v`: %v", uiaddr, err)
			}
			openedExternal = true
		}
	}()
}
<|endoftext|>