<commit_before>//  Copyright (c) 2017 Rackspace
//
//  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 tools

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"strings"
	"testing"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"github.com/stretchr/testify/require"
	"github.com/troubling/hummingbird/accountserver"
	"github.com/troubling/hummingbird/client"
	"github.com/troubling/hummingbird/common/conf"
	"github.com/troubling/hummingbird/common/ring"
	"github.com/troubling/hummingbird/common/srv"
	"github.com/troubling/hummingbird/containerserver"
	"github.com/uber-go/tally"
)

type FakeLowLevelLogger struct{}

func (f *FakeLowLevelLogger) Error(msg string, fields ...zapcore.Field) {
	return
}
func (f *FakeLowLevelLogger) Info(msg string, fields ...zapcore.Field) {
	return
}
func (f *FakeLowLevelLogger) Debug(msg string, fields ...zapcore.Field) {
	return
}
func (f *FakeLowLevelLogger) With(fields ...zapcore.Field) *zap.Logger {
	return nil
}

type FakeRing struct {
	Devs      []ring.Device
	Ip        string
	Port      int
	nodeCalls int
}

func (r *FakeRing) GetNodes(partition uint64) (response []*ring.Device) {
	if r.nodeCalls <= 0 {
		return nil
	}
	r.nodeCalls--
	for i := range r.Devs {
		response = append(response, &r.Devs[i])
	}
	return response
}

func (r *FakeRing) GetNodesInOrder(partition uint64) (response []*ring.Device) {
	if partition > 3 {
		return nil
	}
	for i := range r.Devs {
		response = append(response, &r.Devs[i])
	}
	return response
}

func (r *FakeRing) GetJobNodes(partition uint64, localDevice int) (response []*ring.Device, handoff bool) {
	return nil, false
}

func (r *FakeRing) GetPartition(account string, container string, object string) uint64 {
	objParts := strings.Split(object, "-")
	if p, err := strconv.ParseUint(objParts[0], 10, 64); err == nil {
		return p
	}
	return 0
}

func (r *FakeRing) LocalDevices(localPort int) (devs []*ring.Device, err error) {
	return nil, nil
}

func (r *FakeRing) AllDevices() (devs []ring.Device) {
	return r.Devs
}

func (r *FakeRing) GetMoreNodes(partition uint64) ring.MoreNodes {
	return nil
}

func (r *FakeRing) PartitionCount() uint64 {
	return 1
}

func (r *FakeRing) ReplicaCount() uint64 {
	return 2
}

func TestGetDispersionObjects(t *testing.T) {
	fakeDevs := []ring.Device{
		{Ip: "127.0.0.1", Port: 80, Device: "sda"},
		{Ip: "127.0.0.1", Port: 80, Device: "sdb"}}

	oring := &FakeRing{Devs: fakeDevs, nodeCalls: 3}
	dObjs := make(chan string)
	container := "objs"
	go getDispersionNames(container, "", oring, dObjs)
	for val := range dObjs {
		part := oring.GetPartition(AdminAccount, container, val)
		require.Equal(t, strings.Index(val, fmt.Sprintf("%d-", part)), 0)
	}
}

func TestPutDispersionObjects(t *testing.T) {
	p := conf.Policy{Name: "hat"}
	c := &testDispersionClient{objRing: &FakeRing{Devs: []ring.Device{{Device: "sda"}, {Device: "sdb"}, {Device: "sdc"}}, nodeCalls: 3}}
	require.True(t, putDispersionObjects(c, &p, &FakeLowLevelLogger{}))
	require.Equal(t, 4, c.objPuts)
}

func TestScanDispersionObjects(t *testing.T) {
	//p := conf.Policy{Name: "hat"}

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Index(r.URL.Path, "sda") >= 0 {
			srv.SimpleErrorResponse(w, 404, "")
			return
		}
		srv.SimpleErrorResponse(w, 200, "")
	}))

	defer ts.Close()
	u, err := url.Parse(ts.URL)
	require.Nil(t, err)
	host, ports, err := net.SplitHostPort(u.Host)
	require.Nil(t, err)
	port, err := strconv.Atoi(ports)
	require.Nil(t, err)
	c := &testDispersionClient{objRing: &FakeRing{
		Devs: []ring.Device{
			{Device: "sda", Ip: host, Port: port},
			{Device: "sdb", Ip: host, Port: port},
			{Device: "sdc", Ip: host, Port: port}}, nodeCalls: 3},
		contCalls: 1, objCalls: 1}

	metricsScope := tally.NewTestScope("hb_andrewd", map[string]string{})

	bc := NewBirdCatcher(&FakeLowLevelLogger{}, c, metricsScope)
	bc.onceFullDispersion = true

	dummyCanceler := make(chan struct{})
	defer close(dummyCanceler)
	bc.scanDispersionObjs(dummyCanceler)
	for k, v := range metricsScope.Snapshot().Gauges() {
		if strings.Index(k, "hb_andrewd.dispersion_object_p0_missing_0") >= 0 {
			require.Equal(t, 1, int(v.Value()))
		} else {
			require.Equal(t, 0, int(v.Value()))
		}
	}
}

type testDispersionClient struct {
	objRing   ring.Ring
	contRing  ring.Ring
	objPuts   int
	contCalls int
	objCalls  int
}

func (c *testDispersionClient) PutAccount(account string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PostAccount(account string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) GetAccount(account string, options map[string]string, headers http.Header) *http.Response {
	if c.contCalls > 0 {
		c.contCalls--
		olrs := []accountserver.ContainerListingRecord{{Name: "disp-objs-0"}}
		out, _ := json.Marshal(olrs)
		return client.ResponseStub(200, string(out))
	}
	olrs := []accountserver.ContainerListingRecord{}
	out, _ := json.Marshal(olrs)
	return client.ResponseStub(200, string(out))
}

func (c *testDispersionClient) HeadAccount(account string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) DeleteAccount(account string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PutContainer(account string, container string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PostContainer(account string, container string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) GetContainer(account string, container string, options map[string]string, headers http.Header) *http.Response {
	if c.objCalls > 0 {
		c.objCalls--
		olrs := []containerserver.ObjectListingRecord{{Name: "0-12345"}}
		out, _ := json.Marshal(olrs)
		return client.ResponseStub(200, string(out))
	}
	olrs := []containerserver.ObjectListingRecord{}
	out, _ := json.Marshal(olrs)
	return client.ResponseStub(200, string(out))
}

func (c *testDispersionClient) GetContainerInfo(account string, container string) (*client.ContainerInfo, error) {
	return nil, nil
}

func (c *testDispersionClient) HeadContainer(account string, container string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) DeleteContainer(account string, container string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PutObject(account string, container string, obj string, headers http.Header, src io.Reader) *http.Response {
	c.objPuts++
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PostObject(account string, container string, obj string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) GetObject(account string, container string, obj string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) HeadObject(account string, container string, obj string, headers http.Header) *http.Response {
	if obj == "object-init" {
		return client.ResponseStub(404, "")
	}
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) DeleteObject(account string, container string, obj string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) ObjectRingFor(account string, container string) (ring.Ring, *http.Response) {
	return c.objRing, nil //client.ResponseStub(200, "")
}

func (c *testDispersionClient) ContainerRing() ring.Ring {
	return c.contRing
}
<commit_msg>Updating for dispersion changes<commit_after>//  Copyright (c) 2017 Rackspace
//
//  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 tools

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"strings"
	"testing"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"github.com/stretchr/testify/require"
	"github.com/troubling/hummingbird/accountserver"
	"github.com/troubling/hummingbird/client"
	"github.com/troubling/hummingbird/common/conf"
	"github.com/troubling/hummingbird/common/ring"
	"github.com/troubling/hummingbird/common/srv"
	"github.com/troubling/hummingbird/containerserver"
	"github.com/uber-go/tally"
)

type FakeLowLevelLogger struct{}

func (f *FakeLowLevelLogger) Error(msg string, fields ...zapcore.Field) {
	return
}
func (f *FakeLowLevelLogger) Info(msg string, fields ...zapcore.Field) {
	return
}
func (f *FakeLowLevelLogger) Debug(msg string, fields ...zapcore.Field) {
	return
}
func (f *FakeLowLevelLogger) With(fields ...zapcore.Field) *zap.Logger {
	return nil
}

type FakeRing struct {
	Devs      []*ring.Device
	Ip        string
	Port      int
	nodeCalls int
}

func (r *FakeRing) GetNodes(partition uint64) (response []*ring.Device) {
	if r.nodeCalls <= 0 {
		return nil
	}
	r.nodeCalls--
	for i := range r.Devs {
		response = append(response, r.Devs[i])
	}
	return response
}

func (r *FakeRing) GetNodesInOrder(partition uint64) (response []*ring.Device) {
	if partition > 3 {
		return nil
	}
	for i := range r.Devs {
		response = append(response, r.Devs[i])
	}
	return response
}

func (r *FakeRing) GetJobNodes(partition uint64, localDevice int) (response []*ring.Device, handoff bool) {
	return nil, false
}

func (r *FakeRing) GetPartition(account string, container string, object string) uint64 {
	objParts := strings.Split(object, "-")
	if p, err := strconv.ParseUint(objParts[0], 10, 64); err == nil {
		return p
	}
	return 0
}

func (r *FakeRing) LocalDevices(localPort int) (devs []*ring.Device, err error) {
	return nil, nil
}

func (r *FakeRing) AllDevices() (devs []*ring.Device) {
	return r.Devs
}

func (r *FakeRing) GetMoreNodes(partition uint64) ring.MoreNodes {
	return nil
}

func (r *FakeRing) PartitionCount() uint64 {
	return 1
}

func (r *FakeRing) ReplicaCount() uint64 {
	return 2
}

func TestGetDispersionObjects(t *testing.T) {
	fakeDevs := []*ring.Device{
		{Ip: "127.0.0.1", Port: 80, Device: "sda"},
		{Ip: "127.0.0.1", Port: 80, Device: "sdb"}}

	oring := &FakeRing{Devs: fakeDevs, nodeCalls: 3}
	dObjs := make(chan string)
	container := "objs"
	go getDispersionNames(container, "", oring, dObjs)
	for val := range dObjs {
		part := oring.GetPartition(AdminAccount, container, val)
		require.Equal(t, strings.Index(val, fmt.Sprintf("%d-", part)), 0)
	}
}

func TestPutDispersionObjects(t *testing.T) {
	p := conf.Policy{Name: "hat"}
	c := &testDispersionClient{objRing: &FakeRing{Devs: []*ring.Device{{Device: "sda"}, {Device: "sdb"}, {Device: "sdc"}}, nodeCalls: 3}}
	require.True(t, putDispersionObjects(c, &p, &FakeLowLevelLogger{}))
	require.Equal(t, 4, c.objPuts)
}

func TestScanDispersionObjects(t *testing.T) {
	//p := conf.Policy{Name: "hat"}

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.Index(r.URL.Path, "sda") >= 0 {
			srv.SimpleErrorResponse(w, 404, "")
			return
		}
		srv.SimpleErrorResponse(w, 200, "")
	}))

	defer ts.Close()
	u, err := url.Parse(ts.URL)
	require.Nil(t, err)
	host, ports, err := net.SplitHostPort(u.Host)
	require.Nil(t, err)
	port, err := strconv.Atoi(ports)
	require.Nil(t, err)
	c := &testDispersionClient{objRing: &FakeRing{
		Devs: []*ring.Device{
			{Device: "sda", Ip: host, Port: port},
			{Device: "sdb", Ip: host, Port: port},
			{Device: "sdc", Ip: host, Port: port}}, nodeCalls: 3},
		contCalls: 1, objCalls: 1}

	metricsScope := tally.NewTestScope("hb_andrewd", map[string]string{})

	bc := NewBirdCatcher(&FakeLowLevelLogger{}, c, metricsScope)
	bc.onceFullDispersion = true

	dummyCanceler := make(chan struct{})
	defer close(dummyCanceler)
	bc.scanDispersionObjs(dummyCanceler)
	for k, v := range metricsScope.Snapshot().Gauges() {
		if strings.Index(k, "hb_andrewd.dispersion_object_p0_missing_0") >= 0 {
			require.Equal(t, 1, int(v.Value()))
		} else {
			require.Equal(t, 0, int(v.Value()))
		}
	}
}

type testDispersionClient struct {
	objRing   ring.Ring
	contRing  ring.Ring
	objPuts   int
	contCalls int
	objCalls  int
}

func (c *testDispersionClient) PutAccount(account string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PostAccount(account string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) GetAccount(account string, options map[string]string, headers http.Header) *http.Response {
	if c.contCalls > 0 {
		c.contCalls--
		olrs := []accountserver.ContainerListingRecord{{Name: "disp-objs-0"}}
		out, _ := json.Marshal(olrs)
		return client.ResponseStub(200, string(out))
	}
	olrs := []accountserver.ContainerListingRecord{}
	out, _ := json.Marshal(olrs)
	return client.ResponseStub(200, string(out))
}

func (c *testDispersionClient) HeadAccount(account string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) DeleteAccount(account string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PutContainer(account string, container string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PostContainer(account string, container string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) GetContainer(account string, container string, options map[string]string, headers http.Header) *http.Response {
	if c.objCalls > 0 {
		c.objCalls--
		olrs := []containerserver.ObjectListingRecord{{Name: "0-12345"}}
		out, _ := json.Marshal(olrs)
		return client.ResponseStub(200, string(out))
	}
	olrs := []containerserver.ObjectListingRecord{}
	out, _ := json.Marshal(olrs)
	return client.ResponseStub(200, string(out))
}

func (c *testDispersionClient) GetContainerInfo(account string, container string) (*client.ContainerInfo, error) {
	return nil, nil
}

func (c *testDispersionClient) HeadContainer(account string, container string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) DeleteContainer(account string, container string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PutObject(account string, container string, obj string, headers http.Header, src io.Reader) *http.Response {
	c.objPuts++
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) PostObject(account string, container string, obj string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) GetObject(account string, container string, obj string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) HeadObject(account string, container string, obj string, headers http.Header) *http.Response {
	if obj == "object-init" {
		return client.ResponseStub(404, "")
	}
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) DeleteObject(account string, container string, obj string, headers http.Header) *http.Response {
	return client.ResponseStub(200, "")
}

func (c *testDispersionClient) ObjectRingFor(account string, container string) (ring.Ring, *http.Response) {
	return c.objRing, nil //client.ResponseStub(200, "")
}

func (c *testDispersionClient) ContainerRing() ring.Ring {
	return c.contRing
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"

	"github.com/ninjasphere/go-ninja/logger"
	"gopkg.in/alecthomas/kingpin.v1"
)

var log = logger.GetLogger("config")

var config map[string]interface{}

func init() {
	MustRefresh()
}

func GetAll(flatten bool) map[string]interface{} {
	if flatten {
		return config
	} else {
		return unflatten(config)
	}
}

var serial string

func Serial() string {
	if serial == "" {
		cmd := exec.Command("sphere-serial", os.Args[1:]...)

		var out bytes.Buffer
		cmd.Stdout = &out

		err := cmd.Run()
		if err != nil {
			log.Fatalf("Failed to get sphere serial (sphere-serial must be in the PATH) error:%s", err)
		}

		serial = out.String()
	}

	return serial
}

func String(def string, path ...string) string {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(string)
}

// MustString returns the string property at the path
func MustString(path ...string) string {
	return mustGet(path...).(string)
}

// MustStringArray returns the string array property at the path
func MustStringArray(path ...string) []string {
	a := mustGet(path...).([]interface{})
	b := make([]string, len(a))
	for i := range a {
		b[i] = a[i].(string)
	}
	return b
}

// Int returns the integer property at the path, with a default
func Int(def int, path ...string) int {
	val := get(path...)
	if val == nil {
		return def
	}
	return int(val.(float64))
}

// MustInt returns the string property at the path
func MustInt(path ...string) int {
	return int(mustGet(path...).(float64))
}

// Bool returns the boolean property at the path, with a default
func Bool(def bool, path ...string) bool {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(bool)
}

// MustBool returns the boolean property at the path
func MustBool(path ...string) bool {
	return mustGet(path...).(bool)
}

var hey = "what's up buddy?"

func HasString(path ...string) bool {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return false
	}

	_, ok = val.(string)
	return ok
}

func mustGet(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		log.Fatalf("expected value for %v but found nothing", path)
	}
	return val
}

func get(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return nil
	}
	return val
}

func MustRefresh() {

	flat := make(map[string]interface{})

	// cli overrides
	addArgs(flat)

	// load environments (no value args) from cli args
	environments := []string{"default"}
	for name, value := range flat {
		if value == nil {
			environments = append(environments, name)
		}
	}

	flat["env"] = environments

	userHome := getUserHome()

	// env vars (if starting with "sphere_")
	addEnv(flat)

	// anything that can be parsed as a number, is a number
	parseNumbers(flat)

	installDir := "/opt/ninjablocks"
	if val, ok := flat["installDirectory"]; ok {
		installDir = val.(string)
	}

	flat["installDirectory"] = installDir

	//sphere_configDir=/home/elliot/repos/sphere-dev/sphere-common/config

	if _, err := os.Stat(installDir); err != nil {
		log.Fatalf("Couldn't load sphere install directory. Override with env var sphere_installDirectory. error:%s", err)
	}

	// credentials file
	addFile("/etc/opt/ninja/credentials.json", flat)

	// home directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(userHome, ".sphere", environments[i]+".json"), flat)
	}

	// current directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(".", "config", environments[i]+".json"), flat)
	}

	// common directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(installDir, "sphere-common", "config", environments[i]+".json"), flat)
	}

	// sphere-common credentials config
	addFile(filepath.Join(installDir, "sphere-common", "config", "credentials.json"), flat)

	//log.Debugf("Loaded config: %v", flat)

	config = flat
}

func addEnv(config map[string]interface{}) {
	for _, v := range os.Environ() {
		split := strings.Split(v, "=")

		name, value := split[0], split[1]

		if strings.HasPrefix(name, "sphere_") {
			name = strings.TrimPrefix(name, "sphere_")
			name = strings.Replace(name, "_", ".", 0)

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}
	}
}

func addArgs(config map[string]interface{}) {

	parser := kingpin.Tokenize(os.Args[1:]).Tokens

	for token, parser := parser.Peek(), parser.Next(); token.Type != kingpin.TokenEOL; token, parser = parser.Peek(), parser.Next() {

		if token.IsFlag() {
			var value interface{}
			name := token.Value

			next := parser.Peek()
			if next.Type == kingpin.TokenArg {
				if next.Value == "false" {
					value = false
				} else if next.Value == "true" {
					value = true
				} else {
					value = next.Value
				}
			} else {
				value = true
				// It's an environment indicator... like --cloud-production
			}

			config[name] = value
		}

	}

}

func addFile(path string, config map[string]interface{}) error {
	//log.Debugf("Loading config file: %s", path)

	file, e := ioutil.ReadFile(path)
	if e != nil {
		return fmt.Errorf("Failed to load file: %s error: %s", path, e)
	}

	content := make(map[string]interface{})
	e = json.Unmarshal(file, &content)
	if e != nil {
		return fmt.Errorf("Failed to read file: %s error: %s", path, e)
	}

	flatten(content, nil, config)
	return nil
}

func flatten(input interface{}, lpath []string, flattened map[string]interface{}) {
	if lpath == nil {
		lpath = []string{}
	}

	if reflect.ValueOf(input).Kind() == reflect.Map {
		for rkey, value := range input.(map[string]interface{}) {
			flatten(value, append(lpath, rkey), flattened)
		}
	} else {
		if _, ok := flattened[strings.Join(lpath, ".")]; !ok {
			flattened[strings.Join(lpath, ".")] = input
		}
	}
}

func unflatten(flat map[string]interface{}) map[string]interface{} {
	out := make(map[string]interface{})
	obj := out

	for key, val := range flat {

		obj = out
		keys := strings.Split(key, ".")

		for i, k := range keys {
			if i == len(keys)-1 {
				obj[k] = val
			} else {
				next, ok := obj[k]
				if !ok {
					next = make(map[string]interface{})
				}
				obj[k] = next
				obj = next.(map[string]interface{})
			}
		}

	}

	return out
}

func parseNumbers(config map[string]interface{}) {
	for name, val := range config {
		stringVal, ok := val.(string)
		if ok {
			floatVal, err := strconv.ParseFloat(stringVal, 64)
			if err == nil {
				config[name] = floatVal
			}
		}
	}
}

func getUserHome() string {
	usr, err := user.Current()
	if err != nil {
		return "/root"
	}
	return usr.HomeDir
}
<commit_msg>Load creds from data directory<commit_after>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"

	"github.com/ninjasphere/go-ninja/logger"
	"gopkg.in/alecthomas/kingpin.v1"
)

var log = logger.GetLogger("config")

var config map[string]interface{}

func init() {
	MustRefresh()
}

func GetAll(flatten bool) map[string]interface{} {
	if flatten {
		return config
	} else {
		return unflatten(config)
	}
}

var serial string

func Serial() string {
	if serial == "" {
		cmd := exec.Command("sphere-serial", os.Args[1:]...)

		var out bytes.Buffer
		cmd.Stdout = &out

		err := cmd.Run()
		if err != nil {
			log.Fatalf("Failed to get sphere serial (sphere-serial must be in the PATH) error:%s", err)
		}

		serial = out.String()
	}

	return serial
}

func String(def string, path ...string) string {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(string)
}

// MustString returns the string property at the path
func MustString(path ...string) string {
	return mustGet(path...).(string)
}

// MustStringArray returns the string array property at the path
func MustStringArray(path ...string) []string {
	a := mustGet(path...).([]interface{})
	b := make([]string, len(a))
	for i := range a {
		b[i] = a[i].(string)
	}
	return b
}

// Int returns the integer property at the path, with a default
func Int(def int, path ...string) int {
	val := get(path...)
	if val == nil {
		return def
	}
	return int(val.(float64))
}

// MustInt returns the string property at the path
func MustInt(path ...string) int {
	return int(mustGet(path...).(float64))
}

// Bool returns the boolean property at the path, with a default
func Bool(def bool, path ...string) bool {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(bool)
}

// MustBool returns the boolean property at the path
func MustBool(path ...string) bool {
	return mustGet(path...).(bool)
}

var hey = "what's up buddy?"

func HasString(path ...string) bool {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return false
	}

	_, ok = val.(string)
	return ok
}

func mustGet(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		log.Fatalf("expected value for %v but found nothing", path)
	}
	return val
}

func get(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return nil
	}
	return val
}

func MustRefresh() {

	flat := make(map[string]interface{})

	// cli overrides
	addArgs(flat)

	// load environments (no value args) from cli args
	environments := []string{"default"}
	for name, value := range flat {
		if value == nil {
			environments = append(environments, name)
		}
	}

	flat["env"] = environments

	userHome := getUserHome()

	// env vars (if starting with "sphere_")
	addEnv(flat)

	// anything that can be parsed as a number, is a number
	parseNumbers(flat)

	installDir := "/opt/ninjablocks"
	if val, ok := flat["installDirectory"]; ok {
		installDir = val.(string)
	}

	flat["installDirectory"] = installDir

	//sphere_configDir=/home/elliot/repos/sphere-dev/sphere-common/config

	if _, err := os.Stat(installDir); err != nil {
		log.Fatalf("Couldn't load sphere install directory. Override with env var sphere_installDirectory. error:%s", err)
	}

	// credentials file
	addFile("/etc/opt/ninja/credentials.json", flat)
	addFile("/data/etc/opt/ninja/credentials.json", flat)

	// home directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(userHome, ".sphere", environments[i]+".json"), flat)
	}

	// current directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(".", "config", environments[i]+".json"), flat)
	}

	// common directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(installDir, "sphere-common", "config", environments[i]+".json"), flat)
	}

	// sphere-common credentials config
	addFile(filepath.Join(installDir, "sphere-common", "config", "credentials.json"), flat)

	//log.Debugf("Loaded config: %v", flat)

	config = flat
}

func addEnv(config map[string]interface{}) {
	for _, v := range os.Environ() {
		split := strings.Split(v, "=")

		name, value := split[0], split[1]

		if strings.HasPrefix(name, "sphere_") {
			name = strings.TrimPrefix(name, "sphere_")
			name = strings.Replace(name, "_", ".", 0)

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}
	}
}

func addArgs(config map[string]interface{}) {

	parser := kingpin.Tokenize(os.Args[1:]).Tokens

	for token, parser := parser.Peek(), parser.Next(); token.Type != kingpin.TokenEOL; token, parser = parser.Peek(), parser.Next() {

		if token.IsFlag() {
			var value interface{}
			name := token.Value

			next := parser.Peek()
			if next.Type == kingpin.TokenArg {
				if next.Value == "false" {
					value = false
				} else if next.Value == "true" {
					value = true
				} else {
					value = next.Value
				}
			} else {
				value = true
				// It's an environment indicator... like --cloud-production
			}

			config[name] = value
		}

	}

}

func addFile(path string, config map[string]interface{}) error {
	//log.Debugf("Loading config file: %s", path)

	file, e := ioutil.ReadFile(path)
	if e != nil {
		return fmt.Errorf("Failed to load file: %s error: %s", path, e)
	}

	content := make(map[string]interface{})
	e = json.Unmarshal(file, &content)
	if e != nil {
		return fmt.Errorf("Failed to read file: %s error: %s", path, e)
	}

	flatten(content, nil, config)
	return nil
}

func flatten(input interface{}, lpath []string, flattened map[string]interface{}) {
	if lpath == nil {
		lpath = []string{}
	}

	if reflect.ValueOf(input).Kind() == reflect.Map {
		for rkey, value := range input.(map[string]interface{}) {
			flatten(value, append(lpath, rkey), flattened)
		}
	} else {
		if _, ok := flattened[strings.Join(lpath, ".")]; !ok {
			flattened[strings.Join(lpath, ".")] = input
		}
	}
}

func unflatten(flat map[string]interface{}) map[string]interface{} {
	out := make(map[string]interface{})
	obj := out

	for key, val := range flat {

		obj = out
		keys := strings.Split(key, ".")

		for i, k := range keys {
			if i == len(keys)-1 {
				obj[k] = val
			} else {
				next, ok := obj[k]
				if !ok {
					next = make(map[string]interface{})
				}
				obj[k] = next
				obj = next.(map[string]interface{})
			}
		}

	}

	return out
}

func parseNumbers(config map[string]interface{}) {
	for name, val := range config {
		stringVal, ok := val.(string)
		if ok {
			floatVal, err := strconv.ParseFloat(stringVal, 64)
			if err == nil {
				config[name] = floatVal
			}
		}
	}
}

func getUserHome() string {
	usr, err := user.Current()
	if err != nil {
		return "/root"
	}
	return usr.HomeDir
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// Config is a configuration for browsing in a particualr path.
type Config struct {
	PathScope   string
	Root        http.FileSystem
	BaseURL     string
	AbsoluteURL string
	AddrPath    string
	Token       string // Anti CSRF token
	StyleSheet  string // Costum stylesheet
	FrontMatter string // Default frontmatter to save files in
	HugoEnabled bool   // Enables the Hugo plugin for File Manager

	AllowNew      bool
	AllowEdit     bool
	AllowCommands bool
}

// Parse parses the configuration set by the user so it can
// be used by the middleware
func Parse(c *caddy.Controller) ([]Config, error) {
	var configs []Config

	appendConfig := func(cfg Config) error {
		for _, c := range configs {
			if c.PathScope == cfg.PathScope {
				return fmt.Errorf("duplicate file managing config for %s", c.PathScope)
			}
		}
		configs = append(configs, cfg)
		return nil
	}

	var err error

	for c.Next() {
		var cfg = Config{PathScope: ".", BaseURL: "", FrontMatter: "yaml", HugoEnabled: false}
		for c.NextBlock() {
			switch c.Val() {
			case "show":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.PathScope = c.Val()
				cfg.PathScope = strings.TrimSuffix(cfg.PathScope, "/")
			case "frontmatter":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.FrontMatter = c.Val()
				if cfg.FrontMatter != "yaml" && cfg.FrontMatter != "json" && cfg.FrontMatter != "toml" {
					return configs, c.Err("frontmatter type not supported")
				}
			case "on":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.BaseURL = c.Val()
				cfg.BaseURL = strings.TrimPrefix(cfg.BaseURL, "/")
				cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/")
				cfg.BaseURL = "/" + cfg.BaseURL
			case "styles":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				var tplBytes []byte
				tplBytes, err = ioutil.ReadFile(c.Val())
				if err != nil {
					return configs, err
				}
				cfg.StyleSheet = string(tplBytes)
			case "allow_new":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.AllowNew, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_edit":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.AllowEdit, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_comands":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.AllowCommands, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			}
		}

		caddyConf := httpserver.GetConfig(c)
		cfg.AbsoluteURL = strings.TrimSuffix(caddyConf.Addr.Path, "/") + "/" + cfg.BaseURL
		cfg.AbsoluteURL = strings.Replace(cfg.AbsoluteURL, "//", "/", -1)
		cfg.AbsoluteURL = strings.TrimSuffix(cfg.AbsoluteURL, "/")
		cfg.AddrPath = strings.TrimSuffix(caddyConf.Addr.Path, "/")
		cfg.Root = http.Dir(cfg.PathScope)
		if err := appendConfig(cfg); err != nil {
			return configs, err
		}
	}

	return configs, nil
}
<commit_msg>Add configs #19 #21 #23 #24<commit_after>package config

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"strings"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// Config is a configuration for browsing in a particualr path.
type Config struct {
	*UserConfig
	BaseURL     string
	AbsoluteURL string
	AddrPath    string
	Token       string // Anti CSRF token
	FrontMatter string // Default frontmatter to save files in
	HugoEnabled bool   // Enables the Hugo plugin for File Manager
}

// UserConfig contains the configuration for each user
type UserConfig struct {
	PathScope   string
	Root        http.FileSystem
	StyleSheet  string // Costum stylesheet
	FrontMatter string // Default frontmatter to save files in
	AllowNew    bool   // Can create files and folders
	AllowEdit   bool   // Can edit/rename files

	Allow      []string         // Allowed browse directories/files
	AllowRegex []*regexp.Regexp // Regex of the previous
	Block      []string         // Blocked browse directories/files
	BlockRegex []*regexp.Regexp // Regex of the previous

	AllowCommands   bool     // Can execute commands
	AllowedCommands []string // Allowed commands
	BlockedCommands []string // Blocked Commands
}

// Parse parses the configuration set by the user so it can
// be used by the middleware
func Parse(c *caddy.Controller) ([]Config, error) {
	var configs []Config

	appendConfig := func(cfg Config) error {
		for _, c := range configs {
			if c.PathScope == cfg.PathScope {
				return fmt.Errorf("duplicate file managing config for %s", c.PathScope)
			}
		}
		configs = append(configs, cfg)
		return nil
	}

	var err error

	for c.Next() {
		var cfg = Config{}
		cfg.PathScope = "."
		cfg.BaseURL = ""
		cfg.FrontMatter = "yaml"
		cfg.HugoEnabled = false

		for c.NextBlock() {
			switch c.Val() {
			case "show":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.PathScope = c.Val()
				cfg.PathScope = strings.TrimSuffix(cfg.PathScope, "/")
			case "frontmatter":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.FrontMatter = c.Val()
				if cfg.FrontMatter != "yaml" && cfg.FrontMatter != "json" && cfg.FrontMatter != "toml" {
					return configs, c.Err("frontmatter type not supported")
				}
			case "on":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.BaseURL = c.Val()
				cfg.BaseURL = strings.TrimPrefix(cfg.BaseURL, "/")
				cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/")
				cfg.BaseURL = "/" + cfg.BaseURL
			case "styles":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				var tplBytes []byte
				tplBytes, err = ioutil.ReadFile(c.Val())
				if err != nil {
					return configs, err
				}
				cfg.StyleSheet = string(tplBytes)
			case "allow_new":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.AllowNew, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_edit":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.AllowEdit, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_comands":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cfg.AllowCommands, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			}
		}

		caddyConf := httpserver.GetConfig(c)
		cfg.AbsoluteURL = strings.TrimSuffix(caddyConf.Addr.Path, "/") + "/" + cfg.BaseURL
		cfg.AbsoluteURL = strings.Replace(cfg.AbsoluteURL, "//", "/", -1)
		cfg.AbsoluteURL = strings.TrimSuffix(cfg.AbsoluteURL, "/")
		cfg.AddrPath = strings.TrimSuffix(caddyConf.Addr.Path, "/")
		cfg.Root = http.Dir(cfg.PathScope)
		if err := appendConfig(cfg); err != nil {
			return configs, err
		}
	}

	return configs, nil
}
<|endoftext|>


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

package config

import (
	"errors"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"time"

	"github.com/hooto/hcaptcha/captcha4g"
	"github.com/hooto/hlog4g/hlog"
	"github.com/hooto/iam/iamapi"
	"github.com/lessos/lessgo/encoding/json"
	"github.com/lessos/lessgo/types"
	"github.com/lynkdb/iomix/connect"
	"github.com/lynkdb/iomix/rdb"
	"github.com/lynkdb/iomix/rdb/modeler"
	"github.com/sysinner/incore/inapi"

	"github.com/hooto/hpress/api"
	"github.com/hooto/hpress/store"
)

var (
	Prefix        string
	Config        ConfigCommon
	AppName       = "hooto-press"
	Version       = "0.2.2.dev"
	Release       = "1"
	CaptchaConfig = captcha4g.DefaultConfig

	pod_inst_updated time.Time
	pod_inst         = "/home/action/.sysinner/pod_instance.json"

	User = &user.User{
		Uid:      "2048",
		Gid:      "2048",
		Username: "action",
		HomeDir:  "/home/action",
	}

	SysConfigList = api.SysConfigList{}
)

type ConfigCommon struct {
	UrlBasePath           string                   `json:"url_base_path,omitempty"`
	ModuleDir             string                   `json:"module_dir,omitempty"`
	InstanceID            string                   `json:"instance_id"`
	AppInstance           iamapi.AppInstance       `json:"app_instance"`
	AppTitle              string                   `json:"app_title,omitempty"`
	HttpPort              uint16                   `json:"http_port"`
	HttpPortPprof         uint16                   `json:"http_port_pprof,omitempty"`
	IamServiceUrl         string                   `json:"iam_service_url"`
	IamServiceUrlFrontend string                   `json:"iam_service_url_frontend"`
	IoConnectors          connect.MultiConnOptions `json:"io_connectors"`
	RunMode               string                   `json:"run_mode,omitempty"`
}

func init() {

	SysConfigList.Kind = "SysConfigList"

	//
	SysConfigList.Insert(api.SysConfig{
		"frontend_header_site_name", "Hooto Press",
		"Site's Name", "",
	})
	SysConfigList.Insert(api.SysConfig{
		"frontend_header_site_logo_url", "",
		"", "",
	})
	SysConfigList.Insert(api.SysConfig{
		"frontend_footer_copyright", "2015~2017 hooto.com",
		"", "",
	})

	//
	SysConfigList.Insert(api.SysConfig{
		"frontend_html_head_subtitle", "HP",
		"Sub Title for HTML Head Title", "",
	})
	SysConfigList.Insert(api.SysConfig{
		"frontend_html_head_meta_keywords", "",
		"Meta Keywords in HTML Head for Search engine optimization", "",
	})
	SysConfigList.Insert(api.SysConfig{
		"frontend_html_head_meta_description", "",
		"Meta Description in HTML Head for Search engine optimization", "",
	})

	SysConfigList.Insert(api.SysConfig{
		"frontend_footer_analytics_scripts", "",
		"Embeded analytics scripts, ex. Google Analytics or Piwik ...", "text",
	})
	SysConfigList.Insert(api.SysConfig{
		"ls2_uri", "//127.0.0.1/s2/bucket-name",
		"Storage Service URI", "",
	})

	//
	SysConfigList.Insert(api.SysConfig{
		"http_h_ac_allow_origin", "",
		"HTTP Access-Control-Allow-Origin", "",
	})
}

func Initialize(prefix string) error {

	var err error

	if prefix == "" {
		if prefix, err = filepath.Abs(filepath.Dir(os.Args[0]) + "/.."); err != nil {
			prefix = "/home/action/apps/hooto-press"
		}
	}

	Prefix = filepath.Clean(prefix)
	Config.ModuleDir = Prefix + "/modules"

	file := Prefix + "/etc/main.json"
	if err := json.DecodeFile(file, &Config); err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}

	{
		if Config.HttpPort == 0 {
			Config.HttpPort = 9533
		}
	}

	if Config.RunMode != "local-dev" {

		var inst inapi.Pod
		if err := json.DecodeFile(pod_inst, &inst); err != nil {
			return err
		}

		if inst.Spec == nil ||
			len(inst.Spec.Boxes) == 0 ||
			inst.Spec.Boxes[0].Resources == nil {
			return errors.New("Not Pod Instance Setup")
		}

		var (
			opt       *inapi.AppOption
			optref    *inapi.AppOption
			data_opts connect.ConnOptions
		)

		for _, app := range inst.Apps {

			if app.Spec.Meta.Name != "hooto-press" &&
				app.Spec.Meta.Name != "hooto-press-x1" {
				continue
			}

			//
			if opt == nil {
				opt = app.Operate.Options.Get("cfg/hooto-press") // TODO
			}

			if optref == nil {
				optref = app.Operate.Options.Get("cfg/sysinner-mysql") // TODO
			}
		}

		if opt == nil {
			return errors.New("No Configure Found")
		}

		if v, ok := opt.Items.Get("iam_service_url"); ok {
			Config.IamServiceUrl = v.String()
		} else {
			return errors.New("No Config.IamServiceUrl Found")
		}

		if v, ok := opt.Items.Get("iam_service_url_frontend"); ok {
			Config.IamServiceUrlFrontend = v.String()
		} else {
			Config.IamServiceUrlFrontend = Config.IamServiceUrl
		}

		if v, ok := opt.Items.Get("http_pprof_enable"); ok && v.String() == "1" {
			Config.HttpPortPprof = Config.HttpPort + 1
		} else {
			Config.HttpPortPprof = 0
		}

		if optref == nil || optref.Ref == nil {
			return errors.New("No Database Connection Configure Found")
		}

		data_opts.Name = types.NameIdentifier("hpress_database")
		data_opts.Driver = "lynkdb/mysqlgo"

		if v, ok := optref.Items.Get("db_name"); ok {
			data_opts.SetValue("dbname", v.String())
		}

		if v, ok := optref.Items.Get("db_user"); ok {
			data_opts.SetValue("user", v.String())
		}

		if v, ok := optref.Items.Get("db_auth"); ok {
			data_opts.SetValue("pass", v.String())
		}

		if optref.Ref.PodId == "" {
			return errors.New("No UpStream Pod Found")
		}

		var nsz inapi.NsPodServiceMap
		if err := json.DecodeFile("/dev/shm/sysinner/nsz/"+optref.Ref.PodId, &nsz); err != nil {
			return err
		}

		// TODO
		if srv := nsz.Get(3306); srv == nil || len(srv.Items) == 0 {
			return errors.New("No Pod ServicePort Found")
		} else {
			data_opts.SetValue("host", srv.Items[0].Ip)
			data_opts.SetValue("port", fmt.Sprintf("%d", srv.Items[0].Port))
		}

		Config.IoConnectors.SetOptions(data_opts)
	}

	Save()

	if Config.AppTitle == "" {
		Config.AppTitle = "Hooto Press"
	}

	// Setting CAPTCHA
	CaptchaConfig.DataDir = Prefix + "/var/hcaptchadb"

	if err := captcha4g.Config(CaptchaConfig); err != nil {
		return err
	}

	// Default User
	if User, err = user.Current(); err != nil {
		return err
	}

	if err := store_init(); err != nil {
		return err
	}

	//
	{
		rs, err := store.Data.Query(rdb.NewQuerySet().From("sys_config").Limit(1000))
		if err != nil {
			hlog.Print("error", err.Error())
			return err
		}

		for _, v := range rs {

			SysConfigList.Insert(api.SysConfig{
				Key:   v.Field("key").String(),
				Value: v.Field("value").String(),
			})
		}
	}

	return module_init()
}

//
func store_init() error {

	{
		io_name := types.NewNameIdentifier("hpress_local_cache")
		opts := Config.IoConnectors.Options(io_name)

		if opts == nil {
			opts = &connect.ConnOptions{
				Name:      io_name,
				Connector: "iomix/skv/Connector",
				Driver:    types.NewNameIdentifier("lynkdb/kvgo"),
			}
		}

		if opts.Value("data_dir") == "" {
			opts.SetValue("data_dir", Prefix+"/var/"+string(io_name))
			Save()
		}

		Config.IoConnectors.SetOptions(*opts)
	}

	var dbname = "dbaction"
	{
		io_name := types.NewNameIdentifier("hpress_database")
		opts := Config.IoConnectors.Options(io_name)

		if opts == nil {
			opts = &connect.ConnOptions{
				Name:      io_name,
				Connector: "iomix/rdb/Connector",
				Driver:    types.NewNameIdentifier("lynkdb/mysqlgo"),
			}
		}

		if opts.Value("host") == "" {
			opts.SetValue("host", "localhost")
		}

		if opts.Value("port") == "" {
			opts.SetValue("port", "3306")
		}

		dbname = opts.Value("dbname")

		Config.IoConnectors.SetOptions(*opts)
	}

	if err := store.Init(Config.IoConnectors); err != nil {
		hlog.Printf("error", "store_init %s", err.Error())
		return err
	}

	ds, err := modeler.NewDatabaseEntryFromJson(dsBase)
	if err != nil {
		return err
	}

	mor, err := store.Data.Modeler()
	if err != nil {
		hlog.Printf("error", "store_init %s", err.Error())
		return err
	}

	err = mor.Sync(dbname, ds)
	if err != nil {
		hlog.Printf("error", "store_init %s", err.Error())
	}

	return err
}

func Save() error {
	return json.EncodeToFile(Config, Prefix+"/etc/main.json", "  ")
}
<commit_msg>[BUG] fix single instance config logic error<commit_after>// Copyright 2015 Eryx <evorui аt gmаil dοt cοm>, All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"errors"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"time"

	"github.com/hooto/hcaptcha/captcha4g"
	"github.com/hooto/hlog4g/hlog"
	"github.com/hooto/iam/iamapi"
	"github.com/lessos/lessgo/encoding/json"
	"github.com/lessos/lessgo/types"
	"github.com/lynkdb/iomix/connect"
	"github.com/lynkdb/iomix/rdb"
	"github.com/lynkdb/iomix/rdb/modeler"
	"github.com/sysinner/incore/inapi"

	"github.com/hooto/hpress/api"
	"github.com/hooto/hpress/store"
)

var (
	Prefix        string
	Config        ConfigCommon
	AppName       = "hooto-press"
	Version       = "0.2.2.dev"
	Release       = "1"
	CaptchaConfig = captcha4g.DefaultConfig

	pod_inst_updated time.Time
	pod_inst         = "/home/action/.sysinner/pod_instance.json"

	User = &user.User{
		Uid:      "2048",
		Gid:      "2048",
		Username: "action",
		HomeDir:  "/home/action",
	}

	SysConfigList = api.SysConfigList{}
)

type ConfigCommon struct {
	UrlBasePath           string                   `json:"url_base_path,omitempty"`
	ModuleDir             string                   `json:"module_dir,omitempty"`
	InstanceID            string                   `json:"instance_id"`
	AppInstance           iamapi.AppInstance       `json:"app_instance"`
	AppTitle              string                   `json:"app_title,omitempty"`
	HttpPort              uint16                   `json:"http_port"`
	HttpPortPprof         uint16                   `json:"http_port_pprof,omitempty"`
	IamServiceUrl         string                   `json:"iam_service_url"`
	IamServiceUrlFrontend string                   `json:"iam_service_url_frontend"`
	IoConnectors          connect.MultiConnOptions `json:"io_connectors"`
	RunMode               string                   `json:"run_mode,omitempty"`
}

func init() {

	SysConfigList.Kind = "SysConfigList"

	//
	SysConfigList.Insert(api.SysConfig{
		"frontend_header_site_name", "Hooto Press",
		"Site's Name", "",
	})
	SysConfigList.Insert(api.SysConfig{
		"frontend_header_site_logo_url", "",
		"", "",
	})
	SysConfigList.Insert(api.SysConfig{
		"frontend_footer_copyright", "2015~2017 hooto.com",
		"", "",
	})

	//
	SysConfigList.Insert(api.SysConfig{
		"frontend_html_head_subtitle", "HP",
		"Sub Title for HTML Head Title", "",
	})
	SysConfigList.Insert(api.SysConfig{
		"frontend_html_head_meta_keywords", "",
		"Meta Keywords in HTML Head for Search engine optimization", "",
	})
	SysConfigList.Insert(api.SysConfig{
		"frontend_html_head_meta_description", "",
		"Meta Description in HTML Head for Search engine optimization", "",
	})

	SysConfigList.Insert(api.SysConfig{
		"frontend_footer_analytics_scripts", "",
		"Embeded analytics scripts, ex. Google Analytics or Piwik ...", "text",
	})
	SysConfigList.Insert(api.SysConfig{
		"ls2_uri", "//127.0.0.1/s2/bucket-name",
		"Storage Service URI", "",
	})

	//
	SysConfigList.Insert(api.SysConfig{
		"http_h_ac_allow_origin", "",
		"HTTP Access-Control-Allow-Origin", "",
	})
}

func Initialize(prefix string) error {

	var err error

	if prefix == "" {
		if prefix, err = filepath.Abs(filepath.Dir(os.Args[0]) + "/.."); err != nil {
			prefix = "/home/action/apps/hooto-press"
		}
	}

	Prefix = filepath.Clean(prefix)
	Config.ModuleDir = Prefix + "/modules"

	file := Prefix + "/etc/main.json"
	if err := json.DecodeFile(file, &Config); err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}

	{
		if Config.HttpPort == 0 {
			Config.HttpPort = 9533
		}
	}

	if Config.RunMode != "local-dev" {

		var inst inapi.Pod
		if err := json.DecodeFile(pod_inst, &inst); err != nil {
			return err
		}

		if inst.Spec == nil ||
			len(inst.Spec.Boxes) == 0 ||
			inst.Spec.Boxes[0].Resources == nil {
			return errors.New("Not Pod Instance Setup")
		}

		var (
			opt       *inapi.AppOption
			optref    *inapi.AppOption
			data_opts connect.ConnOptions
		)

		for _, app := range inst.Apps {

			if app.Spec.Meta.Name != "hooto-press" &&
				app.Spec.Meta.Name != "hooto-press-x1" {
				continue
			}

			//
			if opt == nil {
				opt = app.Operate.Options.Get("cfg/hooto-press") // TODO
			}

			if optref == nil {
				optref = app.Operate.Options.Get("cfg/sysinner-mysql") // TODO
			}
		}

		if opt == nil {
			return errors.New("No Configure Found")
		}

		if v, ok := opt.Items.Get("iam_service_url"); ok {
			Config.IamServiceUrl = v.String()
		} else {
			return errors.New("No Config.IamServiceUrl Found")
		}

		if v, ok := opt.Items.Get("iam_service_url_frontend"); ok {
			Config.IamServiceUrlFrontend = v.String()
		} else {
			Config.IamServiceUrlFrontend = Config.IamServiceUrl
		}

		if v, ok := opt.Items.Get("http_pprof_enable"); ok && v.String() == "1" {
			Config.HttpPortPprof = Config.HttpPort + 1
		} else {
			Config.HttpPortPprof = 0
		}

		if optref == nil {
			return errors.New("No Database Connection Configure Found")
		}

		ref_pod_id := inst.Meta.ID
		if optref.Ref != nil && optref.Ref.PodId != "" {
			ref_pod_id = optref.Ref.PodId
		}

		data_opts.Name = types.NameIdentifier("hpress_database")
		data_opts.Driver = "lynkdb/mysqlgo"

		if v, ok := optref.Items.Get("db_name"); ok {
			data_opts.SetValue("dbname", v.String())
		}

		if v, ok := optref.Items.Get("db_user"); ok {
			data_opts.SetValue("user", v.String())
		}

		if v, ok := optref.Items.Get("db_auth"); ok {
			data_opts.SetValue("pass", v.String())
		}

		var nsz inapi.NsPodServiceMap
		if err := json.DecodeFile("/dev/shm/sysinner/nsz/"+ref_pod_id, &nsz); err != nil {
			return err
		}

		// TODO
		if srv := nsz.Get(3306); srv == nil || len(srv.Items) == 0 {
			return errors.New("No Pod ServicePort Found")
		} else {
			data_opts.SetValue("host", srv.Items[0].Ip)
			data_opts.SetValue("port", fmt.Sprintf("%d", srv.Items[0].Port))
		}

		Config.IoConnectors.SetOptions(data_opts)
	}

	Save()

	if Config.AppTitle == "" {
		Config.AppTitle = "Hooto Press"
	}

	// Setting CAPTCHA
	CaptchaConfig.DataDir = Prefix + "/var/hcaptchadb"

	if err := captcha4g.Config(CaptchaConfig); err != nil {
		return err
	}

	// Default User
	if User, err = user.Current(); err != nil {
		return err
	}

	if err := store_init(); err != nil {
		return err
	}

	//
	{
		rs, err := store.Data.Query(rdb.NewQuerySet().From("sys_config").Limit(1000))
		if err != nil {
			hlog.Print("error", err.Error())
			return err
		}

		for _, v := range rs {

			SysConfigList.Insert(api.SysConfig{
				Key:   v.Field("key").String(),
				Value: v.Field("value").String(),
			})
		}
	}

	return module_init()
}

//
func store_init() error {

	{
		io_name := types.NewNameIdentifier("hpress_local_cache")
		opts := Config.IoConnectors.Options(io_name)

		if opts == nil {
			opts = &connect.ConnOptions{
				Name:      io_name,
				Connector: "iomix/skv/Connector",
				Driver:    types.NewNameIdentifier("lynkdb/kvgo"),
			}
		}

		if opts.Value("data_dir") == "" {
			opts.SetValue("data_dir", Prefix+"/var/"+string(io_name))
			Save()
		}

		Config.IoConnectors.SetOptions(*opts)
	}

	var dbname = "dbaction"
	{
		io_name := types.NewNameIdentifier("hpress_database")
		opts := Config.IoConnectors.Options(io_name)

		if opts == nil {
			opts = &connect.ConnOptions{
				Name:      io_name,
				Connector: "iomix/rdb/Connector",
				Driver:    types.NewNameIdentifier("lynkdb/mysqlgo"),
			}
		}

		if opts.Value("host") == "" {
			opts.SetValue("host", "localhost")
		}

		if opts.Value("port") == "" {
			opts.SetValue("port", "3306")
		}

		dbname = opts.Value("dbname")

		Config.IoConnectors.SetOptions(*opts)
	}

	if err := store.Init(Config.IoConnectors); err != nil {
		hlog.Printf("error", "store_init %s", err.Error())
		return err
	}

	ds, err := modeler.NewDatabaseEntryFromJson(dsBase)
	if err != nil {
		return err
	}

	mor, err := store.Data.Modeler()
	if err != nil {
		hlog.Printf("error", "store_init %s", err.Error())
		return err
	}

	err = mor.Sync(dbname, ds)
	if err != nil {
		hlog.Printf("error", "store_init %s", err.Error())
	}

	return err
}

func Save() error {
	return json.EncodeToFile(Config, Prefix+"/etc/main.json", "  ")
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"log"
	"os"
	"os/exec"

	"github.com/bitly/go-simplejson"
)

var cfg *simplejson.Json

// MustString returns the string property at the path
func MustString(path ...string) string {
	nonce := "$splinereticulationid$"
	val := cfg.GetPath(path...).MustString(nonce)
	if val == nonce {
		log.Fatalf("expected value for %v but found nothing", path)
	}
	return val
}

// MustInt returns the string property at the path
func MustInt(path ...string) int {
	return cfg.GetPath(path...).MustInt()
}

// MustBool returns the boolean property at the path
func MustBool(path ...string) bool {
	return cfg.GetPath(path...).MustBool()
}

// Bool returns the boolean property at the path, with a default
func Bool(def bool, path ...string) bool {
	return cfg.GetPath(path...).MustBool(def)
}

// Int returns the integer property at the path, with a default
func Int(def int, path ...string) int {
	return cfg.GetPath(path...).MustInt(def)
}

func String(def string, path ...string) string {
	return cfg.GetPath(path...).MustString(def)
}

var hey = "what's up buddy?"

func HasString(path ...string) bool {
	return String(hey, path...) != hey
}

var serial string

func Serial() string {
	if serial == "" {
		cmd := exec.Command("sphere-serial", os.Args[1:]...)

		var out bytes.Buffer
		cmd.Stdout = &out

		err := cmd.Run()
		if err != nil {
			log.Fatalf("Failed to get sphere serial (sphere-serial must be in the PATH) error:%s", err)
		}

		serial = out.String()
	}

	return serial
}

// MustRefresh parses the output of "sphere-config"
func MustRefresh() {
	cmd := exec.Command("sphere-config", os.Args[1:]...)

	var out bytes.Buffer
	cmd.Stdout = &out

	err := cmd.Run()
	if err != nil {
		log.Fatalf("Failed to load configuration. ('sphere-config' must be in the PATH) error:%s", err)
	}

	cfg, err = simplejson.NewJson(out.Bytes())
	if err != nil {
		log.Fatalf("Failed to parse configuration. error:%s", err)
	}
}

func init() {
	MustRefresh()
}
<commit_msg>Replace node sphere-config with >20x faster go version<commit_after>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"

	"github.com/ninjasphere/go-ninja/logger"
	"gopkg.in/alecthomas/kingpin.v1"
)

var log = logger.GetLogger("config")

var config map[string]interface{}

func init() {
	MustRefresh()
}

func GetAll() map[string]interface{} {
	return unflatten(config)
}

var serial string

func Serial() string {
	if serial == "" {
		cmd := exec.Command("sphere-serial", os.Args[1:]...)

		var out bytes.Buffer
		cmd.Stdout = &out

		err := cmd.Run()
		if err != nil {
			log.Fatalf("Failed to get sphere serial (sphere-serial must be in the PATH) error:%s", err)
		}

		serial = out.String()
	}

	return serial
}

func String(def string, path ...string) string {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(string)
}

// MustString returns the string property at the path
func MustString(path ...string) string {
	return mustGet(path...).(string)
}

// Int returns the integer property at the path, with a default
func Int(def int, path ...string) int {
	val := get(path...)
	if val == nil {
		return def
	}
	return int(val.(float64))
}

// MustInt returns the string property at the path
func MustInt(path ...string) int {
	return int(mustGet(path...).(float64))
}

// Bool returns the boolean property at the path, with a default
func Bool(def bool, path ...string) bool {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(bool)
}

// MustBool returns the boolean property at the path
func MustBool(path ...string) bool {
	return mustGet(path...).(bool)
}

var hey = "what's up buddy?"

func HasString(path ...string) bool {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return false
	}

	_, ok = val.(string)
	return ok
}

func mustGet(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		log.Fatalf("expected value for %v but found nothing", path)
	}
	return val
}

func get(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return nil
	}
	return val
}

func MustRefresh() {

	flat := make(map[string]interface{})

	// cli overrides
	addArgs(flat)

	// load environments (no value args) from cli args
	environments := []string{"default"}
	for name, value := range flat {
		if value == nil {
			environments = append(environments, name)
		}
	}

	flat["env"] = environments

	userHome := getUserHome()

	// env vars (if starting with "sphere_")
	addEnv(flat)

	// anything that can be parsed as a number, is a number
	parseNumbers(flat)

	installDir := "/opt/ninjablocks"
	if val, ok := flat["installDirectory"]; ok {
		installDir = val.(string)
	}

	flat["installDirectory"] = installDir

	//sphere_configDir=/home/elliot/repos/sphere-dev/sphere-common/config

	if _, err := os.Stat(installDir); err != nil {
		log.Fatalf("Couldn't load sphere install directory. Override with env var sphere_installDirectory. error:%s", err)
	}

	// credentials file
	addFile("/etc/opt/ninja/credentials.json", flat)

	// home directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(userHome, ".sphere", environments[i]+".json"), flat)
	}

	// current directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(".", "config", environments[i]+".json"), flat)
	}

	// common directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(installDir, "sphere-common", "config", environments[i]+".json"), flat)
	}

	// sphere-common credentials config
	addFile(filepath.Join(installDir, "sphere-common", "config", "credentials.json"), flat)

	//log.Debugf("Loaded config: %v", flat)

	config = flat
}

func addEnv(config map[string]interface{}) {
	for _, v := range os.Environ() {
		split := strings.Split(v, "=")

		name, value := split[0], split[1]

		if strings.HasPrefix(name, "sphere_") {
			name = strings.TrimPrefix(name, "sphere_")
			name = strings.Replace(name, "_", ".", 0)

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}
	}
}

func addArgs(config map[string]interface{}) {

	parser := kingpin.Tokenize(os.Args[1:]).Tokens

	for token, parser := parser.Peek(), parser.Next(); token.Type != kingpin.TokenEOL; token, parser = parser.Peek(), parser.Next() {

		if token.IsFlag() {
			var value interface{}
			name := token.Value

			next := parser.Peek()
			if next.Type == kingpin.TokenArg {
				value = next.Value
			} else {
				// It's an environment indicator... like --cloud-production
			}

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}

	}

}

func addFile(path string, config map[string]interface{}) error {
	//log.Debugf("Loading config file: %s", path)

	file, e := ioutil.ReadFile(path)
	if e != nil {
		return fmt.Errorf("Failed to load file: %s error: %s", path, e)
	}

	content := make(map[string]interface{})
	e = json.Unmarshal(file, &content)
	if e != nil {
		return fmt.Errorf("Failed to read file: %s error: %s", path, e)
	}

	flatten(content, nil, config)
	return nil
}

func flatten(input interface{}, lpath []string, flattened map[string]interface{}) {
	if lpath == nil {
		lpath = []string{}
	}

	if reflect.ValueOf(input).Kind() == reflect.Map {
		for rkey, value := range input.(map[string]interface{}) {
			flatten(value, append(lpath, rkey), flattened)
		}
	} else {
		if _, ok := flattened[strings.Join(lpath, ".")]; !ok {
			flattened[strings.Join(lpath, ".")] = input
		}
	}
}

func unflatten(flat map[string]interface{}) map[string]interface{} {
	out := make(map[string]interface{})
	obj := out

	for key, val := range flat {

		obj = out
		keys := strings.Split(key, ".")

		for i, k := range keys {
			if i == len(keys)-1 {
				obj[k] = val
			} else {
				next, ok := obj[k]
				if !ok {
					next = make(map[string]interface{})
				}
				obj[k] = next
				obj = next.(map[string]interface{})
			}
		}

	}

	return out
}

func parseNumbers(config map[string]interface{}) {
	for name, val := range config {
		stringVal, ok := val.(string)
		if ok {
			floatVal, err := strconv.ParseFloat(stringVal, 64)
			if err == nil {
				config[name] = floatVal
			}
		}
	}
}

func getUserHome() string {
	usr, err := user.Current()
	if err != nil {
		return "/root"
	}
	return usr.HomeDir
}
<|endoftext|>


<commit_before>package config

import (
	"flag"
	"io"
	"sync"

	"github.com/jinzhu/configor"
	yaml "gopkg.in/yaml.v2"
)

var config *Config
var once sync.Once
var Configpaths = []string{"config/default_config.yml", "config/config.yml"}

func Get() *Config {
	once.Do(func() {
		config = &Config{}
	})
	return config
}

// IsSukebei : Tells if we are on the sukebei website
func IsSukebei() bool {
	return Get().Models.TorrentsTableName == "sukebei_torrents"
}

// WebAddress : Returns web address for current site
func WebAddress() string {
	if IsSukebei() {
		return Get().WebAddress.Sukebei
	} else {
		return Get().WebAddress.Nyaa
	}
}

var allowedDatabaseTypes = map[string]bool{
	"sqlite3":  true,
	"postgres": true,
	"mysql":    true,
	"mssql":    true,
}

var allowedDBLogModes = map[string]bool{
	"default":  true, // errors only
	"detailed": true,
	"silent":   true,
}

func init() {
	Reload()
}

// Reload the configuration from the files provided in the config variables
func Reload() {
	configor.Load(Get(), Configpaths...)
}

// BindFlags returns a function which is to be used after
// flag.Parse to check and copy the flags' values to the Config instance.
func BindFlags() func() {
	confFile := flag.String("conf", Configpaths[1], "path to the configuration file")
	flag.StringVar(&Get().DBType, "dbtype", Get().DBType, "database backend")
	flag.StringVar(&Get().Host, "host", Get().Host, "binding address of the server")
	flag.IntVar(&Get().Port, "port", Get().Port, "port of the server")
	flag.StringVar(&Get().DBParams, "dbparams", Get().DBParams, "parameters to open the database (see Gorm's doc)")
	flag.StringVar(&Get().DBLogMode, "dblogmode", Get().DBLogMode, "database log verbosity (errors only by default)")
	return func() {
		if *confFile != "" {
			Configpaths = append(Configpaths, *confFile)
			Reload()
		}
	}
}

// Pretty : Write config json in a file
func (config *Config) Pretty(output io.Writer) error {
	data, err := yaml.Marshal(config)
	if err != nil {
		return err
	}
	_, err = output.Write(data)
	return err
}
<commit_msg>Fix config load order. Configor has a reverted config files load priority :')<commit_after>package config

import (
	"flag"
	"io"
	"sync"

	"fmt"

	"github.com/jinzhu/configor"
	yaml "gopkg.in/yaml.v2"
)

var config *Config
var once sync.Once
var Configpaths = []string{"config/config.yml", "config/default_config.yml"}

func Get() *Config {
	once.Do(func() {
		config = &Config{}
	})
	return config
}

// IsSukebei : Tells if we are on the sukebei website
func IsSukebei() bool {
	return Get().Models.TorrentsTableName == "sukebei_torrents"
}

// WebAddress : Returns web address for current site
func WebAddress() string {
	if IsSukebei() {
		return Get().WebAddress.Sukebei
	} else {
		return Get().WebAddress.Nyaa
	}
}

var allowedDatabaseTypes = map[string]bool{
	"sqlite3":  true,
	"postgres": true,
	"mysql":    true,
	"mssql":    true,
}

var allowedDBLogModes = map[string]bool{
	"default":  true, // errors only
	"detailed": true,
	"silent":   true,
}

func init() {
	Reload()
}

// Reload the configuration from the files provided in the config variables
func Reload() {
	fmt.Println("Config reload")
	fmt.Println(Configpaths)
	configor.Load(Get(), Configpaths...)
}

// BindFlags returns a function which is to be used after
// flag.Parse to check and copy the flags' values to the Config instance.
func BindFlags() func() {
	confFile := flag.String("conf", Configpaths[1], "path to the configuration file")
	flag.StringVar(&Get().DBType, "dbtype", Get().DBType, "database backend")
	flag.StringVar(&Get().Host, "host", Get().Host, "binding address of the server")
	flag.IntVar(&Get().Port, "port", Get().Port, "port of the server")
	flag.StringVar(&Get().DBParams, "dbparams", Get().DBParams, "parameters to open the database (see Gorm's doc)")
	flag.StringVar(&Get().DBLogMode, "dblogmode", Get().DBLogMode, "database log verbosity (errors only by default)")
	return func() {
		if *confFile != "" {
			Configpaths = append([]string{*confFile}, Configpaths...)
			Reload()
		}
	}
}

// Pretty : Write config json in a file
func (config *Config) Pretty(output io.Writer) error {
	data, err := yaml.Marshal(config)
	if err != nil {
		return err
	}
	_, err = output.Write(data)
	return err
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"os"

	"github.com/jvehent/gozdef"
	"gopkg.in/gcfg.v1"
)

type Config struct {
	General struct {
		Enable         bool
		Postgres       string
		PostgresDB     string
		PostgresUser   string
		PostgresPass   string
		CipherscanPath string
		GoRoutines     int // * cores = The Max number of spawned Goroutines
	}
	TrustStores struct {
		Ubuntu_TS    string
		Mozilla_TS   string
		Microsoft_TS string
		Apple_TS     string
		Android_TS   string
	}
	MozDef gozdef.MqConf
}

func Load(path string) (conf Config, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("configLoad() -> %v", e)
		}
	}()
	err = gcfg.ReadFileInto(&conf, path)
	if err != nil {
		panic(err)
	}
	if os.Getenv("TLSOBS_POSTGRES") != "" {
		conf.General.Postgres = os.Getenv("TLSOBS_POSTGRES")
	}
	if os.Getenv("TLSOBS_POSTGRESDB") != "" {
		conf.General.PostgresDB = os.Getenv("TLSOBS_POSTGRESDB")
	}
	if os.Getenv("TLSOBS_POSTGRESUSER") != "" {
		conf.General.PostgresUser = os.Getenv("TLSOBS_POSTGRESUSER")
	}
	if os.Getenv("TLSOBS_POSTGRESPASS") != "" {
		conf.General.PostgresPass = os.Getenv("TLSOBS_POSTGRESPASS")
	}
	return
}
<commit_msg>changed config struct variable as it seems the _ character is not allowed<commit_after>package config

import (
	"fmt"
	"os"

	"github.com/jvehent/gozdef"
	"gopkg.in/gcfg.v1"
)

type Config struct {
	General struct {
		Enable         bool
		Postgres       string
		PostgresDB     string
		PostgresUser   string
		PostgresPass   string
		CipherscanPath string
		GoRoutines     int // * cores = The Max number of spawned Goroutines
	}
	TrustStores struct {
		UbuntuTS    string
		MozillaTS   string
		MicrosoftTS string
		AppleTS     string
		AndroidTS   string
	}
	MozDef gozdef.MqConf
}

func Load(path string) (conf Config, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("configLoad() -> %v", e)
		}
	}()
	err = gcfg.ReadFileInto(&conf, path)
	if err != nil {
		panic(err)
	}
	if os.Getenv("TLSOBS_POSTGRES") != "" {
		conf.General.Postgres = os.Getenv("TLSOBS_POSTGRES")
	}
	if os.Getenv("TLSOBS_POSTGRESDB") != "" {
		conf.General.PostgresDB = os.Getenv("TLSOBS_POSTGRESDB")
	}
	if os.Getenv("TLSOBS_POSTGRESUSER") != "" {
		conf.General.PostgresUser = os.Getenv("TLSOBS_POSTGRESUSER")
	}
	if os.Getenv("TLSOBS_POSTGRESPASS") != "" {
		conf.General.PostgresPass = os.Getenv("TLSOBS_POSTGRESPASS")
	}
	return
}
<|endoftext|>


<commit_before>package config

//Llog日志配置信息工具集
//
//2013-10-25
//李林(LvanNeo)
//lvan_software@foxmail.com

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
)

type XmlConfig struct {
	XMLName     xml.Name      `xml:"Config"`
	ConsoleInfo ConsoleConfig `xml:"Console"`
	FileInfo    FileConfig    `xml:"File"`
	Appender    []string      `xml:"Appender"`
}

type JsonConfig struct {
	Appender []string
	Console  ConsoleConfig
	File     FileConfig
}

type ConsoleConfig struct {
	Level     string `xml:"Level"`
	ShortFile string `xml:"ShortFile"`
}

type FileConfig struct {
	Level     string `xml:"Level"`
	FileName  string `xml:"FileName"`
	FileSize  int64  `xml:"FileSize"`
	MaxLine   int    `xml:"MaxLine"`
	ShortFile string `xml:"ShortFile"`
}

//获取配置信息中的Llog适配器名称
//configInfo	Llog配置信息
//返回Llog适配器名称集合及错误信息
//
//2013-10-25
//李林(LvanNeo)
//lvan_software@foxmail.com
func GetAppender(configInfo []byte) ([]string, error) {
	conf := new(XmlConfig)
	err := xml.Unmarshal(configInfo, &conf)
	if err == nil {
		return conf.Appender, nil
	}

	jsonconf := new(JsonConfig)
	err = json.Unmarshal(configInfo, &jsonconf)
	if err == nil {
		fmt.Println("Json Appender")
		return jsonconf.Appender, nil
	}

	return conf.Appender, err

}

//获取配置信息中Llog控制台日志的配置信息
//configInfo	Llog配置信息
//返回Llog控制台日志配置信息及错误信息
//
//2013-10-25
//李林(LvanNeo)
//lvan_software@foxmail.com
func GetConsoleConfig(configInfo []byte) (ConsoleConfig, error) {
	conf := new(XmlConfig)
	err := xml.Unmarshal(configInfo, &conf)
	if err == nil {
		return conf.ConsoleInfo, nil
	}

	jsonconf := new(JsonConfig)
	err = json.Unmarshal(configInfo, &jsonconf)
	if err == nil {
		fmt.Println("Json ConsoleInfo")
		return jsonconf.Console, nil
	}

	return conf.ConsoleInfo, err

}

//获取配置信息中Llog文件日志的配置信息
//configInfo	Llog配置信息
//返回Llog文件日志配置信息及错误信息
//
//2013-10-25
//李林(LvanNeo)
//lvan_software@foxmail.com
func GetFileConfig(configInfo []byte) (FileConfig, error) {
	conf := new(XmlConfig)
	err := xml.Unmarshal(configInfo, &conf)
	if err == nil {
		return conf.FileInfo, nil
	}

	jsonconf := new(JsonConfig)
	err = json.Unmarshal(configInfo, &jsonconf)
	if err == nil {
		fmt.Println("Json FileInfo", jsonconf)
		fmt.Println(string(configInfo))
		return jsonconf.File, nil
	}

	return conf.FileInfo, err
}
<commit_msg>删除调试信息<commit_after>package config

//Llog日志配置信息工具集
//
//2013-10-25
//李林(LvanNeo)
//lvan_software@foxmail.com

import (
	"encoding/json"
	"encoding/xml"
)

type XmlConfig struct {
	XMLName     xml.Name      `xml:"Config"`
	ConsoleInfo ConsoleConfig `xml:"Console"`
	FileInfo    FileConfig    `xml:"File"`
	Appender    []string      `xml:"Appender"`
}

type JsonConfig struct {
	Appender []string
	Console  ConsoleConfig
	File     FileConfig
}

type ConsoleConfig struct {
	Level     string `xml:"Level"`
	ShortFile string `xml:"ShortFile"`
}

type FileConfig struct {
	Level     string `xml:"Level"`
	FileName  string `xml:"FileName"`
	FileSize  int64  `xml:"FileSize"`
	MaxLine   int    `xml:"MaxLine"`
	ShortFile string `xml:"ShortFile"`
}

//获取配置信息中的Llog适配器名称
//configInfo	Llog配置信息
//返回Llog适配器名称集合及错误信息
//
//2013-10-25
//李林(LvanNeo)
//lvan_software@foxmail.com
func GetAppender(configInfo []byte) ([]string, error) {
	conf := new(XmlConfig)
	err := xml.Unmarshal(configInfo, &conf)
	if err == nil {
		return conf.Appender, nil
	}

	jsonconf := new(JsonConfig)
	err = json.Unmarshal(configInfo, &jsonconf)
	if err == nil {
		return jsonconf.Appender, nil
	}

	return conf.Appender, err

}

//获取配置信息中Llog控制台日志的配置信息
//configInfo	Llog配置信息
//返回Llog控制台日志配置信息及错误信息
//
//2013-10-25
//李林(LvanNeo)
//lvan_software@foxmail.com
func GetConsoleConfig(configInfo []byte) (ConsoleConfig, error) {
	conf := new(XmlConfig)
	err := xml.Unmarshal(configInfo, &conf)
	if err == nil {
		return conf.ConsoleInfo, nil
	}

	jsonconf := new(JsonConfig)
	err = json.Unmarshal(configInfo, &jsonconf)
	if err == nil {
		return jsonconf.Console, nil
	}

	return conf.ConsoleInfo, err

}

//获取配置信息中Llog文件日志的配置信息
//configInfo	Llog配置信息
//返回Llog文件日志配置信息及错误信息
//
//2013-10-25
//李林(LvanNeo)
//lvan_software@foxmail.com
func GetFileConfig(configInfo []byte) (FileConfig, error) {
	conf := new(XmlConfig)
	err := xml.Unmarshal(configInfo, &conf)
	if err == nil {
		return conf.FileInfo, nil
	}

	jsonconf := new(JsonConfig)
	err = json.Unmarshal(configInfo, &jsonconf)
	if err == nil {
		return jsonconf.File, nil
	}

	return conf.FileInfo, err
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"strings"
)

var Settings *Config

type Config struct {
	General struct {
		GuestPosting     bool
		AutoRegistration bool
	}

	Akismet struct {
		// Akismet settings
		Key  string
		Host string
	}

	StopForumSpam struct {
		// Stop Forum Spam settings
		Confidence float64
	}

	// settings for amazon s3
	Amazon struct {
		Region string
		Bucket string
		Id     string
		Key    string
	}

	// settings for google storage
	Google struct {
		Auth   string
		Bucket string
		Key    string
	}

	Antispam struct {
		// Antispam Key from Prim
		AntispamKey string

		// Antispam cookie
		CookieName  string
		CookieValue string
	}

	Limits struct {
		// Image settings
		ImageMinWidth  int
		ImageMinHeight int
		ImageMaxWidth  int
		ImageMaxHeight int
		ImageMaxSize   int
		WebmMaxLength  int

		// Max posts in a thread
		PostsMax uint

		// Lengths for posting
		CommentMaxLength int
		CommentMinLength int
		TitleMaxLength   int
		TitleMinLength   int
		NameMaxLength    int
		NameMinLength    int
		TagMaxLength     int
		TagMinLength     int

		// Max thumbnail sizes
		ThumbnailMaxWidth  int
		ThumbnailMaxHeight int

		// Max request parameter input size
		ParamMaxSize uint
	}
}

func Print() {
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "General")
	fmt.Printf("%-20v%40v\n", "Guest Posting", Settings.General.GuestPosting)
	fmt.Printf("%-20v%40v\n", "Auto Registration", Settings.General.AutoRegistration)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Antispam")
	fmt.Printf("%-20v%40v\n", "Key", Settings.Antispam.AntispamKey)
	fmt.Printf("%-20v%40v\n", "Cookie Name", Settings.Antispam.CookieName)
	fmt.Printf("%-20v%40v\n", "Cookie Value", Settings.Antispam.CookieValue)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Limits")
	fmt.Printf("%-20v\n\n", "Images")
	fmt.Printf("%-20v%40v\n", "Min Width", Settings.Limits.ImageMinWidth)
	fmt.Printf("%-20v%40v\n", "Min Height", Settings.Limits.ImageMinHeight)
	fmt.Printf("%-20v%40v\n", "Max Width", Settings.Limits.ImageMaxWidth)
	fmt.Printf("%-20v%40v\n", "Max Height", Settings.Limits.ImageMaxHeight)
	fmt.Printf("%-20v%40v\n", "Max Size", Settings.Limits.ImageMaxSize)
	fmt.Printf("%-20v%40v\n", "WebM Length", Settings.Limits.WebmMaxLength)
	fmt.Printf("%-20v%40v\n", "Thumb Max Width", Settings.Limits.ThumbnailMaxWidth)
	fmt.Printf("%-20v%40v\n", "Thumb Max Height", Settings.Limits.ThumbnailMaxHeight)
	fmt.Printf("\n%-20v\n\n", "Posting")
	fmt.Printf("%-20v%40v\n", "Thread Max Posts", Settings.Limits.PostsMax)
	fmt.Printf("%-20v%40v\n", "Comment Max", Settings.Limits.CommentMaxLength)
	fmt.Printf("%-20v%40v\n", "Comment Min", Settings.Limits.CommentMinLength)
	fmt.Printf("%-20v%40v\n", "Title Max", Settings.Limits.TitleMaxLength)
	fmt.Printf("%-20v%40v\n", "Title Min", Settings.Limits.TitleMinLength)
	fmt.Printf("%-20v%40v\n", "Name Max", Settings.Limits.NameMaxLength)
	fmt.Printf("%-20v%40v\n", "Name Min", Settings.Limits.NameMinLength)
	fmt.Printf("%-20v%40v\n", "Tag Max", Settings.Limits.TagMaxLength)
	fmt.Printf("%-20v%40v\n", "Tag Min", Settings.Limits.TagMinLength)
	fmt.Printf("%-20v%40v\n", "Max Param Size", Settings.Limits.ParamMaxSize)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Akismet")
	fmt.Printf("%-20v%40v\n", "Key", Settings.Akismet.Key)
	fmt.Printf("%-20v%40v\n", "Host", Settings.Akismet.Host)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Stop Forum Spam")
	fmt.Printf("%-20v%40v\n", "Confidence", Settings.StopForumSpam.Confidence)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Amazon")
	fmt.Printf("%-20v%40v\n", "Region", Settings.Amazon.Region)
	fmt.Printf("%-20v%40v\n", "Bucket", Settings.Amazon.Bucket)
	fmt.Printf("%-20v%40v\n", "Id", Settings.Amazon.Id)
	fmt.Printf("%-20v%40v\n", "Key", Settings.Amazon.Key)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Google")
	fmt.Printf("%-20v%40v\n", "Auth", Settings.Google.Auth)
	fmt.Printf("%-20v%40v\n", "Bucket", Settings.Google.Bucket)
	fmt.Printf("%-20v%40v\n", "Key", Settings.Google.Key)

}

func init() {

	Settings = &Config{}

}
<commit_msg>enhance print config<commit_after>package config

import (
	"fmt"
	"strings"
)

var Settings *Config

type Config struct {
	General struct {
		GuestPosting     bool
		AutoRegistration bool
	}

	Akismet struct {
		// Akismet settings
		Key  string
		Host string
	}

	StopForumSpam struct {
		// Stop Forum Spam settings
		Confidence float64
	}

	// settings for amazon s3
	Amazon struct {
		Region string
		Bucket string
		Id     string
		Key    string
	}

	// settings for google storage
	Google struct {
		Auth   string
		Bucket string
		Key    string
	}

	Antispam struct {
		// Antispam Key from Prim
		AntispamKey string

		// Antispam cookie
		CookieName  string
		CookieValue string
	}

	Limits struct {
		// Image settings
		ImageMinWidth  int
		ImageMinHeight int
		ImageMaxWidth  int
		ImageMaxHeight int
		ImageMaxSize   int
		WebmMaxLength  int

		// Max posts in a thread
		PostsMax uint

		// Lengths for posting
		CommentMaxLength int
		CommentMinLength int
		TitleMaxLength   int
		TitleMinLength   int
		NameMaxLength    int
		NameMinLength    int
		TagMaxLength     int
		TagMinLength     int

		// Max thumbnail sizes
		ThumbnailMaxWidth  int
		ThumbnailMaxHeight int

		// Max request parameter input size
		ParamMaxSize uint
	}
}

func Print() {
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Global Settings")
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "General")
	fmt.Printf("%-20v%40v\n", "Guest Posting", Settings.General.GuestPosting)
	fmt.Printf("%-20v%40v\n", "Auto Registration", Settings.General.AutoRegistration)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Antispam")
	fmt.Printf("%-20v%40v\n", "Key", Settings.Antispam.AntispamKey)
	fmt.Printf("%-20v%40v\n", "Cookie Name", Settings.Antispam.CookieName)
	fmt.Printf("%-20v%40v\n", "Cookie Value", Settings.Antispam.CookieValue)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Limits")
	fmt.Printf("%-20v\n\n", "Images")
	fmt.Printf("%-20v%40v\n", "Min Width", Settings.Limits.ImageMinWidth)
	fmt.Printf("%-20v%40v\n", "Min Height", Settings.Limits.ImageMinHeight)
	fmt.Printf("%-20v%40v\n", "Max Width", Settings.Limits.ImageMaxWidth)
	fmt.Printf("%-20v%40v\n", "Max Height", Settings.Limits.ImageMaxHeight)
	fmt.Printf("%-20v%40v\n", "Max Size", Settings.Limits.ImageMaxSize)
	fmt.Printf("%-20v%40v\n", "WebM Length", Settings.Limits.WebmMaxLength)
	fmt.Printf("%-20v%40v\n", "Thumb Max Width", Settings.Limits.ThumbnailMaxWidth)
	fmt.Printf("%-20v%40v\n", "Thumb Max Height", Settings.Limits.ThumbnailMaxHeight)
	fmt.Printf("\n%-20v\n\n", "Posting")
	fmt.Printf("%-20v%40v\n", "Thread Max Posts", Settings.Limits.PostsMax)
	fmt.Printf("%-20v%40v\n", "Comment Max", Settings.Limits.CommentMaxLength)
	fmt.Printf("%-20v%40v\n", "Comment Min", Settings.Limits.CommentMinLength)
	fmt.Printf("%-20v%40v\n", "Title Max", Settings.Limits.TitleMaxLength)
	fmt.Printf("%-20v%40v\n", "Title Min", Settings.Limits.TitleMinLength)
	fmt.Printf("%-20v%40v\n", "Name Max", Settings.Limits.NameMaxLength)
	fmt.Printf("%-20v%40v\n", "Name Min", Settings.Limits.NameMinLength)
	fmt.Printf("%-20v%40v\n", "Tag Max", Settings.Limits.TagMaxLength)
	fmt.Printf("%-20v%40v\n", "Tag Min", Settings.Limits.TagMinLength)
	fmt.Printf("%-20v%40v\n", "Max Param Size", Settings.Limits.ParamMaxSize)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Akismet")
	fmt.Printf("%-20v%40v\n", "Key", Settings.Akismet.Key)
	fmt.Printf("%-20v%40v\n", "Host", Settings.Akismet.Host)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Stop Forum Spam")
	fmt.Printf("%-20v%40v\n", "Confidence", Settings.StopForumSpam.Confidence)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Amazon")
	fmt.Printf("%-20v%40v\n", "Region", Settings.Amazon.Region)
	fmt.Printf("%-20v%40v\n", "Bucket", Settings.Amazon.Bucket)
	fmt.Printf("%-20v%40v\n", "Id", Settings.Amazon.Id)
	fmt.Printf("%-20v%40v\n", "Key", Settings.Amazon.Key)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Google")
	fmt.Printf("%-20v%40v\n", "Auth", Settings.Google.Auth)
	fmt.Printf("%-20v%40v\n", "Bucket", Settings.Google.Bucket)
	fmt.Printf("%-20v%40v\n", "Key", Settings.Google.Key)
	fmt.Println(strings.Repeat("*", 60))
}

func init() {

	Settings = &Config{}

}
<|endoftext|>


<commit_before>package wave

import (
	"crypto/tls"
	"sync"
	"time"
)

// ClientPool allows for easy pooling of 'Client' connections
type ClientPool struct {
	*sync.Pool
}

// NewClientPooled returns a new 'Client' instance backed by a pool of connections
func NewClientPooled(serviceName, network, address string, timeout time.Duration, config *tls.Config) *ClientPool {

	return &ClientPool{
		Pool: &sync.Pool{
			New: func() interface{} {

				c, err := NewClient(serviceName, network, address, timeout, config)
				if err != nil {
					return err
				}

				return c
			},
		},
	}
}
<commit_msg>pool updates<commit_after>package wave

import (
	"crypto/tls"
	"sync"
	"time"
)

// ClientPool allows for easy pooling of 'Client' connections
type ClientPool struct {
	pool *sync.Pool
}

// NewClientPooled returns a new 'Client' instance backed by a pool of connections
func NewClientPooled(serviceName, network, address string, timeout time.Duration, config *tls.Config) *ClientPool {

	return &ClientPool{
		pool: &sync.Pool{
			New: func() interface{} {

				c, err := NewClient(serviceName, network, address, timeout, config)
				if err != nil {
					return err
				}

				return c
			},
		},
	}
}

// Get Retrieves and existing connection or creates a new one
// if none exist
func (p *ClientPool) Get() (*Client, error) {

	c := p.pool.Get()
	if err, ok := c.(error); ok {
		return nil, err
	}

	return c.(*Client), nil
}

// Put puts v back into the pool
func (p *ClientPool) Put(v interface{}) {
	p.pool.Put(v)
}
<|endoftext|>


<commit_before>package graph

import (
	"math/rand"
	"time"
)

var topVertex *Vertex
var currentVertex *Vertex

type Vertex struct {
	whiteWins  uint16
	blackWins  uint16
	whiteScore uint8
	blackScore uint8
	boardState Board
	turn       uint8

	plyDepth uint
	outEdges []*Edge
	inEdge   *Edge
}

type Edge struct {
	playX uint8
	playY uint8

	fromVertex *Vertex
	toVertex   *Vertex
}

func Initiate(boardSize uint8) {
	rand.Seed(time.Now().Unix())
	topVertex = new(Vertex)
	topVertex.boardState.Create(uint16(boardSize))
	topVertex.turn = 1
	topVertex.plyDepth = 0
	currentVertex = topVertex
}

func Reset() {
	topVertex = nil
	currentVertex = nil
}

func GetMove(c uint8) (x, y uint8) {
	createGraph()
	var bestMove *Edge = nil
	var mostWins uint16 = 0
	if c != currentVertex.turn {
		panic("Trying to getmove for wrong color")
	}
	if c == 1 {
		for _, v := range currentVertex.outEdges {
			if v.toVertex.blackWins > mostWins {
				bestMove = v
				mostWins = v.toVertex.blackWins
			}
		}
	} else if c == 2 {
		for _, v := range currentVertex.outEdges {
			if v.toVertex.whiteWins > mostWins {
				bestMove = v
				mostWins = v.toVertex.whiteWins
			}
		}
	} else {
		panic("Unknown color in GetMove(..)")
	}
	if bestMove == nil {
		panic("Unable to get a move from graph")
	}
	currentVertex.outEdges = []*Edge{bestMove}

	board := currentVertex.boardState
	currentVertex.boardState = Board{[]uint8{}, 0}
	board.Play(c, bestMove.playX, bestMove.playY)

	currentVertex = bestMove.toVertex
	currentVertex.boardState = board
	return bestMove.playX, bestMove.playY
}

func UpdateCurrentVertex(c, x, y uint8) {
	if c != currentVertex.turn {
		panic("Updating graph to wrong color's move")
	}
	var newCurrent *Vertex = nil
	for _, v := range currentVertex.outEdges {
		if v.playX == x && v.playY == y {
			newCurrent = v.toVertex
			break
		}
	}
	if newCurrent == nil {
		newCurrent = new(Vertex)
		newCurrent.boardState.Create(uint16(currentVertex.boardState.size))
		copy(newCurrent.boardState.s, currentVertex.boardState.s)
		score, _ := newCurrent.boardState.Play(c, x, y)

		if c == 1 {
			newCurrent.blackScore += score
			newCurrent.turn = 2
		} else {
			newCurrent.whiteScore += score
			newCurrent.turn = 1
		}
		newCurrent.plyDepth = currentVertex.plyDepth + 1

		newEdge := new(Edge)
		newEdge.fromVertex = currentVertex
		newEdge.toVertex = newCurrent

		newEdge.playX = x
		newEdge.playY = y

		currentVertex.outEdges = []*Edge{newEdge}
		newCurrent.inEdge = newEdge
	}
	currentVertex = newCurrent
}

func createGraph() {
	maxMoves := uint(currentVertex.boardState.size * currentVertex.boardState.size)
	doUntil := time.Now().Add(15 * time.Second)
	for time.Now().Second() != doUntil.Second() {
		toDepth := maxMoves/2 + currentVertex.plyDepth + 1
		_ = doRoutine(currentVertex, toDepth)
	}
}

func doRoutine(fromVertex *Vertex, toDepth uint) bool {
	if toDepth == fromVertex.plyDepth {
		return true
	}

	var x, y uint8
	var score uint8
	var board Board
	err := "error"

	if fromVertex == currentVertex {
		board.Create(uint16(currentVertex.boardState.size))
		copy(board.s, fromVertex.boardState.s)
	} else {
		board = fromVertex.boardState
		fromVertex.boardState = Board{[]uint8{}, 0}
	}

	for err != "" {
		x, y = getRandomMove(board)
		for _, v := range fromVertex.outEdges {
			if x == v.playX && y == v.playY {
				_, _ = board.Play(fromVertex.turn, x, y)
				v.toVertex.boardState = board
				return doRoutine(v.toVertex, toDepth)
			}
		}

		score, err = board.Play(fromVertex.turn, x, y)

		if fromVertex.plyDepth > 2 {
			toCompare := fromVertex.inEdge.fromVertex.inEdge
			if x == toCompare.playX && y == toCompare.playY {
				err = "KO"
			}
		}
		if err != "" {
			board.Remove(x, y)
		}
	}

	newVertex := new(Vertex)

	newEdge := new(Edge)
	newEdge.playX = x
	newEdge.playY = y
	newEdge.fromVertex = fromVertex
	newEdge.toVertex = newVertex

	fromVertex.outEdges = append(fromVertex.outEdges, newEdge)
	newVertex.inEdge = newEdge

	newVertex.boardState = board
	if fromVertex.turn == 1 {
		newVertex.turn = 2
		newVertex.blackScore += score
	} else {
		newVertex.turn = 1
		newVertex.whiteScore += score
	}
	newVertex.plyDepth = fromVertex.plyDepth + 1

	if newVertex.plyDepth > uint(board.size*board.size/2) {
		_, _ = scoreBoard(newVertex)
	}
	return doRoutine(newVertex, toDepth)
}

func getRandomMove(b Board) (x, y uint8) {
	empty := b.GetEmpty()
	i := rand.Intn(len(empty))
	if i%2 == 0 { //index is x
		x = empty[i]
		y = empty[i+1]
	} else { //index is y
		y = empty[i]
		x = empty[i-1]
	}
	return x, y
}

func scoreBoard(v *Vertex) (scoreBlack, scoreWhite int) {
	b := v.boardState
	empty := b.GetEmpty()

	for len(empty) > 0 {
		xs := []uint8{empty[0]}
		ys := []uint8{empty[1]}
		b.GetGroup(0, 0, xs, ys)

		var adjecentColor uint8 = 0
		found2Colors := false
		for i, x := range xs {
			xa, xb, ya, yb := getAdjecent(x, ys[i])
			if xa < b.size && b.getColor(xa, ys[i]) != adjecentColor {
				if adjecentColor == 0 {
					adjecentColor = b.getColor(xa, ys[i])
				} else {
					found2Colors = true
					break
				}
			}
			if xb < b.size && b.getColor(xb, ys[i]) != adjecentColor {
				if adjecentColor == 0 {
					adjecentColor = b.getColor(xb, ys[i])
				} else {
					found2Colors = true
					break
				}
			}
			if ya < b.size && b.getColor(x, ya) != adjecentColor {
				if adjecentColor == 0 {
					adjecentColor = b.getColor(x, ya)
				} else {
					found2Colors = true
					break
				}
			}
			if yb < b.size && b.getColor(x, yb) != adjecentColor {
				if adjecentColor == 0 {
					adjecentColor = b.getColor(x, yb)
				} else {
					found2Colors = true
					break
				}
			}
		}
		if !found2Colors && adjecentColor == 1 {
			scoreBlack += len(xs)
		} else if !found2Colors && adjecentColor == 2 {
			scoreWhite += len(xs)
		}

		for i := 0; i < len(empty); i += 2 {
			emptyx := empty[i]
			emptyy := empty[i+1]
			if IsPresentinGroup(emptyx, emptyy, xs, ys) {
				empty = append(empty[:i], empty[i+2:]...)
			}
		}
	}
	if scoreBlack > scoreWhite {
		vertex := v
		for vertex != currentVertex {
			vertex.blackWins += 1
			vertex = vertex.inEdge.fromVertex
		}
	} else if scoreBlack < scoreWhite {
		vertex := v
		for vertex != currentVertex {
			vertex.whiteWins += 1
			vertex = vertex.inEdge.fromVertex
		}
	}
	return scoreBlack, scoreWhite
}
<commit_msg>Improved memory usage<commit_after>package graph

import (
	"math/rand"
	"time"
)

var topVertex *Vertex
var currentVertex *Vertex

type Vertex struct {
	whiteWins  uint16
	blackWins  uint16
	whiteScore uint8
	blackScore uint8
	boardState *Board
	turn       uint8

	plyDepth uint
	outEdges []*Edge
	inEdge   *Edge
}

type Edge struct {
	playX uint8
	playY uint8

	fromVertex *Vertex
	toVertex   *Vertex
}

func Initiate(boardSize uint8) {
	rand.Seed(time.Now().Unix())
	topVertex = new(Vertex)
	topVertex.boardState = new(Board)
	topVertex.boardState.Create(uint16(boardSize))
	topVertex.turn = 1
	topVertex.plyDepth = 0
	currentVertex = topVertex
}

func Reset() {
	topVertex = nil
	currentVertex = nil
}

func GetMove(c uint8) (x, y uint8) {
	createGraph()
	var bestMove *Edge = nil
	var mostWins uint16 = 0
	if c != currentVertex.turn {
		panic("Trying to getmove for wrong color")
	}
	if c == 1 {
		for _, v := range currentVertex.outEdges {
			if v.toVertex.blackWins > mostWins {
				bestMove = v
				mostWins = v.toVertex.blackWins
			}
		}
	} else if c == 2 {
		for _, v := range currentVertex.outEdges {
			if v.toVertex.whiteWins > mostWins {
				bestMove = v
				mostWins = v.toVertex.whiteWins
			}
		}
	} else {
		panic("Unknown color in GetMove(..)")
	}
	if bestMove == nil {
		panic("Unable to get a move from graph")
	}
	currentVertex.outEdges = []*Edge{bestMove}

	board := currentVertex.boardState
	currentVertex.boardState = nil
	board.Play(c, bestMove.playX, bestMove.playY)

	currentVertex = bestMove.toVertex
	currentVertex.boardState = board
	return bestMove.playX, bestMove.playY
}

func UpdateCurrentVertex(c, x, y uint8) {
	if c != currentVertex.turn {
		panic("Updating graph to wrong color's move")
	}
	var newCurrent *Vertex = nil
	for _, v := range currentVertex.outEdges {
		if v.playX == x && v.playY == y {
			currentVertex.outEdges = []*Edge{v}
			board := currentVertex.boardState
			currentVertex.boardState = nil
			board.Play(c, x, y)
			newCurrent = v.toVertex
			newCurrent.boardState = board
			break
		}
	}
	if newCurrent == nil {
		newCurrent = new(Vertex)
		board := currentVertex.boardState
		currentVertex.boardState = nil
		newCurrent.boardState = board

		score, _ := newCurrent.boardState.Play(c, x, y)

		if c == 1 {
			newCurrent.blackScore += score
			newCurrent.turn = 2
		} else {
			newCurrent.whiteScore += score
			newCurrent.turn = 1
		}
		newCurrent.plyDepth = currentVertex.plyDepth + 1

		newEdge := new(Edge)
		newEdge.fromVertex = currentVertex
		newEdge.toVertex = newCurrent

		newEdge.playX = x
		newEdge.playY = y

		currentVertex.outEdges = []*Edge{newEdge}
		newCurrent.inEdge = newEdge
	}
	currentVertex = newCurrent
}

func createGraph() {
	maxMoves := uint(currentVertex.boardState.size * currentVertex.boardState.size)
	doUntil := time.Now().Add(15 * time.Second)
	for time.Now().Second() != doUntil.Second() {
		toDepth := maxMoves/2 + currentVertex.plyDepth + 1
		_ = doRoutine(currentVertex, toDepth)
	}
}

func doRoutine(fromVertex *Vertex, toDepth uint) bool {
	if toDepth == fromVertex.plyDepth {
		fromVertex.boardState = nil
		return true
	}

	var x, y uint8
	var score uint8
	var board *Board
	err := "error"

	if fromVertex == currentVertex {
		board = new(Board)
		board.Create(uint16(currentVertex.boardState.size))
		copy(board.s, fromVertex.boardState.s)
	} else {
		board = fromVertex.boardState
		fromVertex.boardState = nil
	}

	for err != "" {
		x, y = getRandomMove(board)
		for _, v := range fromVertex.outEdges {
			if x == v.playX && y == v.playY {
				_, _ = board.Play(fromVertex.turn, x, y)
				v.toVertex.boardState = board
				return doRoutine(v.toVertex, toDepth)
			}
		}

		score, err = board.Play(fromVertex.turn, x, y)

		if fromVertex.plyDepth > 2 {
			toCompare := fromVertex.inEdge.fromVertex.inEdge
			if x == toCompare.playX && y == toCompare.playY {
				err = "KO"
			}
		}
		if err != "" {
			board.Remove(x, y)
		}
	}

	newVertex := new(Vertex)

	newEdge := new(Edge)
	newEdge.playX = x
	newEdge.playY = y
	newEdge.fromVertex = fromVertex
	newEdge.toVertex = newVertex

	fromVertex.outEdges = append(fromVertex.outEdges, newEdge)
	newVertex.inEdge = newEdge

	newVertex.boardState = board
	if fromVertex.turn == 1 {
		newVertex.turn = 2
		newVertex.blackScore += score
	} else {
		newVertex.turn = 1
		newVertex.whiteScore += score
	}
	newVertex.plyDepth = fromVertex.plyDepth + 1

	if newVertex.plyDepth > uint(board.size*board.size/2) {
		_, _ = scoreBoard(newVertex)
	}
	return doRoutine(newVertex, toDepth)
}

func getRandomMove(b *Board) (x, y uint8) {
	empty := b.GetEmpty()
	i := rand.Intn(len(empty))
	if i%2 == 0 { //index is x
		x = empty[i]
		y = empty[i+1]
	} else { //index is y
		y = empty[i]
		x = empty[i-1]
	}
	return x, y
}

func scoreBoard(v *Vertex) (scoreBlack, scoreWhite int) {
	b := v.boardState
	empty := b.GetEmpty()

	for len(empty) > 0 {
		xs := []uint8{empty[0]}
		ys := []uint8{empty[1]}
		b.GetGroup(0, 0, xs, ys)

		var adjecentColor uint8 = 0
		found2Colors := false
		for i, x := range xs {
			xa, xb, ya, yb := getAdjecent(x, ys[i])
			if xa < b.size && b.getColor(xa, ys[i]) != adjecentColor {
				if adjecentColor == 0 {
					adjecentColor = b.getColor(xa, ys[i])
				} else {
					found2Colors = true
					break
				}
			}
			if xb < b.size && b.getColor(xb, ys[i]) != adjecentColor {
				if adjecentColor == 0 {
					adjecentColor = b.getColor(xb, ys[i])
				} else {
					found2Colors = true
					break
				}
			}
			if ya < b.size && b.getColor(x, ya) != adjecentColor {
				if adjecentColor == 0 {
					adjecentColor = b.getColor(x, ya)
				} else {
					found2Colors = true
					break
				}
			}
			if yb < b.size && b.getColor(x, yb) != adjecentColor {
				if adjecentColor == 0 {
					adjecentColor = b.getColor(x, yb)
				} else {
					found2Colors = true
					break
				}
			}
		}
		if !found2Colors && adjecentColor == 1 {
			scoreBlack += len(xs)
		} else if !found2Colors && adjecentColor == 2 {
			scoreWhite += len(xs)
		}

		for i := 0; i < len(empty); i += 2 {
			emptyx := empty[i]
			emptyy := empty[i+1]
			if IsPresentinGroup(emptyx, emptyy, xs, ys) {
				empty = append(empty[:i], empty[i+2:]...)
			}
		}
	}
	if scoreBlack > scoreWhite {
		vertex := v
		for vertex != currentVertex {
			vertex.blackWins += 1
			vertex = vertex.inEdge.fromVertex
		}
	} else if scoreBlack < scoreWhite {
		vertex := v
		for vertex != currentVertex {
			vertex.whiteWins += 1
			vertex = vertex.inEdge.fromVertex
		}
	}
	return scoreBlack, scoreWhite
}
<|endoftext|>