<commit_before>// Tideland Go REST Server Library - REST - Formatter
//
// Copyright (C) 2009-2016 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package rest

//--------------------
// IMPORTS
//--------------------

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/tideland/golib/errors"
	"github.com/tideland/golib/logger"
	"github.com/tideland/golib/stringex"
)

//--------------------
// CONST
//--------------------

const (
	// Standard REST status codes.
	StatusOK                  = http.StatusOK
	StatusCreated             = http.StatusCreated
	StatusNoContent           = http.StatusNoContent
	StatusBadRequest          = http.StatusBadRequest
	StatusUnauthorized        = http.StatusUnauthorized
	StatusNotFound            = http.StatusNotFound
	StatusConflict            = http.StatusConflict
	StatusInternalServerError = http.StatusInternalServerError

	// Standard REST content types.
	ContentTypePlain      = "text/plain"
	ContentTypeHTML       = "text/html"
	ContentTypeXML        = "application/xml"
	ContentTypeJSON       = "application/json"
	ContentTypeGOB        = "application/vnd.tideland.gob"
	ContentTypeURLEncoded = "application/x-www-form-urlencoded"
)

//--------------------
// GLOBAL
//--------------------

var (
	defaulter = stringex.NewDefaulter("job", false)
)

//--------------------
// ENVELOPE
//--------------------

// envelope is a helper to give a qualified feedback in RESTful requests.
// It contains wether the request has been successful, in case of an
// error an additional message and the payload.
type envelope struct {
	Status  string      `json:"status" xml:"status"`
	Message string      `json:"message,omitempty" xml:"message,omitempty"`
	Payload interface{} `json:"payload,omitempty" xml:"payload,omitempty"`
}

//--------------------
// FORMATTER
//--------------------

type Formatter interface {
	// Write encodes the passed data to implementers format and writes
	// it with the passed status code and possible header values to the
	// response writer.
	Write(status int, data interface{}, headers ...KeyValue) error

	// Read checks if the request content type matches the implementers
	// format, reads its body and decodes it to the value pointed to by
	// data.
	Read(data interface{}) error
}

// PositiveFeedback writes a positive feedback envelope to the formatter.
func PositiveFeedback(f Formatter, payload interface{}, msg string, args ...interface{}) (bool, error) {
	fmsg := fmt.Sprintf(msg, args...)
	return false, f.Write(StatusOK, envelope{"success", fmsg, payload})
}

// NegativeFeedback writes a negative feedback envelope to the formatter.
func NegativeFeedback(f Formatter, status int, msg string, args ...interface{}) (bool, error) {
	fmsg := fmt.Sprintf(msg, args...)
	logger.Warningf(fmsg)
	return false, f.Write(status, envelope{"fail", fmsg, nil})
}

//--------------------
// GOB FORMATTER
//--------------------

// gobFormatter implements Formatter for the GOB encoding.
type gobFormatter struct {
	job Job
}

// Write is specified on the Formatter interface.
func (gf *gobFormatter) Write(status int, data interface{}, headers ...KeyValue) error {
	enc := gob.NewEncoder(gf.job.ResponseWriter())
	for _, header := range headers {
		gf.job.ResponseWriter().Header().Add(header.Key, fmt.Sprintf("%v", header.Value))
	}
	gf.job.ResponseWriter().Header().Set("Content-Type", ContentTypeGOB)
	gf.job.ResponseWriter().Header().Set("Version", gf.job.Version().String())
	gf.job.ResponseWriter().WriteHeader(status)
	err := enc.Encode(data)
	if err != nil {
		http.Error(gf.job.ResponseWriter(), err.Error(), http.StatusInternalServerError)
	}
	return err
}

// Read is specified on the Formatter interface.
func (gf *gobFormatter) Read(data interface{}) error {
	if !gf.job.HasContentType(ContentTypeGOB) {
		return errors.New(ErrInvalidContentType, errorMessages, ContentTypeGOB)
	}
	dec := gob.NewDecoder(gf.job.Request().Body)
	err := dec.Decode(data)
	gf.job.Request().Body.Close()
	return err
}

//--------------------
// JSON FORMATTER
//--------------------

// jsonFormatter implements Formatter for the JSON encoding. Writing
// also can be done with HTML escaping.
type jsonFormatter struct {
	job  Job
	html bool
}

// Write is specified on the Formatter interface.
func (jf *jsonFormatter) Write(status int, data interface{}, headers ...KeyValue) error {
	body, err := json.Marshal(data)
	if err != nil {
		http.Error(jf.job.ResponseWriter(), err.Error(), http.StatusInternalServerError)
		return err
	}
	if jf.html {
		var buf bytes.Buffer
		json.HTMLEscape(&buf, body)
		body = buf.Bytes()
	}
	for _, header := range headers {
		jf.job.ResponseWriter().Header().Add(header.Key, fmt.Sprintf("%v", header.Value))
	}
	jf.job.ResponseWriter().Header().Set("Content-Type", ContentTypeJSON)
	jf.job.ResponseWriter().Header().Set("Version", jf.job.Version().String())
	jf.job.ResponseWriter().WriteHeader(status)
	_, err = jf.job.ResponseWriter().Write(body)
	return err
}

// Read is specified on the Formatter interface.
func (jf *jsonFormatter) Read(data interface{}) error {
	if !jf.job.HasContentType(ContentTypeJSON) {
		return errors.New(ErrInvalidContentType, errorMessages, ContentTypeJSON)
	}
	body, err := ioutil.ReadAll(jf.job.Request().Body)
	jf.job.Request().Body.Close()
	if err != nil {
		return err
	}
	return json.Unmarshal(body, &data)
}

//--------------------
// XML FORMATTER
//--------------------

// xmlFormatter implements Formatter for the XML encoding.
type xmlFormatter struct {
	job Job
}

// Write is specified on the Formatter interface.
func (xf *xmlFormatter) Write(status int, data interface{}, headers ...KeyValue) error {
	body, err := xml.Marshal(data)
	if err != nil {
		http.Error(xf.job.ResponseWriter(), err.Error(), http.StatusInternalServerError)
		return err
	}
	for _, header := range headers {
		xf.job.ResponseWriter().Header().Add(header.Key, fmt.Sprintf("%v", header.Value))
	}
	xf.job.ResponseWriter().Header().Set("Content-Type", ContentTypeXML)
	xf.job.ResponseWriter().Header().Set("Version", xf.job.Version().String())
	xf.job.ResponseWriter().WriteHeader(status)
	_, err = xf.job.ResponseWriter().Write(body)
	return err
}

// Read is specified on the Formatter interface.
func (xf *xmlFormatter) Read(data interface{}) error {
	if !xf.job.HasContentType(ContentTypeXML) {
		return errors.New(ErrInvalidContentType, errorMessages, ContentTypeXML)
	}
	body, err := ioutil.ReadAll(xf.job.Request().Body)
	xf.job.Request().Body.Close()
	if err != nil {
		return err
	}
	return xml.Unmarshal(body, &data)
}

//--------------------
// QUERY
//--------------------

// Query allows typed access with default values to a jobs
// request values passed as query.
type Query interface {
	// ValueAsString retrieves the string value of a given key. If it
	// doesn't exist the default value dv is returned.
	ValueAsString(key, dv string) string

	// ValueAsBool retrieves the bool value of a given key. If it
	// doesn't exist the default value dv is returned.
	ValueAsBool(key string, dv bool) bool

	// ValueAsInt retrieves the int value of a given key. If it
	// doesn't exist the default value dv is returned.
	ValueAsInt(key string, dv int) int

	// ValueAsFloat64 retrieves the float64 value of a given key. If it
	// doesn't exist the default value dv is returned.
	ValueAsFloat64(key string, dv float64) float64

	// ValueAsTime retrieves the string value of a given key and
	// interprets it as time with the passed format. If it
	// doesn't exist the default value dv is returned.
	ValueAsTime(key, layout string, dv time.Time) time.Time

	// ValueAsDuration retrieves the duration value of a given key.
	// If it doesn't exist the default value dv is returned.
	ValueAsDuration(key string, dv time.Duration) time.Duration
}

// query implements Query.
type query struct {
	values url.Values
}

// ValueAsString implements the Query interface.
func (q *query) ValueAsString(key, dv string) string {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsString(value, dv)
}

// ValueAsBool implements the Query interface.
func (q *query) ValueAsBool(key string, dv bool) bool {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsBool(value, dv)
}

// ValueAsInt implements the Query interface.
func (q *query) ValueAsInt(key string, dv int) int {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsInt(value, dv)
}

// ValueAsFloat64 implements the Query interface.
func (q *query) ValueAsFloat64(key string, dv float64) float64 {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsFloat64(value, dv)
}

// ValueAsTime implements the Query interface.
func (q *query) ValueAsTime(key, format string, dv time.Time) time.Time {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsTime(value, format, dv)
}

// ValueAsDuration implements the Query interface.
func (q *query) ValueAsDuration(key string, dv time.Duration) time.Duration {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsDuration(value, dv)
}

// queryValues implements the stringex.Valuer interface for
// the usage inside of query.
type queryValuer string

// Value implements the Valuer interface.
func (qv queryValuer) Value() (string, error) {
	v := string(qv)
	if len(v) == 0 {
		return "", errors.New(ErrQueryValueNotFound, errorMessages)
	}
	return v, nil
}

// EOF
<commit_msg>Added status code to positive and negative feedback<commit_after>// Tideland Go REST Server Library - REST - Formatter
//
// Copyright (C) 2009-2016 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package rest

//--------------------
// IMPORTS
//--------------------

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/tideland/golib/errors"
	"github.com/tideland/golib/logger"
	"github.com/tideland/golib/stringex"
)

//--------------------
// CONST
//--------------------

const (
	// Standard REST status codes.
	StatusOK                  = http.StatusOK
	StatusCreated             = http.StatusCreated
	StatusNoContent           = http.StatusNoContent
	StatusBadRequest          = http.StatusBadRequest
	StatusUnauthorized        = http.StatusUnauthorized
	StatusNotFound            = http.StatusNotFound
	StatusConflict            = http.StatusConflict
	StatusInternalServerError = http.StatusInternalServerError

	// Standard REST content types.
	ContentTypePlain      = "text/plain"
	ContentTypeHTML       = "text/html"
	ContentTypeXML        = "application/xml"
	ContentTypeJSON       = "application/json"
	ContentTypeGOB        = "application/vnd.tideland.gob"
	ContentTypeURLEncoded = "application/x-www-form-urlencoded"
)

//--------------------
// GLOBAL
//--------------------

var (
	defaulter = stringex.NewDefaulter("job", false)
)

//--------------------
// ENVELOPE
//--------------------

// envelope is a helper to give a qualified feedback in RESTful requests.
// It contains wether the request has been successful, a message, and in
// case of success some payload if wanted.
type envelope struct {
	StatusCode int         `json:"statusCode" xml:"statusCode"`
	Status     string      `json:"status" xml:"status"`
	Message    string      `json:"message,omitempty" xml:"message,omitempty"`
	Payload    interface{} `json:"payload,omitempty" xml:"payload,omitempty"`
}

//--------------------
// FORMATTER
//--------------------

type Formatter interface {
	// Write encodes the passed data to implementers format and writes
	// it with the passed status code and possible header values to the
	// response writer.
	Write(statusCode int, data interface{}, headers ...KeyValue) error

	// Read checks if the request content type matches the implementers
	// format, reads its body and decodes it to the value pointed to by
	// data.
	Read(data interface{}) error
}

// PositiveFeedback writes a positive feedback envelope to the formatter.
func PositiveFeedback(f Formatter, payload interface{}, msg string, args ...interface{}) (bool, error) {
	fmsg := fmt.Sprintf(msg, args...)
	return false, f.Write(StatusOK, envelope{StatusOK, "success", fmsg, payload})
}

// NegativeFeedback writes a negative feedback envelope to the formatter.
// The message is also logged.
func NegativeFeedback(f Formatter, statusCode int, msg string, args ...interface{}) (bool, error) {
	fmsg := fmt.Sprintf(msg, args...)
	lmsg := fmt.Sprintf("(status code %d) "+fmsg, statusCode)
	logger.Warningf(lmsg)
	return false, f.Write(statusCode, envelope{statusCode, "fail", fmsg, nil})
}

//--------------------
// GOB FORMATTER
//--------------------

// gobFormatter implements Formatter for the GOB encoding.
type gobFormatter struct {
	job Job
}

// Write is specified on the Formatter interface.
func (gf *gobFormatter) Write(statusCode int, data interface{}, headers ...KeyValue) error {
	enc := gob.NewEncoder(gf.job.ResponseWriter())
	for _, header := range headers {
		gf.job.ResponseWriter().Header().Add(header.Key, fmt.Sprintf("%v", header.Value))
	}
	gf.job.ResponseWriter().Header().Set("Content-Type", ContentTypeGOB)
	gf.job.ResponseWriter().Header().Set("Version", gf.job.Version().String())
	gf.job.ResponseWriter().WriteHeader(statusCode)
	err := enc.Encode(data)
	if err != nil {
		http.Error(gf.job.ResponseWriter(), err.Error(), http.StatusInternalServerError)
	}
	return err
}

// Read is specified on the Formatter interface.
func (gf *gobFormatter) Read(data interface{}) error {
	if !gf.job.HasContentType(ContentTypeGOB) {
		return errors.New(ErrInvalidContentType, errorMessages, ContentTypeGOB)
	}
	dec := gob.NewDecoder(gf.job.Request().Body)
	err := dec.Decode(data)
	gf.job.Request().Body.Close()
	return err
}

//--------------------
// JSON FORMATTER
//--------------------

// jsonFormatter implements Formatter for the JSON encoding. Writing
// also can be done with HTML escaping.
type jsonFormatter struct {
	job  Job
	html bool
}

// Write is specified on the Formatter interface.
func (jf *jsonFormatter) Write(statusCode int, data interface{}, headers ...KeyValue) error {
	body, err := json.Marshal(data)
	if err != nil {
		http.Error(jf.job.ResponseWriter(), err.Error(), http.StatusInternalServerError)
		return err
	}
	if jf.html {
		var buf bytes.Buffer
		json.HTMLEscape(&buf, body)
		body = buf.Bytes()
	}
	for _, header := range headers {
		jf.job.ResponseWriter().Header().Add(header.Key, fmt.Sprintf("%v", header.Value))
	}
	jf.job.ResponseWriter().Header().Set("Content-Type", ContentTypeJSON)
	jf.job.ResponseWriter().Header().Set("Version", jf.job.Version().String())
	jf.job.ResponseWriter().WriteHeader(statusCode)
	_, err = jf.job.ResponseWriter().Write(body)
	return err
}

// Read is specified on the Formatter interface.
func (jf *jsonFormatter) Read(data interface{}) error {
	if !jf.job.HasContentType(ContentTypeJSON) {
		return errors.New(ErrInvalidContentType, errorMessages, ContentTypeJSON)
	}
	body, err := ioutil.ReadAll(jf.job.Request().Body)
	jf.job.Request().Body.Close()
	if err != nil {
		return err
	}
	return json.Unmarshal(body, &data)
}

//--------------------
// XML FORMATTER
//--------------------

// xmlFormatter implements Formatter for the XML encoding.
type xmlFormatter struct {
	job Job
}

// Write is specified on the Formatter interface.
func (xf *xmlFormatter) Write(statusCode int, data interface{}, headers ...KeyValue) error {
	body, err := xml.Marshal(data)
	if err != nil {
		http.Error(xf.job.ResponseWriter(), err.Error(), http.StatusInternalServerError)
		return err
	}
	for _, header := range headers {
		xf.job.ResponseWriter().Header().Add(header.Key, fmt.Sprintf("%v", header.Value))
	}
	xf.job.ResponseWriter().Header().Set("Content-Type", ContentTypeXML)
	xf.job.ResponseWriter().Header().Set("Version", xf.job.Version().String())
	xf.job.ResponseWriter().WriteHeader(statusCode)
	_, err = xf.job.ResponseWriter().Write(body)
	return err
}

// Read is specified on the Formatter interface.
func (xf *xmlFormatter) Read(data interface{}) error {
	if !xf.job.HasContentType(ContentTypeXML) {
		return errors.New(ErrInvalidContentType, errorMessages, ContentTypeXML)
	}
	body, err := ioutil.ReadAll(xf.job.Request().Body)
	xf.job.Request().Body.Close()
	if err != nil {
		return err
	}
	return xml.Unmarshal(body, &data)
}

//--------------------
// QUERY
//--------------------

// Query allows typed access with default values to a jobs
// request values passed as query.
type Query interface {
	// ValueAsString retrieves the string value of a given key. If it
	// doesn't exist the default value dv is returned.
	ValueAsString(key, dv string) string

	// ValueAsBool retrieves the bool value of a given key. If it
	// doesn't exist the default value dv is returned.
	ValueAsBool(key string, dv bool) bool

	// ValueAsInt retrieves the int value of a given key. If it
	// doesn't exist the default value dv is returned.
	ValueAsInt(key string, dv int) int

	// ValueAsFloat64 retrieves the float64 value of a given key. If it
	// doesn't exist the default value dv is returned.
	ValueAsFloat64(key string, dv float64) float64

	// ValueAsTime retrieves the string value of a given key and
	// interprets it as time with the passed format. If it
	// doesn't exist the default value dv is returned.
	ValueAsTime(key, layout string, dv time.Time) time.Time

	// ValueAsDuration retrieves the duration value of a given key.
	// If it doesn't exist the default value dv is returned.
	ValueAsDuration(key string, dv time.Duration) time.Duration
}

// query implements Query.
type query struct {
	values url.Values
}

// ValueAsString implements the Query interface.
func (q *query) ValueAsString(key, dv string) string {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsString(value, dv)
}

// ValueAsBool implements the Query interface.
func (q *query) ValueAsBool(key string, dv bool) bool {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsBool(value, dv)
}

// ValueAsInt implements the Query interface.
func (q *query) ValueAsInt(key string, dv int) int {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsInt(value, dv)
}

// ValueAsFloat64 implements the Query interface.
func (q *query) ValueAsFloat64(key string, dv float64) float64 {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsFloat64(value, dv)
}

// ValueAsTime implements the Query interface.
func (q *query) ValueAsTime(key, format string, dv time.Time) time.Time {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsTime(value, format, dv)
}

// ValueAsDuration implements the Query interface.
func (q *query) ValueAsDuration(key string, dv time.Duration) time.Duration {
	value := queryValuer(q.values.Get(key))
	return defaulter.AsDuration(value, dv)
}

// queryValues implements the stringex.Valuer interface for
// the usage inside of query.
type queryValuer string

// Value implements the Valuer interface.
func (qv queryValuer) Value() (string, error) {
	v := string(qv)
	if len(v) == 0 {
		return "", errors.New(ErrQueryValueNotFound, errorMessages)
	}
	return v, nil
}

// EOF
<|endoftext|>


<commit_before>package rest

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/couchbase/sync_gateway/base"
	"github.com/pkg/errors"
)

var (
	// ErrSGCollectInfoAlreadyRunning is returned if sgcollect_info is already running.
	ErrSGCollectInfoAlreadyRunning = errors.New("already running")
	// ErrSGCollectInfoNotRunning is returned if sgcollect_info is not running.
	ErrSGCollectInfoNotRunning = errors.New("not running")

	validateTicketPattern = regexp.MustCompile(`\d{1,7}`)

	sgcollectInstance = sgCollect{status: base.Uint32Ptr(sgStopped)}
)

const (
	sgStopped uint32 = iota
	sgRunning

	defaultSGUploadHost = "https://s3.amazonaws.com/cb-customers"
)

type sgCollect struct {
	cancel context.CancelFunc
	status *uint32
}

// Start will attempt to start sgcollect_info, if another is not already running.
func (sg *sgCollect) Start(zipFilename string, params sgCollectOptions) error {
	if atomic.LoadUint32(sg.status) == sgRunning {
		return ErrSGCollectInfoAlreadyRunning
	}

	sgPath, sgCollectPath, err := sgCollectPaths()
	if err != nil {
		return err
	}

	if params.OutputDirectory == "" {
		// If no output directory specified, default to the directory sgcollect_info is in.
		params.OutputDirectory = filepath.Dir(sgCollectPath)

		// Validate the path, just in case were not getting sgCollectPath correctly.
		if err := validateOutputDirectory(params.OutputDirectory); err != nil {
			return err
		}
	}

	zipPath := filepath.Join(params.OutputDirectory, zipFilename)

	args := params.Args()
	args = append(args, "--sync-gateway-executable", sgPath)
	args = append(args, zipPath)

	ctx, cancelFunc := context.WithCancel(context.Background())
	sg.cancel = cancelFunc
	cmd := exec.CommandContext(ctx, sgCollectPath, args...)

	// Send command stderr/stdout to pipes
	stderrPipeReader, stderrPipeWriter := io.Pipe()
	cmd.Stderr = stderrPipeWriter
	stdoutPipeReader, stdoutpipeWriter := io.Pipe()
	cmd.Stdout = stdoutpipeWriter

	if err := cmd.Start(); err != nil {
		return err
	}

	atomic.StoreUint32(sg.status, sgRunning)
	startTime := time.Now()
	base.Infof(base.KeyAdmin, "sgcollect_info started with args: %v", base.UD(args))

	// Stream sgcollect_info stderr to warn logs
	go func() {
		scanner := bufio.NewScanner(stderrPipeReader)
		for scanner.Scan() {
			base.Warnf(base.KeyAll, "sgcollect_info: %v", scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			base.Errorf(base.KeyAll, "sgcollect_info: unexpected error: %v", err)
		}
	}()

	// Stream sgcollect_info stdout to debug logs
	go func() {
		scanner := bufio.NewScanner(stdoutPipeReader)
		for scanner.Scan() {
			base.Debugf(base.KeyAdmin, "sgcollect_info: %v", scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			base.Errorf(base.KeyAll, "sgcollect_info: unexpected error: %v", err)
		}
	}()

	go func() {
		// Blocks until command finishes
		err := cmd.Wait()

		atomic.StoreUint32(sg.status, sgStopped)
		duration := time.Since(startTime)

		if err != nil {
			if err.Error() == "signal: killed" {
				base.Infof(base.KeyAdmin, "sgcollect_info cancelled after %v", duration)
				return
			}

			base.Errorf(base.KeyAll, "sgcollect_info failed after %v with reason: %v. Check warning level logs for more information.", duration, err)
			return
		}

		base.Infof(base.KeyAdmin, "sgcollect_info finished successfully after %v", duration)
	}()

	return nil
}

// Stop will stop sgcollect_info, if running.
func (sg *sgCollect) Stop() error {
	if atomic.LoadUint32(sg.status) == sgStopped {
		return ErrSGCollectInfoNotRunning
	}

	sg.cancel()
	atomic.StoreUint32(sg.status, sgStopped)

	return nil
}

// IsRunning returns true if sgcollect_info is running
func (sg *sgCollect) IsRunning() bool {
	return atomic.LoadUint32(sg.status) == sgRunning
}

type sgCollectOptions struct {
	RedactLevel     string `json:"redact_level,omitempty"`
	RedactSalt      string `json:"redact_salt,omitempty"`
	OutputDirectory string `json:"output_dir,omitempty"`
	Upload          bool   `json:"upload,omitempty"`
	UploadHost      string `json:"upload_host,omitempty"`
	Customer        string `json:"customer,omitempty"`
	Ticket          string `json:"ticket,omitempty"`
}

// validateOutputDirectory will check that the given path exists, and is a directory.
func validateOutputDirectory(dir string) error {
	// Clean the given path first, mainly for cross-platform compatability.
	dir = filepath.Clean(dir)

	// Validate given output directory exists, and is a directory.
	// This does not check for write permission, however sgcollect_info
	// will fail with an error giving that reason, if this is the case.
	if fileInfo, err := os.Stat(dir); err != nil {
		if os.IsNotExist(err) {
			return errors.Wrap(err, "no such file or directory")
		}
		return err
	} else if !fileInfo.IsDir() {
		return errors.New("not a directory")
	}

	return nil
}

// Validate ensures the options are OK to use in sgcollect_info.
func (c *sgCollectOptions) Validate() error {
	if c.OutputDirectory != "" {
		if err := validateOutputDirectory(c.OutputDirectory); err != nil {
			return err
		}
	}

	if c.Ticket != "" {
		if !validateTicketPattern.MatchString(c.Ticket) {
			return errors.New("ticket number must be 1 to 7 digits")
		}
	}

	if c.Upload {
		// Customer number is required if uploading.
		if c.Customer == "" {
			return errors.New("customer must be set if upload is true")
		}
		// Default uploading to support bucket if upload_host is not specified.
		if c.UploadHost == "" {
			c.UploadHost = defaultSGUploadHost
		}
	} else {
		// These fields suggest the user actually wanted to upload,
		// so we'll enforce "upload: true" if any of these are set.
		if c.UploadHost != "" {
			return errors.New("upload must be set to true if upload_host is specified")
		}
		if c.Customer != "" {
			return errors.New("upload must be set to true if customer is specified")
		}
		if c.Ticket != "" {
			return errors.New("upload must be set to true if ticket is specified")
		}
	}

	return nil
}

// Args returns a set of arguments to pass to sgcollect_info.
func (c *sgCollectOptions) Args() []string {
	var args = make([]string, 0)

	if c.Upload {
		args = append(args, "--upload-host", c.UploadHost)
	}

	if c.Customer != "" {
		args = append(args, "--customer", c.Customer)
	}

	if c.Ticket != "" {
		args = append(args, "--ticket", c.Ticket)
	}

	if c.RedactLevel != "" {
		args = append(args, "--log-redaction-level", c.RedactLevel)
	}

	if c.RedactSalt != "" {
		args = append(args, "--log-redaction-salt", c.RedactSalt)
	}

	return args
}

// sgCollectPaths attempts to return the absolute paths to Sync Gateway and to sgcollect_info binaries.
func sgCollectPaths() (sgBinary, sgCollectBinary string, err error) {
	sgBinary, err = os.Executable()
	if err != nil {
		return "", "", err
	}

	sgBinary, err = filepath.Abs(sgBinary)
	if err != nil {
		return "", "", err
	}

	hasBinDir := true
	sgCollectPath := filepath.Join("tools", "sgcollect_info")

	if runtime.GOOS == "windows" {
		sgCollectPath += ".exe"
		// Windows has no bin directory for the SG executable.
		hasBinDir = false
	}

	for {
		if hasBinDir {
			sgCollectBinary = filepath.Join(filepath.Dir(filepath.Dir(sgBinary)), sgCollectPath)
		} else {
			sgCollectBinary = filepath.Join(filepath.Dir(sgBinary), sgCollectPath)
		}

		// Check sgcollect_info exists at the path we guessed.
		base.Debugf(base.KeyAdmin, "Checking sgcollect_info binary exists at: %v", sgCollectBinary)
		_, err = os.Stat(sgCollectBinary)
		if err != nil {

			// First attempt may fail if there's no bin directory, so we'll try once more without.
			if hasBinDir {
				hasBinDir = false
				continue
			}

			return "", "", err
		}

		return sgBinary, sgCollectBinary, nil
	}
}

// sgcollectFilename returns a Windows-safe filename for sgcollect_info zip files.
func sgcollectFilename() string {

	// get timestamp
	timestamp := time.Now().UTC().Format("2006-01-02t150405")

	// use a shortened product name
	name := "sg"
	if base.ProductName == "Couchbase SG Accel" {
		name = "sga"
	}

	// get primary IP address
	ip, err := base.FindPrimaryAddr()
	if err != nil {
		ip = net.IPv4zero
	}

	// E.g: sgcollectinfo-2018-05-10t133456-sg@203.0.113.123.zip
	filename := fmt.Sprintf("sgcollectinfo-%s-%s@%s.zip", timestamp, name, ip)

	// Strip illegal Windows filename characters
	filename = base.ReplaceAll(filename, "\\/:*?\"<>|", "")

	return filename
}
<commit_msg>#3632 change default sgcollect output dir (#3636)<commit_after>package rest

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/couchbase/sync_gateway/base"
	"github.com/pkg/errors"
)

var (
	// ErrSGCollectInfoAlreadyRunning is returned if sgcollect_info is already running.
	ErrSGCollectInfoAlreadyRunning = errors.New("already running")
	// ErrSGCollectInfoNotRunning is returned if sgcollect_info is not running.
	ErrSGCollectInfoNotRunning = errors.New("not running")

	validateTicketPattern = regexp.MustCompile(`\d{1,7}`)

	sgcollectInstance = sgCollect{status: base.Uint32Ptr(sgStopped)}
)

const (
	sgStopped uint32 = iota
	sgRunning

	defaultSGUploadHost = "https://s3.amazonaws.com/cb-customers"
)

type sgCollect struct {
	cancel context.CancelFunc
	status *uint32
}

// Start will attempt to start sgcollect_info, if another is not already running.
func (sg *sgCollect) Start(zipFilename string, params sgCollectOptions) error {
	if atomic.LoadUint32(sg.status) == sgRunning {
		return ErrSGCollectInfoAlreadyRunning
	}

	sgPath, sgCollectPath, err := sgCollectPaths()
	if err != nil {
		return err
	}

	if params.OutputDirectory == "" {
		// If no output directory specified, default to the configured LogFilePath
		if config != nil && config.Logging != nil && config.Logging.LogFilePath != "" {
			params.OutputDirectory = config.Logging.LogFilePath
			base.Debugf(base.KeyAdmin, "sgcollect_info: no output directory specified, using LogFilePath: %v", params.OutputDirectory)
		} else {
			// If LogFilePath is not set, and DefaultLogFilePath is not set via a service script, error out.
			return errors.New("no output directory or LogFilePath specified")
		}

		// Validate the path, just in case were not getting it correctly.
		if err := validateOutputDirectory(params.OutputDirectory); err != nil {
			return err
		}
	}

	zipPath := filepath.Join(params.OutputDirectory, zipFilename)

	args := params.Args()
	args = append(args, "--sync-gateway-executable", sgPath)
	args = append(args, zipPath)

	ctx, cancelFunc := context.WithCancel(context.Background())
	sg.cancel = cancelFunc
	cmd := exec.CommandContext(ctx, sgCollectPath, args...)

	// Send command stderr/stdout to pipes
	stderrPipeReader, stderrPipeWriter := io.Pipe()
	cmd.Stderr = stderrPipeWriter
	stdoutPipeReader, stdoutpipeWriter := io.Pipe()
	cmd.Stdout = stdoutpipeWriter

	if err := cmd.Start(); err != nil {
		return err
	}

	atomic.StoreUint32(sg.status, sgRunning)
	startTime := time.Now()
	base.Infof(base.KeyAdmin, "sgcollect_info started with args: %v", base.UD(args))

	// Stream sgcollect_info stderr to warn logs
	go func() {
		scanner := bufio.NewScanner(stderrPipeReader)
		for scanner.Scan() {
			base.Warnf(base.KeyAll, "sgcollect_info: %v", scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			base.Errorf(base.KeyAll, "sgcollect_info: unexpected error: %v", err)
		}
	}()

	// Stream sgcollect_info stdout to debug logs
	go func() {
		scanner := bufio.NewScanner(stdoutPipeReader)
		for scanner.Scan() {
			base.Debugf(base.KeyAdmin, "sgcollect_info: %v", scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			base.Errorf(base.KeyAll, "sgcollect_info: unexpected error: %v", err)
		}
	}()

	go func() {
		// Blocks until command finishes
		err := cmd.Wait()

		atomic.StoreUint32(sg.status, sgStopped)
		duration := time.Since(startTime)

		if err != nil {
			if err.Error() == "signal: killed" {
				base.Infof(base.KeyAdmin, "sgcollect_info cancelled after %v", duration)
				return
			}

			base.Errorf(base.KeyAll, "sgcollect_info failed after %v with reason: %v. Check warning level logs for more information.", duration, err)
			return
		}

		base.Infof(base.KeyAdmin, "sgcollect_info finished successfully after %v", duration)
	}()

	return nil
}

// Stop will stop sgcollect_info, if running.
func (sg *sgCollect) Stop() error {
	if atomic.LoadUint32(sg.status) == sgStopped {
		return ErrSGCollectInfoNotRunning
	}

	sg.cancel()
	atomic.StoreUint32(sg.status, sgStopped)

	return nil
}

// IsRunning returns true if sgcollect_info is running
func (sg *sgCollect) IsRunning() bool {
	return atomic.LoadUint32(sg.status) == sgRunning
}

type sgCollectOptions struct {
	RedactLevel     string `json:"redact_level,omitempty"`
	RedactSalt      string `json:"redact_salt,omitempty"`
	OutputDirectory string `json:"output_dir,omitempty"`
	Upload          bool   `json:"upload,omitempty"`
	UploadHost      string `json:"upload_host,omitempty"`
	Customer        string `json:"customer,omitempty"`
	Ticket          string `json:"ticket,omitempty"`
}

// validateOutputDirectory will check that the given path exists, and is a directory.
func validateOutputDirectory(dir string) error {
	// Clean the given path first, mainly for cross-platform compatability.
	dir = filepath.Clean(dir)

	// Validate given output directory exists, and is a directory.
	// This does not check for write permission, however sgcollect_info
	// will fail with an error giving that reason, if this is the case.
	if fileInfo, err := os.Stat(dir); err != nil {
		if os.IsNotExist(err) {
			return errors.Wrap(err, "no such file or directory")
		}
		return err
	} else if !fileInfo.IsDir() {
		return errors.New("not a directory")
	}

	return nil
}

// Validate ensures the options are OK to use in sgcollect_info.
func (c *sgCollectOptions) Validate() error {
	if c.OutputDirectory != "" {
		if err := validateOutputDirectory(c.OutputDirectory); err != nil {
			return err
		}
	}

	if c.Ticket != "" {
		if !validateTicketPattern.MatchString(c.Ticket) {
			return errors.New("ticket number must be 1 to 7 digits")
		}
	}

	if c.Upload {
		// Customer number is required if uploading.
		if c.Customer == "" {
			return errors.New("customer must be set if upload is true")
		}
		// Default uploading to support bucket if upload_host is not specified.
		if c.UploadHost == "" {
			c.UploadHost = defaultSGUploadHost
		}
	} else {
		// These fields suggest the user actually wanted to upload,
		// so we'll enforce "upload: true" if any of these are set.
		if c.UploadHost != "" {
			return errors.New("upload must be set to true if upload_host is specified")
		}
		if c.Customer != "" {
			return errors.New("upload must be set to true if customer is specified")
		}
		if c.Ticket != "" {
			return errors.New("upload must be set to true if ticket is specified")
		}
	}

	return nil
}

// Args returns a set of arguments to pass to sgcollect_info.
func (c *sgCollectOptions) Args() []string {
	var args = make([]string, 0)

	if c.Upload {
		args = append(args, "--upload-host", c.UploadHost)
	}

	if c.Customer != "" {
		args = append(args, "--customer", c.Customer)
	}

	if c.Ticket != "" {
		args = append(args, "--ticket", c.Ticket)
	}

	if c.RedactLevel != "" {
		args = append(args, "--log-redaction-level", c.RedactLevel)
	}

	if c.RedactSalt != "" {
		args = append(args, "--log-redaction-salt", c.RedactSalt)
	}

	return args
}

// sgCollectPaths attempts to return the absolute paths to Sync Gateway and to sgcollect_info binaries.
func sgCollectPaths() (sgBinary, sgCollectBinary string, err error) {
	sgBinary, err = os.Executable()
	if err != nil {
		return "", "", err
	}

	sgBinary, err = filepath.Abs(sgBinary)
	if err != nil {
		return "", "", err
	}

	hasBinDir := true
	sgCollectPath := filepath.Join("tools", "sgcollect_info")

	if runtime.GOOS == "windows" {
		sgCollectPath += ".exe"
		// Windows has no bin directory for the SG executable.
		hasBinDir = false
	}

	for {
		if hasBinDir {
			sgCollectBinary = filepath.Join(filepath.Dir(filepath.Dir(sgBinary)), sgCollectPath)
		} else {
			sgCollectBinary = filepath.Join(filepath.Dir(sgBinary), sgCollectPath)
		}

		// Check sgcollect_info exists at the path we guessed.
		base.Debugf(base.KeyAdmin, "Checking sgcollect_info binary exists at: %v", sgCollectBinary)
		_, err = os.Stat(sgCollectBinary)
		if err != nil {

			// First attempt may fail if there's no bin directory, so we'll try once more without.
			if hasBinDir {
				hasBinDir = false
				continue
			}

			return "", "", err
		}

		return sgBinary, sgCollectBinary, nil
	}
}

// sgcollectFilename returns a Windows-safe filename for sgcollect_info zip files.
func sgcollectFilename() string {

	// get timestamp
	timestamp := time.Now().UTC().Format("2006-01-02t150405")

	// use a shortened product name
	name := "sg"
	if base.ProductName == "Couchbase SG Accel" {
		name = "sga"
	}

	// get primary IP address
	ip, err := base.FindPrimaryAddr()
	if err != nil {
		ip = net.IPv4zero
	}

	// E.g: sgcollectinfo-2018-05-10t133456-sg@203.0.113.123.zip
	filename := fmt.Sprintf("sgcollectinfo-%s-%s@%s.zip", timestamp, name, ip)

	// Strip illegal Windows filename characters
	filename = base.ReplaceAll(filename, "\\/:*?\"<>|", "")

	return filename
}
<|endoftext|>


<commit_before>// Package frame contains functions for parsing FLAC encoded audio data.
package frame

import (
	"encoding/binary"
	"fmt"
	"io"

	"github.com/eaburns/bit"
	"github.com/mewkiz/pkg/hashutil/crc16"
)

// A Frame is an audio frame, consisting of a frame header and one subframe per
// channel.
type Frame struct {
	// Audio frame header.
	Header *Header
	// Audio subframes, one per channel.
	SubFrames []*SubFrame
}

// NewFrame parses and returns a new frame, which consists of a frame header and
// one subframe per channel.
//
// Frame format (pseudo code):
//
//    type FRAME struct {
//       header    FRAME_HEADER
//       subframes []SUBFRAME
//       _         uint0 to uint7 // zero-padding to byte alignment.
//       footer    uint16 // CRC-16 of the entire frame, excluding the footer.
//    }
//
// ref: http://flac.sourceforge.net/format.html#frame
func NewFrame(r io.Reader) (frame *Frame, err error) {
	// Create a new hash reader which adds the data from all read operations to a
	// running hash.
	h := crc16.NewIBM()
	hr := io.TeeReader(r, h)

	// Frame header.
	frame = new(Frame)
	frame.Header, err = NewHeader(hr)
	if err != nil {
		return nil, err
	}

	// Subframes.
	br := bit.NewReader(hr)
	hdr := frame.Header
	for i := 0; i < hdr.ChannelOrder.ChannelCount(); i++ {
		subframe, err := hdr.NewSubFrame(br)
		if err != nil {
			return nil, err
		}
		frame.SubFrames = append(frame.SubFrames, subframe)
	}

	// Padding.
	// ignore bits up to byte boundery.
	br = bit.NewReader(hr)
	/// ### [ TODO ] ###
	///    - verify paddings
	/// ### [/ TODO ] ###
	///bitOff, err := br.Seek(0, bit.SeekCur)
	///if err != nil {
	///	return nil, err
	///}
	///padBitCount := bitOff % 8
	///if padBitCount != 0 {
	///	pad, err := br.Read(int(padBitCount))
	///	if err != nil {
	///		return nil, err
	///	}
	///	if pad.Uint64() != 0 {
	///		return nil, errors.New("frame.NewFrame: invalid padding; must be 0")
	///	}
	///}

	// Frame footer.

	// Verify the CRC-16.
	got := h.Sum16()

	var want uint16
	err = binary.Read(r, binary.BigEndian, &want)
	if err != nil {
		return nil, err
	}
	if got != want {
		return nil, fmt.Errorf("frame.NewFrame: checksum mismatch; expected 0x%04X, got 0x%04X", want, got)
	}

	return frame, nil
}
<commit_msg>frame: Remove old code which was out commented.<commit_after>// Package frame contains functions for parsing FLAC encoded audio data.
package frame

import (
	"encoding/binary"
	"fmt"
	"io"

	"github.com/eaburns/bit"
	"github.com/mewkiz/pkg/hashutil/crc16"
)

// A Frame is an audio frame, consisting of a frame header and one subframe per
// channel.
type Frame struct {
	// Audio frame header.
	Header *Header
	// Audio subframes, one per channel.
	SubFrames []*SubFrame
}

// NewFrame parses and returns a new frame, which consists of a frame header and
// one subframe per channel.
//
// Frame format (pseudo code):
//
//    type FRAME struct {
//       header    FRAME_HEADER
//       subframes []SUBFRAME
//       _         uint0 to uint7 // zero-padding to byte alignment.
//       footer    uint16 // CRC-16 of the entire frame, excluding the footer.
//    }
//
// ref: http://flac.sourceforge.net/format.html#frame
func NewFrame(r io.Reader) (frame *Frame, err error) {
	// Create a new hash reader which adds the data from all read operations to a
	// running hash.
	h := crc16.NewIBM()
	hr := io.TeeReader(r, h)

	// Frame header.
	frame = new(Frame)
	frame.Header, err = NewHeader(hr)
	if err != nil {
		return nil, err
	}

	// Subframes.
	br := bit.NewReader(hr)
	hdr := frame.Header
	for i := 0; i < hdr.ChannelOrder.ChannelCount(); i++ {
		subframe, err := hdr.NewSubFrame(br)
		if err != nil {
			return nil, err
		}
		frame.SubFrames = append(frame.SubFrames, subframe)
	}

	// Padding.
	/// ### [ TODO ] ###
	///    - verify paddings
	/// ### [/ TODO ] ###
	// ignore bits up to byte boundery.
	br = bit.NewReader(hr)

	// Frame footer.

	// Verify the CRC-16.
	got := h.Sum16()

	var want uint16
	err = binary.Read(r, binary.BigEndian, &want)
	if err != nil {
		return nil, err
	}
	if got != want {
		return nil, fmt.Errorf("frame.NewFrame: checksum mismatch; expected 0x%04X, got 0x%04X", want, got)
	}

	return frame, nil
}
<|endoftext|>


<commit_before>package french

import(
	"../"
)

type Suit int
const(
	Naked Suit = 0
	Spades     = 0x2660
	Hearts     = 0x2661
	Diamonds   = 0x2662
	Clubs      = 0x2663
)

type Card struct {
	Id    int
	Suit  Suit
	Value int
	Label string
}

const Back int = 0x1F0A0

var AceOfSpades     = Card{ 0x1F0A1, Spades,    1, "Ace of Spades" }
var	DeuceOfSpades   = Card{ 0x1F0A2, Spades,    2, "Two of Spades" }
var	ThreeOfSpades   = Card{ 0x1F0A3, Spades,    3, "Three of Spades" }
var	FourOfSpades    = Card{ 0x1F0A4, Spades,    4, "Four of Spades" }
var	FiveOfSpades    = Card{ 0x1F0A5, Spades,    5, "Five of Spades" }
var	SixOfSpades     = Card{ 0x1F0A6, Spades,    6, "Six of Spades" }
var	SevenOfSpades   = Card{ 0x1F0A7, Spades,    7, "Seven of Spades" }
var	EightOfSpades   = Card{ 0x1F0A8, Spades,    8, "Eight of Spades" }
var	NineOfSpades    = Card{ 0x1F0A9, Spades,    9, "Nine of Spades" }
var	TenOfSpades     = Card{ 0x1F0AA, Spades,   10, "Ten of Spades" }
var	JackOfSpades    = Card{ 0x1F0AB, Spades,   11, "Jack of Spades" }
var	QueenOfSpades   = Card{ 0x1F0AD, Spades,   12, "Queen of Spades" }
var	KingOfSpades    = Card{ 0x1F0AE, Spades,   13, "King of Spades" }

var AceOfHearts     = Card{ 0x1F0B1, Hearts,    1, "Ace of Hearts" }
var	DeuceOfHearts   = Card{ 0x1F0B2, Hearts,    2, "Two of Hearts" }
var	ThreeOfHearts   = Card{ 0x1F0B3, Hearts,    3, "Three of Hearts" }
var	FourOfHearts    = Card{ 0x1F0B4, Hearts,    4, "Four of Hearts" }
var	FiveOfHearts    = Card{ 0x1F0B5, Hearts,    5, "Five of Hearts" }
var	SixOfHearts     = Card{ 0x1F0B6, Hearts,    6, "Six of Hearts" }
var	SevenOfHearts   = Card{ 0x1F0B7, Hearts,    7, "Seven of Hearts" }
var	EightOfHearts   = Card{ 0x1F0B8, Hearts,    8, "Eight of Hearts" }
var	NineOfHearts    = Card{ 0x1F0B9, Hearts,    9, "Nine of Hearts" }
var	TenOfHearts     = Card{ 0x1F0BA, Hearts,   10, "Ten of Hearts" }
var	JackOfHearts    = Card{ 0x1F0BB, Hearts,   11, "Jack of Hearts" }
var	QueenOfHearts   = Card{ 0x1F0BD, Hearts,   12, "Queen of Hearts" }
var	KingOfHearts    = Card{ 0x1F0BE, Hearts,   13, "King of Hearts" }

var AceOfDiamonds   = Card{ 0x1F0C1, Diamonds,  1, "Ace of Diamonds" }
var	DeuceOfDiamonds = Card{ 0x1F0C2, Diamonds,  2, "Two of Diamonds" }
var	ThreeOfDiamonds = Card{ 0x1F0C3, Diamonds,  3, "Three of Diamonds" }
var	FourOfDiamonds  = Card{ 0x1F0C4, Diamonds,  4, "Four of Diamonds" }
var	FiveOfDiamonds  = Card{ 0x1F0C5, Diamonds,  5, "Five of Diamonds" }
var	SixOfDiamonds   = Card{ 0x1F0C6, Diamonds,  6, "Six of Diamonds" }
var	SevenOfDiamonds = Card{ 0x1F0C7, Diamonds,  7, "Seven of Diamonds" }
var	EightOfDiamonds = Card{ 0x1F0C8, Diamonds,  8, "Eight of Diamonds" }
var	NineOfDiamonds  = Card{ 0x1F0C9, Diamonds,  9, "Nine of Diamonds" }
var	TenOfDiamonds   = Card{ 0x1F0CA, Diamonds, 10, "Ten of Diamonds" }
var	JackOfDiamonds  = Card{ 0x1F0CB, Diamonds, 11, "Jack of Diamonds" }
var	QueenOfDiamonds = Card{ 0x1F0CD, Diamonds, 12, "Queen of Diamonds" }
var	KingOfDiamonds  = Card{ 0x1F0CE, Diamonds, 13, "King of Diamonds" }

var AceOfClubs      = Card{ 0x1F0D1, Clubs,     1, "Ace of Clubs" }
var	DeuceOfClubs    = Card{ 0x1F0D2, Clubs,     2, "Two of Clubs" }
var	ThreeOfClubs    = Card{ 0x1F0D3, Clubs,     3, "Three of Clubs" }
var	FourOfClubs     = Card{ 0x1F0D4, Clubs,     4, "Four of Clubs" }
var	FiveOfClubs     = Card{ 0x1F0D5, Clubs,     5, "Five of Clubs" }
var	SixOfClubs      = Card{ 0x1F0D6, Clubs,     6, "Six of Clubs" }
var	SevenOfClubs    = Card{ 0x1F0D7, Clubs,     7, "Seven of Clubs" }
var	EightOfClubs    = Card{ 0x1F0D8, Clubs,     8, "Eight of Clubs" }
var	NineOfClubs     = Card{ 0x1F0D9, Clubs,     9, "Nine of Clubs" }
var	TenOfClubs      = Card{ 0x1F0DA, Clubs,    10, "Ten of Clubs" }
var	JackOfClubs     = Card{ 0x1F0DB, Clubs,    11, "Jack of Clubs" }
var	QueenOfClubs    = Card{ 0x1F0DD, Clubs,    12, "Queen of Clubs" }
var	KingOfClubs     = Card{ 0x1F0DE, Clubs,    13, "King of Clubs" }

var BlackJoker      = Card{ 0x1F0CF, Naked,    15, "Black Joker" }
var WhiteJoker      = Card{ 0x1F0DF, Naked,    15, "White Joker" }

func Spades() []interface{} {
	return []interface{}{
		AceOfSpades, DeuceOfSpades, ThreeOfSpades, FourOfSpades, FiveOfSpades,
		SixOfSpades, SevenOfSpades, EightOfSpades, NineOfSpades, TenOfSpades,
		JackOfSpades, QueenOfSpades, KingOfSpades,
	}
}

func Hearts() []interface{} {
	return []interface{}{
		AceOfHearts, DeuceOfHearts, ThreeOfHearts, FourOfHearts, FiveOfHearts,
		SixOfHearts, SevenOfHearts, EightOfHearts, NineOfHearts, TenOfHearts,
		JackOfHearts, QueenOfHearts, KingOfHearts,
	}
}

func Diamonds() []interface{} {
	return []interface{}{
		AceOfDiamonds, DeuceOfDiamonds, ThreeOfDiamonds, FourOfDiamonds, FiveOfDiamonds,
		SixOfDiamonds, SevenOfDiamonds, EightOfDiamonds, NineOfDiamonds, TenOfDiamonds,
		JackOfDiamonds, QueenOfDiamonds, KingOfDiamonds,
	}
}

func Clubs() []interface{} {
	return []interface{}{
		AceOfClubs, DeuceOfClubs, ThreeOfClubs, FourOfClubs, FiveOfClubs,
		SixOfClubs, SevenOfClubs, EightOfClubs, NineOfClubs, TenOfClubs,
		JackOfClubs, QueenOfClubs, KingOfClubs,
	}
}

func Decks(count int, jokers int) []interface{} {
	out := make([]interface{}, 0)
	for i := 0; i < count; i++ {
		out = append(out, Spades()[:], Hearts()[:], Diamonds()[:], Clubs()[:])
	}
	for i := 0; i < jokers; i++ {
		if i % 2 == 0 {
			out = append(out, &BlackJoker)
		} else {
			out = append(out, &WhiteJoker)
		}
	}
	return out
}
<commit_msg>Removed invalid import<commit_after>package french

import(
	//"../"
)

type Suit int
const(
	Naked Suit = 0
	Spades     = 0x2660
	Hearts     = 0x2661
	Diamonds   = 0x2662
	Clubs      = 0x2663
)

type Card struct {
	Id    int
	Suit  Suit
	Value int
	Label string
}

const Back int = 0x1F0A0

var AceOfSpades     = Card{ 0x1F0A1, Spades,    1, "Ace of Spades" }
var	DeuceOfSpades   = Card{ 0x1F0A2, Spades,    2, "Two of Spades" }
var	ThreeOfSpades   = Card{ 0x1F0A3, Spades,    3, "Three of Spades" }
var	FourOfSpades    = Card{ 0x1F0A4, Spades,    4, "Four of Spades" }
var	FiveOfSpades    = Card{ 0x1F0A5, Spades,    5, "Five of Spades" }
var	SixOfSpades     = Card{ 0x1F0A6, Spades,    6, "Six of Spades" }
var	SevenOfSpades   = Card{ 0x1F0A7, Spades,    7, "Seven of Spades" }
var	EightOfSpades   = Card{ 0x1F0A8, Spades,    8, "Eight of Spades" }
var	NineOfSpades    = Card{ 0x1F0A9, Spades,    9, "Nine of Spades" }
var	TenOfSpades     = Card{ 0x1F0AA, Spades,   10, "Ten of Spades" }
var	JackOfSpades    = Card{ 0x1F0AB, Spades,   11, "Jack of Spades" }
var	QueenOfSpades   = Card{ 0x1F0AD, Spades,   12, "Queen of Spades" }
var	KingOfSpades    = Card{ 0x1F0AE, Spades,   13, "King of Spades" }

var AceOfHearts     = Card{ 0x1F0B1, Hearts,    1, "Ace of Hearts" }
var	DeuceOfHearts   = Card{ 0x1F0B2, Hearts,    2, "Two of Hearts" }
var	ThreeOfHearts   = Card{ 0x1F0B3, Hearts,    3, "Three of Hearts" }
var	FourOfHearts    = Card{ 0x1F0B4, Hearts,    4, "Four of Hearts" }
var	FiveOfHearts    = Card{ 0x1F0B5, Hearts,    5, "Five of Hearts" }
var	SixOfHearts     = Card{ 0x1F0B6, Hearts,    6, "Six of Hearts" }
var	SevenOfHearts   = Card{ 0x1F0B7, Hearts,    7, "Seven of Hearts" }
var	EightOfHearts   = Card{ 0x1F0B8, Hearts,    8, "Eight of Hearts" }
var	NineOfHearts    = Card{ 0x1F0B9, Hearts,    9, "Nine of Hearts" }
var	TenOfHearts     = Card{ 0x1F0BA, Hearts,   10, "Ten of Hearts" }
var	JackOfHearts    = Card{ 0x1F0BB, Hearts,   11, "Jack of Hearts" }
var	QueenOfHearts   = Card{ 0x1F0BD, Hearts,   12, "Queen of Hearts" }
var	KingOfHearts    = Card{ 0x1F0BE, Hearts,   13, "King of Hearts" }

var AceOfDiamonds   = Card{ 0x1F0C1, Diamonds,  1, "Ace of Diamonds" }
var	DeuceOfDiamonds = Card{ 0x1F0C2, Diamonds,  2, "Two of Diamonds" }
var	ThreeOfDiamonds = Card{ 0x1F0C3, Diamonds,  3, "Three of Diamonds" }
var	FourOfDiamonds  = Card{ 0x1F0C4, Diamonds,  4, "Four of Diamonds" }
var	FiveOfDiamonds  = Card{ 0x1F0C5, Diamonds,  5, "Five of Diamonds" }
var	SixOfDiamonds   = Card{ 0x1F0C6, Diamonds,  6, "Six of Diamonds" }
var	SevenOfDiamonds = Card{ 0x1F0C7, Diamonds,  7, "Seven of Diamonds" }
var	EightOfDiamonds = Card{ 0x1F0C8, Diamonds,  8, "Eight of Diamonds" }
var	NineOfDiamonds  = Card{ 0x1F0C9, Diamonds,  9, "Nine of Diamonds" }
var	TenOfDiamonds   = Card{ 0x1F0CA, Diamonds, 10, "Ten of Diamonds" }
var	JackOfDiamonds  = Card{ 0x1F0CB, Diamonds, 11, "Jack of Diamonds" }
var	QueenOfDiamonds = Card{ 0x1F0CD, Diamonds, 12, "Queen of Diamonds" }
var	KingOfDiamonds  = Card{ 0x1F0CE, Diamonds, 13, "King of Diamonds" }

var AceOfClubs      = Card{ 0x1F0D1, Clubs,     1, "Ace of Clubs" }
var	DeuceOfClubs    = Card{ 0x1F0D2, Clubs,     2, "Two of Clubs" }
var	ThreeOfClubs    = Card{ 0x1F0D3, Clubs,     3, "Three of Clubs" }
var	FourOfClubs     = Card{ 0x1F0D4, Clubs,     4, "Four of Clubs" }
var	FiveOfClubs     = Card{ 0x1F0D5, Clubs,     5, "Five of Clubs" }
var	SixOfClubs      = Card{ 0x1F0D6, Clubs,     6, "Six of Clubs" }
var	SevenOfClubs    = Card{ 0x1F0D7, Clubs,     7, "Seven of Clubs" }
var	EightOfClubs    = Card{ 0x1F0D8, Clubs,     8, "Eight of Clubs" }
var	NineOfClubs     = Card{ 0x1F0D9, Clubs,     9, "Nine of Clubs" }
var	TenOfClubs      = Card{ 0x1F0DA, Clubs,    10, "Ten of Clubs" }
var	JackOfClubs     = Card{ 0x1F0DB, Clubs,    11, "Jack of Clubs" }
var	QueenOfClubs    = Card{ 0x1F0DD, Clubs,    12, "Queen of Clubs" }
var	KingOfClubs     = Card{ 0x1F0DE, Clubs,    13, "King of Clubs" }

var BlackJoker      = Card{ 0x1F0CF, Naked,    15, "Black Joker" }
var WhiteJoker      = Card{ 0x1F0DF, Naked,    15, "White Joker" }

func Spades() []interface{} {
	return []interface{}{
		AceOfSpades, DeuceOfSpades, ThreeOfSpades, FourOfSpades, FiveOfSpades,
		SixOfSpades, SevenOfSpades, EightOfSpades, NineOfSpades, TenOfSpades,
		JackOfSpades, QueenOfSpades, KingOfSpades,
	}
}

func Hearts() []interface{} {
	return []interface{}{
		AceOfHearts, DeuceOfHearts, ThreeOfHearts, FourOfHearts, FiveOfHearts,
		SixOfHearts, SevenOfHearts, EightOfHearts, NineOfHearts, TenOfHearts,
		JackOfHearts, QueenOfHearts, KingOfHearts,
	}
}

func Diamonds() []interface{} {
	return []interface{}{
		AceOfDiamonds, DeuceOfDiamonds, ThreeOfDiamonds, FourOfDiamonds, FiveOfDiamonds,
		SixOfDiamonds, SevenOfDiamonds, EightOfDiamonds, NineOfDiamonds, TenOfDiamonds,
		JackOfDiamonds, QueenOfDiamonds, KingOfDiamonds,
	}
}

func Clubs() []interface{} {
	return []interface{}{
		AceOfClubs, DeuceOfClubs, ThreeOfClubs, FourOfClubs, FiveOfClubs,
		SixOfClubs, SevenOfClubs, EightOfClubs, NineOfClubs, TenOfClubs,
		JackOfClubs, QueenOfClubs, KingOfClubs,
	}
}

func Decks(count int, jokers int) []interface{} {
	out := make([]interface{}, 0)
	for i := 0; i < count; i++ {
		out = append(out, Spades()[:], Hearts()[:], Diamonds()[:], Clubs()[:])
	}
	for i := 0; i < jokers; i++ {
		if i % 2 == 0 {
			out = append(out, &BlackJoker)
		} else {
			out = append(out, &WhiteJoker)
		}
	}
	return out
}
<|endoftext|>


<commit_before>package integration

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/opencontainers/runc/libcontainer"
)

func showFile(t *testing.T, fname string) error {
	t.Logf("=== %s ===\n", fname)

	f, err := os.Open(fname)
	if err != nil {
		t.Log(err)
		return err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		t.Log(scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return err
	}

	t.Logf("=== END ===\n")

	return nil
}

func TestUsernsCheckpoint(t *testing.T) {
	if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
		t.Skip("userns is unsupported")
	}
	cmd := exec.Command("criu", "check", "--feature", "userns")
	if err := cmd.Run(); err != nil {
		t.Skip("Unable to c/r a container with userns")
	}
	testCheckpoint(t, true)
}

func TestCheckpoint(t *testing.T) {
	testCheckpoint(t, false)
}

func testCheckpoint(t *testing.T, userns bool) {
	if testing.Short() {
		return
	}

	if _, err := exec.LookPath("criu"); err != nil {
		t.Skipf("criu binary not found: %v", err)
	}

	root, err := newTestRoot()
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(root)

	rootfs, err := newRootfs()
	if err != nil {
		t.Fatal(err)
	}
	defer remove(rootfs)

	config := newTemplateConfig(&tParam{
		rootfs: rootfs,
		userns: userns,
	})
	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)

	if err != nil {
		t.Fatal(err)
	}

	container, err := factory.Create("test", config)
	if err != nil {
		t.Fatal(err)
	}
	defer container.Destroy()

	stdinR, stdinW, err := os.Pipe()
	if err != nil {
		t.Fatal(err)
	}

	var stdout bytes.Buffer

	pconfig := libcontainer.Process{
		Cwd:    "/",
		Args:   []string{"cat"},
		Env:    standardEnvironment,
		Stdin:  stdinR,
		Stdout: &stdout,
		Init:   true,
	}

	err = container.Run(&pconfig)
	stdinR.Close()
	defer stdinW.Close()
	if err != nil {
		t.Fatal(err)
	}

	pid, err := pconfig.Pid()
	if err != nil {
		t.Fatal(err)
	}

	process, err := os.FindProcess(pid)
	if err != nil {
		t.Fatal(err)
	}

	parentDir, err := ioutil.TempDir("", "criu-parent")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(parentDir)

	preDumpOpts := &libcontainer.CriuOpts{
		ImagesDirectory: parentDir,
		WorkDirectory:   parentDir,
		PreDump:         true,
	}
	preDumpLog := filepath.Join(preDumpOpts.WorkDirectory, "dump.log")

	if err := container.Checkpoint(preDumpOpts); err != nil {
		showFile(t, preDumpLog)
		t.Fatal(err)
	}

	state, err := container.Status()
	if err != nil {
		t.Fatal(err)
	}

	if state != libcontainer.Running {
		t.Fatal("Unexpected preDump state: ", state)
	}

	imagesDir, err := ioutil.TempDir("", "criu")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(imagesDir)

	checkpointOpts := &libcontainer.CriuOpts{
		ImagesDirectory: imagesDir,
		WorkDirectory:   imagesDir,
		ParentImage:     "../criu-parent",
	}
	dumpLog := filepath.Join(checkpointOpts.WorkDirectory, "dump.log")
	restoreLog := filepath.Join(checkpointOpts.WorkDirectory, "restore.log")

	if err := container.Checkpoint(checkpointOpts); err != nil {
		showFile(t, dumpLog)
		t.Fatal(err)
	}

	state, err = container.Status()
	if err != nil {
		t.Fatal(err)
	}

	if state != libcontainer.Stopped {
		t.Fatal("Unexpected state checkpoint: ", state)
	}

	stdinW.Close()
	_, err = process.Wait()
	if err != nil {
		t.Fatal(err)
	}

	// reload the container
	container, err = factory.Load("test")
	if err != nil {
		t.Fatal(err)
	}

	restoreStdinR, restoreStdinW, err := os.Pipe()
	if err != nil {
		t.Fatal(err)
	}

	restoreProcessConfig := &libcontainer.Process{
		Cwd:    "/",
		Stdin:  restoreStdinR,
		Stdout: &stdout,
		Init:   true,
	}

	err = container.Restore(restoreProcessConfig, checkpointOpts)
	restoreStdinR.Close()
	defer restoreStdinW.Close()
	if err != nil {
		showFile(t, restoreLog)
		t.Fatal(err)
	}

	state, err = container.Status()
	if err != nil {
		t.Fatal(err)
	}
	if state != libcontainer.Running {
		t.Fatal("Unexpected restore state: ", state)
	}

	pid, err = restoreProcessConfig.Pid()
	if err != nil {
		t.Fatal(err)
	}

	_, err = os.FindProcess(pid)
	if err != nil {
		t.Fatal(err)
	}

	_, err = restoreStdinW.WriteString("Hello!")
	if err != nil {
		t.Fatal(err)
	}

	restoreStdinW.Close()
	s, err := restoreProcessConfig.Wait()
	if err != nil {
		t.Fatal(err)
	}

	if !s.Success() {
		t.Fatal(s.String(), pid)
	}

	output := stdout.String()
	if !strings.Contains(output, "Hello!") {
		t.Fatal("Did not restore the pipe correctly:", output)
	}
}
<commit_msg>libct/int: fix a data race<commit_after>package integration

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/opencontainers/runc/libcontainer"
)

func showFile(t *testing.T, fname string) error {
	t.Logf("=== %s ===\n", fname)

	f, err := os.Open(fname)
	if err != nil {
		t.Log(err)
		return err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		t.Log(scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return err
	}

	t.Logf("=== END ===\n")

	return nil
}

func TestUsernsCheckpoint(t *testing.T) {
	if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
		t.Skip("userns is unsupported")
	}
	cmd := exec.Command("criu", "check", "--feature", "userns")
	if err := cmd.Run(); err != nil {
		t.Skip("Unable to c/r a container with userns")
	}
	testCheckpoint(t, true)
}

func TestCheckpoint(t *testing.T) {
	testCheckpoint(t, false)
}

func testCheckpoint(t *testing.T, userns bool) {
	if testing.Short() {
		return
	}

	if _, err := exec.LookPath("criu"); err != nil {
		t.Skipf("criu binary not found: %v", err)
	}

	root, err := newTestRoot()
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(root)

	rootfs, err := newRootfs()
	if err != nil {
		t.Fatal(err)
	}
	defer remove(rootfs)

	config := newTemplateConfig(&tParam{
		rootfs: rootfs,
		userns: userns,
	})
	factory, err := libcontainer.New(root, libcontainer.Cgroupfs)

	if err != nil {
		t.Fatal(err)
	}

	container, err := factory.Create("test", config)
	if err != nil {
		t.Fatal(err)
	}
	defer container.Destroy()

	stdinR, stdinW, err := os.Pipe()
	if err != nil {
		t.Fatal(err)
	}

	var stdout bytes.Buffer

	pconfig := libcontainer.Process{
		Cwd:    "/",
		Args:   []string{"cat"},
		Env:    standardEnvironment,
		Stdin:  stdinR,
		Stdout: &stdout,
		Init:   true,
	}

	err = container.Run(&pconfig)
	stdinR.Close()
	defer stdinW.Close()
	if err != nil {
		t.Fatal(err)
	}

	pid, err := pconfig.Pid()
	if err != nil {
		t.Fatal(err)
	}

	process, err := os.FindProcess(pid)
	if err != nil {
		t.Fatal(err)
	}

	parentDir, err := ioutil.TempDir("", "criu-parent")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(parentDir)

	preDumpOpts := &libcontainer.CriuOpts{
		ImagesDirectory: parentDir,
		WorkDirectory:   parentDir,
		PreDump:         true,
	}
	preDumpLog := filepath.Join(preDumpOpts.WorkDirectory, "dump.log")

	if err := container.Checkpoint(preDumpOpts); err != nil {
		showFile(t, preDumpLog)
		t.Fatal(err)
	}

	state, err := container.Status()
	if err != nil {
		t.Fatal(err)
	}

	if state != libcontainer.Running {
		t.Fatal("Unexpected preDump state: ", state)
	}

	imagesDir, err := ioutil.TempDir("", "criu")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(imagesDir)

	checkpointOpts := &libcontainer.CriuOpts{
		ImagesDirectory: imagesDir,
		WorkDirectory:   imagesDir,
		ParentImage:     "../criu-parent",
	}
	dumpLog := filepath.Join(checkpointOpts.WorkDirectory, "dump.log")
	restoreLog := filepath.Join(checkpointOpts.WorkDirectory, "restore.log")

	if err := container.Checkpoint(checkpointOpts); err != nil {
		showFile(t, dumpLog)
		t.Fatal(err)
	}

	state, err = container.Status()
	if err != nil {
		t.Fatal(err)
	}

	if state != libcontainer.Stopped {
		t.Fatal("Unexpected state checkpoint: ", state)
	}

	stdinW.Close()
	_, err = process.Wait()
	if err != nil {
		t.Fatal(err)
	}

	// reload the container
	container, err = factory.Load("test")
	if err != nil {
		t.Fatal(err)
	}

	restoreStdinR, restoreStdinW, err := os.Pipe()
	if err != nil {
		t.Fatal(err)
	}

	var restoreStdout bytes.Buffer
	restoreProcessConfig := &libcontainer.Process{
		Cwd:    "/",
		Stdin:  restoreStdinR,
		Stdout: &restoreStdout,
		Init:   true,
	}

	err = container.Restore(restoreProcessConfig, checkpointOpts)
	restoreStdinR.Close()
	defer restoreStdinW.Close()
	if err != nil {
		showFile(t, restoreLog)
		t.Fatal(err)
	}

	state, err = container.Status()
	if err != nil {
		t.Fatal(err)
	}
	if state != libcontainer.Running {
		t.Fatal("Unexpected restore state: ", state)
	}

	pid, err = restoreProcessConfig.Pid()
	if err != nil {
		t.Fatal(err)
	}

	_, err = os.FindProcess(pid)
	if err != nil {
		t.Fatal(err)
	}

	_, err = restoreStdinW.WriteString("Hello!")
	if err != nil {
		t.Fatal(err)
	}

	restoreStdinW.Close()
	s, err := restoreProcessConfig.Wait()
	if err != nil {
		t.Fatal(err)
	}

	if !s.Success() {
		t.Fatal(s.String(), pid)
	}

	output := restoreStdout.String()
	if !strings.Contains(output, "Hello!") {
		t.Fatal("Did not restore the pipe correctly:", output)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/examples/model"
	"github.com/ligato/cn-infra/messaging"
	"github.com/ligato/cn-infra/utils/safeclose"
)

//********************************************************************
// This example shows how to use the Agent's Kafka APIs to perform
// synchronous/asynchronous calls and how to watch on these events.
//********************************************************************

func main() {
	// Init close channel used to stop the example
	exampleFinished := make(chan struct{}, 1)

	// Start Agent with ExampleFlavor
	// (combination of ExamplePlugin & reused cn-infra plugins).
	flavor := ExampleFlavor{closeChan: &exampleFinished}
	plugins := flavor.Plugins()
	agent := core.NewAgent(flavor.LogRegistry().NewLogger("core"), 15*time.Second, plugins...)
	core.EventLoopWithInterrupt(agent, exampleFinished)
}

// ExamplePlugin demonstrates the use of Kafka plugin API from another plugin.
// The Kafka ConsumerHandle is required to read messages from a topic
// and PluginConnection is needed to start consuming on that topic.
type ExamplePlugin struct {
	Deps // plugin dependencies are injected

	subscription        chan (messaging.ProtoMessage)
	kafkaSyncPublisher  messaging.ProtoPublisher
	kafkaAsyncPublisher messaging.ProtoPublisher
	kafkaWatcher        messaging.ProtoWatcher
	// Successfully published kafka message is sent through the message channel.
	// In case of a failure it sent through the error channel.
	asyncSubscription   chan (messaging.ProtoMessage)
	asyncSuccessChannel chan (messaging.ProtoMessage)
	asyncErrorChannel   chan (messaging.ProtoMessageErr)
	// Fields below are used to properly finish the example.
	syncCaseDone  bool
	asyncCaseDone bool
	closeChannel  *chan struct{}
}

const (
	// Number of sync messages sent. Ensure that syncMessageCount >= syncMessageOffset
	syncMessageCount = 10
	// Partition sync messages are sent and watched on
	syncMessagePartition = 1
	// Offset for sync messages watcher
	syncMessageOffset = 5
	// Partiton async messages are sent and watched on
	asyncMessagePartition = 2
	// Offset for async messages watcher
	asyncMessageOffset = 0
)

// Topics
const (
	topic1 = "example-sync-topic"
	topic2 = "example-async-topic"
)

// Init initializes and starts producers and consumers.
func (plugin *ExamplePlugin) Init() (err error) {
	// Create connection
	connection := plugin.Kafka.NewProtoManualConnection("example-proto-connection")

	// Create a synchronous and asynchronous publisher.
	// In the manual mode, every publisher has selected its target partition.
	plugin.kafkaSyncPublisher, err = connection.NewSyncPublisherToPartition(topic1, syncMessagePartition)
	if err != nil {
		return err
	}
	// Async publisher requires two more channels to send success/error callback.
	plugin.asyncSuccessChannel = make(chan messaging.ProtoMessage, 0)
	plugin.asyncErrorChannel = make(chan messaging.ProtoMessageErr, 0)
	plugin.kafkaAsyncPublisher, err = connection.NewAsyncPublisherToPartition(topic2, asyncMessagePartition,
		messaging.ToProtoMsgChan(plugin.asyncSuccessChannel), messaging.ToProtoMsgErrChan(plugin.asyncErrorChannel))
	if err != nil {
		return err
	}

	// Initialize sync watcher.
	plugin.kafkaWatcher = plugin.Kafka.NewWatcher("example-watcher")

	// Prepare subscription channel. Relevant kafka messages are send to this
	// channel so that the watcher can read it.
	plugin.subscription = make(chan messaging.ProtoMessage)
	// The watcher is consuming messages on a custom partition and an offset.
	// If there is a producer who stores message to the same partition under
	// the same or a newer offset, the message will be consumed.
	err = plugin.kafkaWatcher.WatchPartition(messaging.ToProtoMsgChan(plugin.subscription), topic1,
		syncMessagePartition, syncMessageOffset)
	if err != nil {
		plugin.Log.Error(err)
	}

	// Prepare subscription channel. Relevant kafka messages are send to this
	// channel so that the watcher can read it
	plugin.asyncSubscription = make(chan messaging.ProtoMessage)
	// The watcher is consuming messages on custom partition and offset.
	// If there is a producer who stores message to the same partition under
	// the same or a newer offset, the message will be consumed.
	err = plugin.kafkaWatcher.WatchPartition(messaging.ToProtoMsgChan(plugin.asyncSubscription), topic2,
		asyncMessagePartition, asyncMessageOffset)
	if err != nil {
		plugin.Log.Error(err)
	}

	plugin.Log.Info("Initialization of the custom plugin for the Kafka example is completed")

	// Run sync and async kafka consumers.
	go plugin.syncEventHandler()
	go plugin.asyncEventHandler()

	// Run the producer.
	go plugin.producer()

	// Verify results and close the example if successful.
	go plugin.closeExample()

	return err
}

func (plugin *ExamplePlugin) closeExample() {
	for {
		if plugin.syncCaseDone && plugin.asyncCaseDone {
			plugin.Log.Info("kafka example finished, sending shutdown ...")
			*plugin.closeChannel <- struct{}{}
			break
		}
	}
}

// Close closes the subscription and the channels used by the async producer.
func (plugin *ExamplePlugin) Close() error {
	safeclose.Close(plugin.subscription)
	safeclose.Close(plugin.asyncErrorChannel)
	safeclose.Close(plugin.asyncSuccessChannel)
	return nil
}

/*************
 * Producers *
 *************/

// producer sends messages to a desired topic and in the manual mode also
// to a specified partition.
func (plugin *ExamplePlugin) producer() {
	// Wait for the both event handlers to initialize.
	time.Sleep(2 * time.Second)

	// Synchronous message with protobuf-encoded data.
	enc := &etcdexample.EtcdExample{
		StringVal: "sync-dummy-message",
		Uint32Val: uint32(0),
		BoolVal:   true,
	}
	// Send several sync messages with offsets 0,1,...
	plugin.Log.Infof("Sending %v Kafka notifications (protobuf) ...", syncMessageCount)
	for i := 0; i < syncMessageCount; i++ {
		err := plugin.kafkaSyncPublisher.Put("proto-key", enc)
		if err != nil {
			plugin.Log.Errorf("Failed to sync-send a proto message, error %v", err)
		}
	}

	// Send message with protobuf encoded data asynchronously.
	// Delivery status is propagated back to the application through
	// the configured pair of channels - one for the success events and one for
	// the errors.
	plugin.Log.Info("Sending async Kafka notification (protobuf)")
	err := plugin.kafkaAsyncPublisher.Put("async-proto-key", enc)
	if err != nil {
		plugin.Log.Errorf("Failed to async-send a proto message, error %v", err)
	} else {
		plugin.Log.Info("Async proto message sent")
	}
}

/*************
 * Consumers *
 *************/

// syncEventHandler is a Kafka consumer synchronously processing events from
// a channel associated with a specific topic, partition and a starting offset.
// If a producer sends a message matching this destination criteria, the consumer
// will receive it.
func (plugin *ExamplePlugin) syncEventHandler() {
	plugin.Log.Info("Started Kafka sync event handler...")

	// Producer sends several messages (set in syncMessageCount).
	// Consumer should receive only messages from desired partition and offset.
	messageCounter := 0
	for message := range plugin.subscription {
		plugin.Log.Infof("Received sync Kafka Message, topic '%s', partition '%v', offset '%v', key: '%s', ",
			message.GetTopic(), message.GetPartition(), message.GetOffset(), message.GetKey())
		messageCounter++
		if message.GetPartition() != syncMessagePartition {
			plugin.Log.Errorf("Received sync message with unexpected partition: %v", message.GetOffset())
		}
		if message.GetOffset() < syncMessageOffset {
			plugin.Log.Errorf("Received sync message with unexpected offset: %v", message.GetOffset())
		}
		// For example purpose: let it know that this part of the example is done
		if messageCounter == (syncMessageCount - syncMessageOffset) {
			plugin.syncCaseDone = true
		}
	}

}

// asyncEventHandler is a Kafka consumer asynchronously processing events from
// a channel associated with a specific topic, partition and a starting offset.
// If a producer sends a message matching this destination criteria, the consumer
// will receive it.
func (plugin *ExamplePlugin) asyncEventHandler() {
	plugin.Log.Info("Started Kafka async event handler...")
	for {
		select {
		// Channel subscribed with watcher
		case message := <-plugin.asyncSubscription:
			plugin.Log.Infof("Received async Kafka Message, topic '%s', partition '%v', offset '%v', key: '%s', ",
				message.GetTopic(), message.GetPartition(), message.GetOffset(), message.GetKey())
			if message.GetPartition() != asyncMessagePartition {
				plugin.Log.Errorf("Received async message with unexpected partition: %v", message.GetOffset())
			}
			if message.GetOffset() < asyncMessageOffset {
				plugin.Log.Errorf("Received async message with unexpected offset: %v", message.GetOffset())
			}
			// For example purpose: let it know that this part of the example is done
			plugin.asyncCaseDone = true
		// Success callback channel
		case message := <-plugin.asyncSuccessChannel:
			plugin.Log.Infof("Async message successfully delivered, topic '%s', partition '%v', offset '%v', key: '%s', ",
				message.GetTopic(), message.GetPartition(), message.GetOffset(), message.GetKey())
		// Error callback channel
		case err := <-plugin.asyncErrorChannel:
			plugin.Log.Errorf("Failed to publish async message, %v", err)
		}
	}
}
<commit_msg>rebase<commit_after>package main

import (
	"time"

	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/examples/model"
	"github.com/ligato/cn-infra/messaging"
	"github.com/ligato/cn-infra/utils/safeclose"
)

//********************************************************************
// This example shows how to use the Agent's Kafka APIs to perform
// synchronous/asynchronous calls and how to watch on these events.
//********************************************************************

func main() {
	// Init close channel used to stop the example
	exampleFinished := make(chan struct{}, 1)

	// Start Agent with ExampleFlavor
	// (combination of ExamplePlugin & reused cn-infra plugins).
	flavor := ExampleFlavor{closeChan: &exampleFinished}
	plugins := flavor.Plugins()
	agent := core.NewAgent(flavor.LogRegistry().NewLogger("core"), 15*time.Second, plugins...)
	core.EventLoopWithInterrupt(agent, exampleFinished)
}

// ExamplePlugin demonstrates the use of Kafka plugin API from another plugin.
// The Kafka ConsumerHandle is required to read messages from a topic
// and PluginConnection is needed to start consuming on that topic.
type ExamplePlugin struct {
	Deps // plugin dependencies are injected

	subscription        chan (messaging.ProtoMessage)
	kafkaSyncPublisher  messaging.ProtoPublisher
	kafkaAsyncPublisher messaging.ProtoPublisher
	kafkaWatcher        messaging.ProtoWatcher
	// Successfully published kafka message is sent through the message channel.
	// In case of a failure it sent through the error channel.
	asyncSubscription   chan (messaging.ProtoMessage)
	asyncSuccessChannel chan (messaging.ProtoMessage)
	asyncErrorChannel   chan (messaging.ProtoMessageErr)
	// Fields below are used to properly finish the example.
	syncCaseDone  bool
	asyncCaseDone bool
	closeChannel  *chan struct{}
}

const (
	// Number of sync messages sent. Ensure that syncMessageCount >= syncMessageOffset
	syncMessageCount = 10
	// Partition sync messages are sent and watched on
	syncMessagePartition = 1
	// Offset for sync messages watcher
	syncMessageOffset = 5
	// Partiton async messages are sent and watched on
	asyncMessagePartition = 2
	// Offset for async messages watcher
	asyncMessageOffset = 0
)

// Topics
const (
	topic1 = "example-sync-topic"
	topic2 = "example-async-topic"
	connection = "example-proto-connection"
)

// Init initializes and starts producers and consumers.
func (plugin *ExamplePlugin) Init() (err error) {
	// Create a synchronous and asynchronous publisher.
	// In the manual mode, every publisher has selected its target partition.
	plugin.kafkaSyncPublisher, err = plugin.Kafka.NewSyncPublisherToPartition(connection, topic1, syncMessagePartition)
	if err != nil {
		return err
	}
	// Async publisher requires two more channels to send success/error callback.
	plugin.asyncSuccessChannel = make(chan messaging.ProtoMessage, 0)
	plugin.asyncErrorChannel = make(chan messaging.ProtoMessageErr, 0)
	plugin.kafkaAsyncPublisher, err = plugin.Kafka.NewAsyncPublisherToPartition(connection, topic2, asyncMessagePartition,
		messaging.ToProtoMsgChan(plugin.asyncSuccessChannel), messaging.ToProtoMsgErrChan(plugin.asyncErrorChannel))
	if err != nil {
		return err
	}

	// Initialize sync watcher.
	plugin.kafkaWatcher = plugin.Kafka.NewWatcher("example-watcher")

	// Prepare subscription channel. Relevant kafka messages are send to this
	// channel so that the watcher can read it.
	plugin.subscription = make(chan messaging.ProtoMessage)
	// The watcher is consuming messages on a custom partition and an offset.
	// If there is a producer who stores message to the same partition under
	// the same or a newer offset, the message will be consumed.
	err = plugin.kafkaWatcher.WatchPartition(messaging.ToProtoMsgChan(plugin.subscription), topic1,
		syncMessagePartition, syncMessageOffset)
	if err != nil {
		plugin.Log.Error(err)
	}

	// Prepare subscription channel. Relevant kafka messages are send to this
	// channel so that the watcher can read it
	plugin.asyncSubscription = make(chan messaging.ProtoMessage)
	// The watcher is consuming messages on custom partition and offset.
	// If there is a producer who stores message to the same partition under
	// the same or a newer offset, the message will be consumed.
	err = plugin.kafkaWatcher.WatchPartition(messaging.ToProtoMsgChan(plugin.asyncSubscription), topic2,
		asyncMessagePartition, asyncMessageOffset)
	if err != nil {
		plugin.Log.Error(err)
	}

	plugin.Log.Info("Initialization of the custom plugin for the Kafka example is completed")

	// Run sync and async kafka consumers.
	go plugin.syncEventHandler()
	go plugin.asyncEventHandler()

	// Run the producer.
	go plugin.producer()

	// Verify results and close the example if successful.
	go plugin.closeExample()

	return err
}

func (plugin *ExamplePlugin) closeExample() {
	for {
		if plugin.syncCaseDone && plugin.asyncCaseDone {
			plugin.Log.Info("kafka example finished, sending shutdown ...")
			*plugin.closeChannel <- struct{}{}
			break
		}
	}
}

// Close closes the subscription and the channels used by the async producer.
func (plugin *ExamplePlugin) Close() error {
	safeclose.Close(plugin.subscription)
	safeclose.Close(plugin.asyncErrorChannel)
	safeclose.Close(plugin.asyncSuccessChannel)
	return nil
}

/*************
 * Producers *
 *************/

// producer sends messages to a desired topic and in the manual mode also
// to a specified partition.
func (plugin *ExamplePlugin) producer() {
	// Wait for the both event handlers to initialize.
	time.Sleep(2 * time.Second)

	// Synchronous message with protobuf-encoded data.
	enc := &etcdexample.EtcdExample{
		StringVal: "sync-dummy-message",
		Uint32Val: uint32(0),
		BoolVal:   true,
	}
	// Send several sync messages with offsets 0,1,...
	plugin.Log.Infof("Sending %v Kafka notifications (protobuf) ...", syncMessageCount)
	for i := 0; i < syncMessageCount; i++ {
		err := plugin.kafkaSyncPublisher.Put("proto-key", enc)
		if err != nil {
			plugin.Log.Errorf("Failed to sync-send a proto message, error %v", err)
		}
	}

	// Send message with protobuf encoded data asynchronously.
	// Delivery status is propagated back to the application through
	// the configured pair of channels - one for the success events and one for
	// the errors.
	plugin.Log.Info("Sending async Kafka notification (protobuf)")
	err := plugin.kafkaAsyncPublisher.Put("async-proto-key", enc)
	if err != nil {
		plugin.Log.Errorf("Failed to async-send a proto message, error %v", err)
	} else {
		plugin.Log.Info("Async proto message sent")
	}
}

/*************
 * Consumers *
 *************/

// syncEventHandler is a Kafka consumer synchronously processing events from
// a channel associated with a specific topic, partition and a starting offset.
// If a producer sends a message matching this destination criteria, the consumer
// will receive it.
func (plugin *ExamplePlugin) syncEventHandler() {
	plugin.Log.Info("Started Kafka sync event handler...")

	// Producer sends several messages (set in syncMessageCount).
	// Consumer should receive only messages from desired partition and offset.
	messageCounter := 0
	for message := range plugin.subscription {
		plugin.Log.Infof("Received sync Kafka Message, topic '%s', partition '%v', offset '%v', key: '%s', ",
			message.GetTopic(), message.GetPartition(), message.GetOffset(), message.GetKey())
		messageCounter++
		if message.GetPartition() != syncMessagePartition {
			plugin.Log.Errorf("Received sync message with unexpected partition: %v", message.GetOffset())
		}
		if message.GetOffset() < syncMessageOffset {
			plugin.Log.Errorf("Received sync message with unexpected offset: %v", message.GetOffset())
		}
		// For example purpose: let it know that this part of the example is done
		if messageCounter == (syncMessageCount - syncMessageOffset) {
			plugin.syncCaseDone = true
		}
	}

}

// asyncEventHandler is a Kafka consumer asynchronously processing events from
// a channel associated with a specific topic, partition and a starting offset.
// If a producer sends a message matching this destination criteria, the consumer
// will receive it.
func (plugin *ExamplePlugin) asyncEventHandler() {
	plugin.Log.Info("Started Kafka async event handler...")
	for {
		select {
		// Channel subscribed with watcher
		case message := <-plugin.asyncSubscription:
			plugin.Log.Infof("Received async Kafka Message, topic '%s', partition '%v', offset '%v', key: '%s', ",
				message.GetTopic(), message.GetPartition(), message.GetOffset(), message.GetKey())
			if message.GetPartition() != asyncMessagePartition {
				plugin.Log.Errorf("Received async message with unexpected partition: %v", message.GetOffset())
			}
			if message.GetOffset() < asyncMessageOffset {
				plugin.Log.Errorf("Received async message with unexpected offset: %v", message.GetOffset())
			}
			// For example purpose: let it know that this part of the example is done
			plugin.asyncCaseDone = true
		// Success callback channel
		case message := <-plugin.asyncSuccessChannel:
			plugin.Log.Infof("Async message successfully delivered, topic '%s', partition '%v', offset '%v', key: '%s', ",
				message.GetTopic(), message.GetPartition(), message.GetOffset(), message.GetKey())
		// Error callback channel
		case err := <-plugin.asyncErrorChannel:
			plugin.Log.Errorf("Failed to publish async message, %v", err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Nodetemple <hostmaster@nodetemple.com>

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 command

import (
	"github.com/codegangsta/cli"
)

func DemoCommand() cli.Command{
	return cli.Command{
		Name:  "demo",
		Usage: "a simple `hello world` demo",
		/*Action: func(c *cli.Context) {
			println("Hello: ", c.Args().First())
		},*/
		Subcommands: []cli.Command{
			{
				Name:  "add",
				Usage: "add a new template",
				Flags: []cli.Flag{
					//cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live"},
					cli.StringFlag{Name: "demo-flag, d", Value: "", Usage: "demo flag usage"},
				},
				Action: func(c *cli.Context) {
					println("new task template:", c.Args().First(), c.GlobalString("provider"), c.String("demo-flag"))
				},
			},
			{
				Name:  "remove",
				Usage: "remove an existing template",
				Flags: []cli.Flag{
					//cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live"},
					cli.StringFlag{Name: "demo-flag, d", Value: "", Usage: "demo flag usage"},
				},
				Action: func(c *cli.Context) {
					println("removed task template:", c.Args().First(), c.GlobalString("provider"), c.String("demo-flag"))
				},
			},
		},
	}
}
<commit_msg>Demo cmd: move action and flags to upper cmd<commit_after>/*
Copyright 2015 Nodetemple <hostmaster@nodetemple.com>

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 command

import (
	"github.com/codegangsta/cli"
)

func DemoCommand() cli.Command{
	return cli.Command{
		Name:  "demo",
		Usage: "a simple `hello world` demo",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "demo-flag, d", Value: "", Usage: "demo flag usage"},
			cli.BoolFlag{Name: "demo-bool", Value: true, Usage: "demo bool usage"},
		},
		Action: func(c *cli.Context) {
			println("result:", c.Args().First(), c.GlobalString("provider"), c.String("demo-flag"), c.String("demo-bool"))
		},
	}
}
<|endoftext|>