<commit_before>package kiwi_test

/*
Copyright (c) 2016, Alexander I.Grafov aka Axel
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of kvlog nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"github.com/grafov/kiwi"

	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"
)

// Test logging of string value.
func TestGlobalLogger_LogStringValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", "The sample string with a lot of spaces.")

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"The sample string with a lot of spaces.\"" {
		t.Fail()
	}
}

// Test logging of byte array.
func TestGlobalLogger_LogBytesValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", []byte("The sample string with a lot of spaces."))

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"The sample string with a lot of spaces.\"" {
		t.Fail()
	}
}

// Test logging of integer value.
func TestGlobalLogger_LogIntValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", 123)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=123" {
		t.Fail()
	}
}

// Test logging of negative integer value.
func TestGlobalLogger_LogNegativeIntValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", 123)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=123" {
		t.Fail()
	}
}

// Test logging of float value in default (scientific) format.
func TestGlobalLogger_LogFloatValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", 3.14159265359)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=3.14159265359e+00" {
		t.Fail()
	}
}

// Test logging of float value in fixed format.
func TestGlobalLogger_LogFixedFloatValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.FloatFormat = 'f'
	kiwi.Log("k", 3.14159265359)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=3.14159265359" {
		t.Fail()
	}
	// Turn back to default format.
	kiwi.FloatFormat = 'e'
}

// Test logging of boolean value.
func TestGlobalLogger_LogBoolValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", true, "k2", false)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=true k2=false" {
		t.Fail()
	}
}

// Test logging of complex number.
func TestGlobalLogger_LogComplexValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", .12345E+5i, "k2", 1.e+0i)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=(0.000000+12345.000000i) k2=(0.000000+1.000000i)" {
		t.Fail()
	}
}

// Test logging of time literal.
func TestGlobalLogger_LogTimeValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	value := time.Now()
	valueString := value.Format(kiwi.TimeLayout)
	defer out.Close()

	kiwi.Log("k", value)

	out.Flush()
	if strings.TrimSpace(output.String()) != fmt.Sprintf("k=%s", valueString) {
		t.Fail()
	}
}
<commit_msg>Add verbosity during test fails.<commit_after>package kiwi_test

/*
Copyright (c) 2016, Alexander I.Grafov aka Axel
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of kvlog nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"github.com/grafov/kiwi"

	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"
)

// Test logging of string value.
func TestGlobalLogger_LogStringValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", "The sample string with a lot of spaces.")

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"The sample string with a lot of spaces.\"" {
		println(output.String())
		t.Fail()
	}
}

// Test logging of byte array.
func TestGlobalLogger_LogBytesValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", []byte("The sample string with a lot of spaces."))

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"The sample string with a lot of spaces.\"" {
		println(output.String())
		t.Fail()
	}
}

// Test logging of integer value.
func TestGlobalLogger_LogIntValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", 123)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=123" {
		println(output.String())
		t.Fail()
	}
}

// Test logging of negative integer value.
func TestGlobalLogger_LogNegativeIntValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", 123)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=123" {
		println(output.String())
		t.Fail()
	}
}

// Test logging of float value in default (scientific) format.
func TestGlobalLogger_LogFloatValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", 3.14159265359)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=3.14159265359e+00" {
		println(output.String())
		t.Fail()
	}
}

// Test logging of float value in fixed format.
func TestGlobalLogger_LogFixedFloatValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.FloatFormat = 'f'
	kiwi.Log("k", 3.14159265359)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=3.14159265359" {
		println(output.String())
		t.Fail()
	}
	// Turn back to default format.
	kiwi.FloatFormat = 'e'
}

// Test logging of boolean value.
func TestGlobalLogger_LogBoolValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", true, "k2", false)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=true k2=false" {
		println(output.String())
		t.Fail()
	}
}

// Test logging of complex number.
func TestGlobalLogger_LogComplexValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("k", .12345E+5i, "k2", 1.e+0i)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=(0.000000+12345.000000i) k2=(0.000000+1.000000i)" {
		println(output.String())
		t.Fail()
	}
}

// Test logging of time literal.
func TestGlobalLogger_LogTimeValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	value := time.Now()
	valueString := value.Format(kiwi.TimeLayout)
	defer out.Close()

	kiwi.Log("k", value)

	out.Flush()
	if strings.TrimSpace(output.String()) != fmt.Sprintf("k=%s", valueString) {
		println(output.String())
		t.Fail()
	}
}

// Test logging
func TestGlobalLogger_LogKeyWithSpaces_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	out := kiwi.SinkTo(output, kiwi.UseLogfmt()).Start()
	defer out.Close()

	kiwi.Log("key with spaces", "The sample value.")

	out.Flush()
	if strings.TrimSpace(output.String()) != "\"key with spaces\"=\"The sample value.\"" {
		println(output.String())
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"log"
	"time"
)

const (
	REDIS_KEY = "hchecker"
	REDIS_ADDRESS = "localhost:6379"
	REDIS_PASSWORD = ""
)

var (
	redisAddress string
	redisPassword string
)

type Cache struct {
	pool	  *redis.Pool
	// Maintain a mapping between a backends and several frontend
	// -> map[BACKEND_URL][FRONTEND_NAME] = BACKEND_ID
	backendsMapping map[string]map[string]int
	// Channel used to notify goroutine when a frontend has been added to the
	// backendsMapping
	channelMapping map[string]chan int
}

func NewCache() (*Cache, error) {
	pool := &redis.Pool{
		MaxIdle: 3,
		IdleTimeout: 240 * time.Second,
		Dial: func () (redis.Conn, error) {
			c, err := redis.Dial("tcp", redisAddress)
			if err != nil {
				return nil, err
			}
			if redisPassword != "" {
				if _, err := c.Do("AUTH", redisPassword); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
				_, err := c.Do("PING")
				return err
		},
	}
	cache := &Cache{
		pool: pool,
		backendsMapping: make(map[string]map[string]int),
		channelMapping:  make(map[string]chan int),
	}
	// We're starting, let's clear any previous meta-data
	// WARNING: This can be a problem if there are several processes sharing
	// the same redis on the same machine. If one of them is restarted, it'll
	// clear the meta-data of everyone...
	conn := pool.Get()
	defer conn.Close()
	conn.Send("DEL",REDIS_KEY)
	return cache, nil
}

/*
 * Maintain a mapping between Frontends and Backends ID
 */
func (c *Cache) updateFrontendMapping(check *Check) {
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		m = make(map[string]int)
	}
	m[check.FrontendKey] = check.BackendId
	c.backendsMapping[check.BackendUrl] = m
	// Notify the goroutine that we added a frontend
	ch, exists := c.channelMapping[check.BackendUrl]
	if exists {
		// Non-blocking send
		select {
		case ch <- 1:
		default:
		}
	}
}

/*
 * Lock a backend in Redis by its URL
 */
func (c *Cache) LockBackend(check *Check) (bool, chan int) {
	// The syncKey makes sure an entire backend mapping is keep in the same
	// process (we never update a backend mapping from 2 different processes)
	syncKey := check.BackendUrl + ";" + myId
	// Lock the backend with a temporary value, we'll update this with the
	// goroutine signature later
	var locked bool
	var isMine bool
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("MULTI")
	conn.Send("HSETNX", REDIS_KEY, check.BackendUrl, 1)
	conn.Send("HEXISTS", REDIS_KEY, syncKey)
	resp, _ := redis.Values(conn.Do("EXEC"))
	redis.Scan(resp, &locked, &isMine)
	if locked == false && isMine == false {
		// The backend is being monitored by someone else
		return false, nil
	}
	if locked == false {
		c.updateFrontendMapping(check)
		return false, nil
	}
	// we got the lock, let's create a unique sig for the goroutine
	t := time.Now()
	// This one is done in the lock, this will garanty that no routine
	// will get the same sig
	sig := fmt.Sprintf("%s;%d.%d", myId, t.Unix(), t.Nanosecond())
	conn.Send("HSET",REDIS_KEY, check.BackendUrl, sig)
	conn.Send("HSET",REDIS_KEY, syncKey, 1)
	conn.Flush()
	check.routineSig = sig
	// Create the channel
	ch := make(chan int, 1)
	c.channelMapping[check.BackendUrl] = ch
	c.updateFrontendMapping(check)
	return true, ch
}

func (c *Cache) IsUnlockedBackend(check *Check) bool {
	// On top of checking the lock, we compare the lock content to make sure
	// we still own the lock
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("HGET", REDIS_KEY, check.BackendUrl)
	conn.Flush()
	resp, _ := redis.String(conn.Receive())
	return (resp != check.routineSig)
}

func (c *Cache) UnlockBackend(check *Check) {
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("HDEL",REDIS_KEY, check.BackendUrl, check.BackendUrl+";"+myId)
	conn.Flush()
	delete(c.backendsMapping, check.BackendUrl)
	delete(c.channelMapping, check.BackendUrl)
}

/*
 * Before changing the state (dead or alive) in the Redis, we make sure
 * the backend is still both in memory and in Redis so we'll avoid wrong
 * updates.
 */
func (c *Cache) checkBackendMapping(check *Check, frontendKey string,
	backendId int, mapping *map[string]int) bool {
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("LINDEX","frontend:"+frontendKey, backendId+1)
	conn.Flush()
	resp, _ := redis.String(conn.Receive())
	if resp == check.BackendUrl {
		return true
	}
	log.Println(check.BackendUrl, "Mapping changed for", frontendKey)
	delete(*mapping, frontendKey)
	return false
}

/*
 * Flag the backend dead in Redis
 * Returns false if no update has been performed (backend unlock)
 */
func (c *Cache) MarkBackendDead(check *Check) bool {
	conn := c.pool.Get()
	defer conn.Close()
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		c.UnlockBackend(check)
		return false
	}
	conn.Send("MULTI")
	for frontendKey, id := range m {
		if r := c.checkBackendMapping(check, frontendKey, id, &m); r == false {
			continue
		}
		deadKey := "dead:" + frontendKey
		conn.Send("SADD", deadKey, id)
		// Better way would be to set the same TTL than Hipache. Not
		// critical since we'll clean the backend list
		conn.Send("EXPIRE", deadKey, 60)
	}
	conn.Do("EXEC")
	if len(m) == 0 {
		// checkBackenMapping() removed all frontend mapping, no need to check
		// this backend anymore...
		c.UnlockBackend(check)
		return false
	}
	return true
}

/*
 * Flag the backend live in Redis
 * Returns false if no update has been performed (backend unlock)
 */
func (c *Cache) MarkBackendAlive(check *Check) bool {
	conn := c.pool.Get()
	defer conn.Close()
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		c.UnlockBackend(check)
		return false
	}
	conn.Send("MULTI")
	for frontendKey, id := range m {
		if r := c.checkBackendMapping(check, frontendKey, id, &m); r == false {
			continue
		}
		conn.Send("SREM", "dead:"+frontendKey, id)
	}
	conn.Do("EXEC")
	if len(m) == 0 {
		c.UnlockBackend(check)
		return false
	}
	return true
}

func (c *Cache) ListenToChannel(channel string, callback func(line string)) error {
	// Listening on the "dead" channel to get dead notifications by Hipache
	// Format received on the channel is:
	// -> frontend_key;backend_url;backend_id;number_of_backends
	// Example: "localhost;http://localhost:4242;0;1"
	conn := c.pool.Get()

	psc := redis.PubSubConn{conn}
	psc.Subscribe(channel)

	go func() {
		defer conn.Close()
		for {
			switch v := psc.Receive().(type) {
			case redis.Message:
				callback(string(v.Data[:]))
			case error:
				conn.Close()
				conn := c.pool.Get()
				time.Sleep(10 * time.Second)
				psc = redis.PubSubConn{conn}
				psc.Subscribe(channel)
			}
		}
	}()

	return nil
}

func (c *Cache) PingAlive() {
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("SET", "hchecker_ping", time.Now().Unix())
	conn.Flush()
}
<commit_msg>gofmt -s -w .<commit_after>package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"log"
	"time"
)

const (
	REDIS_KEY      = "hchecker"
	REDIS_ADDRESS  = "localhost:6379"
	REDIS_PASSWORD = ""
)

var (
	redisAddress  string
	redisPassword string
)

type Cache struct {
	pool *redis.Pool
	// Maintain a mapping between a backends and several frontend
	// -> map[BACKEND_URL][FRONTEND_NAME] = BACKEND_ID
	backendsMapping map[string]map[string]int
	// Channel used to notify goroutine when a frontend has been added to the
	// backendsMapping
	channelMapping map[string]chan int
}

func NewCache() (*Cache, error) {
	pool := &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", redisAddress)
			if err != nil {
				return nil, err
			}
			if redisPassword != "" {
				if _, err := c.Do("AUTH", redisPassword); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
	cache := &Cache{
		pool:            pool,
		backendsMapping: make(map[string]map[string]int),
		channelMapping:  make(map[string]chan int),
	}
	// We're starting, let's clear any previous meta-data
	// WARNING: This can be a problem if there are several processes sharing
	// the same redis on the same machine. If one of them is restarted, it'll
	// clear the meta-data of everyone...
	conn := pool.Get()
	defer conn.Close()
	conn.Send("DEL", REDIS_KEY)
	return cache, nil
}

/*
 * Maintain a mapping between Frontends and Backends ID
 */
func (c *Cache) updateFrontendMapping(check *Check) {
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		m = make(map[string]int)
	}
	m[check.FrontendKey] = check.BackendId
	c.backendsMapping[check.BackendUrl] = m
	// Notify the goroutine that we added a frontend
	ch, exists := c.channelMapping[check.BackendUrl]
	if exists {
		// Non-blocking send
		select {
		case ch <- 1:
		default:
		}
	}
}

/*
 * Lock a backend in Redis by its URL
 */
func (c *Cache) LockBackend(check *Check) (bool, chan int) {
	// The syncKey makes sure an entire backend mapping is keep in the same
	// process (we never update a backend mapping from 2 different processes)
	syncKey := check.BackendUrl + ";" + myId
	// Lock the backend with a temporary value, we'll update this with the
	// goroutine signature later
	var locked bool
	var isMine bool
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("MULTI")
	conn.Send("HSETNX", REDIS_KEY, check.BackendUrl, 1)
	conn.Send("HEXISTS", REDIS_KEY, syncKey)
	resp, _ := redis.Values(conn.Do("EXEC"))
	redis.Scan(resp, &locked, &isMine)
	if locked == false && isMine == false {
		// The backend is being monitored by someone else
		return false, nil
	}
	if locked == false {
		c.updateFrontendMapping(check)
		return false, nil
	}
	// we got the lock, let's create a unique sig for the goroutine
	t := time.Now()
	// This one is done in the lock, this will garanty that no routine
	// will get the same sig
	sig := fmt.Sprintf("%s;%d.%d", myId, t.Unix(), t.Nanosecond())
	conn.Send("HSET", REDIS_KEY, check.BackendUrl, sig)
	conn.Send("HSET", REDIS_KEY, syncKey, 1)
	conn.Flush()
	check.routineSig = sig
	// Create the channel
	ch := make(chan int, 1)
	c.channelMapping[check.BackendUrl] = ch
	c.updateFrontendMapping(check)
	return true, ch
}

func (c *Cache) IsUnlockedBackend(check *Check) bool {
	// On top of checking the lock, we compare the lock content to make sure
	// we still own the lock
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("HGET", REDIS_KEY, check.BackendUrl)
	conn.Flush()
	resp, _ := redis.String(conn.Receive())
	return (resp != check.routineSig)
}

func (c *Cache) UnlockBackend(check *Check) {
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("HDEL", REDIS_KEY, check.BackendUrl, check.BackendUrl+";"+myId)
	conn.Flush()
	delete(c.backendsMapping, check.BackendUrl)
	delete(c.channelMapping, check.BackendUrl)
}

/*
 * Before changing the state (dead or alive) in the Redis, we make sure
 * the backend is still both in memory and in Redis so we'll avoid wrong
 * updates.
 */
func (c *Cache) checkBackendMapping(check *Check, frontendKey string,
	backendId int, mapping *map[string]int) bool {
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("LINDEX", "frontend:"+frontendKey, backendId+1)
	conn.Flush()
	resp, _ := redis.String(conn.Receive())
	if resp == check.BackendUrl {
		return true
	}
	log.Println(check.BackendUrl, "Mapping changed for", frontendKey)
	delete(*mapping, frontendKey)
	return false
}

/*
 * Flag the backend dead in Redis
 * Returns false if no update has been performed (backend unlock)
 */
func (c *Cache) MarkBackendDead(check *Check) bool {
	conn := c.pool.Get()
	defer conn.Close()
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		c.UnlockBackend(check)
		return false
	}
	conn.Send("MULTI")
	for frontendKey, id := range m {
		if r := c.checkBackendMapping(check, frontendKey, id, &m); r == false {
			continue
		}
		deadKey := "dead:" + frontendKey
		conn.Send("SADD", deadKey, id)
		// Better way would be to set the same TTL than Hipache. Not
		// critical since we'll clean the backend list
		conn.Send("EXPIRE", deadKey, 60)
	}
	conn.Do("EXEC")
	if len(m) == 0 {
		// checkBackenMapping() removed all frontend mapping, no need to check
		// this backend anymore...
		c.UnlockBackend(check)
		return false
	}
	return true
}

/*
 * Flag the backend live in Redis
 * Returns false if no update has been performed (backend unlock)
 */
func (c *Cache) MarkBackendAlive(check *Check) bool {
	conn := c.pool.Get()
	defer conn.Close()
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		c.UnlockBackend(check)
		return false
	}
	conn.Send("MULTI")
	for frontendKey, id := range m {
		if r := c.checkBackendMapping(check, frontendKey, id, &m); r == false {
			continue
		}
		conn.Send("SREM", "dead:"+frontendKey, id)
	}
	conn.Do("EXEC")
	if len(m) == 0 {
		c.UnlockBackend(check)
		return false
	}
	return true
}

func (c *Cache) ListenToChannel(channel string, callback func(line string)) error {
	// Listening on the "dead" channel to get dead notifications by Hipache
	// Format received on the channel is:
	// -> frontend_key;backend_url;backend_id;number_of_backends
	// Example: "localhost;http://localhost:4242;0;1"
	conn := c.pool.Get()

	psc := redis.PubSubConn{conn}
	psc.Subscribe(channel)

	go func() {
		defer conn.Close()
		for {
			switch v := psc.Receive().(type) {
			case redis.Message:
				callback(string(v.Data[:]))
			case error:
				conn.Close()
				conn := c.pool.Get()
				time.Sleep(10 * time.Second)
				psc = redis.PubSubConn{conn}
				psc.Subscribe(channel)
			}
		}
	}()

	return nil
}

func (c *Cache) PingAlive() {
	conn := c.pool.Get()
	defer conn.Close()
	conn.Send("SET", "hchecker_ping", time.Now().Unix())
	conn.Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/fzzy/radix/redis"
	"time"
)

const (
	REDIS_KEY     = "hchecker"
	REDIS_ADDRESS = "localhost:6379"
)

var (
	redisAddress string
)

type Cache struct {
	redisConn *redis.Client
	redisSub  *redis.Subscription
	// Maintain a mapping between a backends and several frontend
	// -> map[BACKEND_URL][FRONTEND_NAME] = BACKEND_ID
	backendsMapping map[string]map[string]int
	// Channel used to notify goroutine when a frontend has been added to the
	// backendsMapping
	channelMapping map[string]chan int
}

func NewCache() (*Cache, error) {
	conf := redis.DefaultConfig()
	conf.Address = redisAddress
	redisConn := redis.NewClient(conf)
	cache := &Cache{
		redisConn:       redisConn,
		backendsMapping: make(map[string]map[string]int),
		channelMapping:  make(map[string]chan int),
	}
	// We're starting, let's clear any previous meta-data
	// WARNING: This can be a problem if there are several processes sharing
	// the same redis on the same machine. If one of them is restarted, it'll
	// clear the meta-data of everyone...
	redisConn.Del(REDIS_KEY)
	return cache, nil
}

/*
 * Maintain a mapping between Frontends and Backends ID
 */
func (c *Cache) updateFrontendMapping(check *Check) {
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		m = make(map[string]int)
	}
	m[check.FrontendKey] = check.BackendId
	c.backendsMapping[check.BackendUrl] = m
	// Notify the goroutine that we added a frontend
	ch, exists := c.channelMapping[check.BackendUrl]
	if exists {
		// Non-blocking send
		select {
		case ch <- 1:
		default:
		}
	}
}

/*
 * Lock a backend in Redis by its URL
 */
func (c *Cache) LockBackend(check *Check) (bool, chan int) {
	// The syncKey makes sure an entire backend mapping is keep in the same
	// process (we never update a backend mapping from 2 different processes)
	syncKey := check.BackendUrl + ";" + myId
	// Lock the backend with a temporary value, we'll update this with the
	// goroutine signature later
	resp := c.redisConn.Transaction(func(mc *redis.MultiCall) {
		mc.Hsetnx(REDIS_KEY, check.BackendUrl, 1)
		mc.Hexists(REDIS_KEY, syncKey)
	})
	locked, _ := resp.Elems[0].Bool()
	isMine, _ := resp.Elems[1].Bool()
	if locked == false && isMine == false {
		// The backend is being monitored by someone else
		return false, nil
	}
	if locked == false {
		c.updateFrontendMapping(check)
		return false, nil
	}
	// we got the lock, let's create a unique sig for the goroutine
	t := time.Now()
	// This one is done in the lock, this will garanty that no routine
	// will get the same sig
	sig := fmt.Sprintf("%s;%d.%d", myId, t.Unix(), t.Nanosecond())
	c.redisConn.Hset(REDIS_KEY, check.BackendUrl, sig)
	c.redisConn.Hset(REDIS_KEY, syncKey, 1)
	check.routineSig = sig
	// Create the channel
	ch := make(chan int, 1)
	c.channelMapping[check.BackendUrl] = ch
	c.updateFrontendMapping(check)
	return true, ch
}

func (c *Cache) IsUnlockedBackend(check *Check) bool {
	// On top of checking the lock, we compare the lock content to make sure
	// we still own the lock
	resp, _ := c.redisConn.Hget(REDIS_KEY, check.BackendUrl).Str()
	return (resp != check.routineSig)
}

func (c *Cache) UnlockBackend(check *Check) {
	c.redisConn.Hdel(REDIS_KEY, check.BackendUrl,
		check.BackendUrl + ":" + myId)
	delete(c.backendsMapping, check.BackendUrl)
	delete(c.channelMapping, check.BackendUrl)
}

func (c *Cache) checkBackendMapping(check *Check, frontendKey string,
		backendId int, mapping *map[string]int) bool {
	// Before changing the state (dead or alive) in the Redis, we make sure
	// the backend is still both in memory and in Redis so we'll avoid wrong
	// updates.
	resp, _ := c.redisConn.Lindex("frontend:" + frontendKey, backendId + 1).Str()
	if resp == check.BackendUrl {
		return true
	}
	delete(*mapping, frontendKey)
	return false
}

func (c *Cache) MarkBackendDead(check *Check) {
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		c.UnlockBackend(check)
		return
	}
	c.redisConn.Transaction(func(mc *redis.MultiCall) {
		for frontendKey, id := range m {
			if r := c.checkBackendMapping(check, frontendKey, id, &m); r == false {
				continue
			}
			deadKey := "dead:" + frontendKey
			mc.Sadd(deadKey, id)
			// Better way would be to set the same TTL than Hipache. Not
			// critical since we'll clean the backend list
			mc.Expire(deadKey, 60)
		}
	})
	if len(m) == 0 {
		// checkBackenMapping() removed all frontend mapping, no need to check
		// this backend anymore...
		c.UnlockBackend(check)
	}
}

func (c *Cache) MarkBackendAlive(check *Check) {
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		c.UnlockBackend(check)
		return
	}
	c.redisConn.Transaction(func(mc *redis.MultiCall) {
		for frontendKey, id := range m {
			if r := c.checkBackendMapping(check, frontendKey, id, &m); r == false {
				continue
			}
			mc.Srem("dead:" + frontendKey, id)
		}
	})
	if len(m) == 0 {
		c.UnlockBackend(check)
	}
}

func (c *Cache) ListenToChannel(channel string, callback func(line string)) error {
	// Listening on the "dead" channel to get dead notifications by Hipache
	// Format received on the channel is:
	// -> frontend_key;backend_url;backend_id;number_of_backends
	// Example: "localhost;http://localhost:4242;0;1"
	msgHandler := func(msg *redis.Message) {
		switch msg.Type {
		case redis.MessageMessage:
			callback(msg.Payload)
		}
	}
	sub, err := c.redisConn.Subscription(msgHandler)
	if err != nil {
		return errors.New(fmt.Sprintf("Error: cannot subscribe to "+
			"the \"dead\" channel: %#v", err))
	}
	sub.Subscribe(channel)
	c.redisSub = sub
	return nil
}

func (c *Cache) PingAlive() {
	c.redisConn.Set("hchecker_ping", time.Now().Unix())
}
<commit_msg>Code cleaning<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/fzzy/radix/redis"
	"log"
	"time"
)

const (
	REDIS_KEY     = "hchecker"
	REDIS_ADDRESS = "localhost:6379"
)

var (
	redisAddress string
)

type Cache struct {
	redisConn *redis.Client
	redisSub  *redis.Subscription
	// Maintain a mapping between a backends and several frontend
	// -> map[BACKEND_URL][FRONTEND_NAME] = BACKEND_ID
	backendsMapping map[string]map[string]int
	// Channel used to notify goroutine when a frontend has been added to the
	// backendsMapping
	channelMapping map[string]chan int
}

func NewCache() (*Cache, error) {
	conf := redis.DefaultConfig()
	conf.Address = redisAddress
	redisConn := redis.NewClient(conf)
	cache := &Cache{
		redisConn:       redisConn,
		backendsMapping: make(map[string]map[string]int),
		channelMapping:  make(map[string]chan int),
	}
	// We're starting, let's clear any previous meta-data
	// WARNING: This can be a problem if there are several processes sharing
	// the same redis on the same machine. If one of them is restarted, it'll
	// clear the meta-data of everyone...
	redisConn.Del(REDIS_KEY)
	return cache, nil
}

/*
 * Maintain a mapping between Frontends and Backends ID
 */
func (c *Cache) updateFrontendMapping(check *Check) {
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		m = make(map[string]int)
	}
	m[check.FrontendKey] = check.BackendId
	c.backendsMapping[check.BackendUrl] = m
	// Notify the goroutine that we added a frontend
	ch, exists := c.channelMapping[check.BackendUrl]
	if exists {
		// Non-blocking send
		select {
		case ch <- 1:
		default:
		}
	}
}

/*
 * Lock a backend in Redis by its URL
 */
func (c *Cache) LockBackend(check *Check) (bool, chan int) {
	// The syncKey makes sure an entire backend mapping is keep in the same
	// process (we never update a backend mapping from 2 different processes)
	syncKey := check.BackendUrl + ";" + myId
	// Lock the backend with a temporary value, we'll update this with the
	// goroutine signature later
	resp := c.redisConn.Transaction(func(mc *redis.MultiCall) {
		mc.Hsetnx(REDIS_KEY, check.BackendUrl, 1)
		mc.Hexists(REDIS_KEY, syncKey)
	})
	locked, _ := resp.Elems[0].Bool()
	isMine, _ := resp.Elems[1].Bool()
	if locked == false && isMine == false {
		// The backend is being monitored by someone else
		return false, nil
	}
	if locked == false {
		c.updateFrontendMapping(check)
		return false, nil
	}
	// we got the lock, let's create a unique sig for the goroutine
	t := time.Now()
	// This one is done in the lock, this will garanty that no routine
	// will get the same sig
	sig := fmt.Sprintf("%s;%d.%d", myId, t.Unix(), t.Nanosecond())
	c.redisConn.Hset(REDIS_KEY, check.BackendUrl, sig)
	c.redisConn.Hset(REDIS_KEY, syncKey, 1)
	check.routineSig = sig
	// Create the channel
	ch := make(chan int, 1)
	c.channelMapping[check.BackendUrl] = ch
	c.updateFrontendMapping(check)
	return true, ch
}

func (c *Cache) IsUnlockedBackend(check *Check) bool {
	// On top of checking the lock, we compare the lock content to make sure
	// we still own the lock
	resp, _ := c.redisConn.Hget(REDIS_KEY, check.BackendUrl).Str()
	return (resp != check.routineSig)
}

func (c *Cache) UnlockBackend(check *Check) {
	c.redisConn.Hdel(REDIS_KEY, check.BackendUrl,
		check.BackendUrl+":"+myId)
	delete(c.backendsMapping, check.BackendUrl)
	delete(c.channelMapping, check.BackendUrl)
}

func (c *Cache) checkBackendMapping(check *Check, frontendKey string,
	backendId int, mapping *map[string]int) bool {
	// Before changing the state (dead or alive) in the Redis, we make sure
	// the backend is still both in memory and in Redis so we'll avoid wrong
	// updates.
	resp, _ := c.redisConn.Lindex("frontend:"+frontendKey, backendId+1).Str()
	if resp == check.BackendUrl {
		return true
	}
	log.Println(check.BackendUrl, "Mapping changed for", frontendKey)
	delete(*mapping, frontendKey)
	return false
}

func (c *Cache) MarkBackendDead(check *Check) {
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		c.UnlockBackend(check)
		return
	}
	c.redisConn.Transaction(func(mc *redis.MultiCall) {
		for frontendKey, id := range m {
			if r := c.checkBackendMapping(check, frontendKey, id, &m); r == false {
				continue
			}
			deadKey := "dead:" + frontendKey
			mc.Sadd(deadKey, id)
			// Better way would be to set the same TTL than Hipache. Not
			// critical since we'll clean the backend list
			mc.Expire(deadKey, 60)
		}
	})
	if len(m) == 0 {
		// checkBackenMapping() removed all frontend mapping, no need to check
		// this backend anymore...
		c.UnlockBackend(check)
	}
}

func (c *Cache) MarkBackendAlive(check *Check) {
	m, exists := c.backendsMapping[check.BackendUrl]
	if !exists {
		c.UnlockBackend(check)
		return
	}
	c.redisConn.Transaction(func(mc *redis.MultiCall) {
		for frontendKey, id := range m {
			if r := c.checkBackendMapping(check, frontendKey, id, &m); r == false {
				continue
			}
			mc.Srem("dead:"+frontendKey, id)
		}
	})
	if len(m) == 0 {
		c.UnlockBackend(check)
	}
}

func (c *Cache) ListenToChannel(channel string, callback func(line string)) error {
	// Listening on the "dead" channel to get dead notifications by Hipache
	// Format received on the channel is:
	// -> frontend_key;backend_url;backend_id;number_of_backends
	// Example: "localhost;http://localhost:4242;0;1"
	msgHandler := func(msg *redis.Message) {
		switch msg.Type {
		case redis.MessageMessage:
			callback(msg.Payload)
		}
	}
	sub, err := c.redisConn.Subscription(msgHandler)
	if err != nil {
		return errors.New(fmt.Sprintf("Error: cannot subscribe to "+
			"the \"dead\" channel: %#v", err))
	}
	sub.Subscribe(channel)
	c.redisSub = sub
	return nil
}

func (c *Cache) PingAlive() {
	c.redisConn.Set("hchecker_ping", time.Now().Unix())
}
<|endoftext|>


<commit_before>package i18n

import (
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/qor/qor"
	"github.com/qor/admin"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/utils"
	"github.com/theplant/cldr"
)

// Default default locale for i18n
var Default = "en-US"

// I18n struct that hold all translations
type I18n struct {
	scope        string
	value        string
	isInlineEdit bool
	Backends     []Backend
	Translations map[string]map[string]*Translation
}

// ResourceName change display name in qor admin
func (I18n) ResourceName() string {
	return "Translation"
}

// Backend defined methods that needs for translation backend
type Backend interface {
	LoadTranslations() []*Translation
	SaveTranslation(*Translation) error
	DeleteTranslation(*Translation) error
}

// Translation is a struct for translations, including Translation Key, Locale, Value
type Translation struct {
	Key     string
	Locale  string
	Value   string
	Backend Backend
}

// New initialize I18n with backends
func New(backends ...Backend) *I18n {
	i18n := &I18n{Backends: backends, Translations: map[string]map[string]*Translation{}}
	for i := len(backends) - 1; i >= 0; i-- {
		var backend = backends[i]
		for _, translation := range backend.LoadTranslations() {
			translation.Backend = backend
			i18n.AddTranslation(translation)
		}
	}
	return i18n
}

// AddTranslation add translation
func (i18n *I18n) AddTranslation(translation *Translation) {
	if i18n.Translations[translation.Locale] == nil {
		i18n.Translations[translation.Locale] = map[string]*Translation{}
	}
	i18n.Translations[translation.Locale][translation.Key] = translation
}

// SaveTranslation save translation
func (i18n *I18n) SaveTranslation(translation *Translation) error {
	var backends []Backend
	if backend := translation.Backend; backend != nil {
		backends = append(backends, backend)
	}

	for _, backend := range i18n.Backends {
		if backend.SaveTranslation(translation) == nil {
			i18n.AddTranslation(translation)
			return nil
		}
	}

	return errors.New("failed to save translation")
}

// DeleteTranslation delete translation
func (i18n *I18n) DeleteTranslation(translation *Translation) (err error) {
	if translation.Backend == nil {
		if ts := i18n.Translations[translation.Locale]; ts != nil && ts[translation.Key] != nil {
			translation = ts[translation.Key]
		}
	}

	if translation.Backend != nil {
		if err = translation.Backend.DeleteTranslation(translation); err == nil {
			delete(i18n.Translations[translation.Locale], translation.Key)
		}
	}
	return err
}

// EnableInlineEdit enable inline edit, return HTML used to edit the translation
func (i18n *I18n) EnableInlineEdit(isInlineEdit bool) admin.I18n {
	return &I18n{Translations: i18n.Translations, scope: i18n.scope, value: i18n.value, Backends: i18n.Backends, isInlineEdit: isInlineEdit}
}

// Scope i18n scope
func (i18n *I18n) Scope(scope string) admin.I18n {
	return &I18n{Translations: i18n.Translations, scope: scope, value: i18n.value, Backends: i18n.Backends, isInlineEdit: i18n.isInlineEdit}
}

// Default default value of translation if key is missing
func (i18n *I18n) Default(value string) admin.I18n {
	return &I18n{Translations: i18n.Translations, scope: i18n.scope, value: value, Backends: i18n.Backends, isInlineEdit: i18n.isInlineEdit}
}

// T translate with locale, key and arguments
func (i18n *I18n) T(locale, key string, args ...interface{}) template.HTML {
	var value = i18n.value
	var translationKey = key
	if i18n.scope != "" {
		translationKey = strings.Join([]string{i18n.scope, key}, ".")
	}

	if translations := i18n.Translations[locale]; translations != nil && translations[translationKey] != nil && translations[translationKey].Value != "" {
		// Get localized translation
		value = translations[translationKey].Value
	} else if translations := i18n.Translations[Default]; translations != nil && translations[translationKey] != nil {
		// Get default translation if not translated
		value = translations[translationKey].Value
	} else {
		if value == "" {
			value = key
		}
		// Save translations
		i18n.SaveTranslation(&Translation{Key: translationKey, Value: value, Locale: Default, Backend: i18n.Backends[0]})
	}

	if value == "" {
		value = key
	}

	if str, err := cldr.Parse(locale, value, args...); err == nil {
		value = str
	}

	if i18n.isInlineEdit {
		var editType string
		if len(value) > 25 {
			editType = "data-type=\"textarea\""
		}
		value = fmt.Sprintf("%s", editType, locale, key, value)
	}

	return template.HTML(value)
}

// RenderInlineEditAssets render inline edit html, it is using: http://vitalets.github.io/x-editable/index.html
// You could use Bootstrap or JQuery UI by set isIncludeExtendAssetLib to false and load files by yourself
func RenderInlineEditAssets(isIncludeJQuery bool, isIncludeExtendAssetLib bool) (template.HTML, error) {
	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		var content string
		var hasError bool

		if isIncludeJQuery {
			content = `<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>`
		}

		if isIncludeExtendAssetLib {
			if extendLib, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/inline-edit-libs.tmpl")); err == nil {
				content += string(extendLib)
			} else {
				hasError = true
			}

			if css, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/assets/stylesheets/i18n-inline.css")); err == nil {
				content += fmt.Sprintf("", string(css))
			} else {
				hasError = true
			}

		}

		if js, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/assets/javascripts/i18n-inline.js")); err == nil {
			content += fmt.Sprintf("", string(js))
		} else {
			hasError = true
		}

		if !hasError {
			return template.HTML(content), nil
		}
	}

	return template.HTML(""), errors.New("templates not found")
}

func getLocaleFromContext(context *qor.Context) string {
	if locale := utils.GetLocale(context); locale != "" {
		return locale
	}

	return Default
}

type availableLocalesInterface interface {
	AvailableLocales() []string
}

type viewableLocalesInterface interface {
	ViewableLocales() []string
}

type editableLocalesInterface interface {
	EditableLocales() []string
}

func getAvailableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(viewableLocalesInterface); ok {
		return user.ViewableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{Default}
}

func getEditableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(editableLocalesInterface); ok {
		return user.EditableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{Default}
}

// ConfigureQorResource configure qor resource for qor admin
func (i18n *I18n) ConfigureQorResource(res resource.Resourcer) {
	if res, ok := res.(*admin.Resource); ok {
		res.UseTheme("i18n")
		res.GetAdmin().I18n = i18n
		res.SearchAttrs("value") // generate search handler for i18n

		res.GetAdmin().RegisterFuncMap("lt", func(locale, key string, withDefault bool) string {
			if translations := i18n.Translations[locale]; translations != nil {
				if t := translations[key]; t != nil && t.Value != "" {
					return t.Value
				}
			}

			if withDefault {
				if translations := i18n.Translations[Default]; translations != nil {
					if t := translations[key]; t != nil {
						return t.Value
					}
				}
			}

			return ""
		})

		var getPrimaryLocale = func(context *admin.Context) string {
			if locale := context.Request.Form.Get("primary_locale"); locale != "" {
				return locale
			}
			if availableLocales := getAvailableLocales(context.Request, context.CurrentUser); len(availableLocales) > 0 {
				return availableLocales[0]
			}
			return ""
		}

		var getEditingLocale = func(context *admin.Context) string {
			if locale := context.Request.Form.Get("to_locale"); locale != "" {
				return locale
			}
			return getLocaleFromContext(context.Context)
		}

		res.GetAdmin().RegisterFuncMap("i18n_available_keys", func(context *admin.Context) (keys []string) {
			var (
				keysMap       = map[string]bool{}
				keyword       = context.Request.URL.Query().Get("keyword")
				primaryLocale = getPrimaryLocale(context)
				editingLocale = getEditingLocale(context)
			)

			var filterTranslations = func(translations map[string]*Translation) {
				if translations != nil {
					for key, translation := range translations {
						if (keyword == "") || (strings.Index(strings.ToLower(translation.Key), strings.ToLower(keyword)) != -1 ||
							strings.Index(strings.ToLower(translation.Value), keyword) != -1) {
							if _, ok := keysMap[key]; !ok {
								keysMap[key] = true
								keys = append(keys, key)
							}
						}
					}
				}
			}

			filterTranslations(i18n.Translations[getPrimaryLocale(context)])
			if primaryLocale != editingLocale {
				filterTranslations(i18n.Translations[getEditingLocale(context)])
			}

			sort.Strings(keys)

			pagination := context.Searcher.Pagination
			pagination.Total = len(keys)
			pagination.PrePage = 25
			pagination.CurrentPage, _ = strconv.Atoi(context.Request.URL.Query().Get("page"))
			if pagination.CurrentPage == 0 {
				pagination.CurrentPage = 1
			}
			if pagination.CurrentPage > 0 {
				pagination.Pages = pagination.Total / pagination.PrePage
			}
			context.Searcher.Pagination = pagination

			if pagination.CurrentPage == -1 {
				return keys
			}

			lastIndex := pagination.CurrentPage * pagination.PrePage
			if pagination.Total < lastIndex {
				lastIndex = pagination.Total
			}

			startIndex := (pagination.CurrentPage - 1) * pagination.PrePage
			if lastIndex >= startIndex {
				return keys[startIndex:lastIndex]
			}
			return []string{}
		})

		res.GetAdmin().RegisterFuncMap("i18n_primary_locale", getPrimaryLocale)

		res.GetAdmin().RegisterFuncMap("i18n_editing_locale", getEditingLocale)

		res.GetAdmin().RegisterFuncMap("i18n_viewable_locales", func(context admin.Context) []string {
			return getAvailableLocales(context.Request, context.CurrentUser)
		})

		res.GetAdmin().RegisterFuncMap("i18n_editable_locales", func(context admin.Context) []string {
			return getEditableLocales(context.Request, context.CurrentUser)
		})

		controller := i18nController{i18n}
		router := res.GetAdmin().GetRouter()
		router.Get(res.ToParam(), controller.Index)
		router.Post(res.ToParam(), controller.Update)
		router.Put(res.ToParam(), controller.Update)

		admin.RegisterViewPath("github.com/qor/i18n/views")
	}
}
<commit_msg>Improve i18n search function<commit_after>package i18n

import (
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/qor/admin"
	"github.com/qor/qor"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/utils"
	"github.com/theplant/cldr"
)

// Default default locale for i18n
var Default = "en-US"

// I18n struct that hold all translations
type I18n struct {
	scope        string
	value        string
	isInlineEdit bool
	Backends     []Backend
	Translations map[string]map[string]*Translation
}

// ResourceName change display name in qor admin
func (I18n) ResourceName() string {
	return "Translation"
}

// Backend defined methods that needs for translation backend
type Backend interface {
	LoadTranslations() []*Translation
	SaveTranslation(*Translation) error
	DeleteTranslation(*Translation) error
}

// Translation is a struct for translations, including Translation Key, Locale, Value
type Translation struct {
	Key     string
	Locale  string
	Value   string
	Backend Backend
}

// New initialize I18n with backends
func New(backends ...Backend) *I18n {
	i18n := &I18n{Backends: backends, Translations: map[string]map[string]*Translation{}}
	for i := len(backends) - 1; i >= 0; i-- {
		var backend = backends[i]
		for _, translation := range backend.LoadTranslations() {
			translation.Backend = backend
			i18n.AddTranslation(translation)
		}
	}
	return i18n
}

// AddTranslation add translation
func (i18n *I18n) AddTranslation(translation *Translation) {
	if i18n.Translations[translation.Locale] == nil {
		i18n.Translations[translation.Locale] = map[string]*Translation{}
	}
	i18n.Translations[translation.Locale][translation.Key] = translation
}

// SaveTranslation save translation
func (i18n *I18n) SaveTranslation(translation *Translation) error {
	var backends []Backend
	if backend := translation.Backend; backend != nil {
		backends = append(backends, backend)
	}

	for _, backend := range i18n.Backends {
		if backend.SaveTranslation(translation) == nil {
			i18n.AddTranslation(translation)
			return nil
		}
	}

	return errors.New("failed to save translation")
}

// DeleteTranslation delete translation
func (i18n *I18n) DeleteTranslation(translation *Translation) (err error) {
	if translation.Backend == nil {
		if ts := i18n.Translations[translation.Locale]; ts != nil && ts[translation.Key] != nil {
			translation = ts[translation.Key]
		}
	}

	if translation.Backend != nil {
		if err = translation.Backend.DeleteTranslation(translation); err == nil {
			delete(i18n.Translations[translation.Locale], translation.Key)
		}
	}
	return err
}

// EnableInlineEdit enable inline edit, return HTML used to edit the translation
func (i18n *I18n) EnableInlineEdit(isInlineEdit bool) admin.I18n {
	return &I18n{Translations: i18n.Translations, scope: i18n.scope, value: i18n.value, Backends: i18n.Backends, isInlineEdit: isInlineEdit}
}

// Scope i18n scope
func (i18n *I18n) Scope(scope string) admin.I18n {
	return &I18n{Translations: i18n.Translations, scope: scope, value: i18n.value, Backends: i18n.Backends, isInlineEdit: i18n.isInlineEdit}
}

// Default default value of translation if key is missing
func (i18n *I18n) Default(value string) admin.I18n {
	return &I18n{Translations: i18n.Translations, scope: i18n.scope, value: value, Backends: i18n.Backends, isInlineEdit: i18n.isInlineEdit}
}

// T translate with locale, key and arguments
func (i18n *I18n) T(locale, key string, args ...interface{}) template.HTML {
	var value = i18n.value
	var translationKey = key
	if i18n.scope != "" {
		translationKey = strings.Join([]string{i18n.scope, key}, ".")
	}

	if translations := i18n.Translations[locale]; translations != nil && translations[translationKey] != nil && translations[translationKey].Value != "" {
		// Get localized translation
		value = translations[translationKey].Value
	} else if translations := i18n.Translations[Default]; translations != nil && translations[translationKey] != nil {
		// Get default translation if not translated
		value = translations[translationKey].Value
	} else {
		if value == "" {
			value = key
		}
		// Save translations
		i18n.SaveTranslation(&Translation{Key: translationKey, Value: value, Locale: Default, Backend: i18n.Backends[0]})
	}

	if value == "" {
		value = key
	}

	if str, err := cldr.Parse(locale, value, args...); err == nil {
		value = str
	}

	if i18n.isInlineEdit {
		var editType string
		if len(value) > 25 {
			editType = "data-type=\"textarea\""
		}
		value = fmt.Sprintf("%s", editType, locale, key, value)
	}

	return template.HTML(value)
}

// RenderInlineEditAssets render inline edit html, it is using: http://vitalets.github.io/x-editable/index.html
// You could use Bootstrap or JQuery UI by set isIncludeExtendAssetLib to false and load files by yourself
func RenderInlineEditAssets(isIncludeJQuery bool, isIncludeExtendAssetLib bool) (template.HTML, error) {
	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		var content string
		var hasError bool

		if isIncludeJQuery {
			content = `<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>`
		}

		if isIncludeExtendAssetLib {
			if extendLib, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/inline-edit-libs.tmpl")); err == nil {
				content += string(extendLib)
			} else {
				hasError = true
			}

			if css, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/assets/stylesheets/i18n-inline.css")); err == nil {
				content += fmt.Sprintf("", string(css))
			} else {
				hasError = true
			}

		}

		if js, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/assets/javascripts/i18n-inline.js")); err == nil {
			content += fmt.Sprintf("", string(js))
		} else {
			hasError = true
		}

		if !hasError {
			return template.HTML(content), nil
		}
	}

	return template.HTML(""), errors.New("templates not found")
}

func getLocaleFromContext(context *qor.Context) string {
	if locale := utils.GetLocale(context); locale != "" {
		return locale
	}

	return Default
}

type availableLocalesInterface interface {
	AvailableLocales() []string
}

type viewableLocalesInterface interface {
	ViewableLocales() []string
}

type editableLocalesInterface interface {
	EditableLocales() []string
}

func getAvailableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(viewableLocalesInterface); ok {
		return user.ViewableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{Default}
}

func getEditableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(editableLocalesInterface); ok {
		return user.EditableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{Default}
}

// ConfigureQorResource configure qor resource for qor admin
func (i18n *I18n) ConfigureQorResource(res resource.Resourcer) {
	if res, ok := res.(*admin.Resource); ok {
		res.UseTheme("i18n")
		res.GetAdmin().I18n = i18n
		res.SearchAttrs("value") // generate search handler for i18n

		res.GetAdmin().RegisterFuncMap("lt", func(locale, key string, withDefault bool) string {
			if translations := i18n.Translations[locale]; translations != nil {
				if t := translations[key]; t != nil && t.Value != "" {
					return t.Value
				}
			}

			if withDefault {
				if translations := i18n.Translations[Default]; translations != nil {
					if t := translations[key]; t != nil {
						return t.Value
					}
				}
			}

			return ""
		})

		var getPrimaryLocale = func(context *admin.Context) string {
			if locale := context.Request.Form.Get("primary_locale"); locale != "" {
				return locale
			}
			if availableLocales := getAvailableLocales(context.Request, context.CurrentUser); len(availableLocales) > 0 {
				return availableLocales[0]
			}
			return ""
		}

		var getEditingLocale = func(context *admin.Context) string {
			if locale := context.Request.Form.Get("to_locale"); locale != "" {
				return locale
			}
			return getLocaleFromContext(context.Context)
		}

		res.GetAdmin().RegisterFuncMap("i18n_available_keys", func(context *admin.Context) (keys []string) {
			var (
				keysMap       = map[string]bool{}
				keyword       = strings.ToLower(context.Request.URL.Query().Get("keyword"))
				primaryLocale = getPrimaryLocale(context)
				editingLocale = getEditingLocale(context)
			)

			var filterTranslations = func(translations map[string]*Translation) {
				if translations != nil {
					for key, translation := range translations {
						if (keyword == "") || (strings.Index(strings.ToLower(translation.Key), keyword) != -1 ||
							strings.Index(strings.ToLower(translation.Value), keyword) != -1) {
							if _, ok := keysMap[key]; !ok {
								keysMap[key] = true
								keys = append(keys, key)
							}
						}
					}
				}
			}

			filterTranslations(i18n.Translations[getPrimaryLocale(context)])
			if primaryLocale != editingLocale {
				filterTranslations(i18n.Translations[getEditingLocale(context)])
			}

			sort.Strings(keys)

			pagination := context.Searcher.Pagination
			pagination.Total = len(keys)
			pagination.PrePage = 25
			pagination.CurrentPage, _ = strconv.Atoi(context.Request.URL.Query().Get("page"))
			if pagination.CurrentPage == 0 {
				pagination.CurrentPage = 1
			}
			if pagination.CurrentPage > 0 {
				pagination.Pages = pagination.Total / pagination.PrePage
			}
			context.Searcher.Pagination = pagination

			if pagination.CurrentPage == -1 {
				return keys
			}

			lastIndex := pagination.CurrentPage * pagination.PrePage
			if pagination.Total < lastIndex {
				lastIndex = pagination.Total
			}

			startIndex := (pagination.CurrentPage - 1) * pagination.PrePage
			if lastIndex >= startIndex {
				return keys[startIndex:lastIndex]
			}
			return []string{}
		})

		res.GetAdmin().RegisterFuncMap("i18n_primary_locale", getPrimaryLocale)

		res.GetAdmin().RegisterFuncMap("i18n_editing_locale", getEditingLocale)

		res.GetAdmin().RegisterFuncMap("i18n_viewable_locales", func(context admin.Context) []string {
			return getAvailableLocales(context.Request, context.CurrentUser)
		})

		res.GetAdmin().RegisterFuncMap("i18n_editable_locales", func(context admin.Context) []string {
			return getEditableLocales(context.Request, context.CurrentUser)
		})

		controller := i18nController{i18n}
		router := res.GetAdmin().GetRouter()
		router.Get(res.ToParam(), controller.Index)
		router.Post(res.ToParam(), controller.Update)
		router.Put(res.ToParam(), controller.Update)

		admin.RegisterViewPath("github.com/qor/i18n/views")
	}
}
<|endoftext|>


<commit_before>package slack

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
)

type ID interface {
	String() string
	Equals(ID) bool
	Valid() bool
}

type UserID struct {
	UserID string
	TeamID string
}

// SecureID is an opaque, deterministic representation of a Slack user identity
// that can be used in place of UserID to reduce the risk of compromising
// a user's real identity.
//
// A SecureID can be constructed from a UserID
// by calling UserID.Secure()
type SecureID struct {
	HashSum string
}

// Equals indicates if id and o represent the same user identity.
func (id UserID) Equals(o ID) bool {
	switch o := o.(type) {
	case UserID:
		if !(id.Valid() && o.Valid()) {
			return false
		}

		// > Notice that user IDs are not guaranteed to be globally unique across all Slack users.
		// > The combination of user ID and team ID, on the other hand, is guaranteed to be globally unique.
		//
		// - Slack API documentation
		return id.UserID == o.UserID && id.TeamID == o.TeamID
	case SecureID:
		return id.Secure().Equals(o)
	}

	return false
}

func (id UserID) Valid() bool {
	return id.UserID != "" && id.TeamID != ""
}

func (id UserID) String() string {
	return id.TeamID + "." + id.UserID
}

// Secure converts id into a SecureID.
func (id UserID) Secure() SecureID {
	if !id.Valid() {
		return SecureID{}
	}
	var buf bytes.Buffer

	buf.WriteString(id.TeamID)
	buf.WriteRune('.')
	buf.WriteString(id.UserID)

	h := sha256.New()
	h.Write(buf.Bytes())
	s := hex.EncodeToString(h.Sum(nil))

	return SecureID{
		HashSum: s,
	}
}

// Equals indicates if id and o represent the same user identity.
func (id SecureID) Equals(o ID) bool {
	switch o := o.(type) {
	case SecureID:
		if !id.Valid() || !o.Valid() {
			return false
		}

		return id.HashSum == o.HashSum
	case UserID:
		return o.Secure().Equals(id)
	}
	return false
}

func (id SecureID) Valid() bool {
	return id.HashSum != ""
}

func (id SecureID) String() string {
	return id.HashSum
}
<commit_msg>Change formatting<commit_after>package slack

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
)

type ID interface {
	String() string
	Equals(ID) bool
	Valid() bool
}

type UserID struct {
	UserID string
	TeamID string
}

// SecureID is an opaque, deterministic representation of a Slack user identity
// that can be used in place of UserID to reduce the risk of compromising
// a user's real identity.
//
// A SecureID can be constructed from a UserID
// by calling UserID.Secure()
type SecureID struct {
	HashSum string
}

// Equals indicates if id and o represent the same user identity.
func (id UserID) Equals(o ID) bool {
	switch o := o.(type) {
	case UserID:
		if !(id.Valid() && o.Valid()) {
			return false
		}

		// > Notice that user IDs are not guaranteed to be globally unique across all Slack users.
		// > The combination of user ID and team ID, on the other hand, is guaranteed to be globally unique.
		//
		// - Slack API documentation
		return id.UserID == o.UserID && id.TeamID == o.TeamID
	case SecureID:
		return id.Secure().Equals(o)
	}

	return false
}

func (id UserID) Valid() bool {
	return id.UserID != "" && id.TeamID != ""
}

func (id UserID) String() string {
	return id.TeamID + "." + id.UserID
}

// Secure converts id into a SecureID.
func (id UserID) Secure() SecureID {
	if !id.Valid() {
		return SecureID{}
	}

	var buf bytes.Buffer
	buf.WriteString(id.TeamID)
	buf.WriteRune('.')
	buf.WriteString(id.UserID)

	h := sha256.New()
	h.Write(buf.Bytes())
	s := hex.EncodeToString(h.Sum(nil))

	return SecureID{
		HashSum: s,
	}
}

// Equals indicates if id and o represent the same user identity.
func (id SecureID) Equals(o ID) bool {
	switch o := o.(type) {
	case SecureID:
		if !id.Valid() || !o.Valid() {
			return false
		}

		return id.HashSum == o.HashSum
	case UserID:
		return o.Secure().Equals(id)
	}
	return false
}

func (id SecureID) Valid() bool {
	return id.HashSum != ""
}

func (id SecureID) String() string {
	return id.HashSum
}
<|endoftext|>


<commit_before>package influxql

import (
	"errors"
	"fmt"

	"github.com/influxdata/influxql"
	"github.com/influxdata/platform/query"
	"github.com/influxdata/platform/query/execute"
	"github.com/influxdata/platform/query/functions"
)

// createFunctionCursor creates a new cursor that calls a function on one of the columns
// and returns the result.
func createFunctionCursor(t *transpilerState, call *influxql.Call, in cursor) (cursor, error) {
	cur := &functionCursor{
		call:   call,
		parent: in,
	}
	switch call.Name {
	case "mean":
		value, ok := in.Value(call.Args[0])
		if !ok {
			return nil, fmt.Errorf("undefined variable: %s", call.Args[0])
		}
		cur.id = t.op("mean", &functions.MeanOpSpec{
			AggregateConfig: execute.AggregateConfig{
				Columns: []string{value},
				TimeSrc: execute.DefaultStartColLabel,
				TimeDst: execute.DefaultTimeColLabel,
			},
		}, in.ID())
		cur.value = value
		cur.exclude = map[influxql.Expr]struct{}{call.Args[0]: {}}
	case "max":
		value, ok := in.Value(call.Args[0])
		if !ok {
			return nil, fmt.Errorf("undefined variable: %s", call.Args[0])
		}
		cur.id = t.op("max", &functions.MaxOpSpec{
			SelectorConfig: execute.SelectorConfig{
				Column: value,
			},
		}, in.ID())
		cur.value = value
		cur.exclude = map[influxql.Expr]struct{}{call.Args[0]: {}}
	default:
		return nil, errors.New("unimplemented")
	}
	return cur, nil
}

type functionCursor struct {
	id      query.OperationID
	call    *influxql.Call
	value   string
	exclude map[influxql.Expr]struct{}
	parent  cursor
}

func (c *functionCursor) ID() query.OperationID {
	return c.id
}

func (c *functionCursor) Keys() []influxql.Expr {
	keys := []influxql.Expr{c.call}
	if a := c.parent.Keys(); len(a) > 0 {
		for _, e := range a {
			if _, ok := c.exclude[e]; ok {
				continue
			}
			keys = append(keys, e)
		}
	}
	return keys
}

func (c *functionCursor) Value(expr influxql.Expr) (string, bool) {
	if expr == c.call {
		return c.value, true
	} else if _, ok := c.exclude[expr]; ok {
		return "", false
	}
	return c.parent.Value(expr)
}
<commit_msg>fix(transpiler): Include function name in error<commit_after>package influxql

import (
	"fmt"

	"github.com/influxdata/influxql"
	"github.com/influxdata/platform/query"
	"github.com/influxdata/platform/query/execute"
	"github.com/influxdata/platform/query/functions"
)

// createFunctionCursor creates a new cursor that calls a function on one of the columns
// and returns the result.
func createFunctionCursor(t *transpilerState, call *influxql.Call, in cursor) (cursor, error) {
	cur := &functionCursor{
		call:   call,
		parent: in,
	}
	switch call.Name {
	case "mean":
		value, ok := in.Value(call.Args[0])
		if !ok {
			return nil, fmt.Errorf("undefined variable: %s", call.Args[0])
		}
		cur.id = t.op("mean", &functions.MeanOpSpec{
			AggregateConfig: execute.AggregateConfig{
				Columns: []string{value},
				TimeSrc: execute.DefaultStartColLabel,
				TimeDst: execute.DefaultTimeColLabel,
			},
		}, in.ID())
		cur.value = value
		cur.exclude = map[influxql.Expr]struct{}{call.Args[0]: {}}
	case "max":
		value, ok := in.Value(call.Args[0])
		if !ok {
			return nil, fmt.Errorf("undefined variable: %s", call.Args[0])
		}
		cur.id = t.op("max", &functions.MaxOpSpec{
			SelectorConfig: execute.SelectorConfig{
				Column: value,
			},
		}, in.ID())
		cur.value = value
		cur.exclude = map[influxql.Expr]struct{}{call.Args[0]: {}}
	default:
		return nil, fmt.Errorf("unimplemented function: %q", call.Name)
	}
	return cur, nil
}

type functionCursor struct {
	id      query.OperationID
	call    *influxql.Call
	value   string
	exclude map[influxql.Expr]struct{}
	parent  cursor
}

func (c *functionCursor) ID() query.OperationID {
	return c.id
}

func (c *functionCursor) Keys() []influxql.Expr {
	keys := []influxql.Expr{c.call}
	if a := c.parent.Keys(); len(a) > 0 {
		for _, e := range a {
			if _, ok := c.exclude[e]; ok {
				continue
			}
			keys = append(keys, e)
		}
	}
	return keys
}

func (c *functionCursor) Value(expr influxql.Expr) (string, bool) {
	if expr == c.call {
		return c.value, true
	} else if _, ok := c.exclude[expr]; ok {
		return "", false
	}
	return c.parent.Value(expr)
}
<|endoftext|>


<commit_before>// Package embedded defines embedded data types that are shared between the go.rice package and generated code.
package embedded

import (
	"fmt"
	"path/filepath"
	"strings"
	"time"
)

const (
	EmbedTypeGo   = 0
	EmbedTypeSyso = 1
)

// EmbeddedBox defines an embedded box
type EmbeddedBox struct {
	Name      string                   // box name
	Time      time.Time                // embed time
	EmbedType int                      // kind of embedding
	Files     map[string]*EmbeddedFile // ALL embedded files by full path
	Dirs      map[string]*EmbeddedDir  // ALL embedded dirs by full path
}

// Link creates the ChildDirs and ChildFiles links in all EmbeddedDir's
func (e *EmbeddedBox) Link() {
	for path, ed := range e.Dirs {
		fmt.Println(path)
		ed.ChildDirs = make([]*EmbeddedDir, 0)
		ed.ChildFiles = make([]*EmbeddedFile, 0)
	}
	for path, ed := range e.Dirs {
		parentDirpath, _ := filepath.Split(path)
		if strings.HasSuffix(parentDirpath, "/") {
			parentDirpath = parentDirpath[:len(parentDirpath)-1]
		}
		parentDir := e.Dirs[parentDirpath]
		if parentDir == nil {
			panic("parentDir `" + parentDirpath + "` is missing in embedded box")
		}
		parentDir.ChildDirs = append(parentDir.ChildDirs, ed)
	}
	for path, ef := range e.Files {
		dirpath, _ := filepath.Split(path)
		if strings.HasSuffix(dirpath, "/") {
			dirpath = dirpath[:len(dirpath)-1]
		}
		dir := e.Dirs[dirpath]
		if dir == nil {
			panic("dir `" + dirpath + "` is missing in embedded box")
		}
		dir.ChildFiles = append(dir.ChildFiles, ef)
	}
}

// EmbeddedDir is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
type EmbeddedDir struct {
	Filename   string
	DirModTime time.Time
	ChildDirs  []*EmbeddedDir  // direct childs, as returned by virtualDir.Readdir()
	ChildFiles []*EmbeddedFile // direct childs, as returned by virtualDir.Readdir()
}

// EmbeddedFile is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
type EmbeddedFile struct {
	Filename    string // filename
	FileModTime time.Time
	Content     string
}

// EmbeddedBoxes is a public register of embedded boxes
var EmbeddedBoxes = make(map[string]*EmbeddedBox)

// RegisterEmbeddedBox registers an EmbeddedBox
func RegisterEmbeddedBox(name string, box *EmbeddedBox) {
	if _, exists := EmbeddedBoxes[name]; exists {
		panic(fmt.Sprintf("EmbeddedBox with name `%s` exists already", name))
	}
	EmbeddedBoxes[name] = box
}
<commit_msg>prevent recursive structure in embedded.EmbeddedBox.Link<commit_after>// Package embedded defines embedded data types that are shared between the go.rice package and generated code.
package embedded

import (
	"fmt"
	"path/filepath"
	"strings"
	"time"
)

const (
	EmbedTypeGo   = 0
	EmbedTypeSyso = 1
)

// EmbeddedBox defines an embedded box
type EmbeddedBox struct {
	Name      string                   // box name
	Time      time.Time                // embed time
	EmbedType int                      // kind of embedding
	Files     map[string]*EmbeddedFile // ALL embedded files by full path
	Dirs      map[string]*EmbeddedDir  // ALL embedded dirs by full path
}

// Link creates the ChildDirs and ChildFiles links in all EmbeddedDir's
func (e *EmbeddedBox) Link() {
	for path, ed := range e.Dirs {
		fmt.Println(path)
		ed.ChildDirs = make([]*EmbeddedDir, 0)
		ed.ChildFiles = make([]*EmbeddedFile, 0)
	}
	for path, ed := range e.Dirs {
		// skip for root, it'll create a recursion
		if path == "" {
			continue
		}
		parentDirpath, _ := filepath.Split(path)
		if strings.HasSuffix(parentDirpath, "/") {
			parentDirpath = parentDirpath[:len(parentDirpath)-1]
		}
		parentDir := e.Dirs[parentDirpath]
		if parentDir == nil {
			panic("parentDir `" + parentDirpath + "` is missing in embedded box")
		}
		parentDir.ChildDirs = append(parentDir.ChildDirs, ed)
	}
	for path, ef := range e.Files {
		dirpath, _ := filepath.Split(path)
		if strings.HasSuffix(dirpath, "/") {
			dirpath = dirpath[:len(dirpath)-1]
		}
		dir := e.Dirs[dirpath]
		if dir == nil {
			panic("dir `" + dirpath + "` is missing in embedded box")
		}
		dir.ChildFiles = append(dir.ChildFiles, ef)
	}
}

// EmbeddedDir is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
type EmbeddedDir struct {
	Filename   string
	DirModTime time.Time
	ChildDirs  []*EmbeddedDir  // direct childs, as returned by virtualDir.Readdir()
	ChildFiles []*EmbeddedFile // direct childs, as returned by virtualDir.Readdir()
}

// EmbeddedFile is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
type EmbeddedFile struct {
	Filename    string // filename
	FileModTime time.Time
	Content     string
}

// EmbeddedBoxes is a public register of embedded boxes
var EmbeddedBoxes = make(map[string]*EmbeddedBox)

// RegisterEmbeddedBox registers an EmbeddedBox
func RegisterEmbeddedBox(name string, box *EmbeddedBox) {
	if _, exists := EmbeddedBoxes[name]; exists {
		panic(fmt.Sprintf("EmbeddedBox with name `%s` exists already", name))
	}
	EmbeddedBoxes[name] = box
}
<|endoftext|>


<commit_before>package chain

import (
	"time"

	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
	"github.com/roasbeef/btcwallet/waddrmgr"
	"github.com/roasbeef/btcwallet/wtxmgr"
)

// BackEnds returns a list of the available back ends.
// TODO: Refactor each into a driver and use dynamic registration.
func BackEnds() []string {
	return []string{
		"bitcoind",
		"btcd",
		"neutrino",
	}
}

// Interface allows more than one backing blockchain source, such as a
// btcd RPC chain server, or an SPV library, as long as we write a driver for
// it.
type Interface interface {
	Start() error
	Stop()
	WaitForShutdown()
	GetBestBlock() (*chainhash.Hash, int32, error)
	GetBlock(*chainhash.Hash) (*wire.MsgBlock, error)
	GetBlockHash(int64) (*chainhash.Hash, error)
	GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, error)
	BlockStamp() (*waddrmgr.BlockStamp, error)
	SendRawTransaction(*wire.MsgTx, bool) (*chainhash.Hash, error)
	Rescan(*chainhash.Hash, []btcutil.Address, []*wire.OutPoint) error
	NotifyReceived([]btcutil.Address) error
	NotifyBlocks() error
	Notifications() <-chan interface{}
	BackEnd() string
}

// Notification types.  These are defined here and processed from from reading
// a notificationChan to avoid handling these notifications directly in
// rpcclient callbacks, which isn't very Go-like and doesn't allow
// blocking client calls.
type (
	// ClientConnected is a notification for when a client connection is
	// opened or reestablished to the chain server.
	ClientConnected struct{}

	// BlockConnected is a notification for a newly-attached block to the
	// best chain.
	BlockConnected wtxmgr.BlockMeta

	// FilteredBlockConnected is an alternate notification that contains
	// both block and relevant transaction information in one struct, which
	// allows atomic updates.
	FilteredBlockConnected struct {
		Block       *wtxmgr.BlockMeta
		RelevantTxs []*wtxmgr.TxRecord
	}

	// BlockDisconnected is a notifcation that the block described by the
	// BlockStamp was reorganized out of the best chain.
	BlockDisconnected wtxmgr.BlockMeta

	// RelevantTx is a notification for a transaction which spends wallet
	// inputs or pays to a watched address.
	RelevantTx struct {
		TxRecord *wtxmgr.TxRecord
		Block    *wtxmgr.BlockMeta // nil if unmined
	}

	// RescanProgress is a notification describing the current status
	// of an in-progress rescan.
	RescanProgress struct {
		Hash   *chainhash.Hash
		Height int32
		Time   time.Time
	}

	// RescanFinished is a notification that a previous rescan request
	// has finished.
	RescanFinished struct {
		Hash   *chainhash.Hash
		Height int32
		Time   time.Time
	}
)
<commit_msg>chain/interface: add FilterBlocks query<commit_after>package chain

import (
	"time"

	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
	"github.com/roasbeef/btcwallet/waddrmgr"
	"github.com/roasbeef/btcwallet/wtxmgr"
)

// BackEnds returns a list of the available back ends.
// TODO: Refactor each into a driver and use dynamic registration.
func BackEnds() []string {
	return []string{
		"bitcoind",
		"btcd",
		"neutrino",
	}
}

// Interface allows more than one backing blockchain source, such as a
// btcd RPC chain server, or an SPV library, as long as we write a driver for
// it.
type Interface interface {
	Start() error
	Stop()
	WaitForShutdown()
	GetBestBlock() (*chainhash.Hash, int32, error)
	GetBlock(*chainhash.Hash) (*wire.MsgBlock, error)
	GetBlockHash(int64) (*chainhash.Hash, error)
	GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, error)
	FilterBlocks(*FilterBlocksRequest) (*FilterBlocksResponse, error)
	BlockStamp() (*waddrmgr.BlockStamp, error)
	SendRawTransaction(*wire.MsgTx, bool) (*chainhash.Hash, error)
	Rescan(*chainhash.Hash, []btcutil.Address, []*wire.OutPoint) error
	NotifyReceived([]btcutil.Address) error
	NotifyBlocks() error
	Notifications() <-chan interface{}
	BackEnd() string
}

// Notification types.  These are defined here and processed from from reading
// a notificationChan to avoid handling these notifications directly in
// rpcclient callbacks, which isn't very Go-like and doesn't allow
// blocking client calls.
type (
	// ClientConnected is a notification for when a client connection is
	// opened or reestablished to the chain server.
	ClientConnected struct{}

	// BlockConnected is a notification for a newly-attached block to the
	// best chain.
	BlockConnected wtxmgr.BlockMeta

	// FilteredBlockConnected is an alternate notification that contains
	// both block and relevant transaction information in one struct, which
	// allows atomic updates.
	FilteredBlockConnected struct {
		Block       *wtxmgr.BlockMeta
		RelevantTxs []*wtxmgr.TxRecord
	}

	// FilterBlocksRequest specifies a range of blocks and the set of
	// internal and external addresses of interest, indexed by corresponding
	// scoped-index of the child address. A global set of watched outpoints
	// is also included to monitor for spends.
	FilterBlocksRequest struct {
		Blocks           []wtxmgr.BlockMeta
		ExternalAddrs    map[waddrmgr.ScopedIndex]btcutil.Address
		InternalAddrs    map[waddrmgr.ScopedIndex]btcutil.Address
		WatchedOutPoints map[wire.OutPoint]struct{}
	}

	// FilterBlocksResponse reports the set of all internal and external
	// addresses found in response to a FilterBlockRequest, any outpoints
	// found that correspond to those addresses, as well as the relevant
	// transactions that can modify the wallet's balance. The index of the
	// block within the FilterBlocksRequest is returned, such that the
	// caller can reinitiate a request for the subsequent block after
	// updating the addresses of interest.
	FilterBlocksResponse struct {
		BatchIndex         uint32
		BlockMeta          wtxmgr.BlockMeta
		FoundExternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{}
		FoundInternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{}
		FoundOutPoints     map[wire.OutPoint]struct{}
		RelevantTxns       []*wire.MsgTx
	}

	// BlockDisconnected is a notifcation that the block described by the
	// BlockStamp was reorganized out of the best chain.
	BlockDisconnected wtxmgr.BlockMeta

	// RelevantTx is a notification for a transaction which spends wallet
	// inputs or pays to a watched address.
	RelevantTx struct {
		TxRecord *wtxmgr.TxRecord
		Block    *wtxmgr.BlockMeta // nil if unmined
	}

	// RescanProgress is a notification describing the current status
	// of an in-progress rescan.
	RescanProgress struct {
		Hash   *chainhash.Hash
		Height int32
		Time   time.Time
	}

	// RescanFinished is a notification that a previous rescan request
	// has finished.
	RescanFinished struct {
		Hash   *chainhash.Hash
		Height int32
		Time   time.Time
	}
)
<|endoftext|>


<commit_before>// +build e2e

/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package runtime

import (
	"testing"

	v1 "knative.dev/serving/pkg/apis/serving/v1"
	testingv1 "knative.dev/serving/pkg/testing/v1"
	"knative.dev/serving/test"
)

func withWorkingDir(wd string) testingv1.ServiceOption {
	return func(svc *v1.Service) {
		svc.Spec.Template.Spec.Containers[0].WorkingDir = wd
	}
}

func TestWorkingDirService(t *testing.T) {
	t.Parallel()
	clients := test.Setup(t)

	const wd = "/foo/bar/baz"

	_, ri, err := fetchRuntimeInfo(t, clients, withWorkingDir(wd))
	if err != nil {
		t.Fatal("Failed to fetch runtime info:", err)
	}

	if ri.Host.User.Cwd.Directory != wd {
		t.Errorf("cwd = %s, want %s, error=%s", ri.Host.User.Cwd, wd, ri.Host.User.Cwd.Error)
	}
}
<commit_msg>Use existing directory for working directory test (#12079)<commit_after>// +build e2e

/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package runtime

import (
	"testing"

	v1 "knative.dev/serving/pkg/apis/serving/v1"
	testingv1 "knative.dev/serving/pkg/testing/v1"
	"knative.dev/serving/test"
)

func withWorkingDir(wd string) testingv1.ServiceOption {
	return func(svc *v1.Service) {
		svc.Spec.Template.Spec.Containers[0].WorkingDir = wd
	}
}

func TestWorkingDirService(t *testing.T) {
	t.Parallel()
	clients := test.Setup(t)

	// An existing directory inside the test image but different from
	// the default working directory /home/nonroot.
	const wd = "/tmp"

	_, ri, err := fetchRuntimeInfo(t, clients, withWorkingDir(wd))
	if err != nil {
		t.Fatal("Failed to fetch runtime info:", err)
	}

	if ri.Host.User.Cwd.Directory != wd {
		t.Errorf("cwd = %s, want %s, error=%s", ri.Host.User.Cwd, wd, ri.Host.User.Cwd.Error)
	}
}
<|endoftext|>


<commit_before>package operators

import (
	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	s "github.com/onsi/gomega/gstruct"
	t "github.com/onsi/gomega/types"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/kube-openapi/pkg/util/sets"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	config "github.com/openshift/api/config/v1"
	configclient "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
)

var _ = g.Describe("[Feature:Platform] ClusterOperators", func() {
	defer g.GinkgoRecover()

	var clusterOperators []config.ClusterOperator
	whitelistNoNamespace := sets.NewString(
		"cloud-credential",
		"image-registry",
		"machine-api",
		"marketplace",
		"network",
		"operator-lifecycle-manager",
		"operator-lifecycle-manager-catalog",
		"storage",
		"support",
	)
	whitelistNoOperatorConfig := sets.NewString(
		"cloud-credential",
		"cluster-autoscaler",
		"machine-api",
		"machine-config",
		"marketplace",
		"network",
		"node-tuning",
		"operator-lifecycle-manager",
		"operator-lifecycle-manager-catalog",
		"storage",
		"support",
	)

	g.BeforeEach(func() {
		kubeConfig, err := e2e.LoadConfig()
		o.Expect(err).ToNot(o.HaveOccurred())
		configClient, err := configclient.NewForConfig(kubeConfig)
		o.Expect(err).ToNot(o.HaveOccurred())
		clusterOperatorsList, err := configClient.ClusterOperators().List(metav1.ListOptions{})
		o.Expect(err).ToNot(o.HaveOccurred())
		clusterOperators = clusterOperatorsList.Items
	})

	g.Context("should define", func() {
		g.Specify("at least one namespace in their lists of related objects", func() {
			for _, clusterOperator := range clusterOperators {
				if !whitelistNoNamespace.Has(clusterOperator.Name) {
					o.Expect(clusterOperator.Status.RelatedObjects).To(o.ContainElement(isNamespace()), "ClusterOperator: %s", clusterOperator.Name)
				}
			}
		})
		g.Specify("at least one related object that is not a namespace", func() {
			for _, clusterOperator := range clusterOperators {
				if !whitelistNoOperatorConfig.Has(clusterOperator.Name) {
					o.Expect(clusterOperator.Status.RelatedObjects).To(o.ContainElement(o.Not(isNamespace())), "ClusterOperator: %s", clusterOperator.Name)
				}
			}
		})

	})
})

func isNamespace() t.GomegaMatcher {
	return s.MatchFields(s.IgnoreExtras|s.IgnoreMissing, s.Fields{
		"Resource": o.Equal("namespaces"),
		"Group":    o.Equal(""),
	})
}
<commit_msg>e2e: BZ1717739: ClusterOperators should specify related objects<commit_after>package operators

import (
	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	s "github.com/onsi/gomega/gstruct"
	t "github.com/onsi/gomega/types"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/kube-openapi/pkg/util/sets"
	e2e "k8s.io/kubernetes/test/e2e/framework"

	config "github.com/openshift/api/config/v1"
	configclient "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
)

var _ = g.Describe("[Feature:Platform] ClusterOperators", func() {
	defer g.GinkgoRecover()

	var clusterOperators []config.ClusterOperator
	whitelistNoNamespace := sets.NewString(
		"cloud-credential",
		"image-registry",
		"machine-api",
		"marketplace",
		"network",
		"operator-lifecycle-manager",
		"operator-lifecycle-manager-catalog",
		"storage",
		"support",
	)
	whitelistNoOperatorConfig := sets.NewString(
		"cloud-credential",
		"cluster-autoscaler",
		"machine-api",
		"machine-config",
		"marketplace",
		"network",
		"operator-lifecycle-manager",
		"operator-lifecycle-manager-catalog",
		"storage",
		"support",
	)

	g.BeforeEach(func() {
		kubeConfig, err := e2e.LoadConfig()
		o.Expect(err).ToNot(o.HaveOccurred())
		configClient, err := configclient.NewForConfig(kubeConfig)
		o.Expect(err).ToNot(o.HaveOccurred())
		clusterOperatorsList, err := configClient.ClusterOperators().List(metav1.ListOptions{})
		o.Expect(err).ToNot(o.HaveOccurred())
		clusterOperators = clusterOperatorsList.Items
	})

	g.Context("should define", func() {
		g.Specify("at least one namespace in their lists of related objects", func() {
			for _, clusterOperator := range clusterOperators {
				if !whitelistNoNamespace.Has(clusterOperator.Name) {
					o.Expect(clusterOperator.Status.RelatedObjects).To(o.ContainElement(isNamespace()), "ClusterOperator: %s", clusterOperator.Name)
				}
			}
		})
		g.Specify("at least one related object that is not a namespace", func() {
			for _, clusterOperator := range clusterOperators {
				if !whitelistNoOperatorConfig.Has(clusterOperator.Name) {
					o.Expect(clusterOperator.Status.RelatedObjects).To(o.ContainElement(o.Not(isNamespace())), "ClusterOperator: %s", clusterOperator.Name)
				}
			}
		})

	})
})

func isNamespace() t.GomegaMatcher {
	return s.MatchFields(s.IgnoreExtras|s.IgnoreMissing, s.Fields{
		"Resource": o.Equal("namespaces"),
		"Group":    o.Equal(""),
	})
}
<|endoftext|>