<commit_before>package dexcom

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/ecc1/ble"
)

type bleConn struct {
	conn *ble.Connection
	tx   ble.Characteristic
	rx   chan byte
}

const (
	// maximum size of writes to GATT characteristics
	gattMTU = 20
)

func (conn *bleConn) Send(data []byte) error {
	// Dexcom G4 Share expects each BLE message to start with two 01 bytes.
	data = append([]byte{0x01, 0x01}, data...)
	for {
		n := len(data)
		if n == 0 {
			return nil
		}
		if n > gattMTU {
			n = gattMTU
		}
		err := conn.tx.WriteValue(data[:n])
		if err != nil {
			return err
		}
		data = data[n:]
	}
}

func (conn *bleConn) Receive(data []byte) error {
	const receiveTimeout = 5 * time.Second
	for i := 0; i < len(data); i++ {
		select {
		case b := <-conn.rx:
			data[i] = b
		case <-time.After(receiveTimeout):
			return fmt.Errorf("BLE receive timeout")
		}
	}
	return nil
}

var (
	receiverName    = "DEXCOMRX"
	receiverService = dexcomUUID(0xa0b1)
)

func connect(conn *ble.Connection) error {
	forceReauth := false
	device, err := conn.GetDevice(receiverService)
	if err != nil || !device.Connected() {
		forceReauth = true
		// Remove device to avoid "Software caused connection abort" error.
		device, err = conn.GetDeviceByName(receiverName)
		if err == nil {
			adapter, err := conn.GetAdapter()
			if err != nil {
				return err
			}
			err = adapter.RemoveDevice(device)
			if err != nil {
				return err
			}
		}
		device, err = conn.Discover(10*time.Second, receiverService)
		if err != nil {
			return err
		}
	}
	if !device.Connected() {
		forceReauth = true
		err = device.Connect()
		if err != nil {
			return err
		}
	}
	if !device.Paired() {
		forceReauth = true
		err = device.Pair()
		if err != nil {
			return err
		}
	}
	err = conn.Update()
	if err != nil {
		return err
	}
	return authenticate(device, forceReauth)
}

var (
	authentication = dexcomUUID(0xacac)
	authEnvVar     = "DEXCOM_CGM_ID"
	authCode       = []byte{}
)

func initAuthCode() error {
	if len(authCode) != 0 {
		return nil
	}
	id := os.Getenv(authEnvVar)
	if len(id) == 0 {
		return fmt.Errorf("%s environment variable is not set", authEnvVar)
	}
	if len(id) != 10 {
		return fmt.Errorf("%s environment variable must be 2 letters followed by 8 digits", authEnvVar)
	}
	authCode = []byte(id + "000000")
	return nil
}

func authenticate(device ble.Device, reauth bool) error {
	err := initAuthCode()
	if err != nil {
		return err
	}
	auth, err := device.Conn().GetCharacteristic(authentication)
	if err != nil {
		return err
	}
	if !reauth {
		data, err := auth.ReadValue()
		if err != nil {
			return err
		}
		if bytes.Equal(data, authCode) {
			return nil
		}
	}
	err = auth.WriteValue(authCode)
	if err != nil {
		return err
	}
	log.Printf("%s: authenticated", device.Name())
	return nil
}

var (
	heartbeat   = dexcomUUID(0x2b18)
	sendData    = dexcomUUID(0xb20a)
	receiveData = dexcomUUID(0xb20b)
)

func OpenBLE() (Connection, error) {
	conn, err := ble.Open()
	if err != nil {
		return nil, err
	}
	err = connect(conn)
	if err != nil {
		conn.Close()
		return nil, err
	}
	// We need to enable heartbeat notifications
	// or else we won't get any receiveData responses.
	err = conn.HandleNotify(heartbeat, func(data []byte) {})
	if err != nil {
		conn.Close()
		return nil, err
	}
	rx := make(chan byte, 1600)
	err = conn.HandleNotify(receiveData, func(data []byte) {
		for _, b := range data {
			rx <- b
		}
	})
	if err != nil {
		conn.Close()
		return nil, err
	}
	tx, err := conn.GetCharacteristic(sendData)
	if err != nil {
		conn.Close()
		return nil, err
	}
	return &bleConn{conn: conn, tx: tx, rx: rx}, nil
}

func dexcomUUID(id uint16) string {
	return fmt.Sprintf("f0ac%04x-ebfa-f96f-28da-076c35a521db", id)
}

func (conn *bleConn) Close() {
	conn.conn.Close()
}
<commit_msg>Simplify connect function<commit_after>package dexcom

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/ecc1/ble"
)

type bleConn struct {
	conn *ble.Connection
	tx   ble.Characteristic
	rx   chan byte
}

const (
	// maximum size of writes to GATT characteristics
	gattMTU = 20
)

func (conn *bleConn) Send(data []byte) error {
	// Dexcom G4 Share expects each BLE message to start with two 01 bytes.
	data = append([]byte{0x01, 0x01}, data...)
	for {
		n := len(data)
		if n == 0 {
			return nil
		}
		if n > gattMTU {
			n = gattMTU
		}
		err := conn.tx.WriteValue(data[:n])
		if err != nil {
			return err
		}
		data = data[n:]
	}
}

func (conn *bleConn) Receive(data []byte) error {
	const receiveTimeout = 5 * time.Second
	for i := 0; i < len(data); i++ {
		select {
		case b := <-conn.rx:
			data[i] = b
		case <-time.After(receiveTimeout):
			return fmt.Errorf("BLE receive timeout")
		}
	}
	return nil
}

var (
	receiverName    = "DEXCOMRX"
	receiverService = dexcomUUID(0xa0b1)
)

func connect(conn *ble.Connection) error {
	device, err := findDevice(conn)
	reauth := false
	if !device.Connected() {
		reauth = true
		err = device.Connect()
		if err != nil {
			return err
		}
	}
	if !device.Paired() {
		reauth = true
		err = device.Pair()
		if err != nil {
			return err
		}
	}
	err = conn.Update()
	if err != nil {
		return err
	}
	return authenticate(device, reauth)
}

func findDevice(conn *ble.Connection) (ble.Device, error) {
	device, err := conn.GetDevice(receiverService)
	if err == nil && device.Connected() {
		return device, nil
	}
	// Remove device to avoid "Software caused connection abort" error.
	device, err = conn.GetDeviceByName(receiverName)
	if err == nil {
		adapter, err := conn.GetAdapter()
		if err != nil {
			return nil, err
		}
		err = adapter.RemoveDevice(device)
		if err != nil {
			return nil, err
		}
	}
	return conn.Discover(10*time.Second, receiverService)
}

var (
	authentication = dexcomUUID(0xacac)
	authEnvVar     = "DEXCOM_CGM_ID"
	authCode       []byte
)

func initAuthCode() error {
	if len(authCode) != 0 {
		return nil
	}
	id := os.Getenv(authEnvVar)
	if len(id) == 0 {
		return fmt.Errorf("%s environment variable is not set", authEnvVar)
	}
	if len(id) != 10 {
		return fmt.Errorf("%s environment variable must be 2 letters followed by 8 digits", authEnvVar)
	}
	authCode = []byte(id + "000000")
	return nil
}

func authenticate(device ble.Device, reauth bool) error {
	err := initAuthCode()
	if err != nil {
		return err
	}
	auth, err := device.Conn().GetCharacteristic(authentication)
	if err != nil {
		return err
	}
	if !reauth {
		data, err := auth.ReadValue()
		if err != nil {
			return err
		}
		if bytes.Equal(data, authCode) {
			return nil
		}
	}
	err = auth.WriteValue(authCode)
	if err != nil {
		return err
	}
	log.Printf("%s: authenticated", device.Name())
	return nil
}

var (
	heartbeat   = dexcomUUID(0x2b18)
	sendData    = dexcomUUID(0xb20a)
	receiveData = dexcomUUID(0xb20b)
)

func OpenBLE() (Connection, error) {
	conn, err := ble.Open()
	if err != nil {
		return nil, err
	}
	err = connect(conn)
	if err != nil {
		conn.Close()
		return nil, err
	}
	// We need to enable heartbeat notifications
	// or else we won't get any receiveData responses.
	err = conn.HandleNotify(heartbeat, func(data []byte) {})
	if err != nil {
		conn.Close()
		return nil, err
	}
	rx := make(chan byte, 1600)
	err = conn.HandleNotify(receiveData, func(data []byte) {
		for _, b := range data {
			rx <- b
		}
	})
	if err != nil {
		conn.Close()
		return nil, err
	}
	tx, err := conn.GetCharacteristic(sendData)
	if err != nil {
		conn.Close()
		return nil, err
	}
	return &bleConn{conn: conn, tx: tx, rx: rx}, nil
}

func dexcomUUID(id uint16) string {
	return fmt.Sprintf("f0ac%04x-ebfa-f96f-28da-076c35a521db", id)
}

func (conn *bleConn) Close() {
	conn.conn.Close()
}
<|endoftext|>


<commit_before>package scipipe

import (
	"fmt"
	"os/exec"
	re "regexp"
	str "strings"
	// "time"
	"errors"
)

type ShellTask struct {
	task
	_OutOnly     bool
	InPorts      map[string]chan *FileTarget
	InPaths      map[string]string
	OutPorts     map[string]chan *FileTarget
	OutPathFuncs map[string]func() string
	Command      string
}

func NewShellTask(command string) *ShellTask {
	return &ShellTask{
		Command:      command,
		InPorts:      make(map[string]chan *FileTarget),
		InPaths:      make(map[string]string),
		OutPorts:     make(map[string]chan *FileTarget),
		OutPathFuncs: make(map[string]func() string),
	}
}

func Sh(cmd string) *ShellTask {
	// Create task
	t := NewShellTask(cmd)

	// Find in/out port names, and set up in port lists
	r, err := re.Compile("{(o|i):([^{}:]+)}")
	Check(err)
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		if len(m) < 3 {
			Check(errors.New("Too few matches"))
		}
		typ := m[1]
		name := m[2]
		if typ == "o" {
			t.OutPorts[name] = make(chan *FileTarget, BUFSIZE)
		} // else if typ == "i" {
		// Set up a channel on the inports, even though this is
		// often replaced by another tasks output port channel.
		// It might be nice to have it init'ed with a channel
		// anyways, for use cases when we want to send FileTargets
		// on the inport manually.
		// t.InPorts[name] = make(chan *FileTarget, BUFSIZE)
		// }
	}
	return t
}

func (t *ShellTask) Run() {
	fmt.Println("Entering task: ", t.Command)
	// Close output channels
	for _, ochan := range t.OutPorts {
		defer close(ochan)
	}

	// Main loop
	breakLoop := false
	for !breakLoop {
		breakMainLoopPrematurely := false

		// If there are no inports, we know we should exit the loop
		// directly after executing the command, and sending the outputs
		if len(t.InPorts) == 0 {
			breakLoop = true
		}

		// Read input targets on in-ports and set up path mappings
		for iname, ichan := range t.InPorts {
			infile, open := <-ichan
			if !open {
				breakMainLoopPrematurely = true
				continue
			}
			fmt.Println("Receiving file:", infile.GetPath())
			t.InPaths[iname] = infile.GetPath()
		}
		if breakMainLoopPrematurely {
			break
		}

		// Execute command
		t.formatAndExecute(t.Command)

		// Send output targets on out ports
		for oname, ochan := range t.OutPorts {
			fun := t.OutPathFuncs[oname]
			baseName := fun()
			ft := NewFileTarget(baseName)
			fmt.Println("Sending file:  ", ft.GetPath())
			ochan <- ft
		}
		fmt.Println("Exiting task:  ", t.Command)
	}
}

func (t *ShellTask) formatAndExecute(cmd string) {
	cmd = t.ReplacePlaceholdersInCmd(cmd)
	fmt.Println("Executing cmd: ", cmd)
	_, err := exec.Command("bash", "-c", cmd).Output()
	Check(err)
}

func (t *ShellTask) ReplacePlaceholdersInCmd(cmd string) string {
	r, err := re.Compile("{(o|i):([^{}:]+)}")
	Check(err)
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		whole := m[0]
		typ := m[1]
		name := m[2]
		var newstr string
		if typ == "o" {
			if t.OutPathFuncs[name] != nil {
				newstr = t.OutPathFuncs[name]()
			} else {
				msg := fmt.Sprint("Missing outpath function for outport '", name, "' of shell task '", t.Command, "'")
				Check(errors.New(msg))
			}
		} else if typ == "i" {
			if t.InPaths[name] != "" {
				newstr = t.InPaths[name]
			} else {
				msg := fmt.Sprint("Missing inpath for inport '", name, "' of shell task '", t.Command, "'")
				Check(errors.New(msg))
			}
		}
		if newstr == "" {
			msg := fmt.Sprint("Replace failed for port ", name, " in task '", t.Command, "'")
			Check(errors.New(msg))
		}
		cmd = str.Replace(cmd, whole, newstr, -1)
	}
	return cmd
}

func (t *ShellTask) GetInPath(inPort string) string {
	var inPath string
	if t.InPaths[inPort] != "" {
		inPath = t.InPaths[inPort]
	} else {
		msg := fmt.Sprint("Missing inpath for inport '", inPort, "' of shell task '", t.Command, "'")
		Check(errors.New(msg))
	}
	return inPath
}
<commit_msg>Refactoring: Break out receiveInputs() in t.Run()<commit_after>package scipipe

import (
	"fmt"
	"os/exec"
	re "regexp"
	str "strings"
	// "time"
	"errors"
)

type ShellTask struct {
	task
	_OutOnly     bool
	InPorts      map[string]chan *FileTarget
	InPaths      map[string]string
	OutPorts     map[string]chan *FileTarget
	OutPathFuncs map[string]func() string
	Command      string
}

func NewShellTask(command string) *ShellTask {
	return &ShellTask{
		Command:      command,
		InPorts:      make(map[string]chan *FileTarget),
		InPaths:      make(map[string]string),
		OutPorts:     make(map[string]chan *FileTarget),
		OutPathFuncs: make(map[string]func() string),
	}
}

func Sh(cmd string) *ShellTask {
	// Create task
	t := NewShellTask(cmd)

	// Find in/out port names, and set up in port lists
	r, err := re.Compile("{(o|i):([^{}:]+)}")
	Check(err)
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		if len(m) < 3 {
			Check(errors.New("Too few matches"))
		}
		typ := m[1]
		name := m[2]
		if typ == "o" {
			t.OutPorts[name] = make(chan *FileTarget, BUFSIZE)
		} // else if typ == "i" {
		// Set up a channel on the inports, even though this is
		// often replaced by another tasks output port channel.
		// It might be nice to have it init'ed with a channel
		// anyways, for use cases when we want to send FileTargets
		// on the inport manually.
		// t.InPorts[name] = make(chan *FileTarget, BUFSIZE)
		// }
	}
	return t
}

func (t *ShellTask) Run() {
	fmt.Println("Entering task: ", t.Command)
	// Close output channels
	for _, ochan := range t.OutPorts {
		defer close(ochan)
	}

	// Main loop
	breakLoop := false
	for !breakLoop {
		// If there are no inports, we know we should exit the loop
		// directly after executing the command, and sending the outputs
		if len(t.InPorts) == 0 {
			breakLoop = true
		}

		inPortsOpen := t.receiveInputs()
		if !inPortsOpen {
			break
		}

		// Execute command
		t.formatAndExecute(t.Command)

		// Send output targets on out ports
		for oname, ochan := range t.OutPorts {
			fun := t.OutPathFuncs[oname]
			baseName := fun()
			ft := NewFileTarget(baseName)
			fmt.Println("Sending file:  ", ft.GetPath())
			ochan <- ft
		}
		fmt.Println("Exiting task:  ", t.Command)
	}
}

func (t *ShellTask) receiveInputs() bool {
	inPortsOpen := true
	// Read input targets on in-ports and set up path mappings
	for iname, ichan := range t.InPorts {
		infile, open := <-ichan
		if !open {
			inPortsOpen = false
			continue
		}
		fmt.Println("Receiving file:", infile.GetPath())
		t.InPaths[iname] = infile.GetPath()
	}
	return inPortsOpen
}

func (t *ShellTask) formatAndExecute(cmd string) {
	cmd = t.ReplacePlaceholdersInCmd(cmd)
	fmt.Println("Executing cmd: ", cmd)
	_, err := exec.Command("bash", "-c", cmd).Output()
	Check(err)
}

func (t *ShellTask) ReplacePlaceholdersInCmd(cmd string) string {
	r, err := re.Compile("{(o|i):([^{}:]+)}")
	Check(err)
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		whole := m[0]
		typ := m[1]
		name := m[2]
		var newstr string
		if typ == "o" {
			if t.OutPathFuncs[name] != nil {
				newstr = t.OutPathFuncs[name]()
			} else {
				msg := fmt.Sprint("Missing outpath function for outport '", name, "' of shell task '", t.Command, "'")
				Check(errors.New(msg))
			}
		} else if typ == "i" {
			if t.InPaths[name] != "" {
				newstr = t.InPaths[name]
			} else {
				msg := fmt.Sprint("Missing inpath for inport '", name, "' of shell task '", t.Command, "'")
				Check(errors.New(msg))
			}
		}
		if newstr == "" {
			msg := fmt.Sprint("Replace failed for port ", name, " in task '", t.Command, "'")
			Check(errors.New(msg))
		}
		cmd = str.Replace(cmd, whole, newstr, -1)
	}
	return cmd
}

func (t *ShellTask) GetInPath(inPort string) string {
	var inPath string
	if t.InPaths[inPort] != "" {
		inPath = t.InPaths[inPort]
	} else {
		msg := fmt.Sprint("Missing inpath for inport '", inPort, "' of shell task '", t.Command, "'")
		Check(errors.New(msg))
	}
	return inPath
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/bosssauce/ponzu/content"
	"github.com/bosssauce/ponzu/management/editor"
)

//Config represents the confirgurable options of the system
type Config struct {
	content.Item
	editor editor.Editor

	Name            string   `json:"name"`
	Domain          string   `json:"domain"`
	AdminEmail      string   `json:"admin_email"`
	ClientSecret    string   `json:"client_secret"`
	Etag            string   `json:"etag"`
	CacheInvalidate []string `json:"cacheTAG"`
}

// ContentName partially implements editor.Editable
func (c *Config) ContentName() string { return c.Name }

// Editor partially implements editor.Editable
func (c *Config) Editor() *editor.Editor { return &c.editor }

// MarshalEditor writes a buffer of html to edit a Post and partially implements editor.Editable
func (c *Config) MarshalEditor() ([]byte, error) {
	view, err := editor.Form(c,
		editor.Field{
			View: editor.Input("Name", c, map[string]string{
				"label":       "Site Name",
				"placeholder": "Add a name to this site (internal use only)",
			}),
		},
		editor.Field{
			View: editor.Input("Domain", c, map[string]string{
				"label":       "Domain Name (required for SSL certificate)",
				"placeholder": "e.g. www.example.com or example.com",
			}),
		},
		editor.Field{
			View: editor.Input("AdminEmail", c, map[string]string{
				"label": "Adminstrator Email (will be notified of internal system information)",
			}),
		},
		editor.Field{
			View: editor.Input("ClientSecret", c, map[string]string{
				"label":    "Client Secret (used to validate requests, DO NOT SHARE)",
				"disabled": "true",
			}),
		},
		editor.Field{
			View: editor.Input("ClientSecret", c, map[string]string{
				"type": "hidden",
			}),
		},
		editor.Field{
			View: editor.Input("Etag", c, map[string]string{
				"label":    "Etag Header (used for static asset cache)",
				"disabled": "true",
			}),
		},
		editor.Field{
			View: editor.Input("Etag", c, map[string]string{
				"type": "hidden",
			}),
		},
		editor.Field{
			View: editor.Checkbox("CacheInvalidate", c, map[string]string{
				"label": "Invalidate cache on save",
			}, map[string]string{
				"cacheMAP": "Invalidate",
			}),
		},
	)
	if err != nil {
		return nil, err
	}

	open := []byte(`<div class="card"><form action="/admin/configure" method="post">`)
	close := []byte(`</form></div>`)
	script := []byte(`
	<script>
		$(function() {
			// hide default fields & labels unecessary for the config
			var fields = $('.default-fields');
			fields.css('position', 'relative');
			fields.find('input:not([type=submit])').remove();
			fields.find('label').remove();
			fields.find('button').css({
				position: 'absolute',
				top: '-10px',
				right: '0px'
			});

			var contentOnly = $('.content-only.__ponzu');
			contentOnly.hide();
			contentOnly.find('input, textarea, select').attr('name', '');

			// adjust layout of td so save button is in same location as usual
			fields.find('td').css('float', 'right');

			// stop some fixed config settings from being modified
			fields.find('input[name=client_secret]').attr('name', '');
		});
	</script>
	`)

	view = append(open, view...)
	view = append(view, close...)
	view = append(view, script...)

	return view, nil
}
<commit_msg>fixing k/v for Checkbox input on cache invalidation in config<commit_after>package config

import (
	"github.com/bosssauce/ponzu/content"
	"github.com/bosssauce/ponzu/management/editor"
)

//Config represents the confirgurable options of the system
type Config struct {
	content.Item
	editor editor.Editor

	Name            string   `json:"name"`
	Domain          string   `json:"domain"`
	AdminEmail      string   `json:"admin_email"`
	ClientSecret    string   `json:"client_secret"`
	Etag            string   `json:"etag"`
	CacheInvalidate []string `json:"cache"`
}

// ContentName partially implements editor.Editable
func (c *Config) ContentName() string { return c.Name }

// Editor partially implements editor.Editable
func (c *Config) Editor() *editor.Editor { return &c.editor }

// MarshalEditor writes a buffer of html to edit a Post and partially implements editor.Editable
func (c *Config) MarshalEditor() ([]byte, error) {
	view, err := editor.Form(c,
		editor.Field{
			View: editor.Input("Name", c, map[string]string{
				"label":       "Site Name",
				"placeholder": "Add a name to this site (internal use only)",
			}),
		},
		editor.Field{
			View: editor.Input("Domain", c, map[string]string{
				"label":       "Domain Name (required for SSL certificate)",
				"placeholder": "e.g. www.example.com or example.com",
			}),
		},
		editor.Field{
			View: editor.Input("AdminEmail", c, map[string]string{
				"label": "Adminstrator Email (will be notified of internal system information)",
			}),
		},
		editor.Field{
			View: editor.Input("ClientSecret", c, map[string]string{
				"label":    "Client Secret (used to validate requests, DO NOT SHARE)",
				"disabled": "true",
			}),
		},
		editor.Field{
			View: editor.Input("ClientSecret", c, map[string]string{
				"type": "hidden",
			}),
		},
		editor.Field{
			View: editor.Input("Etag", c, map[string]string{
				"label":    "Etag Header (used for static asset cache)",
				"disabled": "true",
			}),
		},
		editor.Field{
			View: editor.Input("Etag", c, map[string]string{
				"type": "hidden",
			}),
		},
		editor.Field{
			View: editor.Checkbox("CacheInvalidate", c, map[string]string{
				"label": "Invalidate cache on save",
			}, map[string]string{
				"Invalidate": "cache",
			}),
		},
	)
	if err != nil {
		return nil, err
	}

	open := []byte(`<div class="card"><form action="/admin/configure" method="post">`)
	close := []byte(`</form></div>`)
	script := []byte(`
	<script>
		$(function() {
			// hide default fields & labels unecessary for the config
			var fields = $('.default-fields');
			fields.css('position', 'relative');
			fields.find('input:not([type=submit])').remove();
			fields.find('label').remove();
			fields.find('button').css({
				position: 'absolute',
				top: '-10px',
				right: '0px'
			});

			var contentOnly = $('.content-only.__ponzu');
			contentOnly.hide();
			contentOnly.find('input, textarea, select').attr('name', '');

			// adjust layout of td so save button is in same location as usual
			fields.find('td').css('float', 'right');

			// stop some fixed config settings from being modified
			fields.find('input[name=client_secret]').attr('name', '');
		});
	</script>
	`)

	view = append(open, view...)
	view = append(view, close...)
	view = append(view, script...)

	return view, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package util

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

type buffer struct {
	b    []byte
	miss int
}

// BufferPool is a 'buffer pool'.
type BufferPool struct {
	pool      [6]chan []byte
	size      [5]uint32
	sizeMiss  [5]uint32
	sizeHalf  [5]uint32
	baseline  [4]int
	baseline0 int

	mu     sync.RWMutex
	closed bool
	closeC chan struct{}

	get     uint32
	put     uint32
	half    uint32
	less    uint32
	equal   uint32
	greater uint32
	miss    uint32
}

func (p *BufferPool) poolNum(n int) int {
	if n <= p.baseline0 && n > p.baseline0/2 {
		return 0
	}
	for i, x := range p.baseline {
		if n <= x {
			return i + 1
		}
	}
	return len(p.baseline) + 1
}

// Get returns buffer with length of n.
func (p *BufferPool) Get(n int) []byte {
	if p == nil {
		return make([]byte, n)
	}

	p.mu.RLock()
	defer p.mu.RUnlock()

	if p.closed {
		return make([]byte, n)
	}

	atomic.AddUint32(&p.get, 1)

	poolNum := p.poolNum(n)
	pool := p.pool[poolNum]
	if poolNum == 0 {
		// Fast path.
		select {
		case b := <-pool:
			switch {
			case cap(b) > n:
				if cap(b)-n >= n {
					atomic.AddUint32(&p.half, 1)
					select {
					case pool <- b:
					default:
					}
					return make([]byte, n)
				} else {
					atomic.AddUint32(&p.less, 1)
					return b[:n]
				}
			case cap(b) == n:
				atomic.AddUint32(&p.equal, 1)
				return b[:n]
			default:
				atomic.AddUint32(&p.greater, 1)
			}
		default:
			atomic.AddUint32(&p.miss, 1)
		}

		return make([]byte, n, p.baseline0)
	} else {
		sizePtr := &p.size[poolNum-1]

		select {
		case b := <-pool:
			switch {
			case cap(b) > n:
				if cap(b)-n >= n {
					atomic.AddUint32(&p.half, 1)
					sizeHalfPtr := &p.sizeHalf[poolNum-1]
					if atomic.AddUint32(sizeHalfPtr, 1) == 20 {
						atomic.StoreUint32(sizePtr, uint32(cap(b)/2))
						atomic.StoreUint32(sizeHalfPtr, 0)
					} else {
						select {
						case pool <- b:
						default:
						}
					}
					return make([]byte, n)
				} else {
					atomic.AddUint32(&p.less, 1)
					return b[:n]
				}
			case cap(b) == n:
				atomic.AddUint32(&p.equal, 1)
				return b[:n]
			default:
				atomic.AddUint32(&p.greater, 1)
				if uint32(cap(b)) >= atomic.LoadUint32(sizePtr) {
					select {
					case pool <- b:
					default:
					}
				}
			}
		default:
			atomic.AddUint32(&p.miss, 1)
		}

		if size := atomic.LoadUint32(sizePtr); uint32(n) > size {
			if size == 0 {
				atomic.CompareAndSwapUint32(sizePtr, 0, uint32(n))
			} else {
				sizeMissPtr := &p.sizeMiss[poolNum-1]
				if atomic.AddUint32(sizeMissPtr, 1) == 20 {
					atomic.StoreUint32(sizePtr, uint32(n))
					atomic.StoreUint32(sizeMissPtr, 0)
				}
			}
			return make([]byte, n)
		} else {
			return make([]byte, n, size)
		}
	}
}

// Put adds given buffer to the pool.
func (p *BufferPool) Put(b []byte) {
	if p == nil {
		return
	}

	p.mu.RLock()
	defer p.mu.RUnlock()

	if p.closed {
		return
	}

	atomic.AddUint32(&p.put, 1)

	pool := p.pool[p.poolNum(cap(b))]
	select {
	case pool <- b:
	default:
	}

}

func (p *BufferPool) Close() {
	if p == nil {
		return
	}

	p.mu.Lock()
	if !p.closed {
		p.closed = true
		p.closeC <- struct{}{}
	}
	p.mu.Unlock()
}

func (p *BufferPool) String() string {
	if p == nil {
		return "<nil>"
	}

	return fmt.Sprintf("BufferPool{B·%d Z·%v Zm·%v Zh·%v G·%d P·%d H·%d <·%d =·%d >·%d M·%d}",
		p.baseline0, p.size, p.sizeMiss, p.sizeHalf, p.get, p.put, p.half, p.less, p.equal, p.greater, p.miss)
}

func (p *BufferPool) drain() {
	ticker := time.NewTicker(2 * time.Second)
	for {
		select {
		case <-ticker.C:
			for _, ch := range p.pool {
				select {
				case <-ch:
				default:
				}
			}
		case <-p.closeC:
			close(p.closeC)
			for _, ch := range p.pool {
				close(ch)
			}
			return
		}
	}
}

// NewBufferPool creates a new initialized 'buffer pool'.
func NewBufferPool(baseline int) *BufferPool {
	if baseline <= 0 {
		panic("baseline can't be <= 0")
	}
	p := &BufferPool{
		baseline0: baseline,
		baseline:  [...]int{baseline / 4, baseline / 2, baseline * 2, baseline * 4},
		closeC:    make(chan struct{}, 1),
	}
	for i, cap := range []int{2, 2, 4, 4, 2, 1} {
		p.pool[i] = make(chan []byte, cap)
	}
	go p.drain()
	return p
}
<commit_msg>Stop ticker when return from drain method<commit_after>// Copyright (c) 2014, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package util

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

type buffer struct {
	b    []byte
	miss int
}

// BufferPool is a 'buffer pool'.
type BufferPool struct {
	pool      [6]chan []byte
	size      [5]uint32
	sizeMiss  [5]uint32
	sizeHalf  [5]uint32
	baseline  [4]int
	baseline0 int

	mu     sync.RWMutex
	closed bool
	closeC chan struct{}

	get     uint32
	put     uint32
	half    uint32
	less    uint32
	equal   uint32
	greater uint32
	miss    uint32
}

func (p *BufferPool) poolNum(n int) int {
	if n <= p.baseline0 && n > p.baseline0/2 {
		return 0
	}
	for i, x := range p.baseline {
		if n <= x {
			return i + 1
		}
	}
	return len(p.baseline) + 1
}

// Get returns buffer with length of n.
func (p *BufferPool) Get(n int) []byte {
	if p == nil {
		return make([]byte, n)
	}

	p.mu.RLock()
	defer p.mu.RUnlock()

	if p.closed {
		return make([]byte, n)
	}

	atomic.AddUint32(&p.get, 1)

	poolNum := p.poolNum(n)
	pool := p.pool[poolNum]
	if poolNum == 0 {
		// Fast path.
		select {
		case b := <-pool:
			switch {
			case cap(b) > n:
				if cap(b)-n >= n {
					atomic.AddUint32(&p.half, 1)
					select {
					case pool <- b:
					default:
					}
					return make([]byte, n)
				} else {
					atomic.AddUint32(&p.less, 1)
					return b[:n]
				}
			case cap(b) == n:
				atomic.AddUint32(&p.equal, 1)
				return b[:n]
			default:
				atomic.AddUint32(&p.greater, 1)
			}
		default:
			atomic.AddUint32(&p.miss, 1)
		}

		return make([]byte, n, p.baseline0)
	} else {
		sizePtr := &p.size[poolNum-1]

		select {
		case b := <-pool:
			switch {
			case cap(b) > n:
				if cap(b)-n >= n {
					atomic.AddUint32(&p.half, 1)
					sizeHalfPtr := &p.sizeHalf[poolNum-1]
					if atomic.AddUint32(sizeHalfPtr, 1) == 20 {
						atomic.StoreUint32(sizePtr, uint32(cap(b)/2))
						atomic.StoreUint32(sizeHalfPtr, 0)
					} else {
						select {
						case pool <- b:
						default:
						}
					}
					return make([]byte, n)
				} else {
					atomic.AddUint32(&p.less, 1)
					return b[:n]
				}
			case cap(b) == n:
				atomic.AddUint32(&p.equal, 1)
				return b[:n]
			default:
				atomic.AddUint32(&p.greater, 1)
				if uint32(cap(b)) >= atomic.LoadUint32(sizePtr) {
					select {
					case pool <- b:
					default:
					}
				}
			}
		default:
			atomic.AddUint32(&p.miss, 1)
		}

		if size := atomic.LoadUint32(sizePtr); uint32(n) > size {
			if size == 0 {
				atomic.CompareAndSwapUint32(sizePtr, 0, uint32(n))
			} else {
				sizeMissPtr := &p.sizeMiss[poolNum-1]
				if atomic.AddUint32(sizeMissPtr, 1) == 20 {
					atomic.StoreUint32(sizePtr, uint32(n))
					atomic.StoreUint32(sizeMissPtr, 0)
				}
			}
			return make([]byte, n)
		} else {
			return make([]byte, n, size)
		}
	}
}

// Put adds given buffer to the pool.
func (p *BufferPool) Put(b []byte) {
	if p == nil {
		return
	}

	p.mu.RLock()
	defer p.mu.RUnlock()

	if p.closed {
		return
	}

	atomic.AddUint32(&p.put, 1)

	pool := p.pool[p.poolNum(cap(b))]
	select {
	case pool <- b:
	default:
	}

}

func (p *BufferPool) Close() {
	if p == nil {
		return
	}

	p.mu.Lock()
	if !p.closed {
		p.closed = true
		p.closeC <- struct{}{}
	}
	p.mu.Unlock()
}

func (p *BufferPool) String() string {
	if p == nil {
		return "<nil>"
	}

	return fmt.Sprintf("BufferPool{B·%d Z·%v Zm·%v Zh·%v G·%d P·%d H·%d <·%d =·%d >·%d M·%d}",
		p.baseline0, p.size, p.sizeMiss, p.sizeHalf, p.get, p.put, p.half, p.less, p.equal, p.greater, p.miss)
}

func (p *BufferPool) drain() {
	ticker := time.NewTicker(2 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			for _, ch := range p.pool {
				select {
				case <-ch:
				default:
				}
			}
		case <-p.closeC:
			close(p.closeC)
			for _, ch := range p.pool {
				close(ch)
			}
			return
		}
	}
}

// NewBufferPool creates a new initialized 'buffer pool'.
func NewBufferPool(baseline int) *BufferPool {
	if baseline <= 0 {
		panic("baseline can't be <= 0")
	}
	p := &BufferPool{
		baseline0: baseline,
		baseline:  [...]int{baseline / 4, baseline / 2, baseline * 2, baseline * 4},
		closeC:    make(chan struct{}, 1),
	}
	for i, cap := range []int{2, 2, 4, 4, 2, 1} {
		p.pool[i] = make(chan []byte, cap)
	}
	go p.drain()
	return p
}
<|endoftext|>


<commit_before>package generator

import (
	"math"
)

const (
	ZipfianConstant = float64(0.99)
)

// Compute the zeta constant needed for the distribution.
// Do this incrementally for a distribution that has n items now
// but used to have st items. Use the zipfian constant theta.
// Remember the new value of n so that if we change the itemCount,
// we'll know to recompute zeta.
func zeta(st, n int64, theta, initialSum float64) (int64, float64) {
	countForzata := n
	return countForzata, zetaStatic(st, n, theta, initialSum)
}

// Compute the zeta constant needed for the distribution. Do this incrementally
// for a distribution that has h items now but used to have st items.
// Use the zipfian constant theta. Remember the new value of n so that
// if we change itemCount, we'll know to recompute zeta.
func zetaStatic(st, n int64, theta, initialSum float64) float64 {
	sum := initialSum
	for i := st; i < n; i++ {
		sum += 1 / math.Pow(float64(i+1), theta)
	}
	return sum
}

// A generator of a zipfian distribution. It produces a sequence of items,
// such that some items are more popular than others, according to
// a zipfian distribution. When you construct an instance of this class,
// you specify the number of items in the set to draw from, either by
// specifying an itemcount (so that the sequence is of items from 0 to
// itemcount-1) or by specifying a min and a max (so that the sequence
// is of items from min to max inclusive). After you construct the instance,
// you can change the number of items by calling NextInt() or nextLong().
//
// Note that the popular items will be clustered together, e.g. item 0
// is the most popular, item 1 the second most popular, and so on (or min
// is the most popular, min+1 the next most popular, etc.)
// If you don't want this clustering, and instead want the popular items
// scattered throughout the item space, then use ScrambledZipfianGenerator
// instead.
//
// Be aware: initializing this generator may take a long time if there are
// lots of items to choose from (e.g. over a minute for 100 million objects).
// This is because certain mathematical values need to be computed to properly
// generate a zipfian skew, and one of those values (zeta) is a sum sequence
// from 1 to n, where n is the itemCount. Note that if you increase the number
// of items in the set, we can compute a new zeta incrementally, so it should
// be fast unless you have added millions of items. However, if you decrease
// the number of items, we recompute zeta from scratch, so this can take
// a long time.
//
// The algorithm used here is from
// "Quickly Generating Billion-Record Synthetic Databases",
// Jim Gray et al, SIGMOD 1994.
//
type ZipfianGenerator struct {
	*IntegerGeneratorBase
	// Number of items.
	items int64
	// Min item to generate.
	base int64
	// The zipfian constant to use.
	zipfianConstant float64
	// Computed parameters for generating the distribution.
	alpha, zetan, eta, theta, zeta2theta float64
	// The number of items used to compute zetan the last time.
	countForzata int64

	// Flag to prevent problems. If you increase the number of items which
	// the zipfian generator is allowed to choose from, this code will
	// incrementally compute a new zeta value for the larger itemcount.
	// However, if you decrease the number of items, the code computes
	// zeta from scratch; this is expensive for large itemsets.
	// Usually this is not intentional; e.g. one goroutine thinks
	// the number of items is 1001 and calls "NextLong()" with that item count;
	// then another goroutine who thinks the number of items is 1000 calls
	// NextLong() with itemCount=1000 triggering the expensive recomputation.
	// (It is expensive for 100 million items, not really for 1000 items.)
	// Why did the second goroutine think there were only 1000 items?
	// maybe it read the item count before the first goroutine incremented it.
	// So this flag allows you to say if you really do want that recomputation.
	// If true, then the code will recompute zeta if the itemcount goes down.
	// If false, the code will assume itemcount only goes up, and never
	// recompute.
	allowItemCountDecrease bool
}

// Create a zipfian generator for items between min and max(inclusive).
func NewZipfianGeneratorByInterval(min, max int64) *ZipfianGenerator {
	zeta := zetaStatic(min, max-min+1, ZipfianConstant, 0)
	return NewZipfianGenerator(min, max, ZipfianConstant, zeta)
}

// Create a zipfian generator for items between min and max(inclusive) for
// the specified zipfian constant, using the precomputed value of zeta.
func NewZipfianGenerator(
	min, max int64, zipfianConstant, zetan float64) *ZipfianGenerator {

	items := max - min + 1
	base := min
	theta := zipfianConstant
	countForzata, zeta2theta := zeta(0, 2, theta, 0)
	alpha := 1.0 / (1.0 - theta)
	countForzata = items
	eta := (1 - math.Pow(float64(2.0/items), 1-theta)) / (1 - zeta2theta/zetan)

	object := &ZipfianGenerator{
		IntegerGeneratorBase:   NewIntegerGeneratorBase(0),
		items:                  items,
		base:                   base,
		zipfianConstant:        zipfianConstant,
		alpha:                  alpha,
		zetan:                  zetan,
		eta:                    eta,
		theta:                  theta,
		zeta2theta:             zeta2theta,
		countForzata:           countForzata,
		allowItemCountDecrease: false,
	}
	object.NextInt()
	return object
}

// Return the next value, skewed by the zipfian distribution. The 0th item will
// be the most popular, followed by the 1st, followed by the 2nd, etc.
// (or, if min != 0, the min-th item is the most popular, the min+1th item
// the next most popular, etc.) If you want the popular items
// scattered throughout the item space, use ScrambledZipfianGenerator instead.
func (self *ZipfianGenerator) NextInt() int64 {
	return self.Next(self.items)
}

// Return the next value, skewed by the zipfian distribution. The 0th item will
// be the most popular, followed by the 1st, followed by the 2nd, etc.
// (same as NextInt())
func (self *ZipfianGenerator) NextLong() int64 {
	return self.Next(self.items)
}

// Generate the next item. this distribution will be skewed toward
// lower itegers; e.g. 0 will be the most popular, 1 the next most popular, etc.
func (self *ZipfianGenerator) Next(itemCount int64) int64 {
	var ret int64
	defer func(r *int64) {
		self.IntegerGeneratorBase.SetLastInt(*r)
	}(&ret)
	if itemCount != self.countForzata {
		if itemCount > self.countForzata {
			self.countForzata, self.zetan = zeta(self.countForzata, itemCount, self.theta, self.zetan)
			self.eta = (1 - math.Pow(float64(2.0/self.items), 1-self.theta)) / (1 - self.zeta2theta/self.zetan)
		} else if (itemCount < self.countForzata) && (self.allowItemCountDecrease) {
			self.countForzata, self.zetan = zeta(0, itemCount, self.theta, 0)
			self.eta = (1 - math.Pow(float64(2.0/self.items), 1-self.theta)) / (1 - self.zeta2theta/self.zetan)
		}
	}

	u := NextFloat64()
	uz := u * self.zetan
	if uz < 1.0 {
		ret = self.base
		return ret
	}
	if uz < 1.0+math.Pow(0.5, self.theta) {
		ret = self.base + 1
		return ret
	}
	ret = self.base + int64(float64(itemCount)*math.Pow(self.eta*u-self.eta+1.0, self.alpha))
	return ret
}

func (self *ZipfianGenerator) NextString() string {
	return self.IntegerGeneratorBase.NextString(self)
}

func (self *ZipfianGenerator) Mean() float64 {
	panic("unsupported operation")
}

var (
	Zetan               = float64(26.46902820178302)
	UsedZipfianConstant = float64(0.99)
	ItemCount           = float64(10000000000)
)

// A generator of a zipfian distribution. It produces a sequence of items,
// such that some items are more popular than others, according to a zipfian
// distribution. When you construct an instance of this class, you specify
// the number of items in the set to draw from, either by specifying
// an itemCount(so that the sequence is of items from 0 to itemCount-1) or
// by specifying a min and a max (so that the sequence is of items from min
// to max inclusive). After you construct the instance, you can change
// the number of items by calling NextInt(itemCount) or Next(itemCount).
// Unlike ZipfianGenerator, this class scatters the "popular" items across
// the item space. Use this, instead of ZipfianGenerator, if you don't want
// the head of the distribution(the popular items) clustered together.
type ScrambledZipfianGenerator struct {
	*IntegerGeneratorBase
	gen       *ZipfianGenerator
	min       int64
	max       int64
	itemCount int64
}

// Create a zipfian generator for the specified number of items.
func NewScrambledZipfianGeneratorByItems(items int64) *ScrambledZipfianGenerator {
	return NewScrambledZipfianGenerator(0, items-1)
}

// Create a zipfian generator for items between min and max (inclusive) for
// the specified zipfian constant. If you use a zipfian constant other than
// 0.99, this will take a long time complete because we need to recompute
// zeta.
func NewScrambledZipfianGeneratorConstant(min, max int64, constant float64) *ScrambledZipfianGenerator {
	var gen *ZipfianGenerator
	itemCount := max - min + 1
	if constant == UsedZipfianConstant {
		gen = NewZipfianGenerator(0, itemCount, constant, Zetan)
	} else {
		zeta := zetaStatic(0, itemCount, constant, 0)
		gen = NewZipfianGenerator(0, itemCount, constant, zeta)
	}
	return &ScrambledZipfianGenerator{
		IntegerGeneratorBase: NewIntegerGeneratorBase(min),
		gen:                  gen,
		min:                  min,
		max:                  max,
		itemCount:            max - min + 1,
	}
}

// Create a zipfian generator for items between min and max(inclusive).
func NewScrambledZipfianGenerator(min, max int64) *ScrambledZipfianGenerator {
	return NewScrambledZipfianGeneratorConstant(min, max, ZipfianConstant)
}

// Return the next int in the sequence.
func (self *ScrambledZipfianGenerator) NextInt() int64 {
	return self.Next()
}

// return the next item in the sequence.
func (self *ScrambledZipfianGenerator) Next() int64 {
	ret := self.gen.NextLong()
	ret = self.min + int64(FNVHash64(uint64(ret))%uint64(self.itemCount))
	self.SetLastInt(ret)
	return ret
}

func (self *ScrambledZipfianGenerator) NextString() string {
	return self.IntegerGeneratorBase.NextString(self)
}

// Since the values are scrambed (hopefully uniformly), the mean is simply
// the middle of the range.
func (self *ScrambledZipfianGenerator) Mean() float64 {
	return float64(self.min+self.max) / 2.0
}

// Hash a integer value.
func Hash(value int64) uint64 {
	return FNVHash64(uint64(value))
}

const (
	FNVOffsetBasis32 = uint32(0x811c9dc5)
	FNVPrime32       = uint32(16777619)
)

// 32 bit FNV hash.
// Refer to http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash
func FNVHash32(value uint32) uint32 {
	hash := FNVOffsetBasis32
	for i := 0; i < 4; i++ {
		octet := value & 0x00FF
		value >>= 8

		hash ^= octet
		hash *= FNVPrime32
	}
	return hash
}

const (
	FNVOffsetBasis64 = uint64(0xCBF29CE484222325)
	FNVPrime64       = uint64(1099511628211)
)

// 64 bit FNV hash.
// Refer to http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash
func FNVHash64(value uint64) uint64 {
	hash := FNVOffsetBasis64
	for i := 0; i < 8; i++ {
		octet := value & 0x00FF
		value >>= 8

		hash ^= octet
		hash *= FNVPrime64
	}
	return hash
}
<commit_msg>Fix zipfian generator eta calculation<commit_after>package generator

import (
	"math"
)

const (
	ZipfianConstant = float64(0.99)
)

// Compute the zeta constant needed for the distribution.
// Do this incrementally for a distribution that has n items now
// but used to have st items. Use the zipfian constant theta.
// Remember the new value of n so that if we change the itemCount,
// we'll know to recompute zeta.
func zeta(st, n int64, theta, initialSum float64) (int64, float64) {
	countForzata := n
	return countForzata, zetaStatic(st, n, theta, initialSum)
}

// Compute the zeta constant needed for the distribution. Do this incrementally
// for a distribution that has h items now but used to have st items.
// Use the zipfian constant theta. Remember the new value of n so that
// if we change itemCount, we'll know to recompute zeta.
func zetaStatic(st, n int64, theta, initialSum float64) float64 {
	sum := initialSum
	for i := st; i < n; i++ {
		sum += 1 / math.Pow(float64(i+1), theta)
	}
	return sum
}

// A generator of a zipfian distribution. It produces a sequence of items,
// such that some items are more popular than others, according to
// a zipfian distribution. When you construct an instance of this class,
// you specify the number of items in the set to draw from, either by
// specifying an itemcount (so that the sequence is of items from 0 to
// itemcount-1) or by specifying a min and a max (so that the sequence
// is of items from min to max inclusive). After you construct the instance,
// you can change the number of items by calling NextInt() or nextLong().
//
// Note that the popular items will be clustered together, e.g. item 0
// is the most popular, item 1 the second most popular, and so on (or min
// is the most popular, min+1 the next most popular, etc.)
// If you don't want this clustering, and instead want the popular items
// scattered throughout the item space, then use ScrambledZipfianGenerator
// instead.
//
// Be aware: initializing this generator may take a long time if there are
// lots of items to choose from (e.g. over a minute for 100 million objects).
// This is because certain mathematical values need to be computed to properly
// generate a zipfian skew, and one of those values (zeta) is a sum sequence
// from 1 to n, where n is the itemCount. Note that if you increase the number
// of items in the set, we can compute a new zeta incrementally, so it should
// be fast unless you have added millions of items. However, if you decrease
// the number of items, we recompute zeta from scratch, so this can take
// a long time.
//
// The algorithm used here is from
// "Quickly Generating Billion-Record Synthetic Databases",
// Jim Gray et al, SIGMOD 1994.
//
type ZipfianGenerator struct {
	*IntegerGeneratorBase
	// Number of items.
	items int64
	// Min item to generate.
	base int64
	// The zipfian constant to use.
	zipfianConstant float64
	// Computed parameters for generating the distribution.
	alpha, zetan, eta, theta, zeta2theta float64
	// The number of items used to compute zetan the last time.
	countForzata int64

	// Flag to prevent problems. If you increase the number of items which
	// the zipfian generator is allowed to choose from, this code will
	// incrementally compute a new zeta value for the larger itemcount.
	// However, if you decrease the number of items, the code computes
	// zeta from scratch; this is expensive for large itemsets.
	// Usually this is not intentional; e.g. one goroutine thinks
	// the number of items is 1001 and calls "NextLong()" with that item count;
	// then another goroutine who thinks the number of items is 1000 calls
	// NextLong() with itemCount=1000 triggering the expensive recomputation.
	// (It is expensive for 100 million items, not really for 1000 items.)
	// Why did the second goroutine think there were only 1000 items?
	// maybe it read the item count before the first goroutine incremented it.
	// So this flag allows you to say if you really do want that recomputation.
	// If true, then the code will recompute zeta if the itemcount goes down.
	// If false, the code will assume itemcount only goes up, and never
	// recompute.
	allowItemCountDecrease bool
}

// Create a zipfian generator for items between min and max(inclusive).
func NewZipfianGeneratorByInterval(min, max int64) *ZipfianGenerator {
	zeta := zetaStatic(min, max-min+1, ZipfianConstant, 0)
	return NewZipfianGenerator(min, max, ZipfianConstant, zeta)
}

// Create a zipfian generator for items between min and max(inclusive) for
// the specified zipfian constant, using the precomputed value of zeta.
func NewZipfianGenerator(
	min, max int64, zipfianConstant, zetan float64) *ZipfianGenerator {

	items := max - min + 1
	base := min
	theta := zipfianConstant
	countForzata, zeta2theta := zeta(0, 2, theta, 0)
	alpha := 1.0 / (1.0 - theta)
	countForzata = items
	eta := (1 - math.Pow(2.0/float64(items), 1-theta)) / (1 - zeta2theta/zetan)

	object := &ZipfianGenerator{
		IntegerGeneratorBase:   NewIntegerGeneratorBase(0),
		items:                  items,
		base:                   base,
		zipfianConstant:        zipfianConstant,
		alpha:                  alpha,
		zetan:                  zetan,
		eta:                    eta,
		theta:                  theta,
		zeta2theta:             zeta2theta,
		countForzata:           countForzata,
		allowItemCountDecrease: false,
	}
	object.NextInt()
	return object
}

// Return the next value, skewed by the zipfian distribution. The 0th item will
// be the most popular, followed by the 1st, followed by the 2nd, etc.
// (or, if min != 0, the min-th item is the most popular, the min+1th item
// the next most popular, etc.) If you want the popular items
// scattered throughout the item space, use ScrambledZipfianGenerator instead.
func (self *ZipfianGenerator) NextInt() int64 {
	return self.Next(self.items)
}

// Return the next value, skewed by the zipfian distribution. The 0th item will
// be the most popular, followed by the 1st, followed by the 2nd, etc.
// (same as NextInt())
func (self *ZipfianGenerator) NextLong() int64 {
	return self.Next(self.items)
}

// Generate the next item. this distribution will be skewed toward
// lower itegers; e.g. 0 will be the most popular, 1 the next most popular, etc.
func (self *ZipfianGenerator) Next(itemCount int64) int64 {
	var ret int64
	defer func(r *int64) {
		self.IntegerGeneratorBase.SetLastInt(*r)
	}(&ret)
	if itemCount != self.countForzata {
		if itemCount > self.countForzata {
			self.countForzata, self.zetan = zeta(self.countForzata, itemCount, self.theta, self.zetan)
			self.eta = (1 - math.Pow(float64(2.0/self.items), 1-self.theta)) / (1 - self.zeta2theta/self.zetan)
		} else if (itemCount < self.countForzata) && (self.allowItemCountDecrease) {
			self.countForzata, self.zetan = zeta(0, itemCount, self.theta, 0)
			self.eta = (1 - math.Pow(float64(2.0/self.items), 1-self.theta)) / (1 - self.zeta2theta/self.zetan)
		}
	}

	u := NextFloat64()
	uz := u * self.zetan
	if uz < 1.0 {
		ret = self.base
		return ret
	}
	if uz < 1.0+math.Pow(0.5, self.theta) {
		ret = self.base + 1
		return ret
	}
	ret = self.base + int64(float64(itemCount)*math.Pow(self.eta*u-self.eta+1.0, self.alpha))
	return ret
}

func (self *ZipfianGenerator) NextString() string {
	return self.IntegerGeneratorBase.NextString(self)
}

func (self *ZipfianGenerator) Mean() float64 {
	panic("unsupported operation")
}

var (
	Zetan               = float64(26.46902820178302)
	UsedZipfianConstant = float64(0.99)
	ItemCount           = float64(10000000000)
)

// A generator of a zipfian distribution. It produces a sequence of items,
// such that some items are more popular than others, according to a zipfian
// distribution. When you construct an instance of this class, you specify
// the number of items in the set to draw from, either by specifying
// an itemCount(so that the sequence is of items from 0 to itemCount-1) or
// by specifying a min and a max (so that the sequence is of items from min
// to max inclusive). After you construct the instance, you can change
// the number of items by calling NextInt(itemCount) or Next(itemCount).
// Unlike ZipfianGenerator, this class scatters the "popular" items across
// the item space. Use this, instead of ZipfianGenerator, if you don't want
// the head of the distribution(the popular items) clustered together.
type ScrambledZipfianGenerator struct {
	*IntegerGeneratorBase
	gen       *ZipfianGenerator
	min       int64
	max       int64
	itemCount int64
}

// Create a zipfian generator for the specified number of items.
func NewScrambledZipfianGeneratorByItems(items int64) *ScrambledZipfianGenerator {
	return NewScrambledZipfianGenerator(0, items-1)
}

// Create a zipfian generator for items between min and max (inclusive) for
// the specified zipfian constant. If you use a zipfian constant other than
// 0.99, this will take a long time complete because we need to recompute
// zeta.
func NewScrambledZipfianGeneratorConstant(min, max int64, constant float64) *ScrambledZipfianGenerator {
	var gen *ZipfianGenerator
	itemCount := max - min + 1
	if constant == UsedZipfianConstant {
		gen = NewZipfianGenerator(0, itemCount, constant, Zetan)
	} else {
		zeta := zetaStatic(0, itemCount, constant, 0)
		gen = NewZipfianGenerator(0, itemCount, constant, zeta)
	}
	return &ScrambledZipfianGenerator{
		IntegerGeneratorBase: NewIntegerGeneratorBase(min),
		gen:                  gen,
		min:                  min,
		max:                  max,
		itemCount:            max - min + 1,
	}
}

// Create a zipfian generator for items between min and max(inclusive).
func NewScrambledZipfianGenerator(min, max int64) *ScrambledZipfianGenerator {
	return NewScrambledZipfianGeneratorConstant(min, max, ZipfianConstant)
}

// Return the next int in the sequence.
func (self *ScrambledZipfianGenerator) NextInt() int64 {
	return self.Next()
}

// return the next item in the sequence.
func (self *ScrambledZipfianGenerator) Next() int64 {
	ret := self.gen.NextLong()
	ret = self.min + int64(FNVHash64(uint64(ret))%uint64(self.itemCount))
	self.SetLastInt(ret)
	return ret
}

func (self *ScrambledZipfianGenerator) NextString() string {
	return self.IntegerGeneratorBase.NextString(self)
}

// Since the values are scrambed (hopefully uniformly), the mean is simply
// the middle of the range.
func (self *ScrambledZipfianGenerator) Mean() float64 {
	return float64(self.min+self.max) / 2.0
}

// Hash a integer value.
func Hash(value int64) uint64 {
	return FNVHash64(uint64(value))
}

const (
	FNVOffsetBasis32 = uint32(0x811c9dc5)
	FNVPrime32       = uint32(16777619)
)

// 32 bit FNV hash.
// Refer to http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash
func FNVHash32(value uint32) uint32 {
	hash := FNVOffsetBasis32
	for i := 0; i < 4; i++ {
		octet := value & 0x00FF
		value >>= 8

		hash ^= octet
		hash *= FNVPrime32
	}
	return hash
}

const (
	FNVOffsetBasis64 = uint64(0xCBF29CE484222325)
	FNVPrime64       = uint64(1099511628211)
)

// 64 bit FNV hash.
// Refer to http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash
func FNVHash64(value uint64) uint64 {
	hash := FNVOffsetBasis64
	for i := 0; i < 8; i++ {
		octet := value & 0x00FF
		value >>= 8

		hash ^= octet
		hash *= FNVPrime64
	}
	return hash
}
<|endoftext|>


<commit_before>package containers

import (
	"fmt"

	"github.com/nanobox-io/golang-docker-client"

	"github.com/nanobox-io/nanobox/util/config"
	"github.com/nanobox-io/nanobox/util/provider"
)

// BuildConfig generate the container configuration for the build container
func BuildConfig(image string) docker.ContainerConfig {
	env := config.EnvID()
	config := docker.ContainerConfig{
		Name:    BuildName(),
		Image:   image,
		Network: "host",
		Binds: []string{
			fmt.Sprintf("%s%s/code:/app", provider.HostShareDir(), env),
			fmt.Sprintf("%s%s/engine:/share/engine", provider.HostShareDir(), env),
			fmt.Sprintf("%s%s/build:/mnt/build", provider.HostMntDir(), env),
			fmt.Sprintf("%s%s/deploy:/mnt/deploy", provider.HostMntDir(), env),
			fmt.Sprintf("%s%s/cache:/mnt/cache", provider.HostMntDir(), env),
		},
	}

	return config
}

// BuildName returns the name of the build container
func BuildName() string {
	return fmt.Sprintf("nanobox_%s_build", config.EnvID())
}
<commit_msg>Adjust build mounts to work with build hooks<commit_after>package containers

import (
	"fmt"

	"github.com/nanobox-io/golang-docker-client"

	"github.com/nanobox-io/nanobox/util/config"
	"github.com/nanobox-io/nanobox/util/provider"
)

// BuildConfig generate the container configuration for the build container
func BuildConfig(image string) docker.ContainerConfig {
	env := config.EnvID()
	config := docker.ContainerConfig{
		Name:    BuildName(),
		Image:   image,
		Network: "host",
		Binds: []string{
			fmt.Sprintf("%s%s/code:/share/code", provider.HostShareDir(), env),
			fmt.Sprintf("%s%s/engine:/share/engine", provider.HostShareDir(), env),
			fmt.Sprintf("%s%s/build:/mnt/build", provider.HostMntDir(), env),
			fmt.Sprintf("%s%s/deploy:/mnt/deploy", provider.HostMntDir(), env),
			fmt.Sprintf("%s%s/cache:/mnt/cache", provider.HostMntDir(), env),
		},
	}

	return config
}

// BuildName returns the name of the build container
func BuildName() string {
	return fmt.Sprintf("nanobox_%s_build", config.EnvID())
}
<|endoftext|>


<commit_before>package syslogd

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"regexp"

	"github.com/deis/deis/logger/syslog"
)

const logRoot = "/var/log/deis"

type handler struct {
	// To simplify implementation of our handler we embed helper
	// syslog.BaseHandler struct.
	*syslog.BaseHandler
}

// Simple fiter for named/bind messages which can be used with BaseHandler
func filter(m syslog.SyslogMessage) bool {
	return true
}

func newHandler() *handler {
	h := handler{syslog.NewBaseHandler(5, filter, false)}
	go h.mainLoop() // BaseHandler needs some gorutine that reads from its queue
	return &h
}

// check if a file path exists
func fileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func getLogFile(message string) (io.Writer, error) {
	r := regexp.MustCompile(`^.* ([-a-z0-9]+)\[[a-z0-9-_\.]+\].*`)
	match := r.FindStringSubmatch(message)
	if match == nil {
		return nil, fmt.Errorf("Could not find app name in message: %s", message)
	}
	appName := match[1]
	filePath := path.Join(logRoot, appName+".log")
	// check if file exists
	exists, err := fileExists(filePath)
	if err != nil {
		return nil, err
	}
	// return a new file or the existing file for appending
	var file io.Writer
	if exists {
		file, err = os.OpenFile(filePath, os.O_RDWR|os.O_APPEND, 0644)
	} else {
		file, err = os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644)
	}
	return file, err
}

func writeToDisk(m syslog.SyslogMessage) error {
	file, err := getLogFile(m.String())
	if err != nil {
		return err
	}
	bytes := []byte(m.String() + "\n")
	file.Write(bytes)
	return nil
}

// mainLoop reads from BaseHandler queue using h.Get and logs messages to stdout
func (h *handler) mainLoop() {
	for {
		m := h.Get()
		if m == nil {
			break
		}
		err := writeToDisk(m)
		if err != nil {
			log.Println(err)
		}
	}
	h.End()
}

func Listen(signalChan chan os.Signal, cleanupDone chan bool) {
	fmt.Println("Starting syslog...")
	// Create a server with one handler and run one listen gorutine
	s := syslog.NewServer()
	s.AddHandler(newHandler())
	s.Listen("0.0.0.0:514")
	fmt.Println("Syslog server started...")

	// Wait for terminating signal
	for _ = range signalChan {
		// Shutdown the server
		fmt.Println("Shutting down...")
		s.Shutdown()
		cleanupDone <- true
	}
}
<commit_msg>fix(logger): the test is expecting the message "deis-logger running".<commit_after>package syslogd

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"regexp"

	"github.com/deis/deis/logger/syslog"
)

const logRoot = "/var/log/deis"

type handler struct {
	// To simplify implementation of our handler we embed helper
	// syslog.BaseHandler struct.
	*syslog.BaseHandler
}

// Simple fiter for named/bind messages which can be used with BaseHandler
func filter(m syslog.SyslogMessage) bool {
	return true
}

func newHandler() *handler {
	h := handler{syslog.NewBaseHandler(5, filter, false)}
	go h.mainLoop() // BaseHandler needs some gorutine that reads from its queue
	return &h
}

// check if a file path exists
func fileExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func getLogFile(message string) (io.Writer, error) {
	r := regexp.MustCompile(`^.* ([-a-z0-9]+)\[[a-z0-9-_\.]+\].*`)
	match := r.FindStringSubmatch(message)
	if match == nil {
		return nil, fmt.Errorf("Could not find app name in message: %s", message)
	}
	appName := match[1]
	filePath := path.Join(logRoot, appName+".log")
	// check if file exists
	exists, err := fileExists(filePath)
	if err != nil {
		return nil, err
	}
	// return a new file or the existing file for appending
	var file io.Writer
	if exists {
		file, err = os.OpenFile(filePath, os.O_RDWR|os.O_APPEND, 0644)
	} else {
		file, err = os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644)
	}
	return file, err
}

func writeToDisk(m syslog.SyslogMessage) error {
	file, err := getLogFile(m.String())
	if err != nil {
		return err
	}
	bytes := []byte(m.String() + "\n")
	file.Write(bytes)
	return nil
}

// mainLoop reads from BaseHandler queue using h.Get and logs messages to stdout
func (h *handler) mainLoop() {
	for {
		m := h.Get()
		if m == nil {
			break
		}
		err := writeToDisk(m)
		if err != nil {
			log.Println(err)
		}
	}
	h.End()
}

func Listen(signalChan chan os.Signal, cleanupDone chan bool) {
	fmt.Println("Starting syslog...")
	// Create a server with one handler and run one listen gorutine
	s := syslog.NewServer()
	s.AddHandler(newHandler())
	s.Listen("0.0.0.0:514")
	fmt.Println("Syslog server started...")
	fmt.Println("deis-logger running")

	// Wait for terminating signal
	for _ = range signalChan {
		// Shutdown the server
		fmt.Println("Shutting down...")
		s.Shutdown()
		cleanupDone <- true
	}
}
<|endoftext|>


<commit_before>package events

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/gorilla/websocket"

	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// EventListenerConnection represents an event listener connection.
type EventListenerConnection interface {
	Reader(ctx context.Context, recvFunc EventHandler)
	WriteJSON(event any) error
	Close() error
	LocalAddr() net.Addr  // Used for logging
	RemoteAddr() net.Addr // Used for logging
}

type websockListenerConnection struct {
	*websocket.Conn

	lock         sync.Mutex
	pongsPending int
}

type streamListenerConnection struct {
	net.Conn

	lock sync.Mutex
}

// NewWebsocketListenerConnection returns a new websocket listener connection.
func NewWebsocketListenerConnection(connection *websocket.Conn) EventListenerConnection {
	return &websockListenerConnection{
		Conn: connection,
	}
}

func (e *websockListenerConnection) Reader(ctx context.Context, recvFunc EventHandler) {
	ctx, cancel := context.WithCancel(ctx)

	close := func() {
		e.lock.Lock()
		defer e.lock.Unlock()

		if ctx.Err() != nil {
			return
		}

		_ = e.Close()
		cancel()
	}

	defer close()

	pingInterval := time.Second * 10
	e.pongsPending = 0

	e.SetPongHandler(func(msg string) error {
		e.lock.Lock()
		e.pongsPending = 0
		e.lock.Unlock()
		return nil
	})

	// Start reader from client.
	go func() {
		defer close()

		if recvFunc != nil {
			for {
				var event api.Event
				err := e.Conn.ReadJSON(&event)
				if err != nil {
					return // This detects if client has disconnected or sent invalid data.
				}

				// Pass received event to the handler.
				recvFunc(event)
			}
		} else {
			// Run a blocking reader to detect if the client has disconnected. We don't expect to get
			// anything from the remote side, so this should remain blocked until disconnected.
			_, _, _ = e.Conn.NextReader()
		}
	}()

	for {
		if ctx.Err() != nil {
			return
		}

		e.lock.Lock()
		if e.pongsPending > 2 {
			e.lock.Unlock()
			return
		}

		err := e.WriteControl(websocket.PingMessage, []byte("keepalive"), time.Now().Add(5*time.Second))
		if err != nil {
			e.lock.Unlock()
			return
		}

		e.pongsPending++
		e.lock.Unlock()

		select {
		case <-time.After(pingInterval):
		case <-ctx.Done():
			return
		}
	}
}

func (e *websockListenerConnection) WriteJSON(event any) error {
	e.lock.Lock()
	defer e.lock.Unlock()

	err := e.Conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
	if err != nil {
		return fmt.Errorf("Failed setting write deadline: %w", err)
	}

	return e.Conn.WriteJSON(event)
}

// NewStreamListenerConnection returns a new http stream listener connection.
func NewStreamListenerConnection(connection net.Conn) (EventListenerConnection, error) {
	// Send HTTP response to let the client know what to expect.
	// This is only sent once, and is followed by events.
	//
	// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate
	// that the MIME types advertised in the Content-Type headers should be followed and not be
	// changed. The header allows you to avoid MIME type sniffing by saying that the MIME types are
	// deliberately configured.
	_, err := io.WriteString(connection, `HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: application/json
X-Content-Type-Options: nosniff

`)
	if err != nil {
		return nil, fmt.Errorf("Failed sending initial HTTP response: %w", err)
	}

	return &streamListenerConnection{
		Conn: connection,
	}, nil
}

func (e *streamListenerConnection) Reader(ctx context.Context, recvFunc EventHandler) {
	ctx, cancelFunc := context.WithCancel(ctx)

	close := func() {
		e.lock.Lock()
		defer e.lock.Unlock()

		if ctx.Err() != nil {
			return
		}

		err := e.Close()
		if err != nil {
			logger.Warn("Failed closing connection", logger.Ctx{"err": err})
		}

		cancelFunc()
	}

	defer close()

	// Start reader from client.
	go func() {
		defer close()

		buf := make([]byte, 1)

		// This is used to determine whether the client has terminated.
		_, err := e.Read(buf)
		if err != nil && errors.Is(err, io.EOF) {
			return
		}
	}()

	if ctx.Err() != nil {
		return
	}

	<-ctx.Done()
}

func (e *streamListenerConnection) WriteJSON(event any) error {
	e.lock.Lock()
	defer e.lock.Unlock()

	err := e.SetWriteDeadline(time.Now().Add(5 * (time.Second)))
	if err != nil {
		return fmt.Errorf("Failed setting write deadline: %w", err)
	}

	err = json.NewEncoder(e.Conn).Encode(event)
	if err != nil {
		return fmt.Errorf("Failed sending event: %w", err)
	}

	return nil
}

func (e *streamListenerConnection) Close() error {
	return e.Conn.Close()
}
<commit_msg>lxd/events: Add simpleListenerConnection<commit_after>package events

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	"github.com/gorilla/websocket"

	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// EventListenerConnection represents an event listener connection.
type EventListenerConnection interface {
	Reader(ctx context.Context, recvFunc EventHandler)
	WriteJSON(event any) error
	Close() error
	LocalAddr() net.Addr  // Used for logging
	RemoteAddr() net.Addr // Used for logging
}

type websockListenerConnection struct {
	*websocket.Conn

	lock         sync.Mutex
	pongsPending int
}

type streamListenerConnection struct {
	net.Conn

	lock sync.Mutex
}

type simpleListenerConnection struct {
	rwc io.ReadWriteCloser

	lock sync.Mutex
}

// NewWebsocketListenerConnection returns a new websocket listener connection.
func NewWebsocketListenerConnection(connection *websocket.Conn) EventListenerConnection {
	return &websockListenerConnection{
		Conn: connection,
	}
}

func (e *websockListenerConnection) Reader(ctx context.Context, recvFunc EventHandler) {
	ctx, cancel := context.WithCancel(ctx)

	close := func() {
		e.lock.Lock()
		defer e.lock.Unlock()

		if ctx.Err() != nil {
			return
		}

		_ = e.Close()
		cancel()
	}

	defer close()

	pingInterval := time.Second * 10
	e.pongsPending = 0

	e.SetPongHandler(func(msg string) error {
		e.lock.Lock()
		e.pongsPending = 0
		e.lock.Unlock()
		return nil
	})

	// Start reader from client.
	go func() {
		defer close()

		if recvFunc != nil {
			for {
				var event api.Event
				err := e.Conn.ReadJSON(&event)
				if err != nil {
					return // This detects if client has disconnected or sent invalid data.
				}

				// Pass received event to the handler.
				recvFunc(event)
			}
		} else {
			// Run a blocking reader to detect if the client has disconnected. We don't expect to get
			// anything from the remote side, so this should remain blocked until disconnected.
			_, _, _ = e.Conn.NextReader()
		}
	}()

	for {
		if ctx.Err() != nil {
			return
		}

		e.lock.Lock()
		if e.pongsPending > 2 {
			e.lock.Unlock()
			return
		}

		err := e.WriteControl(websocket.PingMessage, []byte("keepalive"), time.Now().Add(5*time.Second))
		if err != nil {
			e.lock.Unlock()
			return
		}

		e.pongsPending++
		e.lock.Unlock()

		select {
		case <-time.After(pingInterval):
		case <-ctx.Done():
			return
		}
	}
}

func (e *websockListenerConnection) WriteJSON(event any) error {
	e.lock.Lock()
	defer e.lock.Unlock()

	err := e.Conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
	if err != nil {
		return fmt.Errorf("Failed setting write deadline: %w", err)
	}

	return e.Conn.WriteJSON(event)
}

// NewStreamListenerConnection returns a new http stream listener connection.
func NewStreamListenerConnection(connection net.Conn) (EventListenerConnection, error) {
	// Send HTTP response to let the client know what to expect.
	// This is only sent once, and is followed by events.
	//
	// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate
	// that the MIME types advertised in the Content-Type headers should be followed and not be
	// changed. The header allows you to avoid MIME type sniffing by saying that the MIME types are
	// deliberately configured.
	_, err := io.WriteString(connection, `HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: application/json
X-Content-Type-Options: nosniff

`)
	if err != nil {
		return nil, fmt.Errorf("Failed sending initial HTTP response: %w", err)
	}

	return &streamListenerConnection{
		Conn: connection,
	}, nil
}

func (e *streamListenerConnection) Reader(ctx context.Context, recvFunc EventHandler) {
	ctx, cancelFunc := context.WithCancel(ctx)

	close := func() {
		e.lock.Lock()
		defer e.lock.Unlock()

		if ctx.Err() != nil {
			return
		}

		err := e.Close()
		if err != nil {
			logger.Warn("Failed closing connection", logger.Ctx{"err": err})
		}

		cancelFunc()
	}

	defer close()

	// Start reader from client.
	go func() {
		defer close()

		buf := make([]byte, 1)

		// This is used to determine whether the client has terminated.
		_, err := e.Read(buf)
		if err != nil && errors.Is(err, io.EOF) {
			return
		}
	}()

	if ctx.Err() != nil {
		return
	}

	<-ctx.Done()
}

func (e *streamListenerConnection) WriteJSON(event any) error {
	e.lock.Lock()
	defer e.lock.Unlock()

	err := e.SetWriteDeadline(time.Now().Add(5 * (time.Second)))
	if err != nil {
		return fmt.Errorf("Failed setting write deadline: %w", err)
	}

	err = json.NewEncoder(e.Conn).Encode(event)
	if err != nil {
		return fmt.Errorf("Failed sending event: %w", err)
	}

	return nil
}

func (e *streamListenerConnection) Close() error {
	return e.Conn.Close()
}

// NewSimpleListenerConnection returns a new simple listener connection.
func NewSimpleListenerConnection(rwc io.ReadWriteCloser) EventListenerConnection {
	return &simpleListenerConnection{
		rwc: rwc,
	}
}

func (e *simpleListenerConnection) Reader(ctx context.Context, recvFunc EventHandler) {
	ctx, cancelFunc := context.WithCancel(ctx)

	close := func() {
		e.lock.Lock()
		defer e.lock.Unlock()

		if ctx.Err() != nil {
			return
		}

		err := e.Close()
		if err != nil {
			logger.Warn("Failed closing connection", logger.Ctx{"err": err})
		}

		cancelFunc()
	}

	defer close()

	// Start reader from client.
	go func() {
		defer close()

		buf := make([]byte, 1)

		// This is used to determine whether the client has terminated.
		_, err := e.rwc.Read(buf)
		if err != nil && errors.Is(err, io.EOF) {
			return
		}
	}()

	if ctx.Err() != nil {
		return
	}

	<-ctx.Done()
}

func (e *simpleListenerConnection) WriteJSON(event any) error {
	err := json.NewEncoder(e.rwc).Encode(event)
	if err != nil {
		return err
	}

	return nil
}

func (e *simpleListenerConnection) Close() error {
	return e.rwc.Close()
}

func (e *simpleListenerConnection) LocalAddr() net.Addr { // Used for logging
	return nil
}

func (e *simpleListenerConnection) RemoteAddr() net.Addr { // Used for logging
	return nil
}
<|endoftext|>


<commit_before>package makecoe

import (
	"net/http"
	"testing"

	"fmt"
	"net/http/httptest"

	"github.com/getcarina/carina/common"
)

func TestDeleteClusterThenErrorOutHaltsWait(t *testing.T) {
	common.Log.RegisterTestLogger(t)

	mockCarina := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		switch r.RequestURI {
		case "/":
			fmt.Fprintln(w, `{"versions": [{"status": "current", "min_version": "1.0", "max_version": "1.0", "id": "v1.0"}]}`)
		case "/clusters/99999999-9999-9999-9999-999999999999":
			fmt.Fprintln(w, `{ "status": "error" }`)
		case "/v2.0/tokens":
			fmt.Fprintln(w, `{"access":{"serviceCatalog":[{"endpoints":[{"tenantId":"963451","publicURL":"https:\/\/api.dfw.getcarina.com","region":"DFW"}],"name":"cloudContainer","type":"rax:container"}],"user":{"name":"fake-user","id":"fake-userid"},"token":{"expires":"3000-01-01T12:00:00Z","id":"fake-token","tenant":{"name":"fake-tenantname","id":"fake-tenantid"}}}}`)
		default:
			fmt.Fprintln(w, "unexpected request: "+r.RequestURI)
			w.WriteHeader(404)
		}
	}))
	defer mockCarina.Close()

	acct := &Account{
		AuthEndpointOverride: mockCarina.URL + "/v2.0/",
		EndpointOverride:     mockCarina.URL,
		UserName:             "fake-user",
		APIKey:               "fake-apikey",
		Region:               "DFW",
	}

	svc := &MakeCOE{Account: acct}
	cluster := newCluster()
	cluster.Name = "test"
	cluster.ID = "99999999-9999-9999-9999-999999999999"

	err := svc.WaitUntilClusterIsDeleted(cluster)
	if err == nil {
		t.Error("WaitUntilClusterIsDeleted didn't stop when the cluster was in the error state.")
	} else {
		actualError := err.Error()
		expectedError := "Unable to delete cluster, an error occured while deleting."
		if expectedError != actualError {
			t.Errorf("Expected error: %s\nActual Error: %+v", expectedError, err)

		}
	}
}
<commit_msg>Refactor into re-usable test api and service for make-coe tests<commit_after>package makecoe

import (
	"net/http"
	"testing"
	"regexp"

	"fmt"
	"net/http/httptest"

	"github.com/getcarina/carina/common"
)

const identityAPIVersion = "/v2.0/"

var anyClusterRegexp = regexp.MustCompile("^/clusters/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")

type handler func(w http.ResponseWriter, r *http.Request)

func identityHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	switch r.RequestURI {
	case "/v2.0/tokens":
		fmt.Fprintln(w, `{"access":{"serviceCatalog":[{"endpoints":[{"tenantId":"963451","publicURL":"https:\/\/api.dfw.getcarina.com","region":"DFW"}],"name":"cloudContainer","type":"rax:container"}],"user":{"name":"fake-user","id":"fake-userid"},"token":{"expires":"3000-01-01T12:00:00Z","id":"fake-token","tenant":{"name":"fake-tenantname","id":"fake-tenantid"}}}}`)
	default:
		w.WriteHeader(404)
		fmt.Fprintln(w, "unexpected request: "+r.RequestURI)
	}
}

func clusterInErrorHandler(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		switch {
		case anyClusterRegexp.MatchString(r.RequestURI):
			fmt.Fprintln(w, `{ "status": "error" }`)
		default:
			fmt.Fprintln(w, "unexpected request: "+r.RequestURI)
			w.WriteHeader(404)
		}
}

func createMockCarina(h handler) (*httptest.Server, *httptest.Server) {
	return httptest.NewServer(http.HandlerFunc(h)), httptest.NewServer(http.HandlerFunc(identityHandler))
}

func createMakeCOEService(identityServer *httptest.Server, carinaServer *httptest.Server) *MakeCOE {
	acct := &Account{
		AuthEndpointOverride: identityServer.URL + identityAPIVersion,
		EndpointOverride:     carinaServer.URL,
		UserName:             "fake-user",
		APIKey:               "fake-apikey",
		Region:               "DFW",
	}

	return &MakeCOE{Account: acct}
}

func TestDeleteClusterThenErrorOutHaltsWait(t *testing.T) {
	common.Log.RegisterTestLogger(t)

	mockCarina, mockIdentity := createMockCarina(clusterInErrorHandler)
	defer mockCarina.Close()
	defer mockIdentity.Close()

	svc := createMakeCOEService(mockIdentity, mockCarina)
	cluster := newCluster()
	cluster.Name = "test"
	cluster.ID = "99999999-9999-9999-9999-999999999999"

	err := svc.WaitUntilClusterIsDeleted(cluster)
	if err == nil {
		t.Error("WaitUntilClusterIsDeleted didn't stop when the cluster was in the error state.")
	} else {
		actualError := err.Error()
		expectedError := "Unable to delete cluster, an error occured while deleting."
		if expectedError != actualError {
			t.Errorf("Expected error: %s\nActual Error: %+v", expectedError, err)

		}
	}
}
<|endoftext|>


<commit_before>package setup

import (
	"testing"

	caddylog "github.com/mholt/caddy/middleware/log"
)

func TestLog(t *testing.T) {

	c := newTestController(`log`)

	mid, err := Log(c)

	if err != nil {
		t.Errorf("Expected no errors, got: %v", err)
	}

	if mid == nil {
		t.Fatal("Expected middleware, was nil instead")
	}

	handler := mid(emptyNext)
	myHandler, ok := handler.(caddylog.Logger)

	if !ok {
		t.Fatalf("Expected handler to be type Ext, got: %#v", handler)
	}

	if myHandler.Rules[0].PathScope != "/" {
		t.Errorf("Expected / as the default PathScope")
	}
	if myHandler.Rules[0].OutputFile != caddylog.DefaultLogFilename {
		t.Errorf("Expected %s as the default OutputFile", caddylog.DefaultLogFilename)
	}
	if myHandler.Rules[0].Format != caddylog.DefaultLogFormat {
		t.Errorf("Expected %s as the default Log Format", caddylog.DefaultLogFormat)
	}
	if !sameNext(myHandler.Next, emptyNext) {
		t.Error("'Next' field of handler was not set properly")
	}

}

func TestLogParse(t *testing.T) {
	tests := []struct {
		inputLogRules    string
		shouldErr        bool
		expectedLogRules []caddylog.Rule
	}{
		{`log`, false, []caddylog.Rule{{
			PathScope:  "/",
			OutputFile: caddylog.DefaultLogFilename,
			Format:     caddylog.DefaultLogFormat,
		}}},
		{`log log.txt`, false, []caddylog.Rule{{
			PathScope:  "/",
			OutputFile: "log.txt",
			Format:     caddylog.DefaultLogFormat,
		}}},
		{`log /api log.txt`, false, []caddylog.Rule{{
			PathScope:  "/api",
			OutputFile: "log.txt",
			Format:     caddylog.DefaultLogFormat,
		}}},
	}
	for i, test := range tests {
		c := newTestController(test.inputLogRules)
		actualLogRules, err := logParse(c)

		if err == nil && test.shouldErr {
			t.Errorf("Test %d didn't error, but it should have", i)
		} else if err != nil && !test.shouldErr {
			t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
		}
		if len(actualLogRules) != len(test.expectedLogRules) {
			t.Fatalf("Test %d expected %d no of Log rules, but got %d ",
				i, len(test.expectedLogRules), len(actualLogRules))
		}
		for j, actualLogRule := range actualLogRules {

			if actualLogRule.PathScope != test.expectedLogRules[j].PathScope {
				t.Errorf("Test %d expected %dth LogRule PathScope to be  %s  , but got %s",
					i, j, test.expectedLogRules[j].PathScope, actualLogRule.PathScope)
			}

			if actualLogRule.OutputFile != test.expectedLogRules[j].OutputFile {
				t.Errorf("Test %d expected %dth LogRule OutputFile to be  %s  , but got %s",
					i, j, test.expectedLogRules[j].OutputFile, actualLogRule.OutputFile)
			}

			if actualLogRule.Format != test.expectedLogRules[j].Format {
				t.Errorf("Test %d expected %dth LogRule Format to be  %s  , but got %s",
					i, j, test.expectedLogRules[j].Format, actualLogRule.Format)
			}
		}
	}

}
<commit_msg>more test cases for the test struct<commit_after>package setup

import (
	"testing"

	caddylog "github.com/mholt/caddy/middleware/log"
)

func TestLog(t *testing.T) {

	c := newTestController(`log`)

	mid, err := Log(c)

	if err != nil {
		t.Errorf("Expected no errors, got: %v", err)
	}

	if mid == nil {
		t.Fatal("Expected middleware, was nil instead")
	}

	handler := mid(emptyNext)
	myHandler, ok := handler.(caddylog.Logger)

	if !ok {
		t.Fatalf("Expected handler to be type Ext, got: %#v", handler)
	}

	if myHandler.Rules[0].PathScope != "/" {
		t.Errorf("Expected / as the default PathScope")
	}
	if myHandler.Rules[0].OutputFile != caddylog.DefaultLogFilename {
		t.Errorf("Expected %s as the default OutputFile", caddylog.DefaultLogFilename)
	}
	if myHandler.Rules[0].Format != caddylog.DefaultLogFormat {
		t.Errorf("Expected %s as the default Log Format", caddylog.DefaultLogFormat)
	}
	if !sameNext(myHandler.Next, emptyNext) {
		t.Error("'Next' field of handler was not set properly")
	}

}

func TestLogParse(t *testing.T) {
	tests := []struct {
		inputLogRules    string
		shouldErr        bool
		expectedLogRules []caddylog.Rule
	}{
		{`log`, false, []caddylog.Rule{{
			PathScope:  "/",
			OutputFile: caddylog.DefaultLogFilename,
			Format:     caddylog.DefaultLogFormat,
		}}},
		{`log log.txt`, false, []caddylog.Rule{{
			PathScope:  "/",
			OutputFile: "log.txt",
			Format:     caddylog.DefaultLogFormat,
		}}},
		{`log /api log.txt`, false, []caddylog.Rule{{
			PathScope:  "/api",
			OutputFile: "log.txt",
			Format:     caddylog.DefaultLogFormat,
		}}},
		{`log /serve stdout`, false, []caddylog.Rule{{
			PathScope:  "/serve",
			OutputFile: "stdout",
			Format:     caddylog.DefaultLogFormat,
		}}},
		{`log /myapi log.txt {common}`, false, []caddylog.Rule{{
			PathScope:  "/myapi",
			OutputFile: "log.txt",
			Format:     caddylog.CommonLogFormat,
		}}},
		{`log /test accesslog.txt {combined}`, false, []caddylog.Rule{{
			PathScope:  "/test",
			OutputFile: "accesslog.txt",
			Format:     caddylog.CombinedLogFormat,
		}}},
		{`log /api1 log.txt
		  log /api2 accesslog.txt {combined}`, false, []caddylog.Rule{{
			PathScope:  "/api1",
			OutputFile: "log.txt",
			Format:     caddylog.DefaultLogFormat,
		}, {
			PathScope:  "/api2",
			OutputFile: "accesslog.txt",
			Format:     caddylog.CombinedLogFormat,
		}}},
		{`log /api3 stdout {host}
		  log /api4 log.txt {when}`, false, []caddylog.Rule{{
			PathScope:  "/api3",
			OutputFile: "stdout",
			Format:     "{host}",
		}, {
			PathScope:  "/api4",
			OutputFile: "log.txt",
			Format:     "{when}",
		}}},
	}
	for i, test := range tests {
		c := newTestController(test.inputLogRules)
		actualLogRules, err := logParse(c)

		if err == nil && test.shouldErr {
			t.Errorf("Test %d didn't error, but it should have", i)
		} else if err != nil && !test.shouldErr {
			t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
		}
		if len(actualLogRules) != len(test.expectedLogRules) {
			t.Fatalf("Test %d expected %d no of Log rules, but got %d ",
				i, len(test.expectedLogRules), len(actualLogRules))
		}
		for j, actualLogRule := range actualLogRules {

			if actualLogRule.PathScope != test.expectedLogRules[j].PathScope {
				t.Errorf("Test %d expected %dth LogRule PathScope to be  %s  , but got %s",
					i, j, test.expectedLogRules[j].PathScope, actualLogRule.PathScope)
			}

			if actualLogRule.OutputFile != test.expectedLogRules[j].OutputFile {
				t.Errorf("Test %d expected %dth LogRule OutputFile to be  %s  , but got %s",
					i, j, test.expectedLogRules[j].OutputFile, actualLogRule.OutputFile)
			}

			if actualLogRule.Format != test.expectedLogRules[j].Format {
				t.Errorf("Test %d expected %dth LogRule Format to be  %s  , but got %s",
					i, j, test.expectedLogRules[j].Format, actualLogRule.Format)
			}
		}
	}

}
<|endoftext|>


<commit_before>package mathutil

import (
	"errors"
	"sort"
)

type SliceInt struct {
	Elements []int
	Stats    SliceIntStats
}

func NewSliceInt() SliceInt {
	sint := SliceInt{Elements: []int{}}
	return sint
}

func (sint *SliceInt) Append(num int) {
	sint.Elements = append(sint.Elements, num)
}

func (sint *SliceInt) Count() int {
	return sint.Len()
}

func (sint *SliceInt) Len() int {
	return len(sint.Elements)
}

func (sint *SliceInt) Sort() {
	sort.Ints(sint.Elements)
}

func (sint *SliceInt) Min() (int, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	if !sort.IntsAreSorted(sint.Elements) {
		sort.Ints(sint.Elements)
	}
	return sint.Elements[0], nil
}

func (sint *SliceInt) Max() (int, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	if !sort.IntsAreSorted(sint.Elements) {
		sort.Ints(sint.Elements)
	}
	return sint.Elements[len(sint.Elements)-1], nil
}

func (sint *SliceInt) Sum() (int, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	sum := int(0)
	for _, num := range sint.Elements {
		sum += num
	}
	return sum, nil
}

func (sint *SliceInt) Average() (float64, error) {
	return sint.Mean()
}

func (sint *SliceInt) Mean() (float64, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	sum, err := sint.Sum()
	if err != nil {
		return 0, err
	}
	return float64(sum) / float64(len(sint.Elements)), nil
}

func (sint *SliceInt) Median() (int, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	if !sort.IntsAreSorted(sint.Elements) {
		sort.Ints(sint.Elements)
	}
	mid := int64(float64(len(sint.Elements)) / 2)
	return sint.Elements[mid], nil
}

func (sint *SliceInt) BuildStats() (SliceIntStats, error) {
	stats := NewSliceIntStats()
	stats.Len = sint.Len()
	max, err := sint.Max()
	if err != nil {
		return stats, err
	}
	stats.Max = max
	min, err := sint.Min()
	if err != nil {
		return stats, err
	}
	stats.Min = min
	mean, err := sint.Mean()
	if err != nil {
		return stats, err
	}
	stats.Mean = mean
	median, err := sint.Median()
	if err != nil {
		return stats, err
	}
	stats.Median = median
	sum, err := sint.Sum()
	if err != nil {
		return stats, err
	}
	stats.Sum = sum
	sint.Stats = stats
	return stats, nil
}

type SliceIntStats struct {
	Len    int
	Max    int
	Mean   float64
	Median int
	Min    int
	Sum    int
}

func NewSliceIntStats() SliceIntStats {
	stats := SliceIntStats{
		Len:    0,
		Max:    0,
		Mean:   0,
		Median: 0,
		Min:    0,
		Sum:    0}
	return stats
}
<commit_msg>add comments<commit_after>package mathutil

import (
	"errors"
	"sort"
)

// SliceInt represets a slice of integers and provides functions on that slice.
type SliceInt struct {
	Elements []int
	Stats    SliceIntStats
}

// NewSliceInt creates and returns an empty SliceInt struct.
func NewSliceInt() SliceInt {
	sint := SliceInt{Elements: []int{}}
	return sint
}

// Append adds an integer to the elements slice.
func (sint *SliceInt) Append(num int) {
	sint.Elements = append(sint.Elements, num)
}

// Len returns the number of items in the integer slice.
func (sint *SliceInt) Len() int {
	return len(sint.Elements)
}

// Sort sorts the elements in the integer slice.
func (sint *SliceInt) Sort() {
	sort.Ints(sint.Elements)
}

// Min returns the minimum element value in the integer slice.
func (sint *SliceInt) Min() (int, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	if !sort.IntsAreSorted(sint.Elements) {
		sort.Ints(sint.Elements)
	}
	return sint.Elements[0], nil
}

// Min returns the maximum element value in the integer slice.
func (sint *SliceInt) Max() (int, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	if !sort.IntsAreSorted(sint.Elements) {
		sort.Ints(sint.Elements)
	}
	return sint.Elements[len(sint.Elements)-1], nil
}

// Sum returns sum of all the elements in the integer slice.
func (sint *SliceInt) Sum() (int, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	sum := int(0)
	for _, num := range sint.Elements {
		sum += num
	}
	return sum, nil
}

// Alias for Mean.
func (sint *SliceInt) Average() (float64, error) {
	return sint.Mean()
}

// Mean returns the arithmetic mean of the integer slice.
func (sint *SliceInt) Mean() (float64, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	sum, err := sint.Sum()
	if err != nil {
		return 0, err
	}
	return float64(sum) / float64(len(sint.Elements)), nil
}

// Median returns the median or middle value of the sorted integer slice.
func (sint *SliceInt) Median() (int, error) {
	if len(sint.Elements) == 0 {
		return 0, errors.New("List is empty")
	}
	if !sort.IntsAreSorted(sint.Elements) {
		sort.Ints(sint.Elements)
	}
	mid := int64(float64(len(sint.Elements)) / 2)
	return sint.Elements[mid], nil
}

// BuildStats builds a stats struct for current integer slice elements.
func (sint *SliceInt) BuildStats() (SliceIntStats, error) {
	stats := NewSliceIntStats()
	stats.Len = sint.Len()
	max, err := sint.Max()
	if err != nil {
		return stats, err
	}
	stats.Max = max
	min, err := sint.Min()
	if err != nil {
		return stats, err
	}
	stats.Min = min
	mean, err := sint.Mean()
	if err != nil {
		return stats, err
	}
	stats.Mean = mean
	median, err := sint.Median()
	if err != nil {
		return stats, err
	}
	stats.Median = median
	sum, err := sint.Sum()
	if err != nil {
		return stats, err
	}
	stats.Sum = sum
	sint.Stats = stats
	return stats, nil
}

// SliceIntStats represents a set of statistics for a set of integers.
type SliceIntStats struct {
	Len    int
	Max    int
	Mean   float64
	Median int
	Min    int
	Sum    int
}

// NewSliceIntStats returns a new initialized SliceIntStats struct.
func NewSliceIntStats() SliceIntStats {
	stats := SliceIntStats{
		Len:    0,
		Max:    0,
		Mean:   0,
		Median: 0,
		Min:    0,
		Sum:    0}
	return stats
}
<|endoftext|>


<commit_before>/*
   Copyright 2017 Artem Sidorenko <artem@posteo.de>

   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 connectors contains different connectors for fetching the data
package connectors

import (
	"fmt"
	"time"
)

// Tag describes a git tag
type Tag struct {
	Name   string
	Commit string
	Date   time.Time
}

// Tags is a slice with Tag elements
type Tags []Tag

// Connector describes the interface of connectors to the data sources
type Connector interface {
	Init()
	GetTags() Tags
}

type connector struct {
	name      string
	connector Connector
}

var connectors map[string]connector

func init() {
	connectors = make(map[string]connector)
}

// RegisterConnector registers the new connector for fetching the data.
// id is used as internal id or as value for CLI flag
// name is a text name of connector for humans (e.g. help pages)
// other parameter is the connector interface
func RegisterConnector(id, name string, c Connector) {
	connectors[id] = connector{
		name:      name,
		connector: c,
	}
}

// GetConnector returns the Connector of given id
// if this connector is missing, error is returned
func GetConnector(id string) (Connector, error) {
	if _, ok := connectors[id]; !ok {
		return nil, fmt.Errorf("Unknown connector: %s", id)
	}
	return connectors[id].connector, nil
}
<commit_msg>Removing the connectors init()<commit_after>/*
   Copyright 2017 Artem Sidorenko <artem@posteo.de>

   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 connectors contains different connectors for fetching the data
package connectors

import (
	"fmt"
	"time"
)

// Tag describes a git tag
type Tag struct {
	Name   string
	Commit string
	Date   time.Time
}

// Tags is a slice with Tag elements
type Tags []Tag

// Connector describes the interface of connectors to the data sources
type Connector interface {
	Init()
	GetTags() Tags
}

type connector struct {
	name      string
	connector Connector
}

var connectors = make(map[string]connector)

// RegisterConnector registers the new connector for fetching the data.
// id is used as internal id or as value for CLI flag
// name is a text name of connector for humans (e.g. help pages)
// other parameter is the connector interface
func RegisterConnector(id, name string, c Connector) {
	connectors[id] = connector{
		name:      name,
		connector: c,
	}
}

// GetConnector returns the Connector of given id
// if this connector is missing, error is returned
func GetConnector(id string) (Connector, error) {
	if _, ok := connectors[id]; !ok {
		return nil, fmt.Errorf("Unknown connector: %s", id)
	}
	return connectors[id].connector, nil
}
<|endoftext|>