<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fuseutil

import (
	"time"

	"golang.org/x/net/context"
)

// An interface that must be implemented by file systems to be mounted with
// FUSE. See also the comments on request and response structs.
//
// Not all methods need to have interesting implementations. Embed a field of
// type NotImplementedFileSystem to inherit defaults that return ENOSYS to the
// kernel.
type FileSystem interface {
	// Look up a child by name within a parent directory. The kernel calls this
	// when resolving user paths to dentry structs, which are then cached.
	Lookup(
		ctx context.Context,
		req *LookupRequest) (*LookupResponse, error)

	// Forget an inode ID previously issued (e.g. by Lookup). The kernel calls
	// this when removing an inode from its internal caches.
	//
	// The kernel guarantees that the node ID will not be used in further calls
	// to the file system (unless it is reissued by the file system).
	Forget(
		ctx context.Context,
		req *ForgetRequest) (*ForgetResponse, error)
}

////////////////////////////////////////////////////////////////////////
// Simple types
////////////////////////////////////////////////////////////////////////

// A 64-bit number used to uniquely identify a file or directory in the file
// system.
//
// This corresponds to struct inode::i_no in the VFS layer.
// (Cf. http://goo.gl/tvYyQt)
type InodeID uint64

// A generation number for an inode. Irrelevant for file systems that won't be
// exported over NFS. For those that will and that reuse inode IDs when they
// become free, the generation number must change when an ID is reused.
//
// This corresponds to struct inode::i_generation in the VFS layer.
// (Cf. http://goo.gl/tvYyQt)
//
// Some related reading:
//
//     http://fuse.sourceforge.net/doxygen/structfuse__entry__param.html
//     http://stackoverflow.com/q/11071996/1505451
//     http://goo.gl/CqvwyX
//     http://julipedia.meroh.net/2005/09/nfs-file-handles.html
//     http://goo.gl/wvo3MB
//
type GenerationNumber uint64

// Attributes for a file or directory inode. Corresponds to struct inode (cf.
// http://goo.gl/tvYyQt).
type InodeAttributes struct {
	// The size of the file in bytes.
	Size uint64
}

////////////////////////////////////////////////////////////////////////
// Requests and responses
////////////////////////////////////////////////////////////////////////

type LookupRequest struct {
	// The ID of the directory inode to which the child belongs.
	Parent InodeID

	// The name of the child of interest, relative to the parent. For example, in
	// this directory structure:
	//
	//     foo/
	//         bar/
	//             baz
	//
	// the file system may receive a request to look up the child named "bar" for
	// the parent foo/.
	Name string
}

type LookupResponse struct {
	// The ID of the child inode. The file system must ensure that the returned
	// inode ID remains valid until a later call to Forget.
	Child InodeID

	// A generation number for this incarnation of the inode with the given ID.
	// See comments on type GenerationNumber for more.
	Generation GenerationNumber

	// Current ttributes for the child inode.
	Attributes InodeAttributes

	// The time until which the kernel may maintain an entry for this name to
	// inode mapping in its dentry cache. After this time, it will revalidate the
	// dentry.
	//
	// Leave at the zero value to disable caching.
	//
	// TODO(jacobsa): Make this comment more thorough, ideally with a code walk
	// like the one for AttributesExpiration below.
	EntryExpiration time.Time

	// The FUSE VFS layer in the kernel maintains a cache of file attributes,
	// used whenever up to date information about size, mode, etc. is needed.
	//
	// For example, this is the abridged call chain for fstat(2):
	//
	//  *  (http://goo.gl/tKBH1p) fstat calls vfs_fstat.
	//  *  (http://goo.gl/3HeITq) vfs_fstat eventuall calls vfs_getattr_nosec.
	//  *  (http://goo.gl/DccFQr) vfs_getattr_nosec calls i_op->getattr.
	//  *  (http://goo.gl/dpKkst) fuse_getattr calls fuse_update_attributes.
	//  *  (http://goo.gl/yNlqPw) fuse_update_attributes uses the values in the
	//     struct inode if allowed, otherwise calling out to the user-space code.
	//
	// In addition to obvious cases like fstat, this is also used in more subtle
	// cases like updating size information before seeking (http://goo.gl/2nnMFa)
	// or reading (http://goo.gl/FQSWs8).
	//
	// Most 'real' file systems do not set inode_operations::getattr, and
	// therefore vfs_getattr_nosec calls generic_fillattr which simply grabs the
	// information from the inode struct. This makes sense because these file
	// systems cannot spontaneously change; all modifications go through the
	// kernel which can update the inode struct as appropriate.
	//
	// In contrast, a fuse file system may have spontaneous changes, so it calls
	// out to user space to fetch attributes. However this is expensive, so the
	// FUSE layer in the kernel caches the attributes if requested.
	//
	// This field controls when the attributes returned in this response and
	// stashed in the struct inode should be re-queried. Leave at the zero value
	// to disable caching.
	//
	// More reading:
	//     http://stackoverflow.com/q/21540315/1505451
	AttributesExpiration time.Time
}

type ForgetRequest struct {
	// The inode to be forgotten. The kernel guarantees that the node ID will not
	// be used in further calls to the file system (unless it is reissued by the
	// file system).
	ID InodeID
}

type ForgetResponse struct {
}
<commit_msg>Expanded comments for EntryExpiration.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fuseutil

import (
	"time"

	"golang.org/x/net/context"
)

// An interface that must be implemented by file systems to be mounted with
// FUSE. See also the comments on request and response structs.
//
// Not all methods need to have interesting implementations. Embed a field of
// type NotImplementedFileSystem to inherit defaults that return ENOSYS to the
// kernel.
type FileSystem interface {
	// Look up a child by name within a parent directory. The kernel calls this
	// when resolving user paths to dentry structs, which are then cached.
	Lookup(
		ctx context.Context,
		req *LookupRequest) (*LookupResponse, error)

	// Forget an inode ID previously issued (e.g. by Lookup). The kernel calls
	// this when removing an inode from its internal caches.
	//
	// The kernel guarantees that the node ID will not be used in further calls
	// to the file system (unless it is reissued by the file system).
	Forget(
		ctx context.Context,
		req *ForgetRequest) (*ForgetResponse, error)
}

////////////////////////////////////////////////////////////////////////
// Simple types
////////////////////////////////////////////////////////////////////////

// A 64-bit number used to uniquely identify a file or directory in the file
// system.
//
// This corresponds to struct inode::i_no in the VFS layer.
// (Cf. http://goo.gl/tvYyQt)
type InodeID uint64

// A generation number for an inode. Irrelevant for file systems that won't be
// exported over NFS. For those that will and that reuse inode IDs when they
// become free, the generation number must change when an ID is reused.
//
// This corresponds to struct inode::i_generation in the VFS layer.
// (Cf. http://goo.gl/tvYyQt)
//
// Some related reading:
//
//     http://fuse.sourceforge.net/doxygen/structfuse__entry__param.html
//     http://stackoverflow.com/q/11071996/1505451
//     http://goo.gl/CqvwyX
//     http://julipedia.meroh.net/2005/09/nfs-file-handles.html
//     http://goo.gl/wvo3MB
//
type GenerationNumber uint64

// Attributes for a file or directory inode. Corresponds to struct inode (cf.
// http://goo.gl/tvYyQt).
type InodeAttributes struct {
	// The size of the file in bytes.
	Size uint64
}

////////////////////////////////////////////////////////////////////////
// Requests and responses
////////////////////////////////////////////////////////////////////////

type LookupRequest struct {
	// The ID of the directory inode to which the child belongs.
	Parent InodeID

	// The name of the child of interest, relative to the parent. For example, in
	// this directory structure:
	//
	//     foo/
	//         bar/
	//             baz
	//
	// the file system may receive a request to look up the child named "bar" for
	// the parent foo/.
	Name string
}

type LookupResponse struct {
	// The ID of the child inode. The file system must ensure that the returned
	// inode ID remains valid until a later call to Forget.
	Child InodeID

	// A generation number for this incarnation of the inode with the given ID.
	// See comments on type GenerationNumber for more.
	Generation GenerationNumber

	// Current ttributes for the child inode.
	Attributes InodeAttributes

	// The FUSE VFS layer in the kernel maintains a cache of file attributes,
	// used whenever up to date information about size, mode, etc. is needed.
	//
	// For example, this is the abridged call chain for fstat(2):
	//
	//  *  (http://goo.gl/tKBH1p) fstat calls vfs_fstat.
	//  *  (http://goo.gl/3HeITq) vfs_fstat eventuall calls vfs_getattr_nosec.
	//  *  (http://goo.gl/DccFQr) vfs_getattr_nosec calls i_op->getattr.
	//  *  (http://goo.gl/dpKkst) fuse_getattr calls fuse_update_attributes.
	//  *  (http://goo.gl/yNlqPw) fuse_update_attributes uses the values in the
	//     struct inode if allowed, otherwise calling out to the user-space code.
	//
	// In addition to obvious cases like fstat, this is also used in more subtle
	// cases like updating size information before seeking (http://goo.gl/2nnMFa)
	// or reading (http://goo.gl/FQSWs8).
	//
	// Most 'real' file systems do not set inode_operations::getattr, and
	// therefore vfs_getattr_nosec calls generic_fillattr which simply grabs the
	// information from the inode struct. This makes sense because these file
	// systems cannot spontaneously change; all modifications go through the
	// kernel which can update the inode struct as appropriate.
	//
	// In contrast, a FUSE file system may have spontaneous changes, so it calls
	// out to user space to fetch attributes. However this is expensive, so the
	// FUSE layer in the kernel caches the attributes if requested.
	//
	// This field controls when the attributes returned in this response and
	// stashed in the struct inode should be re-queried. Leave at the zero value
	// to disable caching.
	//
	// More reading:
	//     http://stackoverflow.com/q/21540315/1505451
	AttributesExpiration time.Time

	// The time until which the kernel may maintain an entry for this name to
	// inode mapping in its dentry cache. After this time, it will revalidate the
	// dentry.
	//
	// As in the discussion of attribute caching above, unlike real file systems,
	// FUSE file systems may spontaneously change their name -> inode mapping.
	// Therefore the FUSE VFS layer uses dentry_operations::d_revalidate
	// (http://goo.gl/dVea0h) to intercept lookups and revalidate by calling the
	// user-space Lookup method. However the latter may be slow, so it caches the
	// entries until the time defined by this field.
	//
	// Example code walk:
	//
	//     * (http://goo.gl/M2G3tO) lookup_dcache calls d_revalidate if enabled.
	//     * (http://goo.gl/ef0Elu) fuse_dentry_revalidate just uses the dentry's
	//     inode if fuse_dentry_time(entry) hasn't passed. Otherwise it sends a
	//     lookup request.
	//
	// Leave at the zero value to disable caching.
	EntryExpiration time.Time
}

type ForgetRequest struct {
	// The inode to be forgotten. The kernel guarantees that the node ID will not
	// be used in further calls to the file system (unless it is reissued by the
	// file system).
	ID InodeID
}

type ForgetResponse struct {
}
<|endoftext|>


<commit_before>package lightstep

import (
	"bytes"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/lightstep/lightstep-tracer-go/collectorpb"
	"golang.org/x/net/context"
	"golang.org/x/net/http2"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

var (
	acceptHeader      = http.CanonicalHeaderKey("Accept")
	contentTypeHeader = http.CanonicalHeaderKey("Content-Type")
)

const (
	collectorHttpMethod = "POST"
	collectorHttpPath   = "/api/v2/reports"
	protoContentType    = "application/octet-stream"
)

// grpcCollectorClient specifies how to send reports back to a LightStep
// collector via grpc.
type httpCollectorClient struct {
	// auth and runtime information
	reporterID  uint64
	accessToken string // accessToken is the access token used for explicit trace collection requests.
	attributes  map[string]string

	reportTimeout time.Duration

	// Remote service that will receive reports.
	url    *url.URL
	client *http.Client

	// converters
	converter *protoConverter
}

type transportCloser struct {
	transport http2.Transport
}

func (closer *transportCloser) Close() error {
	closer.transport.CloseIdleConnections()

	return nil
}

func newHttpCollectorClient(
	opts Options,
	reporterID uint64,
	attributes map[string]string,
) (*httpCollectorClient, error) {
	url, err := url.Parse(opts.Collector.HostPort())
	if err != nil {
		fmt.Println("collector config does not produce valid url", err)
		return nil, err
	}
	url.Path = collectorHttpPath

	return &httpCollectorClient{
		reporterID:    reporterID,
		accessToken:   opts.AccessToken,
		attributes:    attributes,
		reportTimeout: opts.ReportTimeout,
		url:           url,
		converter:     newProtoConverter(opts),
	}, nil
}

func (client *httpCollectorClient) ConnectClient() (Connection, error) {
	transport := &http2.Transport{}

	client.client = &http.Client{
		Transport: transport,
		Timeout:   client.reportTimeout,
	}

	return &transportCloser{}, nil
}

func (client *httpCollectorClient) ShouldReconnect() bool {
	// http2 will handle connection reuse under the hood
	return false
}

func (client *httpCollectorClient) Report(context context.Context, buffer *reportBuffer) (collectorResponse, error) {
	httpRequest, err := client.toRequest(context, buffer)
	if err != nil {
		return nil, err
	}

	httpResponse, err := client.client.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer httpResponse.Body.Close()

	response, err := client.toResponse(httpResponse)
	if err != nil {
		return nil, err
	}

	return response, nil
}

func (client *httpCollectorClient) toRequest(
	context context.Context,
	buffer *reportBuffer,
) (*http.Request, error) {
	protoRequest := client.converter.toReportRequest(
		client.reporterID,
		client.attributes,
		client.accessToken,
		buffer,
	)

	buf, err := proto.Marshal(protoRequest)
	if err != nil {
		return nil, err
	}

	requestBody := bytes.NewReader(buf)

	request, err := http.NewRequest(collectorHttpMethod, client.url.String(), requestBody)
	if err != nil {
		return nil, err
	}
	request = request.WithContext(context)
	request.Header.Set(contentTypeHeader, protoContentType)
	request.Header.Set(acceptHeader, protoContentType)

	return request, nil
}

func (client *httpCollectorClient) toResponse(response *http.Response) (collectorResponse, error) {
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	protoResponse := &collectorpb.ReportResponse{}
	if err := proto.Unmarshal(body, protoResponse); err != nil {
		return nil, err
	}

	return protoResponse, nil
}
<commit_msg>Add scheme to the url (http or https) and allow http for http schemes. (#118)<commit_after>package lightstep

import (
	"bytes"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/lightstep/lightstep-tracer-go/collectorpb"
	"golang.org/x/net/context"
	"golang.org/x/net/http2"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

var (
	acceptHeader      = http.CanonicalHeaderKey("Accept")
	contentTypeHeader = http.CanonicalHeaderKey("Content-Type")
)

const (
	collectorHttpMethod = "POST"
	collectorHttpPath   = "/api/v2/reports"
	protoContentType    = "application/octet-stream"
)

// grpcCollectorClient specifies how to send reports back to a LightStep
// collector via grpc.
type httpCollectorClient struct {
	// auth and runtime information
	reporterID  uint64
	accessToken string // accessToken is the access token used for explicit trace collection requests.
	attributes  map[string]string

	reportTimeout time.Duration

	// Remote service that will receive reports.
	url    *url.URL
	client *http.Client

	// converters
	converter *protoConverter
}

type transportCloser struct {
	transport http2.Transport
}

func (closer *transportCloser) Close() error {
	closer.transport.CloseIdleConnections()

	return nil
}

func newHttpCollectorClient(
	opts Options,
	reporterID uint64,
	attributes map[string]string,
) (*httpCollectorClient, error) {
	url, err := url.Parse(opts.Collector.URL())
	if err != nil {
		fmt.Println("collector config does not produce valid url", err)
		return nil, err
	}
	url.Path = collectorHttpPath

	return &httpCollectorClient{
		reporterID:    reporterID,
		accessToken:   opts.AccessToken,
		attributes:    attributes,
		reportTimeout: opts.ReportTimeout,
		url:           url,
		converter:     newProtoConverter(opts),
	}, nil
}

func (client *httpCollectorClient) ConnectClient() (Connection, error) {
	transport := &http2.Transport{
		AllowHTTP: true,
	}

	client.client = &http.Client{
		Transport: transport,
		Timeout:   client.reportTimeout,
	}

	return &transportCloser{}, nil
}

func (client *httpCollectorClient) ShouldReconnect() bool {
	// http2 will handle connection reuse under the hood
	return false
}

func (client *httpCollectorClient) Report(context context.Context, buffer *reportBuffer) (collectorResponse, error) {
	httpRequest, err := client.toRequest(context, buffer)
	if err != nil {
		return nil, err
	}

	httpResponse, err := client.client.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer httpResponse.Body.Close()

	response, err := client.toResponse(httpResponse)
	if err != nil {
		return nil, err
	}

	return response, nil
}

func (client *httpCollectorClient) toRequest(
	context context.Context,
	buffer *reportBuffer,
) (*http.Request, error) {
	protoRequest := client.converter.toReportRequest(
		client.reporterID,
		client.attributes,
		client.accessToken,
		buffer,
	)

	buf, err := proto.Marshal(protoRequest)
	if err != nil {
		return nil, err
	}

	requestBody := bytes.NewReader(buf)

	request, err := http.NewRequest(collectorHttpMethod, client.url.String(), requestBody)
	if err != nil {
		return nil, err
	}
	request = request.WithContext(context)
	request.Header.Set(contentTypeHeader, protoContentType)
	request.Header.Set(acceptHeader, protoContentType)

	return request, nil
}

func (client *httpCollectorClient) toResponse(response *http.Response) (collectorResponse, error) {
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	protoResponse := &collectorpb.ReportResponse{}
	if err := proto.Unmarshal(body, protoResponse); err != nil {
		return nil, err
	}

	return protoResponse, nil
}
<|endoftext|>


<commit_before>package httpd

import (
	"encoding/base64"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"
)

type loggingResponseWriter interface {
	http.ResponseWriter
	Status() int
	Size() int
}

// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
// code and body size
type responseLogger struct {
	w      http.ResponseWriter
	status int
	size   int
}

func (l *responseLogger) Header() http.Header {
	return l.w.Header()
}

func (l *responseLogger) Write(b []byte) (int, error) {
	if l.status == 0 {
		// Set status if WriteHeader has not been called
		l.status = http.StatusOK
	}
	size, err := l.w.Write(b)
	l.size += size
	return size, err
}

func (l *responseLogger) WriteHeader(s int) {
	l.w.WriteHeader(s)
	l.status = s
}

func (l *responseLogger) Status() int {
	return l.status
}

func (l *responseLogger) Size() int {
	return l.size
}

// Common Log Format: http://en.wikipedia.org/wiki/Common_Log_Format

// buildLogLine creates a common log format
// in addittion to the common fields, we also append referrer, user agent and request ID
func buildLogLine(l *responseLogger, r *http.Request, start time.Time) string {
	username := "-"
	url := r.URL

	// get username from the url if passed there
	if url.User != nil {
		if name := url.User.Username(); name != "" {
			username = name
		}
	}

	// Try to get it from the authorization header if set there
	if username == "-" {
		auth := r.Header.Get("Authorization")
		fields := strings.Split(auth, " ")
		if len(fields) == 2 {
			bs, err := base64.StdEncoding.DecodeString(fields[1])
			if err == nil {
				fields = strings.Split(string(bs), ":")
				if len(fields) >= 1 {
					username = fields[0]
				}
			}
		}
	}

	host, _, err := net.SplitHostPort(r.RemoteAddr)

	if err != nil {
		host = r.RemoteAddr
	}

	uri := url.RequestURI()
	return fmt.Sprintf(
		"%s %s %s %s %s %s %s %d %d %s %s %s",
		host,
		"-",
		username,
		fmt.Sprintf("[%s]", start.Format("02/Jan/2006:15:04:05 -0700")),
		r.Method,
		uri,
		r.Proto,
		l.Status(),
		l.Size(),
		r.Referer(),
		r.UserAgent(),
		r.Header.Get("Request-Id"),
	)
}
<commit_msg>refactoring of build log line for http<commit_after>package httpd

import (
	"encoding/base64"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type loggingResponseWriter interface {
	http.ResponseWriter
	Status() int
	Size() int
}

// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
// code and body size
type responseLogger struct {
	w      http.ResponseWriter
	status int
	size   int
}

func (l *responseLogger) Header() http.Header {
	return l.w.Header()
}

func (l *responseLogger) Write(b []byte) (int, error) {
	if l.status == 0 {
		// Set status if WriteHeader has not been called
		l.status = http.StatusOK
	}
	size, err := l.w.Write(b)
	l.size += size
	return size, err
}

func (l *responseLogger) WriteHeader(s int) {
	l.w.WriteHeader(s)
	l.status = s
}

func (l *responseLogger) Status() int {
	return l.status
}

func (l *responseLogger) Size() int {
	return l.size
}

// Common Log Format: http://en.wikipedia.org/wiki/Common_Log_Format

// buildLogLine creates a common log format
// in addittion to the common fields, we also append referrer, user agent and request ID
func buildLogLine(l *responseLogger, r *http.Request, start time.Time) string {
	username := parseUsername(r)

	host, _, err := net.SplitHostPort(r.RemoteAddr)

	if err != nil {
		host = r.RemoteAddr
	}

	uri := r.URL.RequestURI()

	fields := []string{
		host,
		"-",
		username,
		fmt.Sprintf("[%s]", start.Format("02/Jan/2006:15:04:05 -0700")),
		r.Method,
		uri,
		r.Proto,
		strconv.Itoa(l.Status()),
		strconv.Itoa(l.Size()),
		r.Referer(),
		r.UserAgent(),
		r.Header.Get("Request-Id"),
	}

	return strings.Join(fields, " ")
}

// parses the uesrname either from the url or auth header
func parseUsername(r *http.Request) string {
	username := "-"

	url := r.URL

	// get username from the url if passed there
	if url.User != nil {
		if name := url.User.Username(); name != "" {
			username = name
		}
	}

	// Try to get it from the authorization header if set there
	if username == "-" {
		auth := r.Header.Get("Authorization")
		fields := strings.Split(auth, " ")
		if len(fields) == 2 {
			bs, err := base64.StdEncoding.DecodeString(fields[1])
			if err == nil {
				fields = strings.Split(string(bs), ":")
				if len(fields) >= 1 {
					username = fields[0]
				}
			}
		}
	}
	return username
}
<|endoftext|>


<commit_before>package main

import (
	"testing"

	"github.com/docker/go-connections/nat"
	contracts "github.com/estafette/estafette-ci-contracts"
	"github.com/stretchr/testify/assert"
)

var (
	dockerRunner = NewDockerRunner(envvarHelper, NewObfuscator(secretHelper), true, contracts.BuilderConfig{}, make(chan struct{}))
)

func init() {
	dockerRunner.createDockerClient()
}

func TestParsePortSpecs(t *testing.T) {

	t.Run("ParsePortSpecs", func(t *testing.T) {

		// act
		exposedPorts, bindings, err := nat.ParsePortSpecs([]string{"127.0.0.1:8000:8080/tcp"})

		if assert.Nil(t, err, "Error %v", err) {
			assert.Equal(t, 1, len(exposedPorts))
			assert.Equal(t, struct{}{}, exposedPorts["8080/tcp"])
			assert.Equal(t, 1, len(bindings))
			assert.Equal(t, 1, len(bindings["8080/tcp"]))
			assert.Equal(t, "8000", bindings["8080/tcp"][0].HostPort)
			assert.Equal(t, "127.0.0.1", bindings["8080/tcp"][0].HostIP)
		}
	})
}
<commit_msg>create different network in unit tests<commit_after>package main

import (
	"testing"

	"github.com/docker/go-connections/nat"
	contracts "github.com/estafette/estafette-ci-contracts"
	"github.com/stretchr/testify/assert"
)

var (
	dockerRunner = NewDockerRunner(envvarHelper, NewObfuscator(secretHelper), true, contracts.BuilderConfig{DockerNetwork: &contracts.DockerNetworkConfig{Name: "estafette-integration", Subnet: "192.168.4.1/24", Gateway: "192.168.4.1"}}, make(chan struct{}))
)

func init() {
	dockerRunner.createDockerClient()
}

func TestParsePortSpecs(t *testing.T) {

	t.Run("ParsePortSpecs", func(t *testing.T) {

		// act
		exposedPorts, bindings, err := nat.ParsePortSpecs([]string{"127.0.0.1:8000:8080/tcp"})

		if assert.Nil(t, err, "Error %v", err) {
			assert.Equal(t, 1, len(exposedPorts))
			assert.Equal(t, struct{}{}, exposedPorts["8080/tcp"])
			assert.Equal(t, 1, len(bindings))
			assert.Equal(t, 1, len(bindings["8080/tcp"]))
			assert.Equal(t, "8000", bindings["8080/tcp"][0].HostPort)
			assert.Equal(t, "127.0.0.1", bindings["8080/tcp"][0].HostIP)
		}
	})
}
<|endoftext|>


<commit_before>package uploads

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/materials-commons/gohandy/file"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/app/flow"
)

// RequestWriter is the interface used to write a request.
type requestWriter interface {
	write(dir string, req *flow.Request) error
}

// A fileRequestWriter implements writing a request to a file.
type fileRequestWriter struct{}

// Write will write the blocks for a request to the path returned by
// the RequestPath Path call. Write will attempt to create the directory
// path to write to.
func (r *fileRequestWriter) write(dir string, req *flow.Request) error {
	path := filepath.Join(dir, fmt.Sprintf("%d", req.FlowChunkNumber))
	err := r.validateWrite(dir, path, req)
	switch {
	case err == nil:
		return ioutil.WriteFile(path, req.Chunk, 0700)
	case err == app.ErrExists:
		return nil
	default:
		return err
	}
}

// validateWrite determines if a particular chunk can be written.
// If the size of the on disk chunk is smaller than the request
// chunk then that chunk is incomplete and we allow a write to it.
func (r *fileRequestWriter) validateWrite(dir, path string, req *flow.Request) error {
	// Create directory where chunk will be written
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}

	finfo, err := os.Stat(path)
	switch {
	case os.IsNotExist(err):
		return nil
	case err != nil:
		return app.ErrInvalid
	case finfo.Size() < int64(req.FlowChunkSize):
		return nil
	case finfo.Size() == int64(req.FlowChunkSize):
		return app.ErrExists
	default:
		return app.ErrInvalid
	}
}

// blockRequestWriter implements writing requests to a single file. It writes the
// requests in order by creating a sparse file and then seeking to the proper spot
// in the file to write the requests data.
type blockRequestWriter struct{}

// write will write the request to a file located in dir. The file will have
// the name of the flow UploadID(). This method creates a sparse file the
// size of the file to be written and then writes requests in order. Out of
// order chunks are handled by seeking to proper position in the file.
func (r *blockRequestWriter) write(dir string, req *flow.Request) error {
	path := filepath.Join(dir, req.UploadID())
	if err := r.createFile(dir, path, req.FlowTotalSize); err != nil {
		return err
	}
	return r.writeRequest(path, req)
}

// createFile ensures that the path exists. If needed it will create the directory and
// the file. The file is created as a sparse file.
func (r *blockRequestWriter) createFile(dir, path string, size int64) error {
	if !file.Exists(path) {
		if err := os.MkdirAll(dir, 0700); err != nil {
			return err
		}
		return createSparseFile(path, size)
	}
	return nil
}

// createSparseFile creates a new sparse file at path of size.
func createSparseFile(path string, size int64) error {
	if f, err := os.Create(path); err != nil {
		return err
	} else {
		defer f.Close()
		return f.Truncate(size)
	}
}

// writeRequest performs the actual write of the request. It opens the file
// sparse file, seeks to the proper position and then writes the data.
func (r *blockRequestWriter) writeRequest(path string, req *flow.Request) error {
	if f, err := os.OpenFile(path, os.O_WRONLY, 0660); err != nil {
		return err
	} else {
		defer f.Close()

		fromBeginning := 0
		seekTo := int64((req.FlowChunkNumber - 1) * int32(len(req.Chunk)))
		if _, err := f.Seek(seekTo, fromBeginning); err != nil {
			app.Log.Critf("Failed seeking to write chunk #%d for %s: %s", req.FlowChunkNumber, req.UploadID(), err)
			return err
		}

		if _, err := f.Write(req.Chunk); err != nil {
			app.Log.Critf("Failed writing chunk #%d for %s: %s", req.FlowChunkNumber, req.UploadID(), err)
			return err
		}
		return nil
	}
}
<commit_msg>Seek based on multiple of FlowChunkSize and not the length of the data, as the final block could be short.<commit_after>package uploads

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/materials-commons/gohandy/file"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/app/flow"
)

// RequestWriter is the interface used to write a request.
type requestWriter interface {
	write(dir string, req *flow.Request) error
}

// A fileRequestWriter implements writing a request to a file.
type fileRequestWriter struct{}

// Write will write the blocks for a request to the path returned by
// the RequestPath Path call. Write will attempt to create the directory
// path to write to.
func (r *fileRequestWriter) write(dir string, req *flow.Request) error {
	path := filepath.Join(dir, fmt.Sprintf("%d", req.FlowChunkNumber))
	err := r.validateWrite(dir, path, req)
	switch {
	case err == nil:
		return ioutil.WriteFile(path, req.Chunk, 0700)
	case err == app.ErrExists:
		return nil
	default:
		return err
	}
}

// validateWrite determines if a particular chunk can be written.
// If the size of the on disk chunk is smaller than the request
// chunk then that chunk is incomplete and we allow a write to it.
func (r *fileRequestWriter) validateWrite(dir, path string, req *flow.Request) error {
	// Create directory where chunk will be written
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}

	finfo, err := os.Stat(path)
	switch {
	case os.IsNotExist(err):
		return nil
	case err != nil:
		return app.ErrInvalid
	case finfo.Size() < int64(req.FlowChunkSize):
		return nil
	case finfo.Size() == int64(req.FlowChunkSize):
		return app.ErrExists
	default:
		return app.ErrInvalid
	}
}

// blockRequestWriter implements writing requests to a single file. It writes the
// requests in order by creating a sparse file and then seeking to the proper spot
// in the file to write the requests data.
type blockRequestWriter struct{}

// write will write the request to a file located in dir. The file will have
// the name of the flow UploadID(). This method creates a sparse file the
// size of the file to be written and then writes requests in order. Out of
// order chunks are handled by seeking to proper position in the file.
func (r *blockRequestWriter) write(dir string, req *flow.Request) error {
	path := filepath.Join(dir, req.UploadID())
	if err := r.createFile(dir, path, req.FlowTotalSize); err != nil {
		return err
	}
	return r.writeRequest(path, req)
}

// createFile ensures that the path exists. If needed it will create the directory and
// the file. The file is created as a sparse file.
func (r *blockRequestWriter) createFile(dir, path string, size int64) error {
	if !file.Exists(path) {
		if err := os.MkdirAll(dir, 0700); err != nil {
			return err
		}
		return createSparseFile(path, size)
	}
	return nil
}

// createSparseFile creates a new sparse file at path of size.
func createSparseFile(path string, size int64) error {
	if f, err := os.Create(path); err != nil {
		return err
	} else {
		defer f.Close()
		return f.Truncate(size)
	}
}

// writeRequest performs the actual write of the request. It opens the file
// sparse file, seeks to the proper position and then writes the data.
func (r *blockRequestWriter) writeRequest(path string, req *flow.Request) error {
	if f, err := os.OpenFile(path, os.O_WRONLY, 0660); err != nil {
		return err
	} else {
		defer f.Close()

		fromBeginning := 0
		seekTo := int64((req.FlowChunkNumber - 1) * req.FlowChunkSize)
		if _, err := f.Seek(seekTo, fromBeginning); err != nil {
			app.Log.Critf("Failed seeking to write chunk #%d for %s: %s", req.FlowChunkNumber, req.UploadID(), err)
			return err
		}

		if _, err := f.Write(req.Chunk); err != nil {
			app.Log.Critf("Failed writing chunk #%d for %s: %s", req.FlowChunkNumber, req.UploadID(), err)
			return err
		}
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"github.com/xuzhenglun/2048-Go/martix"
	"strconv"
	"time"
)

const MAX_LEN int = 4
const Add_NUM int = 1

var step int
var output_mode = termbox.OutputNormal

type Go2048 struct {
	martix.Martix
}

func (this Go2048) GoUp() bool {
	this.Left90()
	change := this.Combin()
	this.Left90()
	return change
}

func (this Go2048) GoDown() bool {
	this.Right90()
	change := this.Combin()
	this.Right90()
	return change
}

func (this Go2048) GoLeft() bool {
	change := this.Combin()
	return change
}

func (this Go2048) GoRight() bool {
	this.Mirror()
	change := this.Combin()
	this.Mirror()
	return change
}

func (this Go2048) CheckWinOrLose() bool {
	for x, row := range this.Martix {
		for y, _ := range row {
			if this.Martix[x][y] == 0 {
				return true
				//true = Have not been dead yet
			}
		}
	}
	return false
	//false = Lose
}

func (this Go2048) Init_termbox(x, y int) error {
	fg := termbox.ColorYellow
	bg := termbox.ColorBlack
	err := termbox.Clear(fg, bg)
	if err != nil {
		return err
	}
	str := "Enter: restart game"
	for n, c := range str {
		termbox.SetCell(x+n, y-1, c, fg, bg)
	}

	str = "ESC: quit game" + "  Step: " + strconv.Itoa(step)
	for n, c := range str {
		termbox.SetCell(x+n, y-2, c, fg, bg)
	}

	str = "Play with Arrow Key"
	for n, c := range str {
		termbox.SetCell(x+n, y-3, c, fg, bg)
	}

	fg = termbox.ColorBlack
	bg = termbox.ColorGreen
	for i := 0; i <= len(this.Martix); i++ {
		for t := 0; t < 6*len(this.Martix); t++ {
			if t%6 != 0 {
				termbox.SetCell(x+t, y+i*2, '-', fg, bg)
			}
		}
		for t := 0; t <= 2*len(this.Martix); t++ {
			if t%2 == 0 {
				termbox.SetCell(x+i*6, y+t, '+', fg, bg)
			} else {
				termbox.SetCell(x+i*6, y+t, '|', fg, bg)
			}
		}
	}

	for i, row := range this.Martix {
		for j, _ := range row {
			if this.Martix[i][j] > 0 {
				str := fmt.Sprintf("%-5d", this.Martix[i][j])
				for n, char := range str {
					if output_mode == termbox.Output256 {
						termbox.SetCell(x+j*6+1+n, y+i*2+1, char, 0x10+termbox.Attribute(this.Martix[i][j]%256), 0xe0-termbox.Attribute(this.Martix[i][j]*2%256))
					} else {
						termbox.SetCell(x+j*6+1+n, y+i*2+1, char, termbox.ColorWhite, termbox.ColorMagenta)
					}
				}
			}
		}
	}
	return termbox.Flush()
}

func converPrintStr(x, y int, str string, fg, bg termbox.Attribute) error {
	xx := x
	for n, c := range str {
		if c == '\n' {
			y++
			xx = x - n - 1
		}
		termbox.SetCell(xx+n, y, c, fg, bg)
	}
	return termbox.Flush()
}

func (t *Go2048) ListernKey() chan termbox.Event {
	//ev := termbox.PollEvent()
	event_queue := make(chan termbox.Event)
	go func() {
		for {
			event_queue <- termbox.PollEvent() // 开始监听键盘事件
		}
	}()
	return event_queue
}

func (t *Go2048) ActionAndReturnKey(event_queue chan termbox.Event) termbox.Key {
	for {
		ev := <-event_queue
		changed := false

		switch ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyArrowUp:
				changed = t.GoUp()
			case termbox.KeyArrowDown:
				changed = t.GoDown()
			case termbox.KeyArrowLeft:
				changed = t.GoLeft()
			case termbox.KeyArrowRight:
				changed = t.GoRight()
			case termbox.KeyEsc, termbox.KeyEnter:
				changed = true
			default:
				changed = false
			}

			// 如果元素的值没有任何更改，则从新开始循环
			if !changed && t.CheckWinOrLose() {
				continue
			}

		case termbox.EventResize:
			x, y := termbox.Size()
			t.Init_termbox(x/2-10, y/2-4)
			continue
		case termbox.EventError:
			panic(ev.Err)
		}
		return ev.Key
	}
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()
	x, y := termbox.Size()

	output_mode = termbox.SetOutputMode(termbox.Output256)
	if output_mode != termbox.Output256 {
		termbox.SetOutputMode(termbox.OutputNormal)
	}

	martix.Init()
	var t Go2048
	t.Martix, _ = martix.Init_martix(MAX_LEN)
	t.AddNum(Add_NUM)
	step = 0
	ch := t.ListernKey()
	defer close(ch)

	for {
		t.Init_termbox(x/2-10, y/2-4)

		key := t.ActionAndReturnKey(ch)

		if t.CheckWinOrLose() == false {
			str := "Lose!"
			strlen := len(str)
			converPrintStr(x/2-strlen/2, y/2, str, termbox.ColorBlack, termbox.ColorRed)
			for {
				key = t.ActionAndReturnKey(ch)
				if key == termbox.KeyEnter || key == termbox.KeyEsc {
					break
				}
			}
		}

		if key == termbox.KeyEnter {
			t.Martix, _ = martix.Init_martix(MAX_LEN)
			step = -1
		}
		if key == termbox.KeyEsc {
			return
		}

		step++

		t.Init_termbox(x/2-10, y/2-4)
		time.Sleep(500 * time.Millisecond)
		t.AddNum(Add_NUM)
	}
}
<commit_msg>Add 8 color for windows<commit_after>package main

import (
	"fmt"
	"github.com/nsf/termbox-go"
	"github.com/xuzhenglun/2048-Go/martix"
	"math"
	"strconv"
	"time"
)

const MAX_LEN int = 4
const Add_NUM int = 1

var step int
var output_mode = termbox.OutputNormal

var colorTable = [...]termbox.Attribute{
	termbox.ColorMagenta,
	termbox.ColorGreen,
	termbox.ColorBlue,
	termbox.ColorCyan,
	termbox.ColorYellow,
	termbox.ColorBlack,
	termbox.ColorMagenta}

type Go2048 struct {
	martix.Martix
}

func (this Go2048) GoUp() bool {
	this.Left90()
	change := this.Combin()
	this.Left90()
	return change
}

func (this Go2048) GoDown() bool {
	this.Right90()
	change := this.Combin()
	this.Right90()
	return change
}

func (this Go2048) GoLeft() bool {
	change := this.Combin()
	return change
}

func (this Go2048) GoRight() bool {
	this.Mirror()
	change := this.Combin()
	this.Mirror()
	return change
}

func (this Go2048) CheckWinOrLose() bool {
	for x, row := range this.Martix {
		for y, _ := range row {
			if this.Martix[x][y] == 0 {
				return true
				//true = Have not been dead yet
			}
		}
	}
	return false
	//false = Lose
}

func (this Go2048) Init_termbox(x, y int) error {
	fg := termbox.ColorYellow
	bg := termbox.ColorBlack
	err := termbox.Clear(fg, bg)
	if err != nil {
		return err
	}
	str := "Enter: restart game"
	for n, c := range str {
		termbox.SetCell(x+n, y-1, c, fg, bg)
	}

	str = "ESC: quit game" + "  Step: " + strconv.Itoa(step)
	for n, c := range str {
		termbox.SetCell(x+n, y-2, c, fg, bg)
	}

	str = "Play with Arrow Key"
	for n, c := range str {
		termbox.SetCell(x+n, y-3, c, fg, bg)
	}

	fg = termbox.ColorBlack
	bg = termbox.ColorGreen
	for i := 0; i <= len(this.Martix); i++ {
		for t := 0; t < 6*len(this.Martix); t++ {
			if t%6 != 0 {
				termbox.SetCell(x+t, y+i*2, '-', fg, bg)
			}
		}
		for t := 0; t <= 2*len(this.Martix); t++ {
			if t%2 == 0 {
				termbox.SetCell(x+i*6, y+t, '+', fg, bg)
			} else {
				termbox.SetCell(x+i*6, y+t, '|', fg, bg)
			}
		}
	}

	for i, row := range this.Martix {
		for j, _ := range row {
			if this.Martix[i][j] > 0 {
				str := fmt.Sprintf("%-5d", this.Martix[i][j])
				for n, char := range str {
					if output_mode == termbox.Output256 {
						termbox.SetCell(x+j*6+1+n, y+i*2+1, char, 0x10+termbox.Attribute(this.Martix[i][j]%256), 0xe0-termbox.Attribute(this.Martix[i][j]*2%256))
					} else {
						termbox.SetCell(x+j*6+1+n, y+i*2+1, char, termbox.ColorWhite, colorTable[int(math.Log2(float64(this.Martix[i][j])))])
					}
				}
			}
		}
	}
	return termbox.Flush()
}

func converPrintStr(x, y int, str string, fg, bg termbox.Attribute) error {
	xx := x
	for n, c := range str {
		if c == '\n' {
			y++
			xx = x - n - 1
		}
		termbox.SetCell(xx+n, y, c, fg, bg)
	}
	return termbox.Flush()
}

func (t *Go2048) ListernKey() chan termbox.Event {
	//ev := termbox.PollEvent()
	event_queue := make(chan termbox.Event)
	go func() {
		for {
			event_queue <- termbox.PollEvent() // 开始监听键盘事件
		}
	}()
	return event_queue
}

func (t *Go2048) ActionAndReturnKey(event_queue chan termbox.Event) termbox.Key {
	for {
		ev := <-event_queue
		changed := false

		switch ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyArrowUp:
				changed = t.GoUp()
			case termbox.KeyArrowDown:
				changed = t.GoDown()
			case termbox.KeyArrowLeft:
				changed = t.GoLeft()
			case termbox.KeyArrowRight:
				changed = t.GoRight()
			case termbox.KeyEsc, termbox.KeyEnter:
				changed = true
			default:
				changed = false
			}

			// 如果元素的值没有任何更改，则从新开始循环
			if !changed && t.CheckWinOrLose() {
				continue
			}

		case termbox.EventResize:
			x, y := termbox.Size()
			t.Init_termbox(x/2-10, y/2-4)
			continue
		case termbox.EventError:
			panic(ev.Err)
		}
		return ev.Key
	}
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()
	x, y := termbox.Size()

	output_mode = termbox.SetOutputMode(termbox.Output256)

	martix.Init()
	var t Go2048
	t.Martix, _ = martix.Init_martix(MAX_LEN)
	t.AddNum(Add_NUM)
	step = 0
	ch := t.ListernKey()
	defer close(ch)

	for {
		t.Init_termbox(x/2-10, y/2-4)

		key := t.ActionAndReturnKey(ch)

		if t.CheckWinOrLose() == false {
			str := "Lose!"
			strlen := len(str)
			converPrintStr(x/2-strlen/2, y/2, str, termbox.ColorBlack, termbox.ColorRed)
			for {
				key = t.ActionAndReturnKey(ch)
				if key == termbox.KeyEnter || key == termbox.KeyEsc {
					break
				}
			}
		}

		if key == termbox.KeyEnter {
			t.Martix, _ = martix.Init_martix(MAX_LEN)
			step = -1
		}
		if key == termbox.KeyEsc {
			return
		}

		step++

		t.Init_termbox(x/2-10, y/2-4)
		time.Sleep(500 * time.Millisecond)
		t.AddNum(Add_NUM)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"fmt"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/grafana/metrictank/api/middleware"
	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/api/response"
	"github.com/grafana/metrictank/consolidation"
	"github.com/grafana/metrictank/mdata"
	"github.com/grafana/metrictank/util"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
	"github.com/raintank/worldping-api/pkg/log"
	schema "gopkg.in/raintank/schema.v1"
)

type orgID string

type status string

const (
	statusSuccess status = "success"
	statusError   status = "error"
)

type errorType string

const (
	errorNone        errorType = ""
	errorTimeout     errorType = "timeout"
	errorCanceled    errorType = "canceled"
	errorExec        errorType = "execution"
	errorBadData     errorType = "bad_data"
	errorInternal    errorType = "internal"
	errorUnavailable errorType = "unavailable"
)

type prometheusQueryResult struct {
	Status    status      `json:"status"`
	Data      interface{} `json:"data,omitempty"`
	ErrorType errorType   `json:"errorType,omitempty"`
	Error     string      `json:"error,omitempty"`
}

type prometheusQueryData struct {
	ResultType promql.ValueType `json:"resultType"`
	Result     promql.Value     `json:"result"`
}

type querier struct {
	Server
	from  uint32
	to    uint32
	OrgID int
	ctx   context.Context
}

func (s *Server) labelValues(ctx *middleware.Context) {
	name := ctx.Params(":name")

	if !model.LabelNameRE.MatchString(name) {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("invalid label name: %v", name)))
		return
	}

	q, err := s.Querier(context.WithValue(context.Background(), orgID("org-id"), ctx.OrgId), 0, 0)

	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("unable to create queryable: %v", err)))
		return
	}
	defer q.Close()

	vals, err := q.LabelValues(name)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("error: %v", err)))
		return
	}

	response.Write(ctx, response.NewJson(200, prometheusQueryResult{Status: "success", Data: vals}, ""))
	return
}

func (s *Server) queryRange(ctx *middleware.Context, request models.PrometheusQueryRange) {
	start, err := parseTime(request.Start)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse start time: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	end, err := parseTime(request.End)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse end time: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	step, err := parseDuration(request.Step)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse step duration: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	if step <= 0 {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("step value is less than or equal to zero: %v", step),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	qry, err := s.PromQueryEngine.NewRangeQuery(request.Query, start, end, step)

	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("query failed: %v", err),
			ErrorType: errorExec,
		}, ""))
		return
	}

	newCtx := context.WithValue(ctx.Req.Context(), orgID("org-id"), ctx.OrgId)
	res := qry.Exec(newCtx)

	if res.Err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("query failed: %v", res.Err),
			ErrorType: errorExec,
		}, ""))
		return
	}

	response.Write(ctx, response.NewJson(200,
		prometheusQueryResult{
			Data: prometheusQueryData{
				ResultType: res.Value.Type(),
				Result:     res.Value,
			},
			Status: statusSuccess,
		},
		"",
	))
}

func (s *Server) querySeries(ctx *middleware.Context, request models.PrometheusSeriesQuery) {
	start, err := parseTime(request.Start)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("could not parse start time: %v", err)))
		return
	}

	end, err := parseTime(request.End)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("could not parse end time: %v", err)))
		return
	}

	_, err = s.Querier(ctx.Req.Context(), timestamp.FromTime(start), timestamp.FromTime(end))
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusInternalServerError, fmt.Sprintf("query failed: %v", err)))
		return
	}

	response.Write(ctx, response.NewError(200, "test"))
}

func (s *Server) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
	return &querier{
		*s,
		uint32(mint / 1000), //Convert from NS to S
		uint32(maxt / 1000), //TODO abstract this out into a separate function that is more accurate
		ctx.Value(orgID("org-id")).(int),
		ctx,
	}, nil
}

func parseTime(s string) (time.Time, error) {
	if t, err := strconv.ParseFloat(s, 64); err == nil {
		s, ns := math.Modf(t)
		return time.Unix(int64(s), int64(ns*float64(time.Second))), nil
	}
	if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
		return t, nil
	}
	return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s)
}

func parseDuration(s string) (time.Duration, error) {
	if d, err := strconv.ParseFloat(s, 64); err == nil {
		ts := d * float64(time.Second)
		if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
			return 0, fmt.Errorf("cannot parse %q to a valid duration. It overflows int64", s)
		}
		return time.Duration(ts), nil
	}
	if d, err := model.ParseDuration(s); err == nil {
		return time.Duration(d), nil
	}
	return 0, fmt.Errorf("cannot parse %q to a valid duration", s)
}

// Select returns a set of series that matches the given label matchers.
func (q *querier) Select(matchers ...*labels.Matcher) (storage.SeriesSet, error) {
	minFrom := uint32(math.MaxUint32)
	var maxTo uint32
	var target string
	var reqs []models.Req

	expressions := []string{}
	for _, matcher := range matchers {
		if matcher.Name == model.MetricNameLabel {
			matcher.Name = "name"
		}
		expressions = append(expressions, fmt.Sprintf("%s%s%s", matcher.Name, matcher.Type, matcher.Value))
	}

	series, err := q.clusterFindByTag(q.ctx, q.OrgID, expressions, 0)
	if err != nil {
		return nil, err
	}

	minFrom = util.Min(minFrom, q.from)
	maxTo = util.Max(maxTo, q.to)
	for _, s := range series {
		for _, metric := range s.Series {
			for _, archive := range metric.Defs {
				consReq := consolidation.None
				fn := mdata.Aggregations.Get(archive.AggId).AggregationMethod[0]
				cons := consolidation.Consolidator(fn)

				newReq := models.NewReq(archive.Id, archive.NameWithTags(), target, q.from, q.to, math.MaxUint32, uint32(archive.Interval), cons, consReq, s.Node, archive.SchemaId, archive.AggId)
				reqs = append(reqs, newReq)
			}
		}
	}

	select {
	case <-q.ctx.Done():
		//request canceled
		return nil, fmt.Errorf("request canceled")
	default:
	}

	reqRenderSeriesCount.Value(len(reqs))
	if len(reqs) == 0 {
		return nil, fmt.Errorf("no series found")
	}

	// note: if 1 series has a movingAvg that requires a long time range extension, it may push other reqs into another archive. can be optimized later
	reqs, _, _, err = alignRequests(uint32(time.Now().Unix()), minFrom, maxTo, reqs)
	if err != nil {
		log.Error(3, "HTTP Render alignReq error: %s", err)
		return nil, err
	}

	out, err := q.getTargets(q.ctx, reqs)
	if err != nil {
		log.Error(3, "HTTP Render %s", err.Error())
		return nil, err
	}

	seriesSet, err := SeriesToSeriesSet(out)

	return seriesSet, err
}

// LabelValues returns all potential values for a label name.
func (q *querier) LabelValues(name string) ([]string, error) {
	result, err := q.MetricIndex.FindTagValues(q.OrgID, name, "", []string{}, 0, 100000)
	if err != nil {
		return nil, err
	}
	if result == nil {
		result = []string{}
	}
	return result, nil
}

// Close releases the resources of the Querier.
func (q *querier) Close() error {
	return nil
}

func SeriesToSeriesSet(out []models.Series) (*models.PrometheusSeriesSet, error) {
	series := []storage.Series{}
	for _, metric := range out {
		series = append(series, models.NewPrometheusSeries(buildTagSet(metric.Target), dataPointsToPrometheusSamplePairs(metric.Datapoints)))
	}
	return models.NewPrometheusSeriesSet(series), nil
}

func dataPointsToPrometheusSamplePairs(data []schema.Point) []model.SamplePair {
	samples := []model.SamplePair{}
	for _, point := range data {
		if math.IsNaN(point.Val) {
			continue
		}
		samples = append(samples, model.SamplePair{
			Timestamp: model.Time(int64(point.Ts) * 1000),
			Value:     model.SampleValue(point.Val),
		})
	}
	return samples
}

// Turns graphite target name into prometheus graphite name
// TODO models.Series should provide a map of tags but the one returned from getTargets doesn't
func buildTagSet(name string) map[string]string {
	labelMap := map[string]string{}
	tags := strings.Split(name, ";")
	labelMap["__name__"] = tags[0]
	for _, lbl := range tags[1:] {
		kv := strings.Split(lbl, "=")
		labelMap[kv[0]] = kv[1]
	}
	return labelMap
}
<commit_msg>update error return values to better match api<commit_after>package api

import (
	"context"
	"fmt"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/grafana/metrictank/api/middleware"
	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/api/response"
	"github.com/grafana/metrictank/consolidation"
	"github.com/grafana/metrictank/mdata"
	"github.com/grafana/metrictank/util"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/storage"
	"github.com/raintank/worldping-api/pkg/log"
	schema "gopkg.in/raintank/schema.v1"
)

type orgID string

type status string

const (
	statusSuccess status = "success"
	statusError   status = "error"
)

type errorType string

const (
	errorNone        errorType = ""
	errorTimeout     errorType = "timeout"
	errorCanceled    errorType = "canceled"
	errorExec        errorType = "execution"
	errorBadData     errorType = "bad_data"
	errorInternal    errorType = "internal"
	errorUnavailable errorType = "unavailable"
)

type prometheusQueryResult struct {
	Status    status      `json:"status"`
	Data      interface{} `json:"data,omitempty"`
	ErrorType errorType   `json:"errorType,omitempty"`
	Error     string      `json:"error,omitempty"`
}

type prometheusQueryData struct {
	ResultType promql.ValueType `json:"resultType"`
	Result     promql.Value     `json:"result"`
}

type querier struct {
	Server
	from  uint32
	to    uint32
	OrgID int
	ctx   context.Context
}

func (s *Server) labelValues(ctx *middleware.Context) {
	name := ctx.Params(":name")

	if !model.LabelNameRE.MatchString(name) {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("unable to create label name: %v", name),
			ErrorType: errorExec,
		}, ""))
		return
	}

	q, err := s.Querier(context.WithValue(context.Background(), orgID("org-id"), ctx.OrgId), 0, 0)

	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("unable to create queryable: %v", err),
			ErrorType: errorExec,
		}, ""))
		return
	}
	defer q.Close()

	vals, err := q.LabelValues(name)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("query failed: %v", err),
			ErrorType: errorExec,
		}, ""))
		return
	}

	response.Write(ctx, response.NewJson(200, prometheusQueryResult{Status: "success", Data: vals}, ""))
	return
}

func (s *Server) queryRange(ctx *middleware.Context, request models.PrometheusQueryRange) {
	start, err := parseTime(request.Start)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse start time: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	end, err := parseTime(request.End)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse end time: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	step, err := parseDuration(request.Step)
	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("could not parse step duration: %v", err),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	if step <= 0 {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("step value is less than or equal to zero: %v", step),
			ErrorType: errorBadData,
		}, ""))
		return
	}

	qry, err := s.PromQueryEngine.NewRangeQuery(request.Query, start, end, step)

	if err != nil {
		response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
			Status:    statusError,
			Error:     fmt.Sprintf("query failed: %v", err),
			ErrorType: errorExec,
		}, ""))
		return
	}

	newCtx := context.WithValue(ctx.Req.Context(), orgID("org-id"), ctx.OrgId)
	res := qry.Exec(newCtx)

	if res.Err != nil {
		if res.Err != nil {
			switch res.Err.(type) {
			case promql.ErrQueryCanceled:
				response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
					Status:    statusError,
					Error:     fmt.Sprintf("query failed: %v", res.Err),
					ErrorType: errorCanceled,
				}, ""))
			case promql.ErrQueryTimeout:
				response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
					Status:    statusError,
					Error:     fmt.Sprintf("query failed: %v", res.Err),
					ErrorType: errorTimeout,
				}, ""))
			case promql.ErrStorage:
				response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
					Status:    statusError,
					Error:     fmt.Sprintf("query failed: %v", res.Err),
					ErrorType: errorInternal,
				}, ""))
			}
			response.Write(ctx, response.NewJson(http.StatusInternalServerError, prometheusQueryResult{
				Status:    statusError,
				Error:     fmt.Sprintf("query failed: %v", res.Err),
				ErrorType: errorExec,
			}, ""))
		}
		return
	}

	response.Write(ctx, response.NewJson(200,
		prometheusQueryResult{
			Data: prometheusQueryData{
				ResultType: res.Value.Type(),
				Result:     res.Value,
			},
			Status: statusSuccess,
		},
		"",
	))
}

func (s *Server) querySeries(ctx *middleware.Context, request models.PrometheusSeriesQuery) {
	start, err := parseTime(request.Start)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("could not parse start time: %v", err)))
		return
	}

	end, err := parseTime(request.End)
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusBadRequest, fmt.Sprintf("could not parse end time: %v", err)))
		return
	}

	_, err = s.Querier(ctx.Req.Context(), timestamp.FromTime(start), timestamp.FromTime(end))
	if err != nil {
		response.Write(ctx, response.NewError(http.StatusInternalServerError, fmt.Sprintf("query failed: %v", err)))
		return
	}

	response.Write(ctx, response.NewError(200, "test"))
}

func (s *Server) Querier(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
	return &querier{
		*s,
		uint32(mint / 1000), //Convert from NS to S
		uint32(maxt / 1000), //TODO abstract this out into a separate function that is more accurate
		ctx.Value(orgID("org-id")).(int),
		ctx,
	}, nil
}

func parseTime(s string) (time.Time, error) {
	if t, err := strconv.ParseFloat(s, 64); err == nil {
		s, ns := math.Modf(t)
		return time.Unix(int64(s), int64(ns*float64(time.Second))), nil
	}
	if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
		return t, nil
	}
	return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s)
}

func parseDuration(s string) (time.Duration, error) {
	if d, err := strconv.ParseFloat(s, 64); err == nil {
		ts := d * float64(time.Second)
		if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
			return 0, fmt.Errorf("cannot parse %q to a valid duration. It overflows int64", s)
		}
		return time.Duration(ts), nil
	}
	if d, err := model.ParseDuration(s); err == nil {
		return time.Duration(d), nil
	}
	return 0, fmt.Errorf("cannot parse %q to a valid duration", s)
}

// Select returns a set of series that matches the given label matchers.
func (q *querier) Select(matchers ...*labels.Matcher) (storage.SeriesSet, error) {
	minFrom := uint32(math.MaxUint32)
	var maxTo uint32
	var target string
	var reqs []models.Req

	expressions := []string{}
	for _, matcher := range matchers {
		if matcher.Name == model.MetricNameLabel {
			matcher.Name = "name"
		}
		expressions = append(expressions, fmt.Sprintf("%s%s%s", matcher.Name, matcher.Type, matcher.Value))
	}

	series, err := q.clusterFindByTag(q.ctx, q.OrgID, expressions, 0)
	if err != nil {
		return nil, err
	}

	minFrom = util.Min(minFrom, q.from)
	maxTo = util.Max(maxTo, q.to)
	for _, s := range series {
		for _, metric := range s.Series {
			for _, archive := range metric.Defs {
				consReq := consolidation.None
				fn := mdata.Aggregations.Get(archive.AggId).AggregationMethod[0]
				cons := consolidation.Consolidator(fn)

				newReq := models.NewReq(archive.Id, archive.NameWithTags(), target, q.from, q.to, math.MaxUint32, uint32(archive.Interval), cons, consReq, s.Node, archive.SchemaId, archive.AggId)
				reqs = append(reqs, newReq)
			}
		}
	}

	select {
	case <-q.ctx.Done():
		//request canceled
		return nil, fmt.Errorf("request canceled")
	default:
	}

	reqRenderSeriesCount.Value(len(reqs))
	if len(reqs) == 0 {
		return nil, fmt.Errorf("no series found")
	}

	// note: if 1 series has a movingAvg that requires a long time range extension, it may push other reqs into another archive. can be optimized later
	reqs, _, _, err = alignRequests(uint32(time.Now().Unix()), minFrom, maxTo, reqs)
	if err != nil {
		log.Error(3, "HTTP Render alignReq error: %s", err)
		return nil, err
	}

	out, err := q.getTargets(q.ctx, reqs)
	if err != nil {
		log.Error(3, "HTTP Render %s", err.Error())
		return nil, err
	}

	seriesSet, err := SeriesToSeriesSet(out)

	return seriesSet, err
}

// LabelValues returns all potential values for a label name.
func (q *querier) LabelValues(name string) ([]string, error) {
	result, err := q.MetricIndex.FindTagValues(q.OrgID, name, "", []string{}, 0, 100000)
	if err != nil {
		return nil, err
	}
	if result == nil {
		result = []string{}
	}
	return result, nil
}

// Close releases the resources of the Querier.
func (q *querier) Close() error {
	return nil
}

func SeriesToSeriesSet(out []models.Series) (*models.PrometheusSeriesSet, error) {
	series := []storage.Series{}
	for _, metric := range out {
		series = append(series, models.NewPrometheusSeries(buildTagSet(metric.Target), dataPointsToPrometheusSamplePairs(metric.Datapoints)))
	}
	return models.NewPrometheusSeriesSet(series), nil
}

func dataPointsToPrometheusSamplePairs(data []schema.Point) []model.SamplePair {
	samples := []model.SamplePair{}
	for _, point := range data {
		if math.IsNaN(point.Val) {
			continue
		}
		samples = append(samples, model.SamplePair{
			Timestamp: model.Time(int64(point.Ts) * 1000),
			Value:     model.SampleValue(point.Val),
		})
	}
	return samples
}

// Turns graphite target name into prometheus graphite name
// TODO models.Series should provide a map of tags but the one returned from getTargets doesn't
func buildTagSet(name string) map[string]string {
	labelMap := map[string]string{}
	tags := strings.Split(name, ";")
	labelMap["__name__"] = tags[0]
	for _, lbl := range tags[1:] {
		kv := strings.Split(lbl, "=")
		labelMap[kv[0]] = kv[1]
	}
	return labelMap
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/bgentry/heroku-go"
	"github.com/gorilla/mux"
	"github.com/remind101/empire/empire"
)

// newHkRelease converts an empire Release to a heroku Release.
func newHkRelease(r *empire.Release) *heroku.Release {
	return &heroku.Release{
		Id:      string(r.ID),
		Version: int(r.Ver),
		Slug: &struct {
			Id string `json:"id"`
		}{
			Id: string(r.SlugID),
		},
		Description: r.Description,
		CreatedAt:   r.CreatedAt,
	}
}

type GetRelease struct {
	Empire
}

func (h *GetRelease) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
	a, err := findApp(r, h)
	if err != nil {
		return err
	}

	vars := mux.Vars(r)
	i, err := strconv.Atoi(vars["version"])
	if err != nil {
		return err
	}

	vers := empire.ReleaseVersion(i)
	rel, err := h.ReleasesFindByAppAndVersion(a, vers)
	if err != nil {
		return err
	}

	w.WriteHeader(200)
	return Encode(w, newHkRelease(rel))
}

type GetReleases struct {
	Empire
}

func (h *GetReleases) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
	a, err := findApp(r, h)
	if err != nil {
		return err
	}

	rels, err := h.ReleasesFindByApp(a)
	if err != nil {
		return err
	}

	w.WriteHeader(200)
	return Encode(w, rels)
}

type PostReleases struct {
	Empire
}

type PostReleasesForm struct {
	Version string `json:"release"`
}

func (p *PostReleasesForm) ReleaseVersion() (empire.ReleaseVersion, error) {
	var r empire.ReleaseVersion
	i, err := strconv.Atoi(p.Version)
	if err != nil {
		return r, err
	}

	return empire.ReleaseVersion(i), nil
}

func (h *PostReleases) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
	var form PostReleasesForm

	if err := Decode(r, &form); err != nil {
		return err
	}

	version, err := form.ReleaseVersion()
	if err != nil {
		return err
	}

	app, err := findApp(r, h)
	if err != nil {
		return err
	}

	// Find previous release
	rel, err := h.ReleasesFindByAppAndVersion(app, version)
	if err != nil {
		return err
	}

	if rel == nil {
		return ErrNotFound
	}

	// Find config
	config, err := h.ConfigsFind(rel.ConfigID)
	if err != nil {
		return err
	}

	if config == nil {
		return ErrNotFound
	}

	// Find slug
	slug, err := h.SlugsFind(rel.SlugID)
	if err != nil {
		return err
	}

	if slug == nil {
		return ErrNotFound
	}

	// Create new release
	desc := fmt.Sprintf("Rollback to v%d", version)
	release, err := h.ReleasesCreate(app, config, slug, desc)
	if err != nil {
		return err
	}

	w.WriteHeader(200)
	return Encode(w, release)
}
<commit_msg>Alias Release as heroku.Release<commit_after>package server

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/bgentry/heroku-go"
	"github.com/gorilla/mux"
	"github.com/remind101/empire/empire"
)

type Release heroku.Release

// newRelease decorates an empire.Release as a heroku.Release.
func newRelease(r *empire.Release) *Release {
	return &Release{
		Id:      string(r.ID),
		Version: int(r.Ver),
		Slug: &struct {
			Id string `json:"id"`
		}{
			Id: string(r.SlugID),
		},
		Description: r.Description,
		CreatedAt:   r.CreatedAt,
	}
}

type GetRelease struct {
	Empire
}

func (h *GetRelease) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
	a, err := findApp(r, h)
	if err != nil {
		return err
	}

	vars := mux.Vars(r)
	i, err := strconv.Atoi(vars["version"])
	if err != nil {
		return err
	}

	vers := empire.ReleaseVersion(i)
	rel, err := h.ReleasesFindByAppAndVersion(a, vers)
	if err != nil {
		return err
	}

	w.WriteHeader(200)
	return Encode(w, newRelease(rel))
}

type GetReleases struct {
	Empire
}

func (h *GetReleases) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
	a, err := findApp(r, h)
	if err != nil {
		return err
	}

	rels, err := h.ReleasesFindByApp(a)
	if err != nil {
		return err
	}

	w.WriteHeader(200)
	return Encode(w, rels)
}

type PostReleases struct {
	Empire
}

type PostReleasesForm struct {
	Version string `json:"release"`
}

func (p *PostReleasesForm) ReleaseVersion() (empire.ReleaseVersion, error) {
	var r empire.ReleaseVersion
	i, err := strconv.Atoi(p.Version)
	if err != nil {
		return r, err
	}

	return empire.ReleaseVersion(i), nil
}

func (h *PostReleases) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
	var form PostReleasesForm

	if err := Decode(r, &form); err != nil {
		return err
	}

	version, err := form.ReleaseVersion()
	if err != nil {
		return err
	}

	app, err := findApp(r, h)
	if err != nil {
		return err
	}

	// Find previous release
	rel, err := h.ReleasesFindByAppAndVersion(app, version)
	if err != nil {
		return err
	}

	if rel == nil {
		return ErrNotFound
	}

	// Find config
	config, err := h.ConfigsFind(rel.ConfigID)
	if err != nil {
		return err
	}

	if config == nil {
		return ErrNotFound
	}

	// Find slug
	slug, err := h.SlugsFind(rel.SlugID)
	if err != nil {
		return err
	}

	if slug == nil {
		return ErrNotFound
	}

	// Create new release
	desc := fmt.Sprintf("Rollback to v%d", version)
	release, err := h.ReleasesCreate(app, config, slug, desc)
	if err != nil {
		return err
	}

	w.WriteHeader(200)
	return Encode(w, release)
}
<|endoftext|>


<commit_before>package gateway

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/TykTechnologies/tyk/apidef"

	"github.com/TykTechnologies/tyk/ctx"
	"github.com/TykTechnologies/tyk/goplugin"
	"github.com/TykTechnologies/tyk/request"
	"github.com/sirupsen/logrus"
)

// customResponseWriter is a wrapper around standard http.ResponseWriter
// plus it tracks if response was sent and what status code was sent
type customResponseWriter struct {
	http.ResponseWriter
	responseSent   bool
	statusCodeSent int
	copyData       bool
	data           []byte
	dataLength     int64
}

func (w *customResponseWriter) Write(b []byte) (int, error) {
	w.responseSent = true
	if w.statusCodeSent == 0 {
		w.statusCodeSent = http.StatusOK // no WriteHeader was called so it will be set to StatusOK in actual ResponseWriter
	}

	// send actual data
	num, err := w.ResponseWriter.Write(b)

	// copy data sent
	if w.copyData {
		if w.data == nil {
			w.data = make([]byte, num)
			copy(w.data, b[:num])
		} else {
			w.data = append(w.data, b[:num]...)
		}
	}

	// count how many bytes we sent
	w.dataLength += int64(num)

	return num, err
}

func (w *customResponseWriter) WriteHeader(statusCode int) {
	w.responseSent = true
	w.statusCodeSent = statusCode
	w.ResponseWriter.WriteHeader(statusCode)
}

func (w *customResponseWriter) getHttpResponse(r *http.Request) *http.Response {
	// craft response on the fly for analytics
	httpResponse := &http.Response{
		Status:        http.StatusText(w.statusCodeSent),
		StatusCode:    w.statusCodeSent,
		Header:        w.ResponseWriter.Header(), // TODO: worth to think about trailer headers
		Proto:         r.Proto,
		ProtoMajor:    r.ProtoMajor,
		ProtoMinor:    r.ProtoMinor,
		Request:       r,
		ContentLength: w.dataLength,
	}
	if w.copyData {
		httpResponse.Body = ioutil.NopCloser(bytes.NewReader(w.data))
	}

	return httpResponse
}

// GoPluginMiddleware is a generic middleware that will execute Go-plugin code before continuing
type GoPluginMiddleware struct {
	BaseMiddleware
	Path           string // path to .so file
	SymbolName     string // function symbol to look up
	handler        http.HandlerFunc
	logger         *logrus.Entry
	successHandler *SuccessHandler // to record analytics
	Meta           apidef.GoPluginMeta
	APILevel       bool
}

func (m *GoPluginMiddleware) Name() string {
	return "GoPluginMiddleware: " + m.Path + ":" + m.SymbolName
}

func (m *GoPluginMiddleware) EnabledForSpec() bool {

	// global go plugins
	if m.Path != "" && m.SymbolName != "" {
		m.loadPlugin()
		return true
	}

	// per path go plugins
	for _, version := range m.Spec.VersionData.Versions {
		if len(version.ExtendedPaths.GoPlugin) > 0 {
			return true
		}
	}

	return false
}

func (m *GoPluginMiddleware) loadPlugin() bool {
	m.logger = log.WithFields(logrus.Fields{
		"mwPath":       m.Path,
		"mwSymbolName": m.SymbolName,
	})

	if m.handler != nil {
		m.logger.Info("Go-plugin middleware is already initialized")
		return true
	}

	// try to load plugin
	var err error
	if m.handler, err = goplugin.GetHandler(m.Path, m.SymbolName); err != nil {
		m.logger.WithError(err).Error("Could not load Go-plugin")
		return false
	}

	// to record 2XX hits in analytics
	m.successHandler = &SuccessHandler{BaseMiddleware: m.BaseMiddleware}
	return true
}

func (m *GoPluginMiddleware) goPluginConfigFromRequest(r *http.Request) {

	version, _ := m.Spec.Version(r)
	versionPaths := m.Spec.RxPaths[version.Name]

	found, perPathPerMethodGoPlugin := m.Spec.CheckSpecMatchesStatus(r, versionPaths, GoPlugin)
	if found {
		m.handler = perPathPerMethodGoPlugin.(*GoPluginMiddleware).handler
		m.Meta = perPathPerMethodGoPlugin.(*GoPluginMiddleware).Meta
		m.Path = perPathPerMethodGoPlugin.(*GoPluginMiddleware).Path
		m.SymbolName = perPathPerMethodGoPlugin.(*GoPluginMiddleware).SymbolName
		m.logger = perPathPerMethodGoPlugin.(*GoPluginMiddleware).logger
	}
}
func (m *GoPluginMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, conf interface{}) (err error, respCode int) {

	// is there a go plugin per path - we copy the handler etc from the urlspec if we find one
	if !m.APILevel {
		m.goPluginConfigFromRequest(r)
	}

	// make sure tyk recover in case Go-plugin function panics
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
			respCode = http.StatusInternalServerError
			m.logger.WithError(err).Error("Recovered from panic while running Go-plugin middleware func")
		}
	}()

	// prepare data to call Go-plugin function

	// make sure request's body can be re-read again
	nopCloseRequestBody(r)

	// wrap ResponseWriter to check if response was sent
	rw := &customResponseWriter{
		ResponseWriter: w,
		copyData:       recordDetail(r, m.Spec),
	}

	// call Go-plugin function
	t1 := time.Now()

	// Inject definition into request context:
	ctx.SetDefinition(r, m.Spec.APIDefinition)

	m.handler(rw, r)

	// calculate latency
	ms := DurationToMillisecond(time.Since(t1))
	m.logger.WithField("ms", ms).Debug("Go-plugin request processing took")

	// check if response was sent
	if rw.responseSent {
		// check if response code was an error one
		switch {
		case rw.statusCodeSent == http.StatusForbidden:
			m.logger.WithError(err).Error("Authentication error in Go-plugin middleware func")
			m.Base().FireEvent(EventAuthFailure, EventKeyFailureMeta{
				EventMetaDefault: EventMetaDefault{Message: "Auth Failure", OriginatingRequest: EncodeRequestToEvent(r)},
				Path:             r.URL.Path,
				Origin:           request.RealIP(r),
				Key:              "n/a",
			})
			fallthrough
		case rw.statusCodeSent >= http.StatusBadRequest:
			// base middleware will report this error to analytics if needed
			respCode = rw.statusCodeSent
			err = fmt.Errorf("plugin function sent error response code: %d", rw.statusCodeSent)
			m.logger.WithError(err).Error("Failed to process request with Go-plugin middleware func")
		default:
			// record 2XX to analytics
			m.successHandler.RecordHit(r, Latency{Total: int64(ms)}, rw.statusCodeSent, rw.getHttpResponse(r))

			// no need to continue passing this request down to reverse proxy
			respCode = mwStatusRespond
		}
	} else {
		respCode = http.StatusOK
	}

	return
}
<commit_msg>mw_go_plugin: fix middleware references when using per-path Go plugins (#3658)<commit_after>package gateway

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/TykTechnologies/tyk/apidef"

	"github.com/TykTechnologies/tyk/ctx"
	"github.com/TykTechnologies/tyk/goplugin"
	"github.com/TykTechnologies/tyk/request"
	"github.com/sirupsen/logrus"
)

// customResponseWriter is a wrapper around standard http.ResponseWriter
// plus it tracks if response was sent and what status code was sent
type customResponseWriter struct {
	http.ResponseWriter
	responseSent   bool
	statusCodeSent int
	copyData       bool
	data           []byte
	dataLength     int64
}

func (w *customResponseWriter) Write(b []byte) (int, error) {
	w.responseSent = true
	if w.statusCodeSent == 0 {
		w.statusCodeSent = http.StatusOK // no WriteHeader was called so it will be set to StatusOK in actual ResponseWriter
	}

	// send actual data
	num, err := w.ResponseWriter.Write(b)

	// copy data sent
	if w.copyData {
		if w.data == nil {
			w.data = make([]byte, num)
			copy(w.data, b[:num])
		} else {
			w.data = append(w.data, b[:num]...)
		}
	}

	// count how many bytes we sent
	w.dataLength += int64(num)

	return num, err
}

func (w *customResponseWriter) WriteHeader(statusCode int) {
	w.responseSent = true
	w.statusCodeSent = statusCode
	w.ResponseWriter.WriteHeader(statusCode)
}

func (w *customResponseWriter) getHttpResponse(r *http.Request) *http.Response {
	// craft response on the fly for analytics
	httpResponse := &http.Response{
		Status:        http.StatusText(w.statusCodeSent),
		StatusCode:    w.statusCodeSent,
		Header:        w.ResponseWriter.Header(), // TODO: worth to think about trailer headers
		Proto:         r.Proto,
		ProtoMajor:    r.ProtoMajor,
		ProtoMinor:    r.ProtoMinor,
		Request:       r,
		ContentLength: w.dataLength,
	}
	if w.copyData {
		httpResponse.Body = ioutil.NopCloser(bytes.NewReader(w.data))
	}

	return httpResponse
}

// GoPluginMiddleware is a generic middleware that will execute Go-plugin code before continuing
type GoPluginMiddleware struct {
	BaseMiddleware
	Path           string // path to .so file
	SymbolName     string // function symbol to look up
	handler        http.HandlerFunc
	logger         *logrus.Entry
	successHandler *SuccessHandler // to record analytics
	Meta           apidef.GoPluginMeta
	APILevel       bool
}

func (m *GoPluginMiddleware) Name() string {
	return "GoPluginMiddleware: " + m.Path + ":" + m.SymbolName
}

func (m *GoPluginMiddleware) EnabledForSpec() bool {
	// global go plugins
	if m.Path != "" && m.SymbolName != "" {
		m.loadPlugin()
		return true
	}

	// per path go plugins
	for _, version := range m.Spec.VersionData.Versions {
		if len(version.ExtendedPaths.GoPlugin) > 0 {
			return true
		}
	}

	return false
}

func (m *GoPluginMiddleware) loadPlugin() bool {
	m.logger = log.WithFields(logrus.Fields{
		"mwPath":       m.Path,
		"mwSymbolName": m.SymbolName,
	})

	if m.handler != nil {
		m.logger.Info("Go-plugin middleware is already initialized")
		return true
	}

	// try to load plugin
	var err error
	if m.handler, err = goplugin.GetHandler(m.Path, m.SymbolName); err != nil {
		m.logger.WithError(err).Error("Could not load Go-plugin")
		return false
	}

	// to record 2XX hits in analytics
	m.successHandler = &SuccessHandler{BaseMiddleware: m.BaseMiddleware}
	return true
}

func (m *GoPluginMiddleware) goPluginFromRequest(r *http.Request) (*GoPluginMiddleware, bool) {
	version, _ := m.Spec.Version(r)
	versionPaths := m.Spec.RxPaths[version.Name]

	found, perPathPerMethodGoPlugin := m.Spec.CheckSpecMatchesStatus(r, versionPaths, GoPlugin)
	return perPathPerMethodGoPlugin.(*GoPluginMiddleware), found
}
func (m *GoPluginMiddleware) ProcessRequest(w http.ResponseWriter, r *http.Request, conf interface{}) (err error, respCode int) {
	// if a Go plugin is found for this path, override the base handler and logger:
	logger := m.logger
	handler := m.handler
	if !m.APILevel {
		if pluginMw, found := m.goPluginFromRequest(r); found {
			logger = pluginMw.logger
			handler = pluginMw.handler
		}
	}

	// make sure tyk recover in case Go-plugin function panics
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("%v", e)
			respCode = http.StatusInternalServerError
			logger.WithError(err).Error("Recovered from panic while running Go-plugin middleware func")
		}
	}()

	// prepare data to call Go-plugin function

	// make sure request's body can be re-read again
	nopCloseRequestBody(r)

	// wrap ResponseWriter to check if response was sent
	rw := &customResponseWriter{
		ResponseWriter: w,
		copyData:       recordDetail(r, m.Spec),
	}

	// call Go-plugin function
	t1 := time.Now()

	// Inject definition into request context:
	ctx.SetDefinition(r, m.Spec.APIDefinition)

	handler(w, r)

	// calculate latency
	ms := DurationToMillisecond(time.Since(t1))
	logger.WithField("ms", ms).Debug("Go-plugin request processing took")

	// check if response was sent
	if rw.responseSent {
		// check if response code was an error one
		switch {
		case rw.statusCodeSent == http.StatusForbidden:
			logger.WithError(err).Error("Authentication error in Go-plugin middleware func")
			m.Base().FireEvent(EventAuthFailure, EventKeyFailureMeta{
				EventMetaDefault: EventMetaDefault{Message: "Auth Failure", OriginatingRequest: EncodeRequestToEvent(r)},
				Path:             r.URL.Path,
				Origin:           request.RealIP(r),
				Key:              "n/a",
			})
			fallthrough
		case rw.statusCodeSent >= http.StatusBadRequest:
			// base middleware will report this error to analytics if needed
			respCode = rw.statusCodeSent
			err = fmt.Errorf("plugin function sent error response code: %d", rw.statusCodeSent)
			logger.WithError(err).Error("Failed to process request with Go-plugin middleware func")
		default:
			// record 2XX to analytics
			m.successHandler.RecordHit(r, Latency{Total: int64(ms)}, rw.statusCodeSent, rw.getHttpResponse(r))

			// no need to continue passing this request down to reverse proxy
			respCode = mwStatusRespond
		}
	} else {
		respCode = http.StatusOK
	}

	return
}
<|endoftext|>


<commit_before>package via

import (
	"fmt"
	"github.com/str1ngs/util/console"
	"github.com/str1ngs/util/human"
	"github.com/str1ngs/util/json"
	"log"
	"path"
	"path/filepath"
	"sort"
	"time"
)

type Plans []*Plan

// Returns a PlanSlice of all Plans in config.Plans
func GetPlans() (Plans, error) {
	pf, err := PlanFiles()
	if err != nil {
		return nil, err
	}
	plans := Plans{}
	for _, f := range pf {
		p, _ := ReadPath(f)
		plans = append(plans, p)
	}
	return plans, nil
}

// Returns a copy of this PlanSlice sorted by
// field Size.
func (ps Plans) SortSize() Plans {
	nps := append(Plans{}, ps...)
	sort.Sort(Size(nps))
	return nps
}

// Prints this slice to console.
// TODO: use template
func (ps Plans) Print() {
	for _, p := range ps {
		console.Println(p.Name, human.ByteSize(p.Size))
	}
	console.Flush()
}

type Plan struct {
	Name         string
	Version      string
	Url          string
	Group        string
	StageDir     string
	Inherit      string
	BuildInStage bool
	Date         time.Time
	Size         int64
	SubPackages  []string
	Depends      []string
	Flags        Flags
	Patch        []string
	Build        []string
	Package      []string
	PostInstall  []string
	Remove       []string
	Files        []string
}

func (p *Plan) NameVersion() string {
	return fmt.Sprintf("%s-%s", p.Name, p.Version)
}

func (p *Plan) Path() string {
	return path.Join(config.Plans, p.Group, p.Name+".json")
}

// TODO: make this atomic
func (p *Plan) Save() (err error) {
	return json.Write(p, p.Path())
}

func FindPlanPath(n string) (string, error) {
	glob := join(config.Plans, "*", n+".json")
	e, err := filepath.Glob(glob)
	if err != nil {
		return "", err
	}
	if len(e) != 1 {
		return "", fmt.Errorf("expected 1 plan found %d.", len(e))
	}
	return e[0], nil
}

func NewPlan(n string) (plan *Plan, err error) {
	path, err := FindPlanPath(n)
	if err != nil {
		return nil, err
	}
	plan, err = ReadPath(path)
	if err != nil {
		return nil, err
	}
	return plan, err
}

func ReadPath(p string) (plan *Plan, err error) {
	plan = new(Plan)
	err = json.Read(plan, p)
	if err != nil {
		return nil, err
	}
	return plan, nil
}

func (p *Plan) PackageFile() string {
	return fmt.Sprintf("%s-%s-%s.tar.gz", p.NameVersion(), config.OS, config.Arch)
}

func (p Plan) SourceFile() string {
	return join(path.Base(p.Url))
}

func (p Plan) SourcePath() string {
	return path.Join(cache.Sources(), path.Base(p.Url))
}

func (p Plan) BuildDir() string {
	bdir := join(cache.Builds(), p.NameVersion())
	if p.BuildInStage {
		bdir = join(cache.Stages(), p.stageDir())
	}
	return bdir
}

func (p Plan) GetStageDir() string {
	path := join(cache.Stages(), p.stageDir())
	return path
}

func (p Plan) PackagePath() string {
	branch, err := config.Branch()
	if err != nil {
		log.Fatal(err)
	}
	return join(config.Repo, branch, p.PackageFile())
}

func (p Plan) stageDir() string {
	if p.StageDir != "" {
		return p.StageDir
	}
	return p.NameVersion()
}
<commit_msg>record build time<commit_after>package via

import (
	"fmt"
	"github.com/str1ngs/util/console"
	"github.com/str1ngs/util/human"
	"github.com/str1ngs/util/json"
	"log"
	"path"
	"path/filepath"
	"sort"
	"time"
)

type Plans []*Plan

// Returns a PlanSlice of all Plans in config.Plans
func GetPlans() (Plans, error) {
	pf, err := PlanFiles()
	if err != nil {
		return nil, err
	}
	plans := Plans{}
	for _, f := range pf {
		p, _ := ReadPath(f)
		plans = append(plans, p)
	}
	return plans, nil
}

// Returns a copy of this PlanSlice sorted by
// field Size.
func (ps Plans) SortSize() Plans {
	nps := append(Plans{}, ps...)
	sort.Sort(Size(nps))
	return nps
}

// Prints this slice to console.
// TODO: use template
func (ps Plans) Print() {
	for _, p := range ps {
		console.Println(p.Name, human.ByteSize(p.Size))
	}
	console.Flush()
}

type Plan struct {
	Name         string
	Version      string
	Url          string
	Group        string
	StageDir     string
	Inherit      string
	BuildInStage bool
	BuildTime    time.Duration
	Date         time.Time
	Size         int64
	SubPackages  []string
	Depends      []string
	Flags        Flags
	Patch        []string
	Build        []string
	Package      []string
	PostInstall  []string
	Remove       []string
	Files        []string
}

func (p *Plan) NameVersion() string {
	return fmt.Sprintf("%s-%s", p.Name, p.Version)
}

func (p *Plan) Path() string {
	return path.Join(config.Plans, p.Group, p.Name+".json")
}

// TODO: make this atomic
func (p *Plan) Save() (err error) {
	return json.Write(p, p.Path())
}

func FindPlanPath(n string) (string, error) {
	glob := join(config.Plans, "*", n+".json")
	e, err := filepath.Glob(glob)
	if err != nil {
		return "", err
	}
	if len(e) != 1 {
		return "", fmt.Errorf("expected 1 plan found %d.", len(e))
	}
	return e[0], nil
}

func NewPlan(n string) (plan *Plan, err error) {
	path, err := FindPlanPath(n)
	if err != nil {
		return nil, err
	}
	plan, err = ReadPath(path)
	if err != nil {
		return nil, err
	}
	return plan, err
}

func ReadPath(p string) (plan *Plan, err error) {
	plan = new(Plan)
	err = json.Read(plan, p)
	if err != nil {
		return nil, err
	}
	return plan, nil
}

func (p *Plan) PackageFile() string {
	return fmt.Sprintf("%s-%s-%s.tar.gz", p.NameVersion(), config.OS, config.Arch)
}

func (p Plan) SourceFile() string {
	return join(path.Base(p.Url))
}

func (p Plan) SourcePath() string {
	return path.Join(cache.Sources(), path.Base(p.Url))
}

func (p Plan) BuildDir() string {
	bdir := join(cache.Builds(), p.NameVersion())
	if p.BuildInStage {
		bdir = join(cache.Stages(), p.stageDir())
	}
	return bdir
}

func (p Plan) GetStageDir() string {
	path := join(cache.Stages(), p.stageDir())
	return path
}

func (p Plan) PackagePath() string {
	branch, err := config.Branch()
	if err != nil {
		log.Fatal(err)
	}
	return join(config.Repo, branch, p.PackageFile())
}

func (p Plan) stageDir() string {
	if p.StageDir != "" {
		return p.StageDir
	}
	return p.NameVersion()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/grafov/m3u8"
	"gopkg.in/redis.v1"
	"log"
	"net/http"
	"os/exec"
	"time"
)

var broadcastCursor = make(chan int)
var currentPlaylist string
var client *redis.Client

func init() {
	client = redis.NewTCPClient(&redis.Options{
		Addr: "localhost:6379",
	})

	pong, err := client.Ping().Result()
	log.Println(pong, err)
}

type PlaylistGenerator struct {
	cursor chan int
}

func (pl PlaylistGenerator) VideoFileForSequence(seq int) string {
	generated := fmt.Sprintf("http://www.smick.tv/fileSequence%d.ts", seq)
	return generated
}

func (pl PlaylistGenerator) GeneratedVideoFileForSequence(seq int) string {
	prefix := ""
	pref := client.Get("broadcast-prefix").Val()
	prefix = pref

	generated := fmt.Sprintf("fileSequence%d.ts", seq)
	postProcess := fmt.Sprintf("fileSequence%d-post.ts", seq)
	sourceVideo := prefix + generated
	destVideo := prefix + postProcess

	currentTime := time.Now().Format("3:04 PM")

	twoClipsAgo := seq - 2
	if twoClipsAgo > 0 {
		mapKey := fmt.Sprintf("/fileSequence%d-post.ts", twoClipsAgo)
		log.Println("map key is", mapKey)
		if count, ok := lfs.Counter[mapKey]; ok {
			currentTime = fmt.Sprintf("%d active viewers", count)
		}
	}

	err := RenderTextToPNG(currentTime, "time.png")
	if err == nil {
		cmd := exec.Command("avconv", "-i", sourceVideo, "-vf", "movie=time.png [watermark];[in][watermark] overlay=0:0 [out]", "-y", "-map", "0", "-c:a", "copy", "-c:v", "mpeg2video", "-an", destVideo)
		err := cmd.Start()
		if err != nil {
			return sourceVideo
		}
		err = cmd.Wait()
		return destVideo
	}

	return sourceVideo
}

func (pl *PlaylistGenerator) KeepPlaylistUpdated() {
	p, e := m3u8.NewMediaPlaylist(1000, 1000)
	if e != nil {
		log.Println("Error creating media playlist:", e)
		return
	}
	currentPlaylist = p.Encode().String()

	for seqnum := 0; seqnum < 390; seqnum = <-pl.cursor {
		videoFile := pl.VideoFileForSequence(seqnum)
		if err := p.Append(videoFile, 10.0, ""); err != nil {
			log.Println("Error appending item to playlist:", err, fmt.Sprintf("fileSequence%d.ts", seqnum))
		}
		currentPlaylist = p.Encode().String()
	}
}

func (pl *PlaylistGenerator) Start() {
	pl.cursor = make(chan int, 1000)

	go pl.KeepPlaylistUpdated()
	for i := 1; i < 394; i++ {
		log.Println(i)
		pl.cursor <- i
		time.Sleep(10 * time.Second)
	}
}

func (pl PlaylistGenerator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, currentPlaylist)
}
<commit_msg>doubled length<commit_after>package main

import (
	"fmt"
	"github.com/grafov/m3u8"
	"gopkg.in/redis.v1"
	"log"
	"net/http"
	"os/exec"
	"time"
)

var broadcastCursor = make(chan int)
var currentPlaylist string
var client *redis.Client

func init() {
	client = redis.NewTCPClient(&redis.Options{
		Addr: "localhost:6379",
	})

	pong, err := client.Ping().Result()
	log.Println(pong, err)
}

type PlaylistGenerator struct {
	cursor chan int
}

func (pl PlaylistGenerator) VideoFileForSequence(seq int) string {
	generated := fmt.Sprintf("http://www.smick.tv/fileSequence%d.ts", seq)
	return generated
}

func (pl PlaylistGenerator) GeneratedVideoFileForSequence(seq int) string {
	prefix := ""
	pref := client.Get("broadcast-prefix").Val()
	prefix = pref

	generated := fmt.Sprintf("fileSequence%d.ts", seq)
	postProcess := fmt.Sprintf("fileSequence%d-post.ts", seq)
	sourceVideo := prefix + generated
	destVideo := prefix + postProcess

	currentTime := time.Now().Format("3:04 PM")

	twoClipsAgo := seq - 2
	if twoClipsAgo > 0 {
		mapKey := fmt.Sprintf("/fileSequence%d-post.ts", twoClipsAgo)
		log.Println("map key is", mapKey)
		if count, ok := lfs.Counter[mapKey]; ok {
			currentTime = fmt.Sprintf("%d active viewers", count)
		}
	}

	err := RenderTextToPNG(currentTime, "time.png")
	if err == nil {
		cmd := exec.Command("avconv", "-i", sourceVideo, "-vf", "movie=time.png [watermark];[in][watermark] overlay=0:0 [out]", "-y", "-map", "0", "-c:a", "copy", "-c:v", "mpeg2video", "-an", destVideo)
		err := cmd.Start()
		if err != nil {
			return sourceVideo
		}
		err = cmd.Wait()
		return destVideo
	}

	return sourceVideo
}

func (pl *PlaylistGenerator) KeepPlaylistUpdated() {
	p, e := m3u8.NewMediaPlaylist(1000, 1000)
	if e != nil {
		log.Println("Error creating media playlist:", e)
		return
	}
	currentPlaylist = p.Encode().String()

	for seqnum := 0; seqnum < 390; seqnum = <-pl.cursor {
		videoFile := pl.VideoFileForSequence(seqnum)
		if err := p.Append(videoFile, 10.0, ""); err != nil {
			log.Println("Error appending item to playlist:", err, fmt.Sprintf("fileSequence%d.ts", seqnum))
		}
		currentPlaylist = p.Encode().String()
	}
}

func (pl *PlaylistGenerator) Start() {
	pl.cursor = make(chan int, 1000)

	go pl.KeepPlaylistUpdated()
	for i := 1; i < 728; i++ {
		log.Println(i)
		pl.cursor <- i
		time.Sleep(10 * time.Second)
	}
}

func (pl PlaylistGenerator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, currentPlaylist)
}
<|endoftext|>