<commit_before>/*
Copyright 2011 Google Inc.

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

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

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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 server

import (
	"fmt"
	"net/http"

	"camlistore.org/pkg/auth"
	"camlistore.org/pkg/blobserver"
	"camlistore.org/pkg/jsonconfig"
)

// RootHandler handles serving the about/splash page.
type RootHandler struct {
	// Don't advertise anything to non-authenticated clients.
	Stealth bool
}

func init() {
	blobserver.RegisterHandlerConstructor("root", newRootFromConfig)
}

func newRootFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, err error) {
	root := &RootHandler{}
	root.Stealth = conf.OptionalBool("stealth", false)
	if err = conf.Validate(); err != nil {
		return
	}

	return root, nil
}

func (rh *RootHandler) ServeHTTP(conn http.ResponseWriter, req *http.Request) {
	if rh.Stealth {
		return
	}

	configLink := ""
	if auth.LocalhostAuthorized(req) {
		configLink = "
If you're coming from localhost, hit /setup.
"
	}
	fmt.Fprintf(conn,
		"This is camlistored, a "+
			"Camlistore server."+
			"%s\n", configLink)
}
<commit_msg>Add discovery handler on root.<commit_after>/*
Copyright 2011 Google Inc.

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

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

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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 server

import (
	"fmt"
	"net/http"

	"camlistore.org/pkg/auth"
	"camlistore.org/pkg/blobserver"
	"camlistore.org/pkg/jsonconfig"
)

// RootHandler handles serving the about/splash page.
type RootHandler struct {
	// Don't advertise anything to non-authenticated clients.
	Stealth bool

	ui *UIHandler // or nil, if none configured
}

func init() {
	blobserver.RegisterHandlerConstructor("root", newRootFromConfig)
}

func newRootFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, err error) {
	root := &RootHandler{}
	root.Stealth = conf.OptionalBool("stealth", false)
	if err = conf.Validate(); err != nil {
		return
	}

	if _, h, err := ld.FindHandlerByType("ui"); err == nil {
		root.ui = h.(*UIHandler)
	}

	return root, nil
}

func (rh *RootHandler) ServeHTTP(conn http.ResponseWriter, req *http.Request) {
	if rh.ui != nil && camliMode(req) == "config" && auth.IsAuthorized(req) {
		rh.ui.serveDiscovery(conn, req)
		return
	}

	if rh.Stealth {
		return
	}

	configLink := ""
	if auth.LocalhostAuthorized(req) {
		configLink = "
If you're coming from localhost, hit /setup.
"
	}
	fmt.Fprintf(conn,
		"This is camlistored, a "+
			"Camlistore server."+
			"%s\n", configLink)
}
<|endoftext|>


<commit_before>package arduino

import (
	"context"
	"fmt"
	"io"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/tarm/serial"

	log "github.com/sirupsen/logrus"
)

// Holds all our serial connection information
type ArduinoConnection struct {
	Interface *serial.Port
}

/**
 * Read from the connection's serial buffer and writes to the
 * passed byte array.
 * @param  {[]byte}     buf The byte buffer to write to
 * @return {int, error}     The number of read bytes and an error, if any
 */
func (arduino ArduinoConnection) Read(buf []byte) (int, error) {
	return arduino.Interface.Read(buf)
}

/**
 * Write a string to the serial buffer.
 * @param  {string}     data The string to write to the arduino
 * @return {int, error}			 The number of written bytes and an error, if any
 */
func (arduino ArduinoConnection) Write(data string) (int, error) {
	buf := []byte(data) // Ensure our data is a byte array
	return arduino.Interface.Write(buf)
}

/**
 * Send a command to the arduino.
 * @param  {int}  command The command number to pass to the arduino
 * @return {bool} success Notifies if the command was successfully recieved
 */
func (arduino ArduinoConnection) Command(command int, ctx context.Context) (success bool) {
	log.Debugf("Sending command: %d\n", command)

	cmd := fmt.Sprintf("<%d>", command) // Format our command for serial
	done := make(chan bool, 1)          // Create an execution blocker

	// Continually listen for the command response while we send the command
	success = false
	go func() {
		err := arduino.readCommand(command, ctx)
		if err != nil {
			if strings.Contains(fmt.Sprintf("%v", err), "timed out") {
				log.Errorf("Timed out listening for response to command <%d>", command)
				close(done)
				return
			}
			// Release execution blocker and exit the goroutine
			log.Errorf("Error executing command: %v", err)
			close(done)
			return
		}
		success = true
		close(done)
	}()

	// Send the command and await the response before returning success status
	if command != 0 {
		arduino.Write(cmd)
		<-done
		return
	}

	// The arduino doesn't always respond nicely to the first command it gets
	// so send the "Hello" command 5 times rather than once
	var i int
	for i < 5 {
		i++
		arduino.Write(cmd)
	}
	<-done
	return
}

/**
 * Reads a command response from the arduino.
 * @param  {int}   command The command that was sent
 * @return {error}         A timeout or read error
 */
func (arduino ArduinoConnection) readCommand(command int, ctx context.Context) error {
	for {
		value, err := arduino.ReadLine(ctx, true)
		if err != nil {
			return err
		}

		// Command response not in read buffer
		re := regexp.MustCompile(`^\d+$`)
		if !re.MatchString(value) {
			continue
		}

		// The recieved response is for the requested command
		if strings.Contains(value, strconv.Itoa(command)) {
			return nil
		}

		return fmt.Errorf("Failed to read command response")
	}
}

/**
 * Reads a single line, delimeted by < and >, from the arduino.
 * @param  {context.Context} ctx 			  The application context
 * @return {string, error}   line, err  The read line and any error
 */
func (arduino ArduinoConnection) ReadLine(ctx context.Context, enableTimeout bool) (line string, err error) {
	capturing := false
	capturingErr := false
	var timer time.Time
	if enableTimeout {
		timer = time.Now().Add(3 * time.Second)
	}
	for {
		select {
		case <-ctx.Done():
			line = ""
			err = fmt.Errorf("Read terminated for shutdown")
			return
		default:
			if enableTimeout && time.Now().After(timer) {
				return "", fmt.Errorf("Request timed out")
			}
			// Read from the serial buffer
			var buf = make([]byte, 8192)
			var nr int
			nr, err = arduino.Read(buf)
			if err == io.EOF {
				// Read timeout occurred, but we don't care; keep looping
				continue
			}

			value := strings.TrimSpace(string(buf[:nr]))

			wholeError := regexp.MustCompile(`.*\$\$(.*?)\$\$.*`)
			if wholeError.MatchString(value) {
				line = ""
				err = fmt.Errorf("Error from Arduino: %s", strings.TrimSpace(wholeError.FindStringSubmatch(value)[1]))
				return
			}

			errorStart := regexp.MustCompile(`.*\$\$(.*)`)
			if errorStart.MatchString(value) {
				line = ""
				capturing = true
				capturingErr = true

				line = errorStart.FindStringSubmatch(value)[1]
				continue
			}

			errorEnd := regexp.MustCompile(`(.*?)\$\$.*`)
			if capturingErr && errorEnd.MatchString(value) {
				capturingErr = false
				err = fmt.Errorf("Error from Arduino: %s", strings.TrimSpace(line+errorEnd.FindStringSubmatch(value)[1]))
				line = ""
				return
			}

			// Handle lines that contain a whole data point like:
			// <0,1,2,3>
			wholeSequence := regexp.MustCompile(".*<(.*?)>.*")
			if wholeSequence.MatchString(value) {
				// Retrieve the group between the <> delimiters
				line = wholeSequence.FindStringSubmatch(value)[1]
				// Send an EOF error if we recieve "<>"
				if line == "" {
					err = io.EOF
				}
				return
			}

			// Handle lines that have a start delimiter and optional following text
			startSequence := regexp.MustCompile(".*<(.*)")
			if startSequence.MatchString(value) {
				// Ensure the line is empty as this is a new start delimiter
				line = ""
				capturing = true

				line = startSequence.FindStringSubmatch(value)[1]
				continue
			}

			// Handle lines that have end delimiter and preceeding text
			endSequenceText := regexp.MustCompile("(.*?)>.*")
			if capturing && endSequenceText.MatchString(value) {
				capturing = false
				line = line + endSequenceText.FindStringSubmatch(value)[1]
				if line == "" {
					err = io.EOF
				}
				return
			}

			// Handle lines that have an end delimiter only
			endSequence := regexp.MustCompile(">")
			if capturing && endSequence.MatchString(value) {
				if line == "" {
					err = io.EOF
				}
				return
			}

			// Handle lines with no delimiters if we've already seen a start delimiter
			if capturing {
				line = line + value
			}
		}
	}
}

/**
 * Creates a new arduino connection to be used elsewhere
 * @return {ArduinoConnection, error} The connection and any error
 */
func NewArduinoConnection(ctx context.Context) (ArduinoConnection, error) {
	// Discover TTYs
	// matches, err := filepath.Glob("./virtual-tty")
	matches, err := filepath.Glob("/dev/tty[A-Za-z]*")
	if err != nil {
		log.Fatalf("Failed to glob /dev/tty[A-Za-z]*")
	}

	// Attempt to connect to a discovered TTY and say hello to initialize
	var tty *serial.Port
	for _, match := range matches {
		c := &serial.Config{Name: match, Baud: 115200, ReadTimeout: 7 * time.Second}
		tty, err = serial.OpenPort(c)
		if err != nil {
			// Failed to open TTY
			continue
		}

		log.Debug("Opening", match)
		connection := ArduinoConnection{
			Interface: tty,
		}

		log.Debug("Attempting to say hello...")
		i := 0
		for i < 3 {
			success := connection.Command(0, ctx)
			if success {
				return connection, nil
			}
			i++
			log.Debugf("Attempt #%d failed", i)
		}

		return ArduinoConnection{}, fmt.Errorf("Failed to recieve hello response back")
	}

	panic("Failed to connect to any TTY")
}
<commit_msg>Add readline debug message<commit_after>package arduino

import (
	"context"
	"fmt"
	"io"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/tarm/serial"

	log "github.com/sirupsen/logrus"
)

// Holds all our serial connection information
type ArduinoConnection struct {
	Interface *serial.Port
}

/**
 * Read from the connection's serial buffer and writes to the
 * passed byte array.
 * @param  {[]byte}     buf The byte buffer to write to
 * @return {int, error}     The number of read bytes and an error, if any
 */
func (arduino ArduinoConnection) Read(buf []byte) (int, error) {
	return arduino.Interface.Read(buf)
}

/**
 * Write a string to the serial buffer.
 * @param  {string}     data The string to write to the arduino
 * @return {int, error}			 The number of written bytes and an error, if any
 */
func (arduino ArduinoConnection) Write(data string) (int, error) {
	buf := []byte(data) // Ensure our data is a byte array
	return arduino.Interface.Write(buf)
}

/**
 * Send a command to the arduino.
 * @param  {int}  command The command number to pass to the arduino
 * @return {bool} success Notifies if the command was successfully recieved
 */
func (arduino ArduinoConnection) Command(command int, ctx context.Context) (success bool) {
	log.Debugf("Sending command: %d\n", command)

	cmd := fmt.Sprintf("<%d>", command) // Format our command for serial
	done := make(chan bool, 1)          // Create an execution blocker

	// Continually listen for the command response while we send the command
	success = false
	go func() {
		err := arduino.readCommand(command, ctx)
		if err != nil {
			if strings.Contains(fmt.Sprintf("%v", err), "timed out") {
				log.Errorf("Timed out listening for response to command <%d>", command)
				close(done)
				return
			}
			// Release execution blocker and exit the goroutine
			log.Errorf("Error executing command: %v", err)
			close(done)
			return
		}
		success = true
		close(done)
	}()

	// Send the command and await the response before returning success status
	if command != 0 {
		arduino.Write(cmd)
		<-done
		return
	}

	// The arduino doesn't always respond nicely to the first command it gets
	// so send the "Hello" command 5 times rather than once
	var i int
	for i < 5 {
		i++
		arduino.Write(cmd)
	}
	<-done
	return
}

/**
 * Reads a command response from the arduino.
 * @param  {int}   command The command that was sent
 * @return {error}         A timeout or read error
 */
func (arduino ArduinoConnection) readCommand(command int, ctx context.Context) error {
	for {
		value, err := arduino.ReadLine(ctx, true)
		if err != nil {
			return err
		}

		// Command response not in read buffer
		re := regexp.MustCompile(`^\d+$`)
		if !re.MatchString(value) {
			continue
		}

		// The recieved response is for the requested command
		if strings.Contains(value, strconv.Itoa(command)) {
			return nil
		}

		return fmt.Errorf("Failed to read command response")
	}
}

/**
 * Reads a single line, delimeted by < and >, from the arduino.
 * @param  {context.Context} ctx 			  The application context
 * @return {string, error}   line, err  The read line and any error
 */
func (arduino ArduinoConnection) ReadLine(ctx context.Context, enableTimeout bool) (line string, err error) {
	capturing := false
	capturingErr := false
	var timer time.Time
	if enableTimeout {
		timer = time.Now().Add(3 * time.Second)
	}
	for {
		select {
		case <-ctx.Done():
			line = ""
			err = fmt.Errorf("Read terminated for shutdown")
			return
		default:
			if enableTimeout && time.Now().After(timer) {
				return "", fmt.Errorf("Request timed out")
			}
			// Read from the serial buffer
			var buf = make([]byte, 8192)
			var nr int
			nr, err = arduino.Read(buf)
			if err == io.EOF {
				// Read timeout occurred, but we don't care; keep looping
				continue
			}

			log.Debugf("Read: %s", strings.TrimSpace(string(buf[:nr])))

			value := strings.TrimSpace(string(buf[:nr]))

			wholeError := regexp.MustCompile(`.*\$\$(.*?)\$\$.*`)
			if wholeError.MatchString(value) {
				line = ""
				err = fmt.Errorf("Error from Arduino: %s", strings.TrimSpace(wholeError.FindStringSubmatch(value)[1]))
				return
			}

			errorStart := regexp.MustCompile(`.*\$\$(.*)`)
			if errorStart.MatchString(value) {
				line = ""
				capturing = true
				capturingErr = true

				line = errorStart.FindStringSubmatch(value)[1]
				continue
			}

			errorEnd := regexp.MustCompile(`(.*?)\$\$.*`)
			if capturingErr && errorEnd.MatchString(value) {
				capturingErr = false
				err = fmt.Errorf("Error from Arduino: %s", strings.TrimSpace(line+errorEnd.FindStringSubmatch(value)[1]))
				line = ""
				return
			}

			// Handle lines that contain a whole data point like:
			// <0,1,2,3>
			wholeSequence := regexp.MustCompile(".*<(.*?)>.*")
			if wholeSequence.MatchString(value) {
				// Retrieve the group between the <> delimiters
				line = wholeSequence.FindStringSubmatch(value)[1]
				// Send an EOF error if we recieve "<>"
				if line == "" {
					err = io.EOF
				}
				return
			}

			// Handle lines that have a start delimiter and optional following text
			startSequence := regexp.MustCompile(".*<(.*)")
			if startSequence.MatchString(value) {
				// Ensure the line is empty as this is a new start delimiter
				line = ""
				capturing = true

				line = startSequence.FindStringSubmatch(value)[1]
				continue
			}

			// Handle lines that have end delimiter and preceeding text
			endSequenceText := regexp.MustCompile("(.*?)>.*")
			if capturing && endSequenceText.MatchString(value) {
				capturing = false
				line = line + endSequenceText.FindStringSubmatch(value)[1]
				if line == "" {
					err = io.EOF
				}
				return
			}

			// Handle lines that have an end delimiter only
			endSequence := regexp.MustCompile(">")
			if capturing && endSequence.MatchString(value) {
				if line == "" {
					err = io.EOF
				}
				return
			}

			// Handle lines with no delimiters if we've already seen a start delimiter
			if capturing {
				line = line + value
			}
		}
	}
}

/**
 * Creates a new arduino connection to be used elsewhere
 * @return {ArduinoConnection, error} The connection and any error
 */
func NewArduinoConnection(ctx context.Context) (ArduinoConnection, error) {
	// Discover TTYs
	// matches, err := filepath.Glob("./virtual-tty")
	matches, err := filepath.Glob("/dev/tty[A-Za-z]*")
	if err != nil {
		log.Fatalf("Failed to glob /dev/tty[A-Za-z]*")
	}

	// Attempt to connect to a discovered TTY and say hello to initialize
	var tty *serial.Port
	for _, match := range matches {
		c := &serial.Config{Name: match, Baud: 115200, ReadTimeout: 7 * time.Second}
		tty, err = serial.OpenPort(c)
		if err != nil {
			// Failed to open TTY
			continue
		}

		log.Debug("Opening", match)
		connection := ArduinoConnection{
			Interface: tty,
		}

		log.Debug("Attempting to say hello...")
		i := 0
		for i < 3 {
			success := connection.Command(0, ctx)
			if success {
				return connection, nil
			}
			i++
			log.Debugf("Attempt #%d failed", i)
		}

		return ArduinoConnection{}, fmt.Errorf("Failed to recieve hello response back")
	}

	panic("Failed to connect to any TTY")
}
<|endoftext|>


<commit_before>package datapeer

import (
	"bytes"
	"crypto"
	"fmt"
	"io"
	"io/ioutil"
	"sync"

	"github.com/johnny-morrice/godless/api"
	"github.com/johnny-morrice/godless/internal/util"
	"github.com/johnny-morrice/godless/log"
)

func MakeResidentMemoryDataPeer(options ResidentMemoryStorageOptions) api.DataPeer {
	storage := MakeResidentMemoryStorage(options)
	pubsubber := MakeResidentMemoryPubSubBus()

	return Union{
		Storage:    storage,
		Publisher:  pubsubber,
		Subscriber: pubsubber,
	}
}

type ResidentMemoryStorageOptions struct {
	Hash crypto.Hash
}

type residentMemoryStorage struct {
	sync.RWMutex
	ResidentMemoryStorageOptions
	hashes map[string][]byte
}

func MakeResidentMemoryStorage(options ResidentMemoryStorageOptions) api.ContentAddressableStorage {
	return &residentMemoryStorage{
		ResidentMemoryStorageOptions: options,
		hashes: map[string][]byte{},
	}
}

func (storage *residentMemoryStorage) Cat(hash string) (io.ReadCloser, error) {
	log.Info("Catting '%s' from residentMemoryStorage", hash)
	storage.RLock()
	defer storage.RUnlock()
	data, ok := storage.hashes[hash]

	if !ok {
		return nil, fmt.Errorf("Data not found for '%s'", hash)
	}

	return ioutil.NopCloser(bytes.NewReader(data)), nil
}

func (storage *residentMemoryStorage) Add(r io.Reader) (string, error) {
	log.Info("Adding to residentMemoryStorage...")
	storage.Lock()
	defer storage.Unlock()
	data, err := ioutil.ReadAll(r)

	if err != nil {
		return "", err
	}

	hash := storage.Hash.New()
	addressBytes := hash.Sum(data)
	address := util.EncodeBase58(addressBytes)

	_, present := storage.hashes[address]
	if !present {
		storage.hashes[address] = data
	}

	log.Info("Added '%s' to residentMemoryStorage", address)

	return address, nil
}

type residentMemoryPubSubBus struct {
	sync.RWMutex
	bus []residentSubscription
}

func MakeResidentMemoryPubSubBus() api.PubSubber {
	return &residentMemoryPubSubBus{}
}

func (pubsubber *residentMemoryPubSubBus) PubSubPublish(topic, data string) error {
	log.Debug("Publishing '%s' to '%s'...", topic, data)

	pubsubber.RLock()
	defer pubsubber.RUnlock()

	for _, sub := range pubsubber.bus {
		subscription := sub
		if subscription.topic == topic {
			go subscription.publish(topic, data)
		}
	}

	return nil
}

func (pubsubber *residentMemoryPubSubBus) PubSubSubscribe(topic string) (api.PubSubSubscription, error) {
	log.Debug("Subscribing to '%s'...", topic)
	pubsubber.Lock()
	defer pubsubber.Unlock()

	subscription := residentSubscription{
		topic:  topic,
		nextch: make(chan api.PubSubRecord),
	}

	pubsubber.bus = append(pubsubber.bus, subscription)

	return subscription, nil
}

type residentSubscription struct {
	topic  string
	nextch chan api.PubSubRecord
}

func (subscription residentSubscription) publish(topic, data string) {
	record := residentPubSubRecord{
		data:   []byte(data),
		topics: []string{topic},
	}

	subscription.nextch <- record
}

func (subscription residentSubscription) Next() (api.PubSubRecord, error) {
	record := <-subscription.nextch
	return record, nil
}

type residentPubSubRecord struct {
	data   []byte
	topics []string
}

func (record residentPubSubRecord) From() string {
	return "Local Memory Peer"
}

func (record residentPubSubRecord) Data() []byte {
	return record.data
}

func (record residentPubSubRecord) SeqNo() int64 {
	return 0
}

// Not sure if TopicIds==Topics.
func (record residentPubSubRecord) TopicIDs() []string {
	return record.topics
}
<commit_msg>Fix incorrect hash usage for testing routine<commit_after>package datapeer

import (
	"bytes"
	"crypto"
	"fmt"
	"io"
	"io/ioutil"
	"sync"

	"github.com/johnny-morrice/godless/api"
	"github.com/johnny-morrice/godless/internal/util"
	"github.com/johnny-morrice/godless/log"
)

func MakeResidentMemoryDataPeer(options ResidentMemoryStorageOptions) api.DataPeer {
	storage := MakeResidentMemoryStorage(options)
	pubsubber := MakeResidentMemoryPubSubBus()

	return Union{
		Storage:    storage,
		Publisher:  pubsubber,
		Subscriber: pubsubber,
	}
}

type ResidentMemoryStorageOptions struct {
	Hash crypto.Hash
}

type residentMemoryStorage struct {
	sync.RWMutex
	ResidentMemoryStorageOptions
	hashes map[string][]byte
}

func MakeResidentMemoryStorage(options ResidentMemoryStorageOptions) api.ContentAddressableStorage {
	return &residentMemoryStorage{
		ResidentMemoryStorageOptions: options,
		hashes: map[string][]byte{},
	}
}

func (storage *residentMemoryStorage) Cat(hash string) (io.ReadCloser, error) {
	log.Info("Catting '%s' from residentMemoryStorage", hash)
	storage.RLock()
	defer storage.RUnlock()
	data, ok := storage.hashes[hash]

	if !ok {
		return nil, fmt.Errorf("Data not found for '%s'", hash)
	}

	return ioutil.NopCloser(bytes.NewReader(data)), nil
}

func (storage *residentMemoryStorage) Add(r io.Reader) (string, error) {
	log.Info("Adding to residentMemoryStorage...")
	storage.Lock()
	defer storage.Unlock()
	data, err := ioutil.ReadAll(r)

	if err != nil {
		return "", err
	}

	hash := storage.Hash.New()
	hash.Write(data)
	addressBytes := hash.Sum(nil)
	address := util.EncodeBase58(addressBytes)

	_, present := storage.hashes[address]
	if !present {
		storage.hashes[address] = data
	}

	log.Info("Added '%s' to residentMemoryStorage", address)

	return address, nil
}

type residentMemoryPubSubBus struct {
	sync.RWMutex
	bus []residentSubscription
}

func MakeResidentMemoryPubSubBus() api.PubSubber {
	return &residentMemoryPubSubBus{}
}

func (pubsubber *residentMemoryPubSubBus) PubSubPublish(topic, data string) error {
	log.Debug("Publishing '%s' to '%s'...", topic, data)

	pubsubber.RLock()
	defer pubsubber.RUnlock()

	for _, sub := range pubsubber.bus {
		subscription := sub
		if subscription.topic == topic {
			go subscription.publish(topic, data)
		}
	}

	return nil
}

func (pubsubber *residentMemoryPubSubBus) PubSubSubscribe(topic string) (api.PubSubSubscription, error) {
	log.Debug("Subscribing to '%s'...", topic)
	pubsubber.Lock()
	defer pubsubber.Unlock()

	subscription := residentSubscription{
		topic:  topic,
		nextch: make(chan api.PubSubRecord),
	}

	pubsubber.bus = append(pubsubber.bus, subscription)

	return subscription, nil
}

type residentSubscription struct {
	topic  string
	nextch chan api.PubSubRecord
}

func (subscription residentSubscription) publish(topic, data string) {
	record := residentPubSubRecord{
		data:   []byte(data),
		topics: []string{topic},
	}

	subscription.nextch <- record
}

func (subscription residentSubscription) Next() (api.PubSubRecord, error) {
	record := <-subscription.nextch
	return record, nil
}

type residentPubSubRecord struct {
	data   []byte
	topics []string
}

func (record residentPubSubRecord) From() string {
	return "Local Memory Peer"
}

func (record residentPubSubRecord) Data() []byte {
	return record.data
}

func (record residentPubSubRecord) SeqNo() int64 {
	return 0
}

// Not sure if TopicIds==Topics.
func (record residentPubSubRecord) TopicIDs() []string {
	return record.topics
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"go/build"
	"go/format"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/valyala/fasttemplate"
)

const boxFilename = "rice-box.go"

func writeBoxesGo(pkg *build.Package, out io.Writer) error {
	boxMap := findBoxes(pkg)

	// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
	if len(boxMap) == 0 {
		fmt.Println("no calls to rice.FindBox() found")
		return nil
	}

	verbosef("\n")

	var boxes []*boxDataType

	for boxname := range boxMap {
		// find path and filename for this box
		boxPath := filepath.Join(pkg.Dir, boxname)

		// Check to see if the path for the box is a symbolic link.  If so, simply
		// box what the symbolic link points to.  Note: the filepath.Walk function
		// will NOT follow any nested symbolic links.  This only handles the case
		// where the root of the box is a symbolic link.
		symPath, serr := os.Readlink(boxPath)
		if serr == nil {
			boxPath = symPath
		}

		// verbose info
		verbosef("embedding box '%s' to '%s'\n", boxname, boxFilename)

		// read box metadata
		boxInfo, ierr := os.Stat(boxPath)
		if ierr != nil {
			return fmt.Errorf("Error: unable to access box at %s\n", boxPath)
		}

		// create box datastructure (used by template)
		box := &boxDataType{
			BoxName: boxname,
			UnixNow: boxInfo.ModTime().Unix(),
			Files:   make([]*fileDataType, 0),
			Dirs:    make(map[string]*dirDataType),
		}

		if !boxInfo.IsDir() {
			return fmt.Errorf("Error: Box %s must point to a directory but points to %s instead\n",
				boxname, boxPath)
		}

		// fill box datastructure with file data
		err := filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return fmt.Errorf("error walking box: %s\n", err)
			}

			filename := strings.TrimPrefix(path, boxPath)
			filename = strings.Replace(filename, "\\", "/", -1)
			filename = strings.TrimPrefix(filename, "/")
			if info.IsDir() {
				dirData := &dirDataType{
					Identifier: "dir" + nextIdentifier(),
					FileName:   filename,
					ModTime:    info.ModTime().Unix(),
					ChildFiles: make([]*fileDataType, 0),
					ChildDirs:  make([]*dirDataType, 0),
				}
				verbosef("\tincludes dir: '%s'\n", dirData.FileName)
				box.Dirs[dirData.FileName] = dirData

				// add tree entry (skip for root, it'll create a recursion)
				if dirData.FileName != "" {
					pathParts := strings.Split(dirData.FileName, "/")
					parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
					parentDir.ChildDirs = append(parentDir.ChildDirs, dirData)
				}
			} else {
				fileData := &fileDataType{
					Identifier: "file" + nextIdentifier(),
					FileName:   filename,
					ModTime:    info.ModTime().Unix(),
				}
				verbosef("\tincludes file: '%s'\n", fileData.FileName)
				/*
					fileData.Content, err = ioutil.ReadFile(path)
					if err != nil {
						return fmt.Errorf("error reading file content while walking box: %s\n", err)
					}
				*/
				fileData.Content = []byte("{%" + path + "%}")
				box.Files = append(box.Files, fileData)

				// add tree entry
				pathParts := strings.Split(fileData.FileName, "/")
				parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
				if parentDir == nil {
					return fmt.Errorf("Error: parent of %s is not within the box\n", path)
				}
				parentDir.ChildFiles = append(parentDir.ChildFiles, fileData)
			}
			return nil
		})
		if err != nil {
			return err
		}
		boxes = append(boxes, box)

	}

	embedSourceUnformated := bytes.NewBuffer(make([]byte, 0))

	// execute template to buffer
	err := tmplEmbeddedBox.Execute(
		embedSourceUnformated,
		embedFileDataType{pkg.Name, boxes},
	)
	if err != nil {
		return fmt.Errorf("error writing embedded box to file (template execute): %s\n", err)
	}

	// format the source code
	embedSource, err := format.Source(embedSourceUnformated.Bytes())
	if err != nil {
		return fmt.Errorf("error formatting embedSource: %s\n", err)
	}

	// write source to file
	// inject file contents
	ft, err := fasttemplate.NewTemplate(string(embedSource), "{%", "%}")
	if err != nil {
		return fmt.Errorf("error writing embedSource to file (fasttemplate compile): %s\n", err)
	}

	bufWriter := bufio.NewWriterSize(out, 16*1024*1024)

	/*
		_, err = ft.ExecuteFunc(bufWriter, func(w io.Writer, tag string) (int, error) {
			f, err := os.Open(tag)
			if err != nil {
				return 0, err
			}

			bufReader := bufio.NewReaderSize(f, 16*1024*1024)
			n := 0

			for {
				var n2 int
				data, err2 := bufReader.Peek(utf8.MaxRune)
				if err2 == io.EOF {
					break
				}
				if err2 != nil {
					err = err2
					break
				}
				discard := 1
				switch b := data[0]; b {
				case '\\':
					n2, err2 = w.Write([]byte(`\\`))
				case '"':
					n2, err2 = w.Write([]byte(`\"`))
				case '\n':
					n2, err2 = w.Write([]byte(`\n`))

				case '\x00':
					// https://golang.org/ref/spec#Source_code_representation: "Implementation
					// restriction: For compatibility with other tools, a compiler may
					// disallow the NUL character (U+0000) in the source text."
					n2, err2 = w.Write([]byte(`\x00`))

				default:
					// https://golang.org/ref/spec#Source_code_representation: "Implementation
					// restriction: […] A byte order mark may be disallowed anywhere else in
					// the source."
					const byteOrderMark = '\uFEFF'

					if r, size := utf8.DecodeRune(data); r != utf8.RuneError && r != byteOrderMark {
						n2, err2 = w.Write(data[:size])
						discard = size
					} else {
						n2, err2 = fmt.Fprintf(w, `\x%02x`, b)
					}
				}
				n += n2
				bufReader.Discard(discard)
				if err2 != nil {
					err = err2
					break
				}
			}

			//	for {
			//		r, size, err2 := bufReader.ReadRune()
			//		if err2 == io.EOF {
			//			err = nil
			//			break
			//		}
			//		if err2 != nil {
			//			err = err2
			//			break
			//		}
			//		var n2 int
			//		if r == unicode.ReplacementChar && size == 1 {
			//			bufReader.UnreadByte()
			//			b, err2 := bufReader.ReadByte()
			//			if err2 != nil {
			//				err = err2
			//				break
			//			}
			//			n2, err2 = fmt.Fprintf(w, "\\x%x", b)
			//		} else {
			//			if r == '"' {
			//				n2, err2 = fmt.Fprint(w, "\\\"")
			//			} else if r == '\'' {
			//				n2, err2 = fmt.Fprint(w, "'")
			//			} else {
			//				quoted := strconv.QuoteRune(r)
			//				n2, err2 = fmt.Fprintf(w, "%v", quoted[1:len(quoted)-1])
			//			}
			//		}
			//		n += n2
			//		if err2 != nil {
			//			err = err2
			//			break
			//		}
			//	}

			f.Close()

			return int(n), err
		})
	*/

	/**/
	_, err = ft.ExecuteFunc(bufWriter, func(w io.Writer, tag string) (int, error) {
		fileName, err := strconv.Unquote("\"" + tag + "\"")
		if err != nil {
			return 0, err
		}
		fileContent, err := ioutil.ReadFile(fileName)
		if err != nil {
			return 0, err
		}
		quoted := strconv.Quote(string(fileContent))
		return fmt.Fprint(w, quoted[1:len(quoted)-1])
	})
	/**/
	if err != nil {
		return fmt.Errorf("error writing embedSource to file: %s\n", err)
	}
	err = bufWriter.Flush()
	if err != nil {
		return fmt.Errorf("error writing embedSource to file: %s\n", err)
	}
	return nil
}

func operationEmbedGo(pkg *build.Package) {
	// create go file for box
	boxFile, err := os.Create(filepath.Join(pkg.Dir, boxFilename))
	if err != nil {
		log.Printf("error creating embedded box file: %s\n", err)
		os.Exit(1)
	}
	defer boxFile.Close()

	err = writeBoxesGo(pkg, boxFile)
	if err != nil {
		log.Printf("error creating embedded box file: %s\n", err)
		os.Exit(1)
	}
}
<commit_msg>Encode files in parts to reduce memory usage<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"go/build"
	"go/format"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"unicode/utf8"

	"github.com/valyala/fasttemplate"
)

const boxFilename = "rice-box.go"

const lowerhex = "0123456789abcdef"

func writeBoxesGo(pkg *build.Package, out io.Writer) error {
	boxMap := findBoxes(pkg)

	// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
	if len(boxMap) == 0 {
		fmt.Println("no calls to rice.FindBox() found")
		return nil
	}

	verbosef("\n")

	var boxes []*boxDataType

	for boxname := range boxMap {
		// find path and filename for this box
		boxPath := filepath.Join(pkg.Dir, boxname)

		// Check to see if the path for the box is a symbolic link.  If so, simply
		// box what the symbolic link points to.  Note: the filepath.Walk function
		// will NOT follow any nested symbolic links.  This only handles the case
		// where the root of the box is a symbolic link.
		symPath, serr := os.Readlink(boxPath)
		if serr == nil {
			boxPath = symPath
		}

		// verbose info
		verbosef("embedding box '%s' to '%s'\n", boxname, boxFilename)

		// read box metadata
		boxInfo, ierr := os.Stat(boxPath)
		if ierr != nil {
			return fmt.Errorf("Error: unable to access box at %s\n", boxPath)
		}

		// create box datastructure (used by template)
		box := &boxDataType{
			BoxName: boxname,
			UnixNow: boxInfo.ModTime().Unix(),
			Files:   make([]*fileDataType, 0),
			Dirs:    make(map[string]*dirDataType),
		}

		if !boxInfo.IsDir() {
			return fmt.Errorf("Error: Box %s must point to a directory but points to %s instead\n",
				boxname, boxPath)
		}

		// fill box datastructure with file data
		err := filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return fmt.Errorf("error walking box: %s\n", err)
			}

			filename := strings.TrimPrefix(path, boxPath)
			filename = strings.Replace(filename, "\\", "/", -1)
			filename = strings.TrimPrefix(filename, "/")
			if info.IsDir() {
				dirData := &dirDataType{
					Identifier: "dir" + nextIdentifier(),
					FileName:   filename,
					ModTime:    info.ModTime().Unix(),
					ChildFiles: make([]*fileDataType, 0),
					ChildDirs:  make([]*dirDataType, 0),
				}
				verbosef("\tincludes dir: '%s'\n", dirData.FileName)
				box.Dirs[dirData.FileName] = dirData

				// add tree entry (skip for root, it'll create a recursion)
				if dirData.FileName != "" {
					pathParts := strings.Split(dirData.FileName, "/")
					parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
					parentDir.ChildDirs = append(parentDir.ChildDirs, dirData)
				}
			} else {
				fileData := &fileDataType{
					Identifier: "file" + nextIdentifier(),
					FileName:   filename,
					ModTime:    info.ModTime().Unix(),
				}
				verbosef("\tincludes file: '%s'\n", fileData.FileName)
				/*
					fileData.Content, err = ioutil.ReadFile(path)
					if err != nil {
						return fmt.Errorf("error reading file content while walking box: %s\n", err)
					}
				*/
				fileData.Content = []byte("{%" + path + "%}")
				box.Files = append(box.Files, fileData)

				// add tree entry
				pathParts := strings.Split(fileData.FileName, "/")
				parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
				if parentDir == nil {
					return fmt.Errorf("Error: parent of %s is not within the box\n", path)
				}
				parentDir.ChildFiles = append(parentDir.ChildFiles, fileData)
			}
			return nil
		})
		if err != nil {
			return err
		}
		boxes = append(boxes, box)

	}

	embedSourceUnformated := bytes.NewBuffer(make([]byte, 0))

	// execute template to buffer
	err := tmplEmbeddedBox.Execute(
		embedSourceUnformated,
		embedFileDataType{pkg.Name, boxes},
	)
	if err != nil {
		return fmt.Errorf("error writing embedded box to file (template execute): %s\n", err)
	}

	// format the source code
	embedSource, err := format.Source(embedSourceUnformated.Bytes())
	if err != nil {
		return fmt.Errorf("error formatting embedSource: %s\n", err)
	}

	// write source to file
	// inject file contents
	ft, err := fasttemplate.NewTemplate(string(embedSource), "{%", "%}")
	if err != nil {
		return fmt.Errorf("error writing embedSource to file (fasttemplate compile): %s\n", err)
	}

	bufWriter := bufio.NewWriterSize(out, 100*1024)
	bufReader := bufio.NewReaderSize(nil, 100*1024)

	/**/
	_, err = ft.ExecuteFunc(bufWriter, func(w io.Writer, tag string) (int, error) {
		fileName, err := strconv.Unquote("\"" + tag + "\"")
		if err != nil {
			return 0, err
		}
		f, err := os.Open(fileName)
		if err != nil {
			return 0, err
		}

		bufReader.Reset(f)
		n := 0

		for {
			data, peekErr := bufReader.Peek(utf8.UTFMax)
			// even if peekErr is io.EOF, we need to process data
			if peekErr != nil && peekErr != io.EOF {
				err = peekErr
				break
			}
			// break if done
			if len(data) == 0 {
				break
			}
			var discard, n2 int
			r, width := utf8.DecodeRune(data)
			if width == 1 && r == utf8.RuneError {
				w.Write([]byte{'\\', 'x', lowerhex[data[0]>>4], lowerhex[data[0]&0xF]})
				n2 = 4
				discard = 1
			} else {
				discard = width
				if r == rune('"') || r == '\\' { // always backslashed
					w.Write([]byte{'\\', byte(r)})
					n2 = 2
				} else if strconv.IsPrint(r) {
					w.Write(data[:width])
					n2 = width
				} else {
					switch r {
					case '\a':
						w.Write([]byte{'\\', 'a'})
						n2 = 2
					case '\b':
						w.Write([]byte{'\\', 'b'})
						n2 = 2
					case '\f':
						w.Write([]byte{'\\', 'f'})
						n2 = 2
					case '\n':
						w.Write([]byte{'\\', 'n'})
						n2 = 2
					case '\r':
						w.Write([]byte{'\\', 'r'})
						n2 = 2
					case '\t':
						w.Write([]byte{'\\', 't'})
						n2 = 2
					case '\v':
						w.Write([]byte{'\\', 'v'})
						n2 = 2
					default:
						switch {
						case r < ' ':
							w.Write([]byte{'\\', 'x', lowerhex[data[0]>>4], lowerhex[data[0]&0xF]})
							n2 = 4
						case r > utf8.MaxRune:
							r = 0xFFFD
							fallthrough
						case r < 0x10000:
							w.Write([]byte{'\\', 'u'})
							n2 = 2
							for s := 12; s >= 0; s -= 4 {
								w.Write([]byte{lowerhex[r>>uint(s)&0xF]})
								n2++
							}
						default:
							w.Write([]byte{'\\', 'U'})
							n2 = 2
							for s := 28; s >= 0; s -= 4 {
								w.Write([]byte{lowerhex[r>>uint(s)&0xF]})
								n2++
							}
						}
					}
				}
			}
			bufReader.Discard(discard)
			n += n2
		}

		//	for {
		//		var n2 int
		//		data, err2 := bufReader.Peek(utf8.UTFMax)
		//		if err2 == io.EOF {
		//			break
		//		}
		//		if err2 != nil {
		//			err = err2
		//			break
		//		}
		//		discard := 1
		//		switch b := data[0]; b {
		//		case '\\':
		//			n2, err2 = w.Write([]byte(`\\`))
		//		case '"':
		//			n2, err2 = w.Write([]byte(`\"`))
		//		case '\n':
		//			n2, err2 = w.Write([]byte(`\n`))

		//		case '\x00':
		//			// https://golang.org/ref/spec#Source_code_representation: "Implementation
		//			// restriction: For compatibility with other tools, a compiler may
		//			// disallow the NUL character (U+0000) in the source text."
		//			n2, err2 = w.Write([]byte(`\x00`))

		//		default:
		//			// https://golang.org/ref/spec#Source_code_representation: "Implementation
		//			// restriction: […] A byte order mark may be disallowed anywhere else in
		//			// the source."
		//			const byteOrderMark = '\uFEFF'

		//			if r, size := utf8.DecodeRune(data); r != utf8.RuneError && r != byteOrderMark {
		//				n2, err2 = w.Write(data[:size])
		//				discard = size
		//			} else {
		//				n2, err2 = fmt.Fprintf(w, `\x%02x`, b)
		//			}
		//		}
		//		n += n2
		//		bufReader.Discard(discard)
		//		if err2 != nil {
		//			err = err2
		//			break
		//		}
		//	}

		//	for {
		//		r, size, err2 := bufReader.ReadRune()
		//		if err2 == io.EOF {
		//			err = nil
		//			break
		//		}
		//		if err2 != nil {
		//			err = err2
		//			break
		//		}
		//		var n2 int
		//		if r == unicode.ReplacementChar && size == 1 {
		//			bufReader.UnreadByte()
		//			b, err2 := bufReader.ReadByte()
		//			if err2 != nil {
		//				err = err2
		//				break
		//			}
		//			n2, err2 = fmt.Fprintf(w, "\\x%x", b)
		//		} else {
		//			if r == '"' {
		//				n2, err2 = fmt.Fprint(w, "\\\"")
		//			} else if r == '\'' {
		//				n2, err2 = fmt.Fprint(w, "'")
		//			} else {
		//				quoted := strconv.QuoteRune(r)
		//				n2, err2 = fmt.Fprintf(w, "%v", quoted[1:len(quoted)-1])
		//			}
		//		}
		//		n += n2
		//		if err2 != nil {
		//			err = err2
		//			break
		//		}
		//	}

		f.Close()

		return int(n), err
	})
	/**/

	/*
		_, err = ft.ExecuteFunc(bufWriter, func(w io.Writer, tag string) (int, error) {
			fileContent, err := ioutil.ReadFile(tag)
			if err != nil {
				return 0, err
			}
			quoted := strconv.Quote(string(fileContent))
			return fmt.Fprint(w, quoted[1:len(quoted)-1])
		})
	*/
	if err != nil {
		return fmt.Errorf("error writing embedSource to file: %s\n", err)
	}
	err = bufWriter.Flush()
	if err != nil {
		return fmt.Errorf("error writing embedSource to file: %s\n", err)
	}
	return nil
}

func operationEmbedGo(pkg *build.Package) {
	// create go file for box
	boxFile, err := os.Create(filepath.Join(pkg.Dir, boxFilename))
	if err != nil {
		log.Printf("error creating embedded box file: %s\n", err)
		os.Exit(1)
	}
	defer boxFile.Close()

	err = writeBoxesGo(pkg, boxFile)
	if err != nil {
		log.Printf("error creating embedded box file: %s\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package expr

import (
	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/consolidation"
)

type Context struct {
	from   uint32
	to     uint32
	consol consolidation.Consolidator // can be 0 to mean undefined
}

type GraphiteFunc interface {
	// Signature declares input and output arguments (return values)
	// input args can be optional in which case they can be specified positionally or via keys if you want to specify params that come after un-specified optional params
	// the val pointers of each input Arg should point to a location accessible to the function,
	// so that the planner can set up the inputs for your function based on user input.
	// NewPlan() will only create the plan if the expressions it parsed correspond to the signatures provided by the function
	Signature() ([]Arg, []Arg)

	// Context allows a func to alter the context that will be passed down the expression tree.
	// this function will be called after validating and setting up all non-series and non-serieslist parameters.
	// (as typically, context alterations require integer/string/bool/etc parameters, and shall affect series[list] parameters)
	// examples:
	// * movingAverage(foo,5min) -> the 5min arg will be parsed, so we can request 5min of earlier data, which will affect the request for foo.
	// * consolidateBy(bar, "sum") -> the "sum" arg will be parsed, so we can pass on the fact that bar needs to be sum-consolidated
	Context(c Context) Context
	// Exec executes the function. the function should call any input functions, do its processing, and return output.
	// IMPORTANT: for performance and correctness, functions should
	// * not modify slices of points that they get from their inputs
	// * use the pool to get new slices in which to store any new/modified dat
	// * add the newly created slices into the cache so they can be reclaimed after the output is consumed
	Exec(map[Req][]models.Series) ([]models.Series, error)
}

type funcConstructor func() GraphiteFunc

type funcDef struct {
	constr funcConstructor
	stable bool
}

var funcs map[string]funcDef

func init() {
	// keys must be sorted alphabetically. but functions with aliases can go together, in which case they are sorted by the first of their aliases
	funcs = map[string]funcDef{
		"alias":             {NewAlias, true},
		"aliasByTags":       {NewAliasByNode, true},
		"aliasByNode":       {NewAliasByNode, true},
		"aliasSub":          {NewAliasSub, true},
		"avg":               {NewAggregateConstructor("average", crossSeriesAvg), true},
		"averageSeries":     {NewAggregateConstructor("average", crossSeriesAvg), true},
		"consolidateBy":     {NewConsolidateBy, true},
		"diffSeries":        {NewAggregateConstructor("diff", crossSeriesDiff), true},
		"divideSeries":      {NewDivideSeries, true},
		"divideSeriesLists": {NewDivideSeriesLists, true},
		"exclude":           {NewExclude, true},
		"grep":              {NewGrep, true},
		"groupByTags":       {NewGroupByTags, true},
		"max":               {NewAggregateConstructor("max", crossSeriesMax), true},
		"maxSeries":         {NewAggregateConstructor("max", crossSeriesMax), true},
		"min":               {NewAggregateConstructor("min", crossSeriesMin), true},
		"minSeries":         {NewAggregateConstructor("min", crossSeriesMin), true},
		"multiplySeries":    {NewAggregateConstructor("multiply", crossSeriesMultiply), true},
		"movingAverage":     {NewMovingAverage, false},
		"perSecond":         {NewPerSecond, true},
		"rangeOfSeries":     {NewAggregateConstructor("rangeOf", crossSeriesRange), true},
		"scale":             {NewScale, true},
		"smartSummarize":    {NewSmartSummarize, false},
		"sortByName":        {NewSortByName, true},
		"stddevSeries":      {NewAggregateConstructor("stddev", crossSeriesStddev), true},
		"sum":               {NewAggregateConstructor("sum", crossSeriesSum), true},
		"sumSeries":         {NewAggregateConstructor("sum", crossSeriesSum), true},
		"summarize":         {NewSummarize, true},
		"transformNull":     {NewTransformNull, true},
	}
}

// summarizeCons returns the first explicitly specified Consolidator, QueryCons for the given set of input series,
// or the first one, otherwise.
func summarizeCons(series []models.Series) (consolidation.Consolidator, consolidation.Consolidator) {
	for _, serie := range series {
		if serie.QueryCons != 0 {
			return serie.Consolidator, serie.QueryCons
		}
	}
	return series[0].Consolidator, series[0].QueryCons
}

func consumeFuncs(cache map[Req][]models.Series, fns []GraphiteFunc) ([]models.Series, []string, error) {
	var series []models.Series
	var queryPatts []string
	for i := range fns {
		in, err := fns[i].Exec(cache)
		if err != nil {
			return nil, nil, err
		}
		if len(in) != 0 {
			series = append(series, in...)
			queryPatts = append(queryPatts, in[0].QueryPatt)
		}
	}
	return series, queryPatts, nil
}
<commit_msg>mark summarize as unstable.<commit_after>package expr

import (
	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/consolidation"
)

type Context struct {
	from   uint32
	to     uint32
	consol consolidation.Consolidator // can be 0 to mean undefined
}

type GraphiteFunc interface {
	// Signature declares input and output arguments (return values)
	// input args can be optional in which case they can be specified positionally or via keys if you want to specify params that come after un-specified optional params
	// the val pointers of each input Arg should point to a location accessible to the function,
	// so that the planner can set up the inputs for your function based on user input.
	// NewPlan() will only create the plan if the expressions it parsed correspond to the signatures provided by the function
	Signature() ([]Arg, []Arg)

	// Context allows a func to alter the context that will be passed down the expression tree.
	// this function will be called after validating and setting up all non-series and non-serieslist parameters.
	// (as typically, context alterations require integer/string/bool/etc parameters, and shall affect series[list] parameters)
	// examples:
	// * movingAverage(foo,5min) -> the 5min arg will be parsed, so we can request 5min of earlier data, which will affect the request for foo.
	// * consolidateBy(bar, "sum") -> the "sum" arg will be parsed, so we can pass on the fact that bar needs to be sum-consolidated
	Context(c Context) Context
	// Exec executes the function. the function should call any input functions, do its processing, and return output.
	// IMPORTANT: for performance and correctness, functions should
	// * not modify slices of points that they get from their inputs
	// * use the pool to get new slices in which to store any new/modified dat
	// * add the newly created slices into the cache so they can be reclaimed after the output is consumed
	Exec(map[Req][]models.Series) ([]models.Series, error)
}

type funcConstructor func() GraphiteFunc

type funcDef struct {
	constr funcConstructor
	stable bool
}

var funcs map[string]funcDef

func init() {
	// keys must be sorted alphabetically. but functions with aliases can go together, in which case they are sorted by the first of their aliases
	funcs = map[string]funcDef{
		"alias":             {NewAlias, true},
		"aliasByTags":       {NewAliasByNode, true},
		"aliasByNode":       {NewAliasByNode, true},
		"aliasSub":          {NewAliasSub, true},
		"avg":               {NewAggregateConstructor("average", crossSeriesAvg), true},
		"averageSeries":     {NewAggregateConstructor("average", crossSeriesAvg), true},
		"consolidateBy":     {NewConsolidateBy, true},
		"diffSeries":        {NewAggregateConstructor("diff", crossSeriesDiff), true},
		"divideSeries":      {NewDivideSeries, true},
		"divideSeriesLists": {NewDivideSeriesLists, true},
		"exclude":           {NewExclude, true},
		"grep":              {NewGrep, true},
		"groupByTags":       {NewGroupByTags, true},
		"max":               {NewAggregateConstructor("max", crossSeriesMax), true},
		"maxSeries":         {NewAggregateConstructor("max", crossSeriesMax), true},
		"min":               {NewAggregateConstructor("min", crossSeriesMin), true},
		"minSeries":         {NewAggregateConstructor("min", crossSeriesMin), true},
		"multiplySeries":    {NewAggregateConstructor("multiply", crossSeriesMultiply), true},
		"movingAverage":     {NewMovingAverage, false},
		"perSecond":         {NewPerSecond, true},
		"rangeOfSeries":     {NewAggregateConstructor("rangeOf", crossSeriesRange), true},
		"scale":             {NewScale, true},
		"smartSummarize":    {NewSmartSummarize, false},
		"sortByName":        {NewSortByName, true},
		"stddevSeries":      {NewAggregateConstructor("stddev", crossSeriesStddev), true},
		"sum":               {NewAggregateConstructor("sum", crossSeriesSum), true},
		"sumSeries":         {NewAggregateConstructor("sum", crossSeriesSum), true},
		"summarize":         {NewSummarize, false},
		"transformNull":     {NewTransformNull, true},
	}
}

// summarizeCons returns the first explicitly specified Consolidator, QueryCons for the given set of input series,
// or the first one, otherwise.
func summarizeCons(series []models.Series) (consolidation.Consolidator, consolidation.Consolidator) {
	for _, serie := range series {
		if serie.QueryCons != 0 {
			return serie.Consolidator, serie.QueryCons
		}
	}
	return series[0].Consolidator, series[0].QueryCons
}

func consumeFuncs(cache map[Req][]models.Series, fns []GraphiteFunc) ([]models.Series, []string, error) {
	var series []models.Series
	var queryPatts []string
	for i := range fns {
		in, err := fns[i].Exec(cache)
		if err != nil {
			return nil, nil, err
		}
		if len(in) != 0 {
			series = append(series, in...)
			queryPatts = append(queryPatts, in[0].QueryPatt)
		}
	}
	return series, queryPatts, nil
}
<|endoftext|>


<commit_before>package mountinfo

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

// GetMountsFromReader retrieves a list of mounts from the
// reader provided, with an optional filter applied (use nil
// for no filter). This can be useful in tests or benchmarks
// that provide a fake mountinfo data.
//
// This function is Linux-specific.
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
	s := bufio.NewScanner(r)
	out := []*Info{}
	var err error
	for s.Scan() {
		if err = s.Err(); err != nil {
			return nil, err
		}
		/*
		   See http://man7.org/linux/man-pages/man5/proc.5.html

		   36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
		   (1)(2)(3)   (4)   (5)      (6)      (7)   (8) (9)   (10)         (11)

		   (1) mount ID:  unique identifier of the mount (may be reused after umount)
		   (2) parent ID:  ID of parent (or of self for the top of the mount tree)
		   (3) major:minor:  value of st_dev for files on filesystem
		   (4) root:  root of the mount within the filesystem
		   (5) mount point:  mount point relative to the process's root
		   (6) mount options:  per mount options
		   (7) optional fields:  zero or more fields of the form "tag[:value]"
		   (8) separator:  marks the end of the optional fields
		   (9) filesystem type:  name of filesystem of the form "type[.subtype]"
		   (10) mount source:  filesystem specific information or "none"
		   (11) super options:  per super block options

		   In other words, we have:
		    * 6 mandatory fields	(1)..(6)
		    * 0 or more optional fields	(7)
		    * a separator field		(8)
		    * 3 mandatory fields	(9)..(11)
		*/

		text := s.Text()
		fields := strings.Split(text, " ")
		numFields := len(fields)
		if numFields < 10 {
			// should be at least 10 fields
			return nil, fmt.Errorf("Parsing '%s' failed: not enough fields (%d)", text, numFields)
		}

		// separator field
		sepIdx := numFields - 4
		// In Linux <= 3.9 mounting a cifs with spaces in a share
		// name (like "//srv/My Docs") _may_ end up having a space
		// in the last field of mountinfo (like "unc=//serv/My Docs").
		// Since kernel 3.10-rc1, cifs option "unc=" is ignored,
		// so spaces should not appear.
		//
		// Check for a separator, and work around the spaces bug
		for fields[sepIdx] != "-" {
			sepIdx--
			if sepIdx == 5 {
				return nil, fmt.Errorf("Parsing '%s' failed: missing - separator", text)
			}
		}

		p := &Info{}

		// Fill in the fields that a filter might check
		p.Mountpoint, err = unescape(fields[4])
		if err != nil {
			return nil, fmt.Errorf("Parsing '%s' failed: mount point: %w", fields[4], err)
		}
		p.FSType, err = unescape(fields[sepIdx+1])
		if err != nil {
			return nil, fmt.Errorf("Parsing '%s' failed: fstype: %w", fields[sepIdx+1], err)
		}
		p.Source, err = unescape(fields[sepIdx+2])
		if err != nil {
			return nil, fmt.Errorf("Parsing '%s' failed: source: %w", fields[sepIdx+2], err)
		}
		p.VFSOptions = fields[sepIdx+3]

		// Run a filter early so we can skip parsing/adding entries
		// the caller is not interested in
		var skip, stop bool
		if filter != nil {
			skip, stop = filter(p)
			if skip {
				continue
			}
		}

		// Fill in the rest of the fields

		// ignore any numbers parsing errors, as there should not be any
		p.ID, _ = strconv.Atoi(fields[0])
		p.Parent, _ = strconv.Atoi(fields[1])
		mm := strings.Split(fields[2], ":")
		if len(mm) != 2 {
			return nil, fmt.Errorf("Parsing '%s' failed: unexpected minor:major pair %s", text, mm)
		}
		p.Major, _ = strconv.Atoi(mm[0])
		p.Minor, _ = strconv.Atoi(mm[1])

		p.Root, err = unescape(fields[3])
		if err != nil {
			return nil, fmt.Errorf("Parsing '%s' failed: root: %w", fields[3], err)
		}

		p.Options = fields[5]

		// zero or more optional fields
		switch {
		case sepIdx == 6:
			// zero, do nothing
		case sepIdx == 7:
			p.Optional = fields[6]
		default:
			p.Optional = strings.Join(fields[6:sepIdx-1], " ")
		}

		out = append(out, p)
		if stop {
			break
		}
	}
	return out, nil
}

// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts
func parseMountTable(filter FilterFunc) ([]*Info, error) {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return GetMountsFromReader(f, filter)
}

// PidMountInfo collects the mounts for a specific process ID. If the process
// ID is unknown, it is better to use `GetMounts` which will inspect
// "/proc/self/mountinfo" instead.
func PidMountInfo(pid int) ([]*Info, error) {
	f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return GetMountsFromReader(f, nil)
}

// A few specific characters in mountinfo path entries (root and mountpoint)
// are escaped using a backslash followed by a character's ascii code in octal.
//
//   space              -- as \040
//   tab (aka \t)       -- as \011
//   newline (aka \n)   -- as \012
//   backslash (aka \\) -- as \134
//
// This function converts path from mountinfo back, i.e. it unescapes the above sequences.
func unescape(path string) (string, error) {
	// try to avoid copying
	if strings.IndexByte(path, '\\') == -1 {
		return path, nil
	}

	// The following code is UTF-8 transparent as it only looks for some
	// specific characters (backslash and 0..7) with values < utf8.RuneSelf,
	// and everything else is passed through as is.
	buf := make([]byte, len(path))
	bufLen := 0
	for i := 0; i < len(path); i++ {
		if path[i] != '\\' {
			buf[bufLen] = path[i]
			bufLen++
			continue
		}
		s := path[i:]
		if len(s) < 4 {
			// too short
			return "", fmt.Errorf("bad escape sequence %q: too short", s)
		}
		c := s[1]
		switch c {
		case '0', '1', '2', '3', '4', '5', '6', '7':
			v := c - '0'
			for j := 2; j < 4; j++ { // one digit already; two more
				if s[j] < '0' || s[j] > '7' {
					return "", fmt.Errorf("bad escape sequence %q: not a digit", s[:3])
				}
				x := s[j] - '0'
				v = (v << 3) | x
			}
			if v > 255 {
				return "", fmt.Errorf("bad escape sequence %q: out of range" + s[:3])
			}
			buf[bufLen] = v
			bufLen++
			i += 3
			continue
		default:
			return "", fmt.Errorf("bad escape sequence %q: not a digit" + s[:3])

		}
	}

	return string(buf[:bufLen]), nil
}
<commit_msg>mountinfo: deprecate PidMountInfo<commit_after>package mountinfo

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

// GetMountsFromReader retrieves a list of mounts from the
// reader provided, with an optional filter applied (use nil
// for no filter). This can be useful in tests or benchmarks
// that provide a fake mountinfo data.
//
// This function is Linux-specific.
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
	s := bufio.NewScanner(r)
	out := []*Info{}
	var err error
	for s.Scan() {
		if err = s.Err(); err != nil {
			return nil, err
		}
		/*
		   See http://man7.org/linux/man-pages/man5/proc.5.html

		   36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
		   (1)(2)(3)   (4)   (5)      (6)      (7)   (8) (9)   (10)         (11)

		   (1) mount ID:  unique identifier of the mount (may be reused after umount)
		   (2) parent ID:  ID of parent (or of self for the top of the mount tree)
		   (3) major:minor:  value of st_dev for files on filesystem
		   (4) root:  root of the mount within the filesystem
		   (5) mount point:  mount point relative to the process's root
		   (6) mount options:  per mount options
		   (7) optional fields:  zero or more fields of the form "tag[:value]"
		   (8) separator:  marks the end of the optional fields
		   (9) filesystem type:  name of filesystem of the form "type[.subtype]"
		   (10) mount source:  filesystem specific information or "none"
		   (11) super options:  per super block options

		   In other words, we have:
		    * 6 mandatory fields	(1)..(6)
		    * 0 or more optional fields	(7)
		    * a separator field		(8)
		    * 3 mandatory fields	(9)..(11)
		*/

		text := s.Text()
		fields := strings.Split(text, " ")
		numFields := len(fields)
		if numFields < 10 {
			// should be at least 10 fields
			return nil, fmt.Errorf("Parsing '%s' failed: not enough fields (%d)", text, numFields)
		}

		// separator field
		sepIdx := numFields - 4
		// In Linux <= 3.9 mounting a cifs with spaces in a share
		// name (like "//srv/My Docs") _may_ end up having a space
		// in the last field of mountinfo (like "unc=//serv/My Docs").
		// Since kernel 3.10-rc1, cifs option "unc=" is ignored,
		// so spaces should not appear.
		//
		// Check for a separator, and work around the spaces bug
		for fields[sepIdx] != "-" {
			sepIdx--
			if sepIdx == 5 {
				return nil, fmt.Errorf("Parsing '%s' failed: missing - separator", text)
			}
		}

		p := &Info{}

		// Fill in the fields that a filter might check
		p.Mountpoint, err = unescape(fields[4])
		if err != nil {
			return nil, fmt.Errorf("Parsing '%s' failed: mount point: %w", fields[4], err)
		}
		p.FSType, err = unescape(fields[sepIdx+1])
		if err != nil {
			return nil, fmt.Errorf("Parsing '%s' failed: fstype: %w", fields[sepIdx+1], err)
		}
		p.Source, err = unescape(fields[sepIdx+2])
		if err != nil {
			return nil, fmt.Errorf("Parsing '%s' failed: source: %w", fields[sepIdx+2], err)
		}
		p.VFSOptions = fields[sepIdx+3]

		// Run a filter early so we can skip parsing/adding entries
		// the caller is not interested in
		var skip, stop bool
		if filter != nil {
			skip, stop = filter(p)
			if skip {
				continue
			}
		}

		// Fill in the rest of the fields

		// ignore any numbers parsing errors, as there should not be any
		p.ID, _ = strconv.Atoi(fields[0])
		p.Parent, _ = strconv.Atoi(fields[1])
		mm := strings.Split(fields[2], ":")
		if len(mm) != 2 {
			return nil, fmt.Errorf("Parsing '%s' failed: unexpected minor:major pair %s", text, mm)
		}
		p.Major, _ = strconv.Atoi(mm[0])
		p.Minor, _ = strconv.Atoi(mm[1])

		p.Root, err = unescape(fields[3])
		if err != nil {
			return nil, fmt.Errorf("Parsing '%s' failed: root: %w", fields[3], err)
		}

		p.Options = fields[5]

		// zero or more optional fields
		switch {
		case sepIdx == 6:
			// zero, do nothing
		case sepIdx == 7:
			p.Optional = fields[6]
		default:
			p.Optional = strings.Join(fields[6:sepIdx-1], " ")
		}

		out = append(out, p)
		if stop {
			break
		}
	}
	return out, nil
}

// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
// bind mounts
func parseMountTable(filter FilterFunc) ([]*Info, error) {
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return GetMountsFromReader(f, filter)
}

// PidMountInfo retrieves the list of mounts from a given process' mount
// namespace. Unless there is a need to get mounts from a mount namespace
// different from that of a calling process, use GetMounts.
//
// This function is Linux-specific.
//
// Deprecated: this will be removed before v1; use GetMountsFromReader with
// opened /proc/<pid>/mountinfo as an argument instead.
func PidMountInfo(pid int) ([]*Info, error) {
	f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return GetMountsFromReader(f, nil)
}

// A few specific characters in mountinfo path entries (root and mountpoint)
// are escaped using a backslash followed by a character's ascii code in octal.
//
//   space              -- as \040
//   tab (aka \t)       -- as \011
//   newline (aka \n)   -- as \012
//   backslash (aka \\) -- as \134
//
// This function converts path from mountinfo back, i.e. it unescapes the above sequences.
func unescape(path string) (string, error) {
	// try to avoid copying
	if strings.IndexByte(path, '\\') == -1 {
		return path, nil
	}

	// The following code is UTF-8 transparent as it only looks for some
	// specific characters (backslash and 0..7) with values < utf8.RuneSelf,
	// and everything else is passed through as is.
	buf := make([]byte, len(path))
	bufLen := 0
	for i := 0; i < len(path); i++ {
		if path[i] != '\\' {
			buf[bufLen] = path[i]
			bufLen++
			continue
		}
		s := path[i:]
		if len(s) < 4 {
			// too short
			return "", fmt.Errorf("bad escape sequence %q: too short", s)
		}
		c := s[1]
		switch c {
		case '0', '1', '2', '3', '4', '5', '6', '7':
			v := c - '0'
			for j := 2; j < 4; j++ { // one digit already; two more
				if s[j] < '0' || s[j] > '7' {
					return "", fmt.Errorf("bad escape sequence %q: not a digit", s[:3])
				}
				x := s[j] - '0'
				v = (v << 3) | x
			}
			if v > 255 {
				return "", fmt.Errorf("bad escape sequence %q: out of range" + s[:3])
			}
			buf[bufLen] = v
			bufLen++
			i += 3
			continue
		default:
			return "", fmt.Errorf("bad escape sequence %q: not a digit" + s[:3])

		}
	}

	return string(buf[:bufLen]), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/tent/tent-client-go"
)

var meta *tent.MetaPost
var client *tent.Client

func discover() []*request {
	var err error
	meta, err = tent.Discover(os.Args[1])
	maybePanic(err)
	client = &tent.Client{Servers: meta.Servers}
	return getRequests()
}

func createApp() []*request {
	post := tent.NewAppPost(&tent.App{
		Name: "Example App",
		URL:  "https://app.example.com",
		PostTypes: tent.AppPostTypes{
			Write: []string{"https://tent.io/types/status/v0", "https://tent.io/types/photo/v0"},
			Read:  []string{"https://tent.io/types/app/v0"},
		},
		RedirectURI: "https://app.example.com/oauth",
	})
	err := client.CreatePost(post)
	maybePanic(err)
	client.Credentials, _, err = post.LinkedCredentials()
	maybePanic(err)
	oauthURL, _ := meta.Servers[0].URLs.OAuthURL(post.ID, "d173d2bb868a")
	req, _ := http.NewRequest("GET", oauthURL, nil)
	res, err := tent.HTTP.Transport.RoundTrip(req)
	maybePanic(err)
	u, err := url.Parse(res.Header.Get("Location"))
	maybePanic(err)
	client.Credentials, err = client.RequestAccessToken(u.Query().Get("code"))
	maybePanic(err)
	return getRequests()
}

func newPost() *request {
	post := &tent.Post{
		Type:    "https://tent.io/types/status/v0#",
		Content: []byte(`{"text": "example post"}`),
	}
	err := client.CreatePost(post)
	maybePanic(err)
	return getRequests()[0]
}

func newMultipartPost() []*request {
	post := &tent.Post{
		Type:    "https://tent.io/types/photo/v0#",
		Content: []byte(`{"caption": "example photo"}`),
		Attachments: []*tent.PostAttachment{{
			Name:        "example.jpeg",
			Category:    "photo",
			ContentType: "image/jpeg",
			Data:        strings.NewReader("example attachment data"),
		}},
	}
	err := client.CreatePost(post)
	maybePanic(err)

	_, err = io.Copy(ioutil.Discard, post.Attachments[0])
	maybePanic(err)
	post.Attachments[0].Close()

	body, err := client.GetPostAttachment(post.Entity, post.ID, "latest", post.Attachments[0].Name, "*/*")
	maybePanic(err)
	_, err = io.Copy(ioutil.Discard, post.Attachments[0])
	body.Close()

	return getRequests()
}

func main() {
	examples := make(map[string]*request)
	tent.HTTP.Transport = &roundTripRecorder{roundTripper: tent.HTTP.Transport}

	discoveryReqs := discover()
	examples["discover_head"] = discoveryReqs[0]
	examples["discover_meta"] = discoveryReqs[1]

	appReqs := createApp()
	examples["app_create"] = appReqs[0]
	examples["app_credentials"] = appReqs[1]
	examples["oauth_redirect"] = appReqs[2]
	examples["oauth_token"] = appReqs[3]

	examples["new_post"] = newPost()

	multipartReqs := newMultipartPost()
	examples["new_multipart_post"] = multipartReqs[0]
	examples["get_attachment"] = multipartReqs[1]
	examples["get_post_attachment"] = multipartReqs[2]

	res := make(map[string]string)
	for k, v := range examples {
		res[k] = requestMarkdown(v)
	}

	data, _ := json.Marshal(res)
	ioutil.WriteFile(os.Args[2], data, 0644)
}

func maybePanic(err error) {
	if err != nil {
		if resErr, ok := err.(*tent.BadResponseError); ok && resErr.TentError != nil {
			fmt.Println(resErr.TentError)
		}
		panic(err)
	}
}
<commit_msg>Add posts_feed example<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/tent/tent-client-go"
)

var meta *tent.MetaPost
var client *tent.Client

func discover() []*request {
	var err error
	meta, err = tent.Discover(os.Args[1])
	maybePanic(err)
	client = &tent.Client{Servers: meta.Servers}
	return getRequests()
}

func createApp() []*request {
	post := tent.NewAppPost(&tent.App{
		Name: "Example App",
		URL:  "https://app.example.com",
		PostTypes: tent.AppPostTypes{
			Write: []string{"https://tent.io/types/status/v0", "https://tent.io/types/photo/v0"},
			Read:  []string{"https://tent.io/types/app/v0"},
		},
		RedirectURI: "https://app.example.com/oauth",
	})
	err := client.CreatePost(post)
	maybePanic(err)
	client.Credentials, _, err = post.LinkedCredentials()
	maybePanic(err)
	oauthURL, _ := meta.Servers[0].URLs.OAuthURL(post.ID, "d173d2bb868a")
	req, _ := http.NewRequest("GET", oauthURL, nil)
	res, err := tent.HTTP.Transport.RoundTrip(req)
	maybePanic(err)
	u, err := url.Parse(res.Header.Get("Location"))
	maybePanic(err)
	client.Credentials, err = client.RequestAccessToken(u.Query().Get("code"))
	maybePanic(err)
	return getRequests()
}

func newPost() *request {
	post := &tent.Post{
		Type:    "https://tent.io/types/status/v0#",
		Content: []byte(`{"text": "example post"}`),
	}
	err := client.CreatePost(post)
	maybePanic(err)
	return getRequests()[0]
}

func newMultipartPost() []*request {
	post := &tent.Post{
		Type:    "https://tent.io/types/photo/v0#",
		Content: []byte(`{"caption": "example photo"}`),
		Attachments: []*tent.PostAttachment{{
			Name:        "example.jpeg",
			Category:    "photo",
			ContentType: "image/jpeg",
			Data:        strings.NewReader("example attachment data"),
		}},
	}
	err := client.CreatePost(post)
	maybePanic(err)

	_, err = io.Copy(ioutil.Discard, post.Attachments[0])
	maybePanic(err)
	post.Attachments[0].Close()

	body, err := client.GetPostAttachment(post.Entity, post.ID, "latest", post.Attachments[0].Name, "*/*")
	maybePanic(err)
	_, err = io.Copy(ioutil.Discard, post.Attachments[0])
	body.Close()

	return getRequests()
}

func getPostsFeed() *request {
	_, err := client.GetFeed(tent.NewPostsFeedQuery().Limit(2))
	maybePanic(err)
	return getRequests()[0]
}

func main() {
	examples := make(map[string]*request)
	tent.HTTP.Transport = &roundTripRecorder{roundTripper: tent.HTTP.Transport}

	discoveryReqs := discover()
	examples["discover_head"] = discoveryReqs[0]
	examples["discover_meta"] = discoveryReqs[1]

	appReqs := createApp()
	examples["app_create"] = appReqs[0]
	examples["app_credentials"] = appReqs[1]
	examples["oauth_redirect"] = appReqs[2]
	examples["oauth_token"] = appReqs[3]

	examples["new_post"] = newPost()

	multipartReqs := newMultipartPost()
	examples["new_multipart_post"] = multipartReqs[0]
	examples["get_attachment"] = multipartReqs[1]
	examples["get_post_attachment"] = multipartReqs[2]

	examples["posts_feed"] = getPostsFeed()

	res := make(map[string]string)
	for k, v := range examples {
		res[k] = requestMarkdown(v)
	}

	data, _ := json.Marshal(res)
	ioutil.WriteFile(os.Args[2], data, 0644)
}

func maybePanic(err error) {
	if err != nil {
		if resErr, ok := err.(*tent.BadResponseError); ok && resErr.TentError != nil {
			fmt.Println(resErr.TentError)
		}
		panic(err)
	}
}
<|endoftext|>


<commit_before>package fblib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/davecgh/go-spew/spew"

	"github.com/novatrixtech/go-fbmessenger/fbmodelsend"
)

var logLevelDebug = false

/*
SendTextMessage - Send text message to a recipient on Facebook Messenger
*/
func SendTextMessage(text string, recipient string, accessToken string) {
	letter := new(fbmodelsend.Letter)
	letter.Message.Text = text
	letter.Recipient.ID = recipient

	if err := sendMessage(letter, recipient, accessToken); err != nil {
		fmt.Print("[sendTextMessage] Error during the call to Facebook to send the text message: " + err.Error())
		return
	}
}

/*
SendImageMessage - Sends image message to a recipient on Facebook Messenger
*/
func SendImageMessage(url string, recipient string, accessToken string) {
	message := new(fbmodelsend.Letter)

	attch := new(fbmodelsend.Attachment)
	attch.AttachmentType = "image"
	attch.Payload.URL = url
	message.Message.Attachment = attch

	message.Recipient.ID = recipient

	if err := sendMessage(message, recipient, accessToken); err != nil {
		fmt.Print("[sendImageMessage] Error during the call to Facebook to send the image message: " + err.Error())
		return
	}
}

/*
SendAudioMessage - Sends audio message to a recipient on Facebook Messenger
*/
func SendAudioMessage(url string, recipient string, accessToken string) {
	message := new(fbmodelsend.Letter)

	attch := new(fbmodelsend.Attachment)
	attch.AttachmentType = "audio"
	attch.Payload.URL = url
	message.Message.Attachment = attch

	message.Recipient.ID = recipient

	if err := sendMessage(message, recipient, accessToken); err != nil {
		fmt.Print("[sendImageMessage] Error during the call to Facebook to send the audio message: " + err.Error())
		return
	}
}

/*
SendTypingMessage - Sends typing message to user
*/
func SendTypingMessage(onoff bool, recipient string, accessToken string) {
	senderAction := new(fbmodelsend.SenderAction)
	senderAction.Recipient.ID = recipient
	if onoff {
		senderAction.SenderActionState = "typing_on"
	} else {
		senderAction.SenderActionState = "typing_off"
	}
	if err := sendMessage(senderAction, recipient, accessToken); err != nil {
		fmt.Print("[sendImageMessage] Error during the call to Facebook to send the typing message: " + err.Error())
		return
	}
}

/*
SendGenericTemplateMessage - Sends a generic rich message to Facebook user.
It can include text, buttons, URLs Butttons, lists to reply
*/
func SendGenericTemplateMessage(template []*fbmodelsend.TemplateElement, recipient string, accessToken string) {
	msg := new(fbmodelsend.Letter)
	msg.Recipient.ID = recipient

	attch := new(fbmodelsend.Attachment)
	attch.AttachmentType = "template"
	attch.Payload.TemplateType = "generic"
	attch.Payload.Elements = template

	msg.Message.Attachment = attch

	if err := sendMessage(msg, recipient, accessToken); err != nil {
		fmt.Print("[SendGenericTemplateMessage] Error during the call to Facebook to send the text message: " + err.Error())
		return
	}
}

/*
SendButtonMessage - Sends a generic rich message to Facebook user.
It can include text, buttons, URLs Butttons, lists to reply
*/
func SendButtonMessage(template []*fbmodelsend.Button, text string, recipient string, accessToken string) {
	msg := new(fbmodelsend.Letter)
	msg.Recipient.ID = recipient

	attch := new(fbmodelsend.Attachment)
	attch.AttachmentType = "template"
	attch.Payload.TemplateType = "button"
	attch.Payload.Text = text
	attch.Payload.Buttons = template

	msg.Message.Attachment = attch

	if err := sendMessage(msg, recipient, accessToken); err != nil {
		fmt.Print("[sendTextMessage] Error during the call to Facebook to send the text message: " + err.Error())
		return
	}
}

/*
SendURLButtonMessage - Sends a message with a button that redirects the user to an external web page.
*/
func SendURLButtonMessage(text string, buttonTitle string, URL string, recipient string, accessToken string) {

	msgElement := new(fbmodelsend.TemplateElement)
	msgElement.Title = text

	opt1 := new(fbmodelsend.Button)
	opt1.ButtonType = "web_url"
	opt1.Title = buttonTitle
	opt1.URL = URL

	buttons := []*fbmodelsend.Button{opt1}

	msgElement.Buttons = buttons
	elements := []*fbmodelsend.TemplateElement{msgElement}
	SendGenericTemplateMessage(elements, recipient, accessToken)
}

/*
SendShareMessage sends the message along with Share Button
*/
func SendShareMessage(title string, subtitle string, recipient string, accessToken string) {

	msgElement := new(fbmodelsend.TemplateElement)
	msgElement.Title = title
	msgElement.Subtitle = subtitle

	opt1 := new(fbmodelsend.Button)
	opt1.ButtonType = "element_share"
	buttons := []*fbmodelsend.Button{opt1}

	msgElement.Buttons = buttons
	elements := []*fbmodelsend.TemplateElement{msgElement}
	SendGenericTemplateMessage(elements, recipient, accessToken)

}

/*
SendShareMessage sends the message along with Share Button
*/
func SendShareContent(title string, subtitle string, buttonTitle string, imageURL string, destinationURL string, recipient string, accessToken string) {

	btnElementButton := new(fbmodelsend.Button)
	btnElementButton.ButtonType = "web_url"
	btnElementButton.URL = destinationURL
	btnElementButton.Title = buttonTitle
	buttonsElementButton := []*fbmodelsend.Button{btnElementButton}

	btnElement := new(fbmodelsend.TemplateElement)
	btnElement.Title = title
	btnElement.Subtitle = subtitle
	btnElement.ImageURL = imageURL
	btnElement.DefaultAction.Type = "web_url"
	btnElement.DefaultAction.URL = destinationURL
	btnElement.Buttons = buttonsElementButton
	elementsButtonElement := []*fbmodelsend.TemplateElement{btnElement}

	opt1 := new(fbmodelsend.Button)
	opt1.ButtonType = "element_share"
	opt1.ShareContents.Attachment.Type = "template"
	opt1.ShareContents.Attachment.Payload.TemplateType = "generic"
	opt1.ShareContents.Attachment.Payload.Elements = elementsButtonElement
	buttons := []*fbmodelsend.Button{opt1}

	msgElement := new(fbmodelsend.TemplateElement)
	msgElement.Title = title
	msgElement.Subtitle = subtitle
	msgElement.Buttons = buttons
	elements := []*fbmodelsend.TemplateElement{msgElement}
	SendGenericTemplateMessage(elements, recipient, accessToken)

}

/*
SendQuickReply sends small messages in order to get small and quick answers from the users
*/
func SendQuickReply(text string, options []*fbmodelsend.QuickReply, recipient string, accessToken string) {
	msg := new(fbmodelsend.Letter)
	msg.Recipient.ID = recipient
	msg.Message.Text = text
	msg.Message.QuickReplies = options
	//log.Printf("[SendQuickReply] Enviado: [%s]\n", text)
	if err := sendMessage(msg, recipient, accessToken); err != nil {
		log.Print("[SendQuickReply] Error during the call to Facebook to send the text message: " + err.Error())
		return
	}
}

/*
SendAskUserLocation sends small message asking the users their location
*/
func SendAskUserLocation(text string, recipient string, accessToken string) {
	qr := new(fbmodelsend.QuickReply)
	qr.ContentType = "location"

	arrayQr := []*fbmodelsend.QuickReply{qr}

	SendQuickReply(text, arrayQr, recipient, accessToken)
}

/*
Send Message - Sends a generic message to Facebook Messenger
*/
func sendMessage(message interface{}, recipient string, accessToken string) error {

	if logLevelDebug {
		scs := spew.ConfigState{Indent: "\t"}
		scs.Dump(message)
		return nil
	}

	var url string
	if strings.Contains(accessToken, "http") {
		url = accessToken
	} else {
		url = "https://graph.facebook.com/v2.8/me/messages?access_token=" + accessToken
	}

	data, err := json.Marshal(message)
	if err != nil {
		fmt.Print("[sendMessage] Error to convert message object: " + err.Error())
		return err
	}

	reqFb, _ := http.NewRequest("POST", url, bytes.NewBuffer(data))
	reqFb.Header.Set("Content-Type", "application/json")
	reqFb.Header.Set("Connection", "close")
	reqFb.Close = true

	client := &http.Client{}

	//fmt.Println("[sendMessage] Replying at: " + url + " the message " + string(data))

	respFb, err := client.Do(reqFb)
	if err != nil {
		fmt.Print("[sendMessage] Error during the call to Facebook to send the message: " + err.Error())
		return err
	}
	defer respFb.Body.Close()

	if respFb.StatusCode < 200 || respFb.StatusCode >= 300 {
		bodyFromFb, _ := ioutil.ReadAll(respFb.Body)
		status := string(bodyFromFb)
		fmt.Printf("[sendMessage] Response status code: [%d]\n", respFb.StatusCode)
		fmt.Println("[sendMessage] Response status: ", respFb.Status)
		fmt.Println("[sendMessage] Response Body from Facebook: ", status)
		fmt.Printf("[sendMessage] Facebook URL Called: [%s]\n", url)
		fmt.Printf("[sendMessage] Object sent to Facebook: [%s]\n", string(data))
	}

	return nil
}
<commit_msg>Adjust docs<commit_after>package fblib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/davecgh/go-spew/spew"

	"github.com/novatrixtech/go-fbmessenger/fbmodelsend"
)

var logLevelDebug = false

/*
SendTextMessage - Send text message to a recipient on Facebook Messenger
*/
func SendTextMessage(text string, recipient string, accessToken string) {
	letter := new(fbmodelsend.Letter)
	letter.Message.Text = text
	letter.Recipient.ID = recipient

	if err := sendMessage(letter, recipient, accessToken); err != nil {
		fmt.Print("[sendTextMessage] Error during the call to Facebook to send the text message: " + err.Error())
		return
	}
}

/*
SendImageMessage - Sends image message to a recipient on Facebook Messenger
*/
func SendImageMessage(url string, recipient string, accessToken string) {
	message := new(fbmodelsend.Letter)

	attch := new(fbmodelsend.Attachment)
	attch.AttachmentType = "image"
	attch.Payload.URL = url
	message.Message.Attachment = attch

	message.Recipient.ID = recipient

	if err := sendMessage(message, recipient, accessToken); err != nil {
		fmt.Print("[sendImageMessage] Error during the call to Facebook to send the image message: " + err.Error())
		return
	}
}

/*
SendAudioMessage - Sends audio message to a recipient on Facebook Messenger
*/
func SendAudioMessage(url string, recipient string, accessToken string) {
	message := new(fbmodelsend.Letter)

	attch := new(fbmodelsend.Attachment)
	attch.AttachmentType = "audio"
	attch.Payload.URL = url
	message.Message.Attachment = attch

	message.Recipient.ID = recipient

	if err := sendMessage(message, recipient, accessToken); err != nil {
		fmt.Print("[sendImageMessage] Error during the call to Facebook to send the audio message: " + err.Error())
		return
	}
}

/*
SendTypingMessage - Sends typing message to user
*/
func SendTypingMessage(onoff bool, recipient string, accessToken string) {
	senderAction := new(fbmodelsend.SenderAction)
	senderAction.Recipient.ID = recipient
	if onoff {
		senderAction.SenderActionState = "typing_on"
	} else {
		senderAction.SenderActionState = "typing_off"
	}
	if err := sendMessage(senderAction, recipient, accessToken); err != nil {
		fmt.Print("[sendImageMessage] Error during the call to Facebook to send the typing message: " + err.Error())
		return
	}
}

/*
SendGenericTemplateMessage - Sends a generic rich message to Facebook user.
It can include text, buttons, URLs Butttons, lists to reply
*/
func SendGenericTemplateMessage(template []*fbmodelsend.TemplateElement, recipient string, accessToken string) {
	msg := new(fbmodelsend.Letter)
	msg.Recipient.ID = recipient

	attch := new(fbmodelsend.Attachment)
	attch.AttachmentType = "template"
	attch.Payload.TemplateType = "generic"
	attch.Payload.Elements = template

	msg.Message.Attachment = attch

	if err := sendMessage(msg, recipient, accessToken); err != nil {
		fmt.Print("[SendGenericTemplateMessage] Error during the call to Facebook to send the text message: " + err.Error())
		return
	}
}

/*
SendButtonMessage - Sends a generic rich message to Facebook user.
It can include text, buttons, URLs Butttons, lists to reply
*/
func SendButtonMessage(template []*fbmodelsend.Button, text string, recipient string, accessToken string) {
	msg := new(fbmodelsend.Letter)
	msg.Recipient.ID = recipient

	attch := new(fbmodelsend.Attachment)
	attch.AttachmentType = "template"
	attch.Payload.TemplateType = "button"
	attch.Payload.Text = text
	attch.Payload.Buttons = template

	msg.Message.Attachment = attch

	if err := sendMessage(msg, recipient, accessToken); err != nil {
		fmt.Print("[sendTextMessage] Error during the call to Facebook to send the text message: " + err.Error())
		return
	}
}

/*
SendURLButtonMessage - Sends a message with a button that redirects the user to an external web page.
*/
func SendURLButtonMessage(text string, buttonTitle string, URL string, recipient string, accessToken string) {

	msgElement := new(fbmodelsend.TemplateElement)
	msgElement.Title = text

	opt1 := new(fbmodelsend.Button)
	opt1.ButtonType = "web_url"
	opt1.Title = buttonTitle
	opt1.URL = URL

	buttons := []*fbmodelsend.Button{opt1}

	msgElement.Buttons = buttons
	elements := []*fbmodelsend.TemplateElement{msgElement}
	SendGenericTemplateMessage(elements, recipient, accessToken)
}

/*
SendShareMessage sends the message along with Share Button
*/
func SendShareMessage(title string, subtitle string, recipient string, accessToken string) {

	msgElement := new(fbmodelsend.TemplateElement)
	msgElement.Title = title
	msgElement.Subtitle = subtitle

	opt1 := new(fbmodelsend.Button)
	opt1.ButtonType = "element_share"
	buttons := []*fbmodelsend.Button{opt1}

	msgElement.Buttons = buttons
	elements := []*fbmodelsend.TemplateElement{msgElement}
	SendGenericTemplateMessage(elements, recipient, accessToken)

}

/*
SendShareContent share rich content media and url button
*/
func SendShareContent(title string, subtitle string, buttonTitle string, imageURL string, destinationURL string, recipient string, accessToken string) {

	btnElementButton := new(fbmodelsend.Button)
	btnElementButton.ButtonType = "web_url"
	btnElementButton.URL = destinationURL
	btnElementButton.Title = buttonTitle
	buttonsElementButton := []*fbmodelsend.Button{btnElementButton}

	btnElement := new(fbmodelsend.TemplateElement)
	btnElement.Title = title
	btnElement.Subtitle = subtitle
	btnElement.ImageURL = imageURL
	btnElement.DefaultAction.Type = "web_url"
	btnElement.DefaultAction.URL = destinationURL
	btnElement.Buttons = buttonsElementButton
	elementsButtonElement := []*fbmodelsend.TemplateElement{btnElement}

	opt1 := new(fbmodelsend.Button)
	opt1.ButtonType = "element_share"
	opt1.ShareContents.Attachment.Type = "template"
	opt1.ShareContents.Attachment.Payload.TemplateType = "generic"
	opt1.ShareContents.Attachment.Payload.Elements = elementsButtonElement
	buttons := []*fbmodelsend.Button{opt1}

	msgElement := new(fbmodelsend.TemplateElement)
	msgElement.Title = title
	msgElement.Subtitle = subtitle
	msgElement.Buttons = buttons
	elements := []*fbmodelsend.TemplateElement{msgElement}
	SendGenericTemplateMessage(elements, recipient, accessToken)

}

/*
SendQuickReply sends small messages in order to get small and quick answers from the users
*/
func SendQuickReply(text string, options []*fbmodelsend.QuickReply, recipient string, accessToken string) {
	msg := new(fbmodelsend.Letter)
	msg.Recipient.ID = recipient
	msg.Message.Text = text
	msg.Message.QuickReplies = options
	//log.Printf("[SendQuickReply] Enviado: [%s]\n", text)
	if err := sendMessage(msg, recipient, accessToken); err != nil {
		log.Print("[SendQuickReply] Error during the call to Facebook to send the text message: " + err.Error())
		return
	}
}

/*
SendAskUserLocation sends small message asking the users their location
*/
func SendAskUserLocation(text string, recipient string, accessToken string) {
	qr := new(fbmodelsend.QuickReply)
	qr.ContentType = "location"

	arrayQr := []*fbmodelsend.QuickReply{qr}

	SendQuickReply(text, arrayQr, recipient, accessToken)
}

/*
Send Message - Sends a generic message to Facebook Messenger
*/
func sendMessage(message interface{}, recipient string, accessToken string) error {

	if logLevelDebug {
		scs := spew.ConfigState{Indent: "\t"}
		scs.Dump(message)
		return nil
	}

	var url string
	if strings.Contains(accessToken, "http") {
		url = accessToken
	} else {
		url = "https://graph.facebook.com/v2.8/me/messages?access_token=" + accessToken
	}

	data, err := json.Marshal(message)
	if err != nil {
		fmt.Print("[sendMessage] Error to convert message object: " + err.Error())
		return err
	}

	reqFb, _ := http.NewRequest("POST", url, bytes.NewBuffer(data))
	reqFb.Header.Set("Content-Type", "application/json")
	reqFb.Header.Set("Connection", "close")
	reqFb.Close = true

	client := &http.Client{}

	//fmt.Println("[sendMessage] Replying at: " + url + " the message " + string(data))

	respFb, err := client.Do(reqFb)
	if err != nil {
		fmt.Print("[sendMessage] Error during the call to Facebook to send the message: " + err.Error())
		return err
	}
	defer respFb.Body.Close()

	if respFb.StatusCode < 200 || respFb.StatusCode >= 300 {
		bodyFromFb, _ := ioutil.ReadAll(respFb.Body)
		status := string(bodyFromFb)
		fmt.Printf("[sendMessage] Response status code: [%d]\n", respFb.StatusCode)
		fmt.Println("[sendMessage] Response status: ", respFb.Status)
		fmt.Println("[sendMessage] Response Body from Facebook: ", status)
		fmt.Printf("[sendMessage] Facebook URL Called: [%s]\n", url)
		fmt.Printf("[sendMessage] Object sent to Facebook: [%s]\n", string(data))
	}

	return nil
}
<|endoftext|>


<commit_before>package felixcheck

import (
	"fmt"
	"net"
	"time"

	"github.com/aleasoluciones/goaleasoluciones/scheduledtask"
	"github.com/aleasoluciones/gosnmpquerier"
	"github.com/tatsushid/go-fastping"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

type Device struct {
	Id        string
	DevType   string
	Ip        string
	Community string
}

type CheckResult struct {
	device  Device
	checker Checker
	result  bool
	err     error
}

type CheckEngine struct {
	checkPublisher CheckPublisher
	results        chan CheckResult
}

func NewCheckEngine(checkPublisher CheckPublisher) CheckEngine {
	checkEngine := CheckEngine{checkPublisher, make(chan CheckResult)}
	go func() {
		for result := range checkEngine.results {
			checkEngine.checkPublisher.PublishCheckResult(result.device, result.checker, result.result, result.err)
		}
	}()
	return checkEngine
}

func (ce CheckEngine) AddCheck(device Device, c Checker, period time.Duration) {
	scheduledtask.NewScheduledTask(func() {
		result, err := c.Check(device)
		ce.results <- CheckResult{device, c, result, err}

	}, period, 0)
}

type CheckPublisher interface {
	PublishCheckResult(device Device, checker Checker, result bool, err error)
}

type Checker interface {
	Check(device Device) (bool, error)
}

type TcpCheckerConf struct {
	retries   int
	timeout   time.Duration
	retrytime time.Duration
}

var DefaultTcpCheckConf = TcpCheckerConf{
	retries:   3,
	timeout:   2 * time.Second,
	retrytime: 1 * time.Second,
}

type TcpPortChecker struct {
	port int
	conf TcpCheckerConf
}

func NewTcpPortChecker(port int, conf TcpCheckerConf) TcpPortChecker {
	return TcpPortChecker{port: port, conf: conf}
}

func (c TcpPortChecker) Check(device Device) (bool, error) {
	var err error
	var conn net.Conn

	for attempt := 0; attempt < c.conf.retries; attempt++ {
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", device.Ip, c.port), c.conf.timeout)
		if err == nil {
			conn.Close()
			return true, nil
		}
		time.Sleep(c.conf.retrytime)
	}
	return false, err
}

type ICMPChecker struct {
}

func NewICMPChecker() ICMPChecker {
	return ICMPChecker{}
}

func (c ICMPChecker) String() string {
	return "ICMPChecker"
}

func (c ICMPChecker) Check(device Device) (bool, error) {
	var retRtt time.Duration = 0
	var isUp bool = false

	p := fastping.NewPinger()
	p.MaxRTT = maxPingTime
	ra, err := net.ResolveIPAddr("ip4:icmp", device.Ip)

	if err != nil {
		return false, err
	}

	p.AddIPAddr(ra)
	p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
		isUp = true
		retRtt = rtt
	}

	err = p.Run()
	if err != nil {
		return false, err
	}

	return isUp, nil
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

type SnmpChecker struct {
	SnmpQuerier gosnmpquerier.SyncQuerier
	conf        SnmpCheckerConf
}

func NewSnmpChecker(conf SnmpCheckerConf) SnmpChecker {
	return SnmpChecker{SnmpQuerier: gosnmpquerier.NewSyncQuerier(1, 1, 4*time.Second), conf: conf}
}

func (c SnmpChecker) Check(device Device) (bool, error) {
	_, err := c.SnmpQuerier.Get(device.Ip, device.Community, []string{c.conf.oidToCheck}, c.conf.timeout, c.conf.retries)
	if err == nil {
		return true, nil
	} else {
		return false, err
	}

}
<commit_msg>String interface for other checkers<commit_after>package felixcheck

import (
	"fmt"
	"net"
	"time"

	"github.com/aleasoluciones/goaleasoluciones/scheduledtask"
	"github.com/aleasoluciones/gosnmpquerier"
	"github.com/tatsushid/go-fastping"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

type Device struct {
	Id        string
	DevType   string
	Ip        string
	Community string
}

type CheckResult struct {
	device  Device
	checker Checker
	result  bool
	err     error
}

type CheckEngine struct {
	checkPublisher CheckPublisher
	results        chan CheckResult
}

func NewCheckEngine(checkPublisher CheckPublisher) CheckEngine {
	checkEngine := CheckEngine{checkPublisher, make(chan CheckResult)}
	go func() {
		for result := range checkEngine.results {
			checkEngine.checkPublisher.PublishCheckResult(result.device, result.checker, result.result, result.err)
		}
	}()
	return checkEngine
}

func (ce CheckEngine) AddCheck(device Device, c Checker, period time.Duration) {
	scheduledtask.NewScheduledTask(func() {
		result, err := c.Check(device)
		ce.results <- CheckResult{device, c, result, err}

	}, period, 0)
}

type CheckPublisher interface {
	PublishCheckResult(device Device, checker Checker, result bool, err error)
}

type Checker interface {
	Check(device Device) (bool, error)
}

type TcpCheckerConf struct {
	retries   int
	timeout   time.Duration
	retrytime time.Duration
}

var DefaultTcpCheckConf = TcpCheckerConf{
	retries:   3,
	timeout:   2 * time.Second,
	retrytime: 1 * time.Second,
}

type TcpPortChecker struct {
	port int
	conf TcpCheckerConf
}

func NewTcpPortChecker(port int, conf TcpCheckerConf) TcpPortChecker {
	return TcpPortChecker{port: port, conf: conf}
}

func (c TcpPortChecker) String() string {
	return fmt.Sprintf("TcpPortChecker %d", c.port)
}

func (c TcpPortChecker) Check(device Device) (bool, error) {
	var err error
	var conn net.Conn

	for attempt := 0; attempt < c.conf.retries; attempt++ {
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", device.Ip, c.port), c.conf.timeout)
		if err == nil {
			conn.Close()
			return true, nil
		}
		time.Sleep(c.conf.retrytime)
	}
	return false, err
}

type ICMPChecker struct {
}

func NewICMPChecker() ICMPChecker {
	return ICMPChecker{}
}

func (c ICMPChecker) String() string {
	return "ICMPChecker"
}

func (c ICMPChecker) Check(device Device) (bool, error) {
	var retRtt time.Duration = 0
	var isUp bool = false

	p := fastping.NewPinger()
	p.MaxRTT = maxPingTime
	ra, err := net.ResolveIPAddr("ip4:icmp", device.Ip)

	if err != nil {
		return false, err
	}

	p.AddIPAddr(ra)
	p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
		isUp = true
		retRtt = rtt
	}

	err = p.Run()
	if err != nil {
		return false, err
	}

	return isUp, nil
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

type SnmpChecker struct {
	SnmpQuerier gosnmpquerier.SyncQuerier
	conf        SnmpCheckerConf
}

func NewSnmpChecker(conf SnmpCheckerConf) SnmpChecker {
	return SnmpChecker{SnmpQuerier: gosnmpquerier.NewSyncQuerier(1, 1, 4*time.Second), conf: conf}
}

func (c SnmpChecker) String() string {
	return "SNMPChecker"
}

func (c SnmpChecker) Check(device Device) (bool, error) {
	_, err := c.SnmpQuerier.Get(device.Ip, device.Community, []string{c.conf.oidToCheck}, c.conf.timeout, c.conf.retries)
	if err == nil {
		return true, nil
	} else {
		return false, err
	}

}
<|endoftext|>


<commit_before><commit_msg>Create chaincode_myexample02.go<commit_after><|endoftext|>