<commit_before>package i18n

import (
	"errors"
	"fmt"

	"github.com/volatile/core"

	"golang.org/x/text/language"
)

const contextDataKey = "i18nLocale"

// TnPlaceholder is the placeholder replaced by n in a translation, when using the TransN function.
const TnPlaceholder = "{{.n}}"

var (
	locales       Locales
	defaultLocale language.Tag
	matcher       language.Matcher
)

// Errors
var (
	ErrUnknownLocale = errors.New("i18n: unknown locale")
)

// TemplatesFuncs provides i18n functions that can be set for templates.
var TemplatesFuncs = map[string]interface{}{
	"clientLocale": ClientLocale,
	"fmtn":         Fmtn,
	"t":            T,
	"tn":           Tn,
}

// Translations is a map of translations.
type Translations map[string]string

// Locales is a map of translations associated to language tags.
type Locales map[language.Tag]Translations

// Has checks if locale t exists in the locales map.
func (ll *Locales) Has(t language.Tag) bool {
	_, ok := (*ll)[t]
	return ok
}

// Init registers locales ll and default locale def for the entire app.
func Init(ll Locales, def language.Tag) {
	if locales != nil {
		panic(errors.New("i18n: Init called multiple times"))
	}

	locales = ll
	defaultLocale = def

	if !locales.Has(def) {
		panic(fmt.Errorf("i18n: default locale %q doesn't exist", def))
	}

	tt := []language.Tag{def}
	for t := range ll {
		if t != def {
			tt = append(tt, t)
		}
	}
	matcher = language.NewMatcher(tt)
}

// Use adds a handler to the default handlers stack.
// It matches locale for client, thanks to matchers.
// Multiple matchers can be used.
// The client locale is set as soon as a matcher is confident.
func Use(matchers ...Matcher) {
	core.Use(func(c *core.Context) {
		for _, m := range matchers {
			if t, conf := m(c); conf != language.No {
				if err := SetClientLocale(c, t); err == nil {
					break
				}
			}
		}
		c.Next()
	})
}

// CleanAcceptLanguage parses, cleans and returns the contents of a Accept-Language header.
// If an error is encountered, the returned string is the same as given.
func CleanAcceptLanguage(s string) (string, error) {
	tt, q, err := language.ParseAcceptLanguage(s)
	if err != nil {
		return s, err
	}

	s = ""
	for i := 0; i < len(tt); i++ {
		if i > 0 {
			s += ","
		}
		s += fmt.Sprintf("%s;q=%g", tt[i].String(), q[i])
	}
	return s, nil
}
<commit_msg>Fix doc<commit_after>package i18n

import (
	"errors"
	"fmt"

	"github.com/volatile/core"

	"golang.org/x/text/language"
)

const contextDataKey = "i18nLocale"

// TnPlaceholder is the placeholder replaced by n in a translation, when using the TransN function.
const TnPlaceholder = "{{.n}}"

var (
	locales       Locales
	defaultLocale language.Tag
	matcher       language.Matcher
)

// Errors
var (
	ErrUnknownLocale = errors.New("i18n: unknown locale")
)

// TemplatesFuncs provides i18n functions that can be set for templates.
var TemplatesFuncs = map[string]interface{}{
	"clientLocale": ClientLocale,
	"fmtn":         Fmtn,
	"t":            T,
	"tn":           Tn,
}

// Translations is a map of translations.
type Translations map[string]string

// Locales is a map of translations associated to language tags.
type Locales map[language.Tag]Translations

// Has checks if locale t exists in the locales map.
func (ll *Locales) Has(t language.Tag) bool {
	_, ok := (*ll)[t]
	return ok
}

// Init registers locales ll and default locale def for the entire app.
func Init(ll Locales, def language.Tag) {
	if locales != nil {
		panic(errors.New("i18n: Init called multiple times"))
	}

	locales = ll
	defaultLocale = def

	if !locales.Has(def) {
		panic(fmt.Errorf("i18n: default locale %q doesn't exist", def))
	}

	tt := []language.Tag{def}
	for t := range ll {
		if t != def {
			tt = append(tt, t)
		}
	}
	matcher = language.NewMatcher(tt)
}

// Use adds the handler to the default handlers stack.
// It matches locale for client, thanks to matchers.
// Multiple matchers can be used.
// The client locale is set as soon as a matcher is confident.
func Use(matchers ...Matcher) {
	core.Use(func(c *core.Context) {
		for _, m := range matchers {
			if t, conf := m(c); conf != language.No {
				if err := SetClientLocale(c, t); err == nil {
					break
				}
			}
		}
		c.Next()
	})
}

// CleanAcceptLanguage parses, cleans and returns the contents of a Accept-Language header.
// If an error is encountered, the returned string is the same as given.
func CleanAcceptLanguage(s string) (string, error) {
	tt, q, err := language.ParseAcceptLanguage(s)
	if err != nil {
		return s, err
	}

	s = ""
	for i := 0; i < len(tt); i++ {
		if i > 0 {
			s += ","
		}
		s += fmt.Sprintf("%s;q=%g", tt[i].String(), q[i])
	}
	return s, nil
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

// Package credentials implements various credentials supported by gRPC library,
// which encapsulate all the state needed by a client to authenticate with a
// server and make various assertions, e.g., about the client's identity, role,
// or whether it is authorized to make a particular call.
package credentials // import "google.golang.org/grpc/credentials"

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
)

var (
	// alpnProtoStr are the specified application level protocols for gRPC.
	alpnProtoStr = []string{"h2-14", "h2-15", "h2-16"}
)

// Credentials defines the common interface all supported credentials must
// implement.
type Credentials interface {
	// GetRequestMetadata gets the current request metadata, refreshing
	// tokens if required. This should be called by the transport layer on
	// each request, and the data should be populated in headers or other
	// context. When supported by the underlying implementation, ctx can
	// be used for timeout and cancellation.
	// TODO(zhaoq): Define the set of the qualified keys instead of leaving
	// it as an arbitrary string.
	GetRequestMetadata(ctx context.Context) (map[string]string, error)
}

// TransportAuthenticator defines the common interface all supported transport
// authentication protocols (e.g., TLS, SSL) must implement.
type TransportAuthenticator interface {
	// Dial connects to the given network address and does the authentication
	// handshake specified by the corresponding authentication protocol.
	Dial(add string) (net.Conn, error)
	// NewListener creates a listener which accepts connections with requested
	// authentication handshake.
	NewListener(lis net.Listener) net.Listener
	Credentials
}

// tlsCreds is the credentials required for authenticating a connection.
type tlsCreds struct {
	// serverName is used to verify the hostname on the returned
	// certificates. It is also included in the client's handshake
	// to support virtual hosting. This is optional. If it is not
	// set gRPC internals will use the dialing address instead.
	serverName string
	// rootCAs defines the set of root certificate authorities
	// that clients use when verifying server certificates.
	// If rootCAs is nil, tls uses the host's root CA set.
	rootCAs *x509.CertPool
	// certificates contains one or more certificate chains
	// to present to the other side of the connection.
	// Server configurations must include at least one certificate.
	certificates []tls.Certificate
}

// GetRequestMetadata returns nil, nil since TLS credentials does not have
// metadata.
func (c *tlsCreds) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
	return nil, nil
}

// Dial connects to addr and performs TLS handshake.
func (c *tlsCreds) Dial(addr string) (_ net.Conn, err error) {
	name := c.serverName
	if name == "" {
		name, _, err = net.SplitHostPort(addr)
		if err != nil {
			return nil, fmt.Errorf("credentials: failed to parse server address %v", err)
		}
	}
	return tls.Dial("tcp", addr, &tls.Config{
		RootCAs:    c.rootCAs,
		NextProtos: alpnProtoStr,
		ServerName: name,
	})
}

// NewListener creates a net.Listener with a TLS configuration constructed
// from the information in tlsCreds.
func (c *tlsCreds) NewListener(lis net.Listener) net.Listener {
	return tls.NewListener(lis, &tls.Config{
		Certificates: c.certificates,
		NextProtos:   alpnProtoStr,
	})
}

// NewClientTLSFromCert constructs a TLS from the input certificate for client.
func NewClientTLSFromCert(cp *x509.CertPool, serverName string) TransportAuthenticator {
	return &tlsCreds{
		serverName: serverName,
		rootCAs:    cp,
	}
}

// NewClientTLSFromFile constructs a TLS from the input certificate file for client.
func NewClientTLSFromFile(certFile, serverName string) (TransportAuthenticator, error) {
	b, err := ioutil.ReadFile(certFile)
	if err != nil {
		return nil, err
	}
	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM(b) {
		return nil, fmt.Errorf("credentials: failed to append certificates")
	}
	return &tlsCreds{
		serverName: serverName,
		rootCAs:    cp,
	}, nil
}

// NewServerTLSFromCert constructs a TLS from the input certificate for server.
func NewServerTLSFromCert(cert *tls.Certificate) TransportAuthenticator {
	return &tlsCreds{
		certificates: []tls.Certificate{*cert},
	}
}

// NewServerTLSFromFile constructs a TLS from the input certificate file and key
// file for server.
func NewServerTLSFromFile(certFile, keyFile string) (TransportAuthenticator, error) {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}
	return &tlsCreds{
		certificates: []tls.Certificate{cert},
	}, nil
}

// computeEngine represents credentials for the built-in service account for
// the currently running Google Compute Engine (GCE) instance. It uses the
// metadata server to get access tokens.
type computeEngine struct {
	ts oauth2.TokenSource
}

func (c computeEngine) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
	token, err := c.ts.Token()
	if err != nil {
		return nil, err
	}
	return map[string]string{
		"authorization": token.TokenType + " " + token.AccessToken,
	}, nil
}

// NewComputeEngine constructs the credentials that fetches access tokens from
// Google Compute Engine (GCE)'s metadata server. It is only valid to use this
// if your program is running on a GCE instance.
func NewComputeEngine() Credentials {
	return computeEngine{
		ts: google.ComputeTokenSource(""),
	}
}

// serviceAccount represents credentials via JWT signing key.
type serviceAccount struct {
	config *jwt.Config
}

func (s serviceAccount) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
	c, ok := ctx.(oauth2.Context)
	if !ok {
		return nil, fmt.Errorf("credentials: the context %v is invalid", ctx)
	}
	token, err := s.config.TokenSource(c).Token()
	if err != nil {
		return nil, err
	}
	return map[string]string{
		"authorization": token.TokenType + " " + token.AccessToken,
	}, nil
}

// NewServiceAccountFromKey constructs the credentials using the JSON key slice
// from a Google Developers service account.
func NewServiceAccountFromKey(jsonKey []byte, scope ...string) (Credentials, error) {
	config, err := google.JWTConfigFromJSON(jsonKey, scope...)
	if err != nil {
		return nil, err
	}
	return serviceAccount{config: config}, nil
}

// NewServiceAccountFromFile constructs the credentials using the JSON key file
// of a Google Developers service account.
func NewServiceAccountFromFile(keyFile string, scope ...string) (Credentials, error) {
	jsonKey, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
	}
	return NewServiceAccountFromKey(jsonKey, scope...)
}
<commit_msg>fix typo<commit_after>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

// Package credentials implements various credentials supported by gRPC library,
// which encapsulate all the state needed by a client to authenticate with a
// server and make various assertions, e.g., about the client's identity, role,
// or whether it is authorized to make a particular call.
package credentials // import "google.golang.org/grpc/credentials"

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
)

var (
	// alpnProtoStr are the specified application level protocols for gRPC.
	alpnProtoStr = []string{"h2-14", "h2-15", "h2-16"}
)

// Credentials defines the common interface all supported credentials must
// implement.
type Credentials interface {
	// GetRequestMetadata gets the current request metadata, refreshing
	// tokens if required. This should be called by the transport layer on
	// each request, and the data should be populated in headers or other
	// context. When supported by the underlying implementation, ctx can
	// be used for timeout and cancellation.
	// TODO(zhaoq): Define the set of the qualified keys instead of leaving
	// it as an arbitrary string.
	GetRequestMetadata(ctx context.Context) (map[string]string, error)
}

// TransportAuthenticator defines the common interface all supported transport
// authentication protocols (e.g., TLS, SSL) must implement.
type TransportAuthenticator interface {
	// Dial connects to the given network address and does the authentication
	// handshake specified by the corresponding authentication protocol.
	Dial(addr string) (net.Conn, error)
	// NewListener creates a listener which accepts connections with requested
	// authentication handshake.
	NewListener(lis net.Listener) net.Listener
	Credentials
}

// tlsCreds is the credentials required for authenticating a connection.
type tlsCreds struct {
	// serverName is used to verify the hostname on the returned
	// certificates. It is also included in the client's handshake
	// to support virtual hosting. This is optional. If it is not
	// set gRPC internals will use the dialing address instead.
	serverName string
	// rootCAs defines the set of root certificate authorities
	// that clients use when verifying server certificates.
	// If rootCAs is nil, tls uses the host's root CA set.
	rootCAs *x509.CertPool
	// certificates contains one or more certificate chains
	// to present to the other side of the connection.
	// Server configurations must include at least one certificate.
	certificates []tls.Certificate
}

// GetRequestMetadata returns nil, nil since TLS credentials does not have
// metadata.
func (c *tlsCreds) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
	return nil, nil
}

// Dial connects to addr and performs TLS handshake.
func (c *tlsCreds) Dial(addr string) (_ net.Conn, err error) {
	name := c.serverName
	if name == "" {
		name, _, err = net.SplitHostPort(addr)
		if err != nil {
			return nil, fmt.Errorf("credentials: failed to parse server address %v", err)
		}
	}
	return tls.Dial("tcp", addr, &tls.Config{
		RootCAs:    c.rootCAs,
		NextProtos: alpnProtoStr,
		ServerName: name,
	})
}

// NewListener creates a net.Listener with a TLS configuration constructed
// from the information in tlsCreds.
func (c *tlsCreds) NewListener(lis net.Listener) net.Listener {
	return tls.NewListener(lis, &tls.Config{
		Certificates: c.certificates,
		NextProtos:   alpnProtoStr,
	})
}

// NewClientTLSFromCert constructs a TLS from the input certificate for client.
func NewClientTLSFromCert(cp *x509.CertPool, serverName string) TransportAuthenticator {
	return &tlsCreds{
		serverName: serverName,
		rootCAs:    cp,
	}
}

// NewClientTLSFromFile constructs a TLS from the input certificate file for client.
func NewClientTLSFromFile(certFile, serverName string) (TransportAuthenticator, error) {
	b, err := ioutil.ReadFile(certFile)
	if err != nil {
		return nil, err
	}
	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM(b) {
		return nil, fmt.Errorf("credentials: failed to append certificates")
	}
	return &tlsCreds{
		serverName: serverName,
		rootCAs:    cp,
	}, nil
}

// NewServerTLSFromCert constructs a TLS from the input certificate for server.
func NewServerTLSFromCert(cert *tls.Certificate) TransportAuthenticator {
	return &tlsCreds{
		certificates: []tls.Certificate{*cert},
	}
}

// NewServerTLSFromFile constructs a TLS from the input certificate file and key
// file for server.
func NewServerTLSFromFile(certFile, keyFile string) (TransportAuthenticator, error) {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}
	return &tlsCreds{
		certificates: []tls.Certificate{cert},
	}, nil
}

// computeEngine represents credentials for the built-in service account for
// the currently running Google Compute Engine (GCE) instance. It uses the
// metadata server to get access tokens.
type computeEngine struct {
	ts oauth2.TokenSource
}

func (c computeEngine) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
	token, err := c.ts.Token()
	if err != nil {
		return nil, err
	}
	return map[string]string{
		"authorization": token.TokenType + " " + token.AccessToken,
	}, nil
}

// NewComputeEngine constructs the credentials that fetches access tokens from
// Google Compute Engine (GCE)'s metadata server. It is only valid to use this
// if your program is running on a GCE instance.
func NewComputeEngine() Credentials {
	return computeEngine{
		ts: google.ComputeTokenSource(""),
	}
}

// serviceAccount represents credentials via JWT signing key.
type serviceAccount struct {
	config *jwt.Config
}

func (s serviceAccount) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
	c, ok := ctx.(oauth2.Context)
	if !ok {
		return nil, fmt.Errorf("credentials: the context %v is invalid", ctx)
	}
	token, err := s.config.TokenSource(c).Token()
	if err != nil {
		return nil, err
	}
	return map[string]string{
		"authorization": token.TokenType + " " + token.AccessToken,
	}, nil
}

// NewServiceAccountFromKey constructs the credentials using the JSON key slice
// from a Google Developers service account.
func NewServiceAccountFromKey(jsonKey []byte, scope ...string) (Credentials, error) {
	config, err := google.JWTConfigFromJSON(jsonKey, scope...)
	if err != nil {
		return nil, err
	}
	return serviceAccount{config: config}, nil
}

// NewServiceAccountFromFile constructs the credentials using the JSON key file
// of a Google Developers service account.
func NewServiceAccountFromFile(keyFile string, scope ...string) (Credentials, error) {
	jsonKey, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
	}
	return NewServiceAccountFromKey(jsonKey, scope...)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/smw1218/windygo/db"
	"github.com/smw1218/windygo/plot"
	"github.com/smw1218/windygo/vantage"
	"log"
)

type DataRecorder struct{}

func (dr *DataRecorder) record(loopRecord *vantage.LoopRecord) {
	log.Printf("R: %#v\n", loopRecord)
}

func main() {
	var host string
	var doDmp bool
	flag.StringVar(&host, "h", "", "host:port of the Vantage device")
	flag.BoolVar(&doDmp, "dmp", false, "run archive dump and exit")
	flag.Parse()

	// On my unit, dmp didn't work (it was missing random bytes)
	// The DMPAFT worked but the data was all screwed up with dates jumping around
	// also some of the dates are in the future (multiple days)
	if doDmp {
		vc, err := vantage.Dial(host)
		if err != nil {
			log.Fatalf("Error connecting to vantage: %v", err)
		}

		ars, err := vc.GetArchiveRecords()
		if ars != nil {
			for _, ar := range ars {
				fmt.Printf("I:%v\tJ:%v\t%v\t%v\t%v\n", ar.ArchivePage, ar.ArchivePageRecord, ar.ArchiveTime, ar.WindAvg, ar.OutsideTemp)
			}
		}
		if err != nil {
			log.Fatalf("Error getting archive: %v\n", err)
		}

		return
	}
	db, err := db.NewMysql("windygo", "")
	if err != nil {
		log.Fatal(err)
	}

	gp, err := plot.NewGnuPlot(db)
	if err != nil {
		log.Fatal(err)
	}

	go vantage.CollectDataForever(host, db.Record)
	for {
		select {
		case err := <-gp.ErrChan:
			log.Printf("GP error: %v\n", err)
		case err := <-db.ErrChan:
			log.Printf("DB error: %v\n", err)
		}
	}
}
<commit_msg>logging gp errors<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/smw1218/windygo/db"
	"github.com/smw1218/windygo/plot"
	"github.com/smw1218/windygo/vantage"
	"log"
)

type DataRecorder struct{}

func (dr *DataRecorder) record(loopRecord *vantage.LoopRecord) {
	log.Printf("R: %#v\n", loopRecord)
}

func main() {
	var host string
	var doDmp bool
	flag.StringVar(&host, "h", "", "host:port of the Vantage device")
	flag.BoolVar(&doDmp, "dmp", false, "run archive dump and exit")
	flag.Parse()

	// On my unit, dmp didn't work (it was missing random bytes)
	// The DMPAFT worked but the data was all screwed up with dates jumping around
	// also some of the dates are in the future (multiple days)
	if doDmp {
		vc, err := vantage.Dial(host)
		if err != nil {
			log.Fatalf("Error connecting to vantage: %v", err)
		}

		ars, err := vc.GetArchiveRecords()
		if ars != nil {
			for _, ar := range ars {
				fmt.Printf("I:%v\tJ:%v\t%v\t%v\t%v\n", ar.ArchivePage, ar.ArchivePageRecord, ar.ArchiveTime, ar.WindAvg, ar.OutsideTemp)
			}
		}
		if err != nil {
			log.Fatalf("Error getting archive: %v\n", err)
		}

		return
	}
	db, err := db.NewMysql("windygo", "")
	if err != nil {
		log.Fatal(err)
	}

	gp, err := plot.NewGnuPlot(db)
	if err != nil {
		log.Fatal(err)
	}

	go vantage.CollectDataForever(host, db.Record)
	for {
		select {
		case err := <-gp.ErrChan:
			log.Printf("GP error: %v\n", err)
		case err := <-db.ErrChan:
			log.Printf("DB error: %v\n", err)
		case err := <-gp.ErrChan:
			log.Printf("GP error: %v\n", err)
		}
	}
}
<|endoftext|>


<commit_before>/*
 * Minio Cloud Storage, (C) 2015, 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"sort"

	"github.com/minio/cli"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio/pkg/probe"
	"github.com/pkg/profile"
)

var (
	// global flags for minio.
	minioFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "help, h",
			Usage: "Show help.",
		},
	}
)

// Help template for minio.
var minioHelpTemplate = `NAME:
  {{.Name}} - {{.Usage}}

DESCRIPTION:
  {{.Description}}

USAGE:
  minio {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
  {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
  {{end}}{{if .Flags}}
FLAGS:
  {{range .Flags}}{{.}}
  {{end}}{{end}}
VERSION:
  ` + minioVersion +
	`{{ "\n"}}`

// init - check the environment before main starts
func init() {
	// Check if minio was compiled using a supported version of Golang.
	checkGoVersion()

	// It is an unsafe practice to run network services as
	// root. Containers are an exception.
	if !isContainerized() && os.Geteuid() == 0 {
		console.Fatalln("Please run ‘minio’ as a non-root user.")
	}
}

func migrate() {
	// Migrate config file
	migrateConfig()

	// Migrate other configs here.
}

func enableLoggers() {
	// Enable all loggers here.
	enableConsoleLogger()
	enableFileLogger()

	// Add your logger here.
}

func findClosestCommands(command string) []string {
	var closestCommands []string
	for _, value := range commandsTree.PrefixMatch(command) {
		closestCommands = append(closestCommands, value.(string))
	}
	sort.Strings(closestCommands)
	// Suggest other close commands - allow missed, wrongly added and
	// even transposed characters
	for _, value := range commandsTree.walk(commandsTree.root) {
		if sort.SearchStrings(closestCommands, value.(string)) < len(closestCommands) {
			continue
		}
		// 2 is arbitrary and represents the max
		// allowed number of typed errors
		if DamerauLevenshteinDistance(command, value.(string)) < 2 {
			closestCommands = append(closestCommands, value.(string))
		}
	}
	return closestCommands
}

func registerApp() *cli.App {
	// Register all commands.
	registerCommand(serverCmd)
	registerCommand(versionCmd)
	registerCommand(updateCmd)

	// Set up app.
	app := cli.NewApp()
	app.Name = "Minio"
	app.Author = "Minio.io"
	app.Usage = "Distributed Object Storage Server for Micro Services."
	app.Description = `Micro services environment provisions one Minio server per application instance. Scalability is achieved through large number of smaller personalized instances. This version of the Minio binary is built using Filesystem storage backend for magnetic and solid state disks.`
	app.Flags = append(minioFlags, globalFlags...)
	app.Commands = commands
	app.CustomAppHelpTemplate = minioHelpTemplate
	app.CommandNotFound = func(ctx *cli.Context, command string) {
		msg := fmt.Sprintf("‘%s’ is not a minio sub-command. See ‘minio --help’.", command)
		closestCommands := findClosestCommands(command)
		if len(closestCommands) > 0 {
			msg += fmt.Sprintf("\n\nDid you mean one of these?\n")
			for _, cmd := range closestCommands {
				msg += fmt.Sprintf("        ‘%s’\n", cmd)
			}
		}
		console.Fatalln(msg)
	}
	return app
}

func checkMainSyntax(c *cli.Context) {
	configPath, err := getConfigPath()
	if err != nil {
		console.Fatalf("Unable to obtain user's home directory. \nError: %s\n", err)
	}
	if configPath == "" {
		console.Fatalln("Config folder cannot be empty, please specify --config-dir <foldername>.")
	}
}

// mustGetProfilePath must get location that the profile will be written to.
func mustGetProfilePath() string {
	return filepath.Join(mustGetConfigPath(), globalMinioProfilePath)
}

func main() {
	// Enable profiling supported modes are [cpu, mem, block].
	// ``MINIO_PROFILER`` supported options are [cpu, mem, block].
	switch os.Getenv("MINIO_PROFILER") {
	case "cpu":
		defer profile.Start(profile.CPUProfile, profile.ProfilePath(mustGetProfilePath())).Stop()
	case "mem":
		defer profile.Start(profile.MemProfile, profile.ProfilePath(mustGetProfilePath())).Stop()
	case "block":
		defer profile.Start(profile.BlockProfile, profile.ProfilePath(mustGetProfilePath())).Stop()
	}

	// Set global trace flag.
	trace := os.Getenv("MINIO_TRACE")
	globalTrace = trace == "1"

	probe.Init() // Set project's root source path.
	probe.SetAppInfo("Release-Tag", minioReleaseTag)
	probe.SetAppInfo("Commit-ID", minioShortCommitID)

	app := registerApp()
	app.Before = func(c *cli.Context) error {
		// Sets new config folder.
		setGlobalConfigPath(c.GlobalString("config-dir"))

		// Valid input arguments to main.
		checkMainSyntax(c)

		// Migrate any old version of config / state files to newer format.
		migrate()

		// Initialize config.
		err := initConfig()
		fatalIf(err, "Unable to initialize minio config.")

		// Enable all loggers by now.
		enableLoggers()

		// Initialize name space lock.
		initNSLock()

		// Set global quiet flag.
		globalQuiet = c.Bool("quiet") || c.GlobalBool("quiet")

		// Do not print update messages, if quiet flag is set.
		if !globalQuiet {
			// Do not print any errors in release update function.
			noError := true
			updateMsg := getReleaseUpdate(minioUpdateStableURL, noError)
			if updateMsg.Update {
				console.Println(updateMsg)
			}
		}

		// Return here.
		return nil
	}
	// Run the app - exit on error.
	app.RunAndExitOnError()
}
<commit_msg>Write pprof output files under config dir supplied (#1660)<commit_after>/*
 * Minio Cloud Storage, (C) 2015, 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"sort"

	"github.com/minio/cli"
	"github.com/minio/mc/pkg/console"
	"github.com/minio/minio/pkg/probe"
	"github.com/pkg/profile"
)

var (
	// global flags for minio.
	minioFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "help, h",
			Usage: "Show help.",
		},
	}
)

// Help template for minio.
var minioHelpTemplate = `NAME:
  {{.Name}} - {{.Usage}}

DESCRIPTION:
  {{.Description}}

USAGE:
  minio {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
  {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
  {{end}}{{if .Flags}}
FLAGS:
  {{range .Flags}}{{.}}
  {{end}}{{end}}
VERSION:
  ` + minioVersion +
	`{{ "\n"}}`

// init - check the environment before main starts
func init() {
	// Check if minio was compiled using a supported version of Golang.
	checkGoVersion()

	// It is an unsafe practice to run network services as
	// root. Containers are an exception.
	if !isContainerized() && os.Geteuid() == 0 {
		console.Fatalln("Please run ‘minio’ as a non-root user.")
	}
}

func migrate() {
	// Migrate config file
	migrateConfig()

	// Migrate other configs here.
}

func enableLoggers() {
	// Enable all loggers here.
	enableConsoleLogger()
	enableFileLogger()

	// Add your logger here.
}

func findClosestCommands(command string) []string {
	var closestCommands []string
	for _, value := range commandsTree.PrefixMatch(command) {
		closestCommands = append(closestCommands, value.(string))
	}
	sort.Strings(closestCommands)
	// Suggest other close commands - allow missed, wrongly added and
	// even transposed characters
	for _, value := range commandsTree.walk(commandsTree.root) {
		if sort.SearchStrings(closestCommands, value.(string)) < len(closestCommands) {
			continue
		}
		// 2 is arbitrary and represents the max
		// allowed number of typed errors
		if DamerauLevenshteinDistance(command, value.(string)) < 2 {
			closestCommands = append(closestCommands, value.(string))
		}
	}
	return closestCommands
}

func registerApp() *cli.App {
	// Register all commands.
	registerCommand(serverCmd)
	registerCommand(versionCmd)
	registerCommand(updateCmd)

	// Set up app.
	app := cli.NewApp()
	app.Name = "Minio"
	app.Author = "Minio.io"
	app.Usage = "Distributed Object Storage Server for Micro Services."
	app.Description = `Micro services environment provisions one Minio server per application instance. Scalability is achieved through large number of smaller personalized instances. This version of the Minio binary is built using Filesystem storage backend for magnetic and solid state disks.`
	app.Flags = append(minioFlags, globalFlags...)
	app.Commands = commands
	app.CustomAppHelpTemplate = minioHelpTemplate
	app.CommandNotFound = func(ctx *cli.Context, command string) {
		msg := fmt.Sprintf("‘%s’ is not a minio sub-command. See ‘minio --help’.", command)
		closestCommands := findClosestCommands(command)
		if len(closestCommands) > 0 {
			msg += fmt.Sprintf("\n\nDid you mean one of these?\n")
			for _, cmd := range closestCommands {
				msg += fmt.Sprintf("        ‘%s’\n", cmd)
			}
		}
		console.Fatalln(msg)
	}
	return app
}

func checkMainSyntax(c *cli.Context) {
	configPath, err := getConfigPath()
	if err != nil {
		console.Fatalf("Unable to obtain user's home directory. \nError: %s\n", err)
	}
	if configPath == "" {
		console.Fatalln("Config folder cannot be empty, please specify --config-dir <foldername>.")
	}
}

// mustGetProfilePath must get location that the profile will be written to.
func mustGetProfilePath() string {
	return filepath.Join(mustGetConfigPath(), globalMinioProfilePath)
}

func main() {
	// Set global trace flag.
	trace := os.Getenv("MINIO_TRACE")
	globalTrace = trace == "1"

	probe.Init() // Set project's root source path.
	probe.SetAppInfo("Release-Tag", minioReleaseTag)
	probe.SetAppInfo("Commit-ID", minioShortCommitID)

	app := registerApp()
	app.Before = func(c *cli.Context) error {
		// Sets new config folder.
		setGlobalConfigPath(c.GlobalString("config-dir"))

		// Valid input arguments to main.
		checkMainSyntax(c)

		// Migrate any old version of config / state files to newer format.
		migrate()

		// Initialize config.
		err := initConfig()
		fatalIf(err, "Unable to initialize minio config.")

		// Enable all loggers by now.
		enableLoggers()

		// Initialize name space lock.
		initNSLock()

		// Set global quiet flag.
		globalQuiet = c.Bool("quiet") || c.GlobalBool("quiet")

		// Do not print update messages, if quiet flag is set.
		if !globalQuiet {
			// Do not print any errors in release update function.
			noError := true
			updateMsg := getReleaseUpdate(minioUpdateStableURL, noError)
			if updateMsg.Update {
				console.Println(updateMsg)
			}
		}

		// Enable profiling supported modes are [cpu, mem, block].
		// ``MINIO_PROFILER`` supported options are [cpu, mem, block].
		switch os.Getenv("MINIO_PROFILER") {
		case "cpu":
			defer profile.Start(profile.CPUProfile, profile.ProfilePath(mustGetProfilePath())).Stop()
		case "mem":
			defer profile.Start(profile.MemProfile, profile.ProfilePath(mustGetProfilePath())).Stop()
		case "block":
			defer profile.Start(profile.BlockProfile, profile.ProfilePath(mustGetProfilePath())).Stop()
		}

		// Return here.
		return nil
	}
	// Run the app - exit on error.
	app.RunAndExitOnError()
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/pressly/prefixer"

	"golang.org/x/crypto/ssh"
	"gopkg.in/yaml.v2"
)

// Config represents the configuration data that are
// loaded from the Supfile YAML file.
type Config struct {
	Networks map[string]Network  `yaml:"networks"`
	Commands map[string]Command  `yaml:"commands"`
	Targets  map[string][]string `yaml:"targets"`
	Env      map[string]string   `yaml:"env"`
}

// Network represents the group of hosts with a custom env.
type Network struct {
	Hosts []string          `yaml:"hosts"`
	Env   map[string]string `yaml:"env"`
}

// Command represents set of commands to be run remotely.
type Command struct {
	Name   string `yaml:-` // To be parsed manually.
	Desc   string `yaml:"desc"`
	Exec   string `yaml:"exec`
	Script string `yaml:"script"` // A file to be read into Exec.
}

// usage prints help for an arg and exits.
func usage(conf *Config, arg int) {
	log.Println("Usage: sup <network> <target/command>\n")
	switch arg {
	case 1:
		// <network> missing, print available hosts.
		log.Println("Available networks (from Supfile):")
		for name, network := range conf.Networks {
			log.Printf("- %v\n", name)
			for _, host := range network.Hosts {
				log.Printf("   - %v\n", host)
			}
		}
	case 2:
		// <target/command> not found or missing,
		// print available targets/commands.
		log.Println("Available targets (from Supfile):")
		for target, _ := range conf.Targets {
			log.Printf("- %v\n", target)
		}
		log.Println("Available commands (from Supfile):")
		for cmd, _ := range conf.Commands {
			log.Printf("- %v\n", cmd)
		}
	}
	os.Exit(1)
}

func main() {
	var (
		conf       Config
		commands   []Command
		paddingLen int
	)

	// Read configuration file.
	data, _ := ioutil.ReadFile("./Supfile")
	if err := yaml.Unmarshal(data, &conf); err != nil {
		log.Fatal(err)
	}

	// Check for the first argument first
	if len(os.Args) < 2 {
		usage(&conf, len(os.Args))
	}
	// Does the <network> exist?
	network, ok := conf.Networks[os.Args[1]]
	if !ok {
		log.Printf("Unknown network \"%v\"\n\n", os.Args[1])
		usage(&conf, 1)
	}

	// Does <network> have any hosts?
	if len(network.Hosts) == 0 {
		log.Printf("No hosts specified for network \"%v\"", os.Args[1])
		usage(&conf, 1)
	}

	// Check for the second argument
	if len(os.Args) < 3 {
		usage(&conf, len(os.Args))
	}
	// Does the <target/command> exist?
	target, isTarget := conf.Targets[os.Args[2]]
	if isTarget {
		// It's the target. Loop over its commands.
		for _, cmd := range target {
			// Does the target's command exist?
			command, isCommand := conf.Commands[cmd]
			if !isCommand {
				log.Printf("Unknown command \"%v\" (from target \"%v\": %v)\n\n", cmd, os.Args[2], target)
				usage(&conf, 2)
			}
			command.Name = cmd
			commands = append(commands, command)
		}
	} else {
		// It's probably a command. Does it exist?
		command, isCommand := conf.Commands[os.Args[2]]
		if !isCommand {
			// Not a target, nor command.
			log.Printf("Unknown target/command \"%v\"\n\n", os.Args[2])
			usage(&conf, 2)
		}
		command.Name = os.Args[2]
		commands = append(commands, command)
	}

	// Check for extra arguments
	if len(os.Args) != 3 {
		usage(&conf, len(os.Args))
	}

	// Process all ENVs into a string of form
	// `export FOO="bar"; export BAR="baz";`.
	env := ``
	for name, value := range conf.Env {
		env += `export ` + name + `="` + value + `";`
	}
	for name, value := range network.Env {
		env += `export ` + name + `="` + value + `";`
	}

	// Open SSH connection to all the hosts.
	clients := make([]*SSHClient, len(network.Hosts))
	for i, host := range network.Hosts {
		c := &SSHClient{
			Env: env,
		}
		if err := c.Connect(host); err != nil {
			log.Fatal(err)
		}
		defer c.Close()

		len := len(c.User) + 1 + len(c.Host)
		if len > paddingLen {
			paddingLen = len
		}

		clients[i] = c
	}

	// Run the command(s) remotely on all hosts in parallel.
	// Run multiple commands (from) sequentally.
	for _, cmd := range commands {
		// Script? Read it into the Exec as string of commands.
		if cmd.Script != "" {
			f, err := os.Open(cmd.Script)
			if err != nil {
				log.Fatal(err)
			}
			data, err = ioutil.ReadAll(f)
			if err != nil {
				log.Fatal(err)
			}
			cmd.Exec = string(data)
		}

		// No commands specified for the command.
		if cmd.Exec == "" {
			log.Fatalf("Run command \"%v\": Nothing to run", cmd.Name)
		}

		// Run the command on all hosts in parallel.
		for i, c := range clients {
			padding := strings.Repeat(" ", paddingLen-(len(c.User)+1+len(c.Host)))
			color := Colors[i%len(Colors)]

			c.Prefix = color + padding + c.User + "@" + c.Host + " | "
			c.Run(cmd)

			go func(c *SSHClient) {
				if _, err := io.Copy(os.Stdout, prefixer.New(c.RemoteStdout, c.Prefix)); err != nil {
					log.Printf("%sSTDOUT error: %v", c.Prefix, c.Host, err)
				}
			}(c)
			go func(c *SSHClient) {
				if _, err := io.Copy(os.Stderr, prefixer.New(c.RemoteStderr, c.Prefix)); err != nil {
					log.Printf("%sSTDERR error: %v", c.Prefix, c.Host, err)
				}
			}(c)
		}

		// Wait for all hosts to finish.
		for _, c := range clients {
			if err := c.Wait(); err != nil {
				//TODO: Handle the SSH ExitError in ssh.go?
				e, ok := err.(*ssh.ExitError)
				if !ok {
					log.Fatalf("%sexpected *ExitError but got %T", c.Prefix, err)
				}
				if e.ExitStatus() != 15 {
					log.Fatalf("%sexit %v", c.Prefix, e.ExitStatus())
				}
			}
		}
	}

	//TODO: We should wait for all io.Copy() goroutines.
	//TODO: We should not exit 0, if there was an error.
}
<commit_msg>Split argument parsing into its own function<commit_after>package main

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/pressly/prefixer"

	"golang.org/x/crypto/ssh"
	"gopkg.in/yaml.v2"
)

// Config represents the configuration data that are
// loaded from the Supfile YAML file.
type Config struct {
	Networks map[string]Network  `yaml:"networks"`
	Commands map[string]Command  `yaml:"commands"`
	Targets  map[string][]string `yaml:"targets"`
	Env      map[string]string   `yaml:"env"`
}

// Network represents the group of hosts with a custom env.
type Network struct {
	Hosts []string          `yaml:"hosts"`
	Env   map[string]string `yaml:"env"`
}

// Command represents set of commands to be run remotely.
type Command struct {
	Name   string `yaml:-` // To be parsed manually.
	Desc   string `yaml:"desc"`
	Exec   string `yaml:"exec`
	Script string `yaml:"script"` // A file to be read into Exec.
}

// usage prints help for an arg and exits.
func usage(conf *Config, arg int) {
	log.Println("Usage: sup <network> <target/command>\n")
	switch arg {
	case 1:
		// <network> missing, print available hosts.
		log.Println("Available networks (from Supfile):")
		for name, network := range conf.Networks {
			log.Printf("- %v\n", name)
			for _, host := range network.Hosts {
				log.Printf("   - %v\n", host)
			}
		}
	case 2:
		// <target/command> not found or missing,
		// print available targets/commands.
		log.Println("Available targets (from Supfile):")
		for target, _ := range conf.Targets {
			log.Printf("- %v\n", target)
		}
		log.Println("Available commands (from Supfile):")
		for cmd, _ := range conf.Commands {
			log.Printf("- %v\n", cmd)
		}
	}
	os.Exit(1)
}

// parseArgs parses os.Args for network and commands to be run.
func parseArgs(conf *Config) (Network, []Command) {
	var commands []Command

	// Check for the first argument first
	if len(os.Args) < 2 {
		usage(conf, len(os.Args))
	}
	// Does the <network> exist?
	network, ok := conf.Networks[os.Args[1]]
	if !ok {
		log.Printf("Unknown network \"%v\"\n\n", os.Args[1])
		usage(conf, 1)
	}

	// Does <network> have any hosts?
	if len(network.Hosts) == 0 {
		log.Printf("No hosts specified for network \"%v\"", os.Args[1])
		usage(conf, 1)
	}

	// Check for the second argument
	if len(os.Args) < 3 {
		usage(conf, len(os.Args))
	}
	// Does the <target/command> exist?
	target, isTarget := conf.Targets[os.Args[2]]
	if isTarget {
		// It's the target. Loop over its commands.
		for _, cmd := range target {
			// Does the target's command exist?
			command, isCommand := conf.Commands[cmd]
			if !isCommand {
				log.Printf("Unknown command \"%v\" (from target \"%v\": %v)\n\n", cmd, os.Args[2], target)
				usage(conf, 2)
			}
			command.Name = cmd
			commands = append(commands, command)
		}
	} else {
		// It's probably a command. Does it exist?
		command, isCommand := conf.Commands[os.Args[2]]
		if !isCommand {
			// Not a target, nor command.
			log.Printf("Unknown target/command \"%v\"\n\n", os.Args[2])
			usage(conf, 2)
		}
		command.Name = os.Args[2]
		commands = append(commands, command)
	}

	// Check for extra arguments
	if len(os.Args) != 3 {
		usage(conf, len(os.Args))
	}

	return network, commands
}

func main() {
	var (
		conf       Config
		paddingLen int
	)

	// Read configuration file.
	data, _ := ioutil.ReadFile("./Supfile")
	if err := yaml.Unmarshal(data, &conf); err != nil {
		log.Fatal(err)
	}

	// Parse network and commands to be run from os.Args.
	network, commands := parseArgs(&conf)

	// Process all ENVs into a string of form
	// `export FOO="bar"; export BAR="baz";`.
	env := ``
	for name, value := range conf.Env {
		env += `export ` + name + `="` + value + `";`
	}
	for name, value := range network.Env {
		env += `export ` + name + `="` + value + `";`
	}

	// Open SSH connection to all the hosts.
	clients := make([]*SSHClient, len(network.Hosts))
	for i, host := range network.Hosts {
		c := &SSHClient{
			Env: env,
		}
		if err := c.Connect(host); err != nil {
			log.Fatal(err)
		}
		defer c.Close()

		len := len(c.User) + 1 + len(c.Host)
		if len > paddingLen {
			paddingLen = len
		}

		clients[i] = c
	}

	// Run the command(s) remotely on all hosts in parallel.
	// Run multiple commands (from) sequentally.
	for _, cmd := range commands {
		// Script? Read it into the Exec as string of commands.
		if cmd.Script != "" {
			f, err := os.Open(cmd.Script)
			if err != nil {
				log.Fatal(err)
			}
			data, err = ioutil.ReadAll(f)
			if err != nil {
				log.Fatal(err)
			}
			cmd.Exec = string(data)
		}

		// No commands specified for the command.
		if cmd.Exec == "" {
			log.Fatalf("Run command \"%v\": Nothing to run", cmd.Name)
		}

		// Run the command on all hosts in parallel.
		for i, c := range clients {
			padding := strings.Repeat(" ", paddingLen-(len(c.User)+1+len(c.Host)))
			color := Colors[i%len(Colors)]

			c.Prefix = color + padding + c.User + "@" + c.Host + " | "
			c.Run(cmd)

			go func(c *SSHClient) {
				if _, err := io.Copy(os.Stdout, prefixer.New(c.RemoteStdout, c.Prefix)); err != nil {
					log.Printf("%sSTDOUT error: %v", c.Prefix, c.Host, err)
				}
			}(c)
			go func(c *SSHClient) {
				if _, err := io.Copy(os.Stderr, prefixer.New(c.RemoteStderr, c.Prefix)); err != nil {
					log.Printf("%sSTDERR error: %v", c.Prefix, c.Host, err)
				}
			}(c)
		}

		// Wait for all hosts to finish.
		for _, c := range clients {
			if err := c.Wait(); err != nil {
				//TODO: Handle the SSH ExitError in ssh.go?
				e, ok := err.(*ssh.ExitError)
				if !ok {
					log.Fatalf("%sexpected *ExitError but got %T", c.Prefix, err)
				}
				if e.ExitStatus() != 15 {
					log.Fatalf("%sexit %v", c.Prefix, e.ExitStatus())
				}
			}
		}
	}

	//TODO: We should wait for all io.Copy() goroutines.
	//TODO: We should not exit 0, if there was an error.
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"net/url"
	"time"
)

// Version is initialized at compilation time
var Version = "0.0.0"

// BuildTime is initialized at compilation time
var BuildTime = time.Now().Format(time.RFC3339)

func main() {
	var urlFlag = flag.String("url", "mongodb://localhost:27017/test", "MongoDB connection URI.")
	var cFlag = flag.String("c", "sessions", "MongoDB collection to cleanup.")
	var fFlag = flag.String("f", "updated_at", "MongoDB collection field with type 'time.Time'.")
	var rFlag = flag.Int("r", 168, "MongoDB retention delai in hour(s). Default is 7 days (168 hours).")
	var sFlag = flag.Bool("s", false, "Simulation mode, no deletion are send to the MongoDB database.")

	flag.Parse()

	hostPort, err := url.Parse(*urlFlag)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("[Mongo clean session - Version %s (build at %s)]", Version, BuildTime))
	fmt.Println("MongoDB connection URL:              ", hostPort.Host)
	fmt.Println("MongoDB collection to clean:         ", *cFlag)
	fmt.Println("MongoDB collection field:            ", *fFlag)
	fmt.Println("MongoDB retention periode in hour(s):", *rFlag)
	fmt.Println("Simulation mode:                     ", *sFlag)
	fmt.Println()

	session, err := mgo.Dial(*urlFlag)
	if err != nil {
		panic(err)
	}
	defer session.Close()

	session.SetMode(mgo.Monotonic, true)

	Sessions := session.DB("").C(*cFlag)
	counter, err := Sessions.Count()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Number of item(s) in           %s: %d", *cFlag, counter))

	duration := time.Now().Add(time.Duration(-*rFlag) * time.Hour)
	query := bson.M{fmt.Sprintf("%s", *fFlag): bson.M{"$lte": duration}}

	counter_to_delete, err := Sessions.Find(query).Count()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Number of item(s) to delete in %s: %d", *cFlag, counter_to_delete))

	if *sFlag == false {
		info, err := Sessions.RemoveAll(query)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(fmt.Sprintf("Number of item(s) deleted in   %s: %d", *cFlag, info.Removed))
	} else {
		fmt.Println("[SIMULATION_MODE] In simulation mode no elements are deleted!!!")
	}
}
<commit_msg>Fix BuildTime value.<commit_after>package main

import (
	"flag"
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"net/url"
	"time"
)

// Version is initialized at compilation time
var Version = "0.0.0"

// BuildTime is initialized at compilation time
var BuildTime = "2016-06-29T00:39:38+0200"

func main() {
	var urlFlag = flag.String("url", "mongodb://localhost:27017/test", "MongoDB connection URI.")
	var cFlag = flag.String("c", "sessions", "MongoDB collection to cleanup.")
	var fFlag = flag.String("f", "updated_at", "MongoDB collection field with type 'time.Time'.")
	var rFlag = flag.Int("r", 168, "MongoDB retention delai in hour(s). Default is 7 days (168 hours).")
	var sFlag = flag.Bool("s", false, "Simulation mode, no deletion are send to the MongoDB database.")

	flag.Parse()

	hostPort, err := url.Parse(*urlFlag)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("[Mongo clean session - Version %s (build at %s)]", Version, BuildTime))
	fmt.Println("MongoDB connection URL:              ", hostPort.Host)
	fmt.Println("MongoDB collection to clean:         ", *cFlag)
	fmt.Println("MongoDB collection field:            ", *fFlag)
	fmt.Println("MongoDB retention periode in hour(s):", *rFlag)
	fmt.Println("Simulation mode:                     ", *sFlag)
	fmt.Println()

	session, err := mgo.Dial(*urlFlag)
	if err != nil {
		panic(err)
	}
	defer session.Close()

	session.SetMode(mgo.Monotonic, true)

	Sessions := session.DB("").C(*cFlag)
	counter, err := Sessions.Count()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Number of item(s) in           %s: %d", *cFlag, counter))

	duration := time.Now().Add(time.Duration(-*rFlag) * time.Hour)
	query := bson.M{fmt.Sprintf("%s", *fFlag): bson.M{"$lte": duration}}

	counter_to_delete, err := Sessions.Find(query).Count()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Number of item(s) to delete in %s: %d", *cFlag, counter_to_delete))

	if *sFlag == false {
		info, err := Sessions.RemoveAll(query)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(fmt.Sprintf("Number of item(s) deleted in   %s: %d", *cFlag, info.Removed))
	} else {
		fmt.Println("[SIMULATION_MODE] In simulation mode no elements are deleted!!!")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/textproto"
	"os"
	"strings"
	"time"

	rethink "github.com/dancannon/gorethink"
)

// GlobalConfig put together all configs
type GlobalConfig struct {
	Connect   *ConnectConfig `json:"connect"`
	*DBConfig `json:"db"`
	UserCnf   *UserConfig              `json:"user_cnf"`
	ChConfs   map[string]ChannelConfig `json:"channels"`
}

// DBConfig settings for RethinkDb
type DBConfig struct {
	Host string `json:"host"`
	Port string `json:"port"`
}

// UserConfig for nickname and other things
type UserConfig struct {
	NickName string `json:"nick"`
}

// ChannelConfig settigns for separate channel
type ChannelConfig struct {
	ChanName string `json:"name"`
	URLs     bool   `json:"store_urls"`
}

// ConnectConfig settings for connecting to the twitch server
type ConnectConfig struct {
	URL      string `json:"host"`
	Port     string `json:"port"`
	UserName string `json:"user"`
	Password string `json:"oauth"`
}

// Channel settings for specific channel
type Channel struct {
	Conn   *net.Conn
	Config ChannelConfig
}

// Message every message in chat
type Message struct {
	CreatedAt time.Time `gorethink:"created_at"`
	Author    string    `gorethink:"msg_author"`
	ChanName  string    `gorethink:"channel_name"`
	Formated  string    `gorethink:"formated_message"`
	HasURL    bool      `gorethink:"has_url"`
	RawMsg    string    `gorethink:"raw_msg"`
}

// ParseConfig read config file and return Config struct
func ParseConfig(path string) (conf GlobalConfig, err error) {
	data, err := ioutil.ReadFile("./config.json")
	if err != nil {
		log.Fatalf("Couldn't read config file: %s  \n", err)
		return
	}
	err = json.Unmarshal(data, &conf)
	return
}

// InitConnect init coonect to the twitch chat
func InitConnect(cfg ConnectConfig) (conn net.Conn, err error) {
	conn, err = net.Dial("tcp", cfg.URL+":"+cfg.Port)
	if err != nil {
		log.Fatal(err)
		return
	}
	fmt.Fprintf(conn, "USER %s 8 * :%s\r\n", cfg.UserName, cfg.UserName)
	fmt.Fprintf(conn, "PASS %s\r\n", cfg.Password)
	fmt.Fprintf(conn, "NICK %s\r\n", cfg.UserName)
	return
}

// JoinChannel join specific channel with given nick
func (c Channel) JoinChannel(out chan string) {
	fmt.Fprintf(*c.Conn, "JOIN %s\r\n", "#"+c.Config.ChanName)
	reader := bufio.NewReader(*c.Conn)
	tp := textproto.NewReader(reader)
	for {
		line, err := tp.ReadLine()
		if strings.Contains(line, "PING") {
			pong := strings.Split(line, "PING ")
			fmt.Fprintf(*c.Conn, "PONG %s\r\n", pong[0])
			continue
		}
		//TODO I have receive every 10-20 mins EOF, must think about it
		// Find - Twitch sendns me PINg every ~10-15 mins PING and i musy answer it
		if err != nil {
			log.Fatalf("while read %s \n", err)
		}
		out <- line
	}
}

func stringBetweenChars(text, f, l string) (between string) {
	first, last := strings.IndexAny(text, f), strings.IndexAny(text, l)
	if first == -1 || last == -1 {
		return
	}
	between = text[first:last-1]
	return
}

// FormatMessage converts raw data to Message
// raw message looks like :feikga!feikga@feikga.tmi.twitch.tv PRIVMSG #test :No?
func formatMessage(raw string) (msg Message) {
	defer func() {
		if p := recover(); p != nil {
			log.Printf("Error : %v \n with message: %s \n ", p, raw)
		}
	}()
	msg = Message{CreatedAt: time.Now(), HasURL: false, RawMsg: raw}
	if strings.Contains(raw, "PRIVMSG") {
		info := strings.Split(raw, ".tmi.twitch.tv PRIVMSG #")[0]
		message := strings.Split(raw, ".tmi.twitch.tv PRIVMSG #")[1]
		msg.Author = stringBetweenChars(info, ":", "!")
		msg.ChanName = stringBetweenChars(message, "#", ":")
		msgBegins := strings.IndexAny(message, ":")
		msg.Formated = message[msgBegins:]
		if strings.Contains(msg.Formated, "http") {
			msg.HasURL = true
		}
	} else {
		fmt.Fprintln(os.Stderr, raw)
	}
	return
}

// ConsumeData ouputed all data
func ConsumeData(data chan string, db *rethink.Session) {
	for i := range data {
		if !strings.Contains(i, "_bot") {
			msg := formatMessage(i)
			result, err := rethink.DB("Channels").Table("test").Insert(msg).RunWrite(session)
			if err != nil {
				log.Fatalf("error %s while inserting data %v \n", err, result.GeneratedKeys[0])
			}
		}
	}
}

var session *rethink.Session

func main() {
	conf, err := ParseConfig("./config.json")
	if err != nil {
		log.Fatal(err)
	}
	session, err = rethink.Connect(rethink.ConnectOpts{
		Address:  conf.DBConfig.Host + ":" + conf.DBConfig.Port,
		Database: "Channels",
	})
	if err != nil {
		log.Fatalln(err)
	}
	conn, err := InitConnect(*conf.Connect)
	if err != nil {
		log.Fatal(err)
	}
	activeCh := make(map[string]Channel, len(conf.ChConfs))
	data := make(chan string, 3*len(conf.ChConfs))
	for k, v := range conf.ChConfs {
		activeCh[k] = Channel{Conn: &conn, Config: v}
		go activeCh[k].JoinChannel(data)
	}
	ConsumeData(data, session)
}
<commit_msg>rewrite formatMessage, try emprove parsing<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/textproto"
	"os"
	"strings"
	"time"

	rethink "github.com/dancannon/gorethink"
)

// GlobalConfig put together all configs
type GlobalConfig struct {
	Connect   *ConnectConfig `json:"connect"`
	*DBConfig `json:"db"`
	UserCnf   *UserConfig              `json:"user_cnf"`
	ChConfs   map[string]ChannelConfig `json:"channels"`
}

// DBConfig settings for RethinkDb
type DBConfig struct {
	Host string `json:"host"`
	Port string `json:"port"`
}

// UserConfig for nickname and other things
type UserConfig struct {
	NickName string `json:"nick"`
}

// ChannelConfig settigns for separate channel
type ChannelConfig struct {
	ChanName string `json:"name"`
	URLs     bool   `json:"store_urls"`
}

// ConnectConfig settings for connecting to the twitch server
type ConnectConfig struct {
	URL      string `json:"host"`
	Port     string `json:"port"`
	UserName string `json:"user"`
	Password string `json:"oauth"`
}

// Channel settings for specific channel
type Channel struct {
	Conn   *net.Conn
	Config ChannelConfig
}

// Message every message in chat
type Message struct {
	CreatedAt time.Time `gorethink:"created_at"`
	Author    string    `gorethink:"msg_author"`
	ChanName  string    `gorethink:"channel_name"`
	Formated  string    `gorethink:"formated_message"`
	HasURL    bool      `gorethink:"has_url"`
	RawMsg    string    `gorethink:"raw_msg"`
}

// ParseConfig read config file and return Config struct
func ParseConfig(path string) (conf GlobalConfig, err error) {
	data, err := ioutil.ReadFile("./config.json")
	if err != nil {
		log.Fatalf("Couldn't read config file: %s  \n", err)
		return
	}
	err = json.Unmarshal(data, &conf)
	return
}

// InitConnect init coonect to the twitch chat
func InitConnect(cfg ConnectConfig) (conn net.Conn, err error) {
	conn, err = net.Dial("tcp", cfg.URL+":"+cfg.Port)
	if err != nil {
		log.Fatal(err)
		return
	}
	fmt.Fprintf(conn, "USER %s 8 * :%s\r\n", cfg.UserName, cfg.UserName)
	fmt.Fprintf(conn, "PASS %s\r\n", cfg.Password)
	fmt.Fprintf(conn, "NICK %s\r\n", cfg.UserName)
	return
}

// JoinChannel join specific channel with given nick
func (c Channel) JoinChannel(out chan Message) {
	fmt.Fprintf(*c.Conn, "JOIN %s\r\n", "#"+c.Config.ChanName)

	reader := bufio.NewReader(*c.Conn)
	tp := textproto.NewReader(reader)
	for {
		line, err := tp.ReadLine()
		if strings.Contains(line, "PING") {
			pong := strings.Split(line, "PING ")
			fmt.Fprintf(*c.Conn, "PONG %s\r\n", pong[0])
			continue
		}
		//TODO I have receive every 10-20 mins EOF, must think about it
		// Find - Twitch sendns me PING's every ~10-15 mins PING and i musy answer it
		if err != nil {
			log.Fatalf("while read %s \n", err)
		}
		msg := Message{RawMsg: line, ChanName: c.Config.ChanName, CreatedAt: time.Now()}
		out <- msg
	}
}

func stringBetweenChars(text, f, l string) (between string) {
	first, last := strings.IndexAny(text, f), strings.IndexAny(text, l)
	if first == -1 || last == -1 {
		return
	}
	between = text[first+1 : last]
	return
}

// FormatMessage converts raw data to Message
// raw message looks like :feikga!feikga@feikga.tmi.twitch.tv PRIVMSG #test :No?
func formatMessage(input *Message) {
	defer func() {
		if p := recover(); p != nil {
			log.Printf("Error : %v \n with message: %s \n ", p, input.RawMsg)
		}
	}()
	if strings.Contains(input.RawMsg, "PRIVMSG") {
		info := strings.Split(input.RawMsg, ".tmi.twitch.tv PRIVMSG #"+input.ChanName+" :")[0]
		message := strings.Split(input.RawMsg, ".tmi.twitch.tv PRIVMSG #"+input.ChanName+" :")[1]
		input.Author = stringBetweenChars(info, ":", "!")
		input.Formated = message
		if strings.Contains(input.Formated, "http") {
			input.HasURL = true
		}
	} else {
		fmt.Fprintln(os.Stderr, input.RawMsg)
	}
}

// ConsumeData ouputed all data
func ConsumeData(data chan Message, db *rethink.Session) {
	for i := range data {
		if !strings.Contains(i.RawMsg, "_bot") {
			formatMessage(&i)
			result, err := rethink.DB("Channels").Table("test").Insert(&i).RunWrite(session)
			if err != nil {
				log.Fatalf("error %s while inserting data %v \n", err, result.GeneratedKeys[0])
			}
		}
	}
}

var session *rethink.Session

func main() {
	conf, err := ParseConfig("./config.json")
	if err != nil {
		log.Fatal(err)
	}
	session, err = rethink.Connect(rethink.ConnectOpts{
		Address:  conf.DBConfig.Host + ":" + conf.DBConfig.Port,
		Database: "Channels",
	})
	if err != nil {
		log.Fatalln(err)
	}
	conn, err := InitConnect(*conf.Connect)
	if err != nil {
		log.Fatal(err)
	}
	activeCh := make(map[string]Channel, len(conf.ChConfs))
	data := make(chan Message, 3*len(conf.ChConfs))
	for k, v := range conf.ChConfs {
		activeCh[k] = Channel{Conn: &conn, Config: v}
		go activeCh[k].JoinChannel(data)
	}
	ConsumeData(data, session)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/drone/drone-go/drone"
	"github.com/drone/drone-go/plugin"
	"golang.org/x/crypto/ssh"
)

type Rsync struct {
	Hosts drone.StringSlice `json:"host"`
	host  string            `json:"-"`

	User      string   `json:"user"`
	Port      int      `json:"port"`
	Source    string   `json:"source"`
	Target    string   `json:"target"`
	Delete    bool     `json:"delete"`
	Recursive bool     `json:"recursive"`
	Exclude   []string `json:"exclude"`
	Commands  []string `json:"commands"`
}

func main() {
	w := new(drone.Workspace)
	v := new(Rsync)
	plugin.Param("workspace", w)
	plugin.Param("vargs", v)
	plugin.Parse()

	// write the rsa private key if provided
	if err := writeKey(w); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// default values
	if v.Port == 0 {
		v.Port = 22
	}
	if len(v.User) == 0 {
		v.User = "root"
	}
	if len(v.Source) == 0 {
		v.Source = "./"
	}

	// execute for each host
	for _, host := range v.Hosts.Slice() {

		// sets the current host
		v.host = host

		// sync the files on the remote machine
		rs := buildRsync(v)
		rs.Dir = w.Path
		rs.Stderr = os.Stderr
		rs.Stdout = os.Stdout
		trace(rs)
		err := rs.Run()
		if err != nil {
			os.Exit(1)
			return
		}

		// continue if no commands
		if len(v.Commands) == 0 {
			continue
		}

		// execute commands on remote server (reboot instance, etc)
		if err := run(v, w.Keys); err != nil {
			os.Exit(1)
			return
		}
	}
}

// Build rsync command
func buildRsync(rs *Rsync) *exec.Cmd {

	var args []string
	args = append(args, "-az")

	// append recursive flag
	if rs.Recursive {
		args = append(args, "-r")
	}

	// append delete flag
	if rs.Delete {
		args = append(args, "--del")
	}

	// append custom ssh parameters
	args = append(args, "-e")
	args = append(args, fmt.Sprintf("ssh -p %d -o UserKnownHostsFile=/dev/null -o LogLevel=quiet -o StrictHostKeyChecking=no", rs.Port))

	// append files to exclude
	if len(rs.Exclude) != 0 {
		for _,pattern := range rs.Exclude {
			args = append(args, fmt.Sprintf("--exclude=%s", pattern))
		}
	}

	args = append(args, rs.Source)
	args = append(args, fmt.Sprintf("%s@%s:%s", rs.User, rs.host, rs.Target))

	return exec.Command("rsync", args...)
}

// Run commands on the remote host
func run(rs *Rsync, keys *drone.Key) error {

	// join the host and port if necessary
	addr := net.JoinHostPort(
		rs.host,
		strconv.Itoa(rs.Port),
	)

	// trace command used for debugging in the build logs
	fmt.Printf("$ ssh %s@%s -p %d\n", rs.User, addr, rs.Port)

	signer, err := ssh.ParsePrivateKey([]byte(keys.Private))
	if err != nil {
		return fmt.Errorf("Error parsing private key. %s.", err)
	}

	config := &ssh.ClientConfig{
		User: rs.User,
		Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
	}

	client, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		return fmt.Errorf("Error dialing server. %s.", err)
	}

	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("Error starting ssh session. %s.", err)
	}
	defer session.Close()

	session.Stdout = os.Stdout
	session.Stderr = os.Stderr
	return session.Run(strings.Join(rs.Commands, "\n"))
}

// Trace writes each command to standard error (preceded by a ‘$ ’) before it
// is executed. Used for debugging your build.
func trace(cmd *exec.Cmd) {
	fmt.Println("$", strings.Join(cmd.Args, " "))
}

// Writes the RSA private key
func writeKey(in *drone.Workspace) error {
	if in.Keys == nil || len(in.Keys.Private) == 0 {
		return nil
	}
	home := "/root"
	u, err := user.Current()
	if err == nil {
		home = u.HomeDir
	}
	sshpath := filepath.Join(home, ".ssh")
	if err := os.MkdirAll(sshpath, 0700); err != nil {
		return err
	}
	confpath := filepath.Join(sshpath, "config")
	privpath := filepath.Join(sshpath, "id_rsa")
	ioutil.WriteFile(confpath, []byte("StrictHostKeyChecking no\n"), 0700)
	return ioutil.WriteFile(privpath, []byte(in.Keys.Private), 0600)
}
<commit_msg>use drone.StringSlice instead of array<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/drone/drone-go/drone"
	"github.com/drone/drone-go/plugin"
	"golang.org/x/crypto/ssh"
)

type Rsync struct {
	Hosts drone.StringSlice `json:"host"`
	host  string            `json:"-"`

	User      string   					`json:"user"`
	Port      int      					`json:"port"`
	Source    string   					`json:"source"`
	Target    string   					`json:"target"`
	Delete    bool     					`json:"delete"`
	Recursive bool     					`json:"recursive"`
	Exclude   drone.StringSlice `json:"exclude"`
	Commands  []string          `json:"commands"`
}

func main() {
	w := new(drone.Workspace)
	v := new(Rsync)
	plugin.Param("workspace", w)
	plugin.Param("vargs", v)
	plugin.Parse()

	// write the rsa private key if provided
	if err := writeKey(w); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// default values
	if v.Port == 0 {
		v.Port = 22
	}
	if len(v.User) == 0 {
		v.User = "root"
	}
	if len(v.Source) == 0 {
		v.Source = "./"
	}

	// execute for each host
	for _, host := range v.Hosts.Slice() {

		// sets the current host
		v.host = host

		// sync the files on the remote machine
		rs := buildRsync(v)
		rs.Dir = w.Path
		rs.Stderr = os.Stderr
		rs.Stdout = os.Stdout
		trace(rs)
		err := rs.Run()
		if err != nil {
			os.Exit(1)
			return
		}

		// continue if no commands
		if len(v.Commands) == 0 {
			continue
		}

		// execute commands on remote server (reboot instance, etc)
		if err := run(v, w.Keys); err != nil {
			os.Exit(1)
			return
		}
	}
}

// Build rsync command
func buildRsync(rs *Rsync) *exec.Cmd {

	var args []string
	args = append(args, "-az")

	// append recursive flag
	if rs.Recursive {
		args = append(args, "-r")
	}

	// append delete flag
	if rs.Delete {
		args = append(args, "--del")
	}

	// append custom ssh parameters
	args = append(args, "-e")
	args = append(args, fmt.Sprintf("ssh -p %d -o UserKnownHostsFile=/dev/null -o LogLevel=quiet -o StrictHostKeyChecking=no", rs.Port))

	// append files to exclude
	for _,pattern := range rs.Exclude.Slice {
		args = append(args, fmt.Sprintf("--exclude=%s", pattern))
	}

	args = append(args, rs.Source)
	args = append(args, fmt.Sprintf("%s@%s:%s", rs.User, rs.host, rs.Target))

	return exec.Command("rsync", args...)
}

// Run commands on the remote host
func run(rs *Rsync, keys *drone.Key) error {

	// join the host and port if necessary
	addr := net.JoinHostPort(
		rs.host,
		strconv.Itoa(rs.Port),
	)

	// trace command used for debugging in the build logs
	fmt.Printf("$ ssh %s@%s -p %d\n", rs.User, addr, rs.Port)

	signer, err := ssh.ParsePrivateKey([]byte(keys.Private))
	if err != nil {
		return fmt.Errorf("Error parsing private key. %s.", err)
	}

	config := &ssh.ClientConfig{
		User: rs.User,
		Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
	}

	client, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		return fmt.Errorf("Error dialing server. %s.", err)
	}

	session, err := client.NewSession()
	if err != nil {
		return fmt.Errorf("Error starting ssh session. %s.", err)
	}
	defer session.Close()

	session.Stdout = os.Stdout
	session.Stderr = os.Stderr
	return session.Run(strings.Join(rs.Commands, "\n"))
}

// Trace writes each command to standard error (preceded by a ‘$ ’) before it
// is executed. Used for debugging your build.
func trace(cmd *exec.Cmd) {
	fmt.Println("$", strings.Join(cmd.Args, " "))
}

// Writes the RSA private key
func writeKey(in *drone.Workspace) error {
	if in.Keys == nil || len(in.Keys.Private) == 0 {
		return nil
	}
	home := "/root"
	u, err := user.Current()
	if err == nil {
		home = u.HomeDir
	}
	sshpath := filepath.Join(home, ".ssh")
	if err := os.MkdirAll(sshpath, 0700); err != nil {
		return err
	}
	confpath := filepath.Join(sshpath, "config")
	privpath := filepath.Join(sshpath, "id_rsa")
	ioutil.WriteFile(confpath, []byte("StrictHostKeyChecking no\n"), 0700)
	return ioutil.WriteFile(privpath, []byte(in.Keys.Private), 0600)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/clawio/lib/apiserver"
	"github.com/clawio/lib/config"
	"github.com/clawio/lib/pidfile"
	"github.com/clawio/lib/signaler"
)

const PRODUCTNAME = "clawio"

func main() {

	/*********************************************
	 *** 1. Parse CLI flags   ********************
	 *********************************************/
	flags := struct {
		pidFile string // the pidfile that will be used by the daemon
		cfg     string
	}{}
	flag.StringVar(&flags.pidFile, "pidfile", fmt.Sprintf("/var/run/%s.pid", PRODUCTNAME), "PID file location")
	flag.StringVar(&flags.cfg, "config", fmt.Sprintf("/etc/sysconfig/%s.conf", PRODUCTNAME), "Configuration file location")
	flag.Parse()

	/*********************************************
	 *** 2. Create PID file   ********************
	 *********************************************/
	_, err := pidfile.New(flags.pidFile)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	/************************************************
	 *** 3. Load configuration   ********************
	 ************************************************/
	cfg, err := config.New(flags.cfg)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	/***************************************************
	 *** 4. Start HTTP/HTTPS Server ********************
	 ***************************************************/
	srv := apiserver.New(cfg, nil)
	go srv.Start()

	/***************************************************
	 *** 5. Listen to OS signals to control the daemon *
	 ***************************************************/
	sig := signaler.New(cfg, srv)
	endc := sig.Start()
	<-endc
	fmt.Println("END")
	os.Exit(0)
}
<commit_msg>Daemon is very functional now.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	apidisp "github.com/clawio/lib/api/dispatcher"
	apiauth "github.com/clawio/lib/api/providers/auth"
	apiwebdav "github.com/clawio/lib/api/providers/webdav"
	"github.com/clawio/lib/apiserver"
	authdisp "github.com/clawio/lib/auth/dispatcher"
	"github.com/clawio/lib/auth/providers/file"
	"github.com/clawio/lib/config"
	"github.com/clawio/lib/logger"
	"github.com/clawio/lib/pidfile"
	"github.com/clawio/lib/signaler"
	storagedisp "github.com/clawio/lib/storage/dispatcher"
	"github.com/clawio/lib/storage/providers/local"
)

func main() {

	/*********************************************
	 *** 1. Parse CLI flags   ********************
	 *********************************************/
	flags := struct {
		pidFile string // the pidfile that will be used by the daemon
		cfg     string
	}{}
	flag.StringVar(&flags.pidFile, "p", "", "PID file")
	flag.StringVar(&flags.cfg, "c", "", "Configuration file")
	flag.Parse()

	/*********************************************
	 *** 2. Create PID file   ********************
	 *********************************************/
	if flags.pidFile == "" {
		fmt.Println("Set pidfile with -p flag")
		os.Exit(1)
	}
	_, err := pidfile.New(flags.pidFile)
	if err != nil {
		fmt.Println("Cannot create PID file: ", err)
		os.Exit(1)
	}

	/************************************************
	 *** 3. Load configuration   ********************
	 ************************************************/
	if flags.cfg == "" {
		fmt.Println("Set configuration with -c flag")
		os.Exit(1)
	}
	cfg, err := config.New(flags.cfg)
	if err != nil {
		fmt.Println("Cannot load configuration: ", err)
		os.Exit(1)
	}

	/******************************************
	 ** 4. Connect to the syslog daemon *******
	 ******************************************/
	syslogWriter, err := logger.NewSyslogWriter("", "", cfg.GetDirectives().LogLevel)
	if err != nil {
		fmt.Println("Cannot connect to syslog: ", err)
		os.Exit(1)
	}

	/******************************************
	 ** 5. Create auth dispatcher       *******
	 ******************************************/
	fileAuthLog := logger.New(syslogWriter, cfg.GetDirectives().LogLevel, "FILEAUTH")
	fauth, err := file.New("fileauth", cfg, fileAuthLog)
	if err != nil {
		fmt.Println("Cannot create file auth provider: ", err)
		os.Exit(1)
	}
	adispLog := logger.New(syslogWriter, cfg.GetDirectives().LogLevel, "AUTHDISP")
	adisp := authdisp.New(cfg, adispLog)
	err = adisp.AddAuth(fauth) // add file auth strategy
	if err != nil {
		fmt.Println("Cannot add file auth provider to auth dispatcher: ", err)
		os.Exit(1)
	}

	/******************************************
	 ** 6. Create storage dispatcher      *****
	 ******************************************/
	localStorageLog := logger.New(syslogWriter, cfg.GetDirectives().LogLevel, "LOCALSTORAGE")
	localStorage := local.New("local", cfg, localStorageLog)

	sdispLog := logger.New(syslogWriter, cfg.GetDirectives().LogLevel, "STORAGEDISP")
	sdisp := storagedisp.New(cfg, sdispLog)
	err = sdisp.AddStorage(localStorage)
	if err != nil {
		fmt.Println("Cannot add local storage to storage dispatcher: ", err)
		os.Exit(1)
	}

	/******************************************
	 ** 7. Create API dispatcher aka router  **
	 ******************************************/
	authAPI := apiauth.New("auth", cfg, adisp, sdisp)
	webdavAPI := apiwebdav.New("webdav", cfg, adisp, sdisp)
	apdisp := apidisp.New(cfg)
	err = apdisp.AddAPI(authAPI)
	if err != nil {
		fmt.Println("Cannot add auth API to API dispatcher: ", err)
		os.Exit(1)
	}
	err = apdisp.AddAPI(webdavAPI)
	if err != nil {
		fmt.Println("Cannot add WebDAV API to API dispatcher: ", err)
		os.Exit(1)
	}

	/***************************************************
	 *** 8. Start HTTP/HTTPS Server ********************
	 ***************************************************/
	srv := apiserver.New(cfg, syslogWriter, apdisp, adisp, sdisp)
	go func() {
		err = srv.Start()
		if err != nil {
			fmt.Println("Cannot start HTTP/HTTPS API server: ", err)
			os.Exit(1)
		}
	}()

	/***************************************************
	 *** 9. Listen to OS signals to control the daemon *
	 ***************************************************/
	sig := signaler.New(cfg, srv)
	endc := sig.Start()
	<-endc
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/mitchellh/go-homedir"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
)

type Base36Url struct {
	Root string
}

func (s *Base36Url) Init(root string) {
	s.Root = root
	os.MkdirAll(s.Root, 0744)
}

func (s *Base36Url) Save(url string) string {
	files, _ := ioutil.ReadDir(s.Root)
	code := strconv.FormatUint(uint64(len(files)+1), 36)

	ioutil.WriteFile(filepath.Join(s.Root, code), []byte(url), 0744)
	return code
}

func (s *Base36Url) Load(code string) ([]byte, error) {
	return ioutil.ReadFile(filepath.Join(s.Root, code))
}

func (s *Base36Url) EncodeHandler(w http.ResponseWriter, r *http.Request) {
	url := r.PostFormValue("url")
	if url != "" {
		w.Write([]byte(s.Save(url)))
	}
}

func (s *Base36Url) DecodeHandler(w http.ResponseWriter, r *http.Request) {
    code := r.URL.Path[len("/dec/"):]
	url, err := s.Load(code)

	if err == nil {
		w.Write(url)
	} else {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("Error: URL Not Found"))
	}
}

func (s *Base36Url) RedirectHandler(w http.ResponseWriter, r *http.Request) {
    code := r.URL.Path[len("/red/"):]
	url, err := s.Load(code)

	if err == nil {
		http.Redirect(w, r, string(url), 301)
	} else {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("URL Not Found"))
	}
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	dir, _ := homedir.Dir()
	storage := &Base36Url{}
	storage.Init(filepath.Join(dir, "shawty"))

	http.HandleFunc("/", storage.EncodeHandler)
	http.HandleFunc("/dec/", storage.DecodeHandler)
	http.HandleFunc("/red/", storage.RedirectHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	http.ListenAndServe(":"+port, nil)
}
<commit_msg>go fmt<commit_after>package main

import (
	"github.com/mitchellh/go-homedir"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
)

type Base36Url struct {
	Root string
}

func (s *Base36Url) Init(root string) {
	s.Root = root
	os.MkdirAll(s.Root, 0744)
}

func (s *Base36Url) Save(url string) string {
	files, _ := ioutil.ReadDir(s.Root)
	code := strconv.FormatUint(uint64(len(files)+1), 36)

	ioutil.WriteFile(filepath.Join(s.Root, code), []byte(url), 0744)
	return code
}

func (s *Base36Url) Load(code string) ([]byte, error) {
	return ioutil.ReadFile(filepath.Join(s.Root, code))
}

func (s *Base36Url) EncodeHandler(w http.ResponseWriter, r *http.Request) {
	url := r.PostFormValue("url")
	if url != "" {
		w.Write([]byte(s.Save(url)))
	}
}

func (s *Base36Url) DecodeHandler(w http.ResponseWriter, r *http.Request) {
	code := r.URL.Path[len("/dec/"):]
	url, err := s.Load(code)

	if err == nil {
		w.Write(url)
	} else {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("Error: URL Not Found"))
	}
}

func (s *Base36Url) RedirectHandler(w http.ResponseWriter, r *http.Request) {
	code := r.URL.Path[len("/red/"):]
	url, err := s.Load(code)

	if err == nil {
		http.Redirect(w, r, string(url), 301)
	} else {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("URL Not Found"))
	}
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	dir, _ := homedir.Dir()
	storage := &Base36Url{}
	storage.Init(filepath.Join(dir, "shawty"))

	http.HandleFunc("/", storage.EncodeHandler)
	http.HandleFunc("/dec/", storage.DecodeHandler)
	http.HandleFunc("/red/", storage.RedirectHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	http.ListenAndServe(":"+port, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/", handleRoot)
	http.ListenAndServe(":4200", nil)
}

func handleRoot(rw http.ResponseWriter, req *http.Request) {
	time.Sleep(5 * time.Second)
	rw.Write([]byte(`<div style="font-size:442px; text-align:center;">42</div>`))
}
<commit_msg>add timestamp<commit_after>package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/", handleRoot)
	http.ListenAndServe(":4200", nil)
}

func handleRoot(rw http.ResponseWriter, req *http.Request) {
	t := time.Now()
	time.Sleep(5 * time.Second)

	template := `
42

	Calculated at %v`

	content := []byte(fmt.Sprintf(template, t.String()))

	rw.Write(content)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Google Inc.

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

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

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

// Command template is a trivial web server that uses the text/template (and
// html/template) package's "block" feature to implement a kind of template
// inheritance.
//
// It should be executed from the directory in which the source resides,
// as it will look for its template files in the current directory.
package main

import (
	"bufio"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/julienschmidt/httprouter"
)

var (
	songs_root = "./songs"
	books_root = "./books"
)

func main() {
	filepath.Walk(songs_root, loadSongs)
	filepath.Walk(books_root, loadBooks)

	fmt.Printf("%d Songs loaded.\n", len(loadedSongs))
	fmt.Printf("%d Books loaded.\n", len(loadedBooks))

	r := httprouter.New()

	r.GET("/", indexHandler)
	r.GET("/song/:song", songHandler)
	r.GET("/pdf/:song", pdfHandler)
	r.GET("/book/:book", bookIndexHandler)
	r.GET("/book/:book/:number", bookHandler)
	r.ServeFiles("/css/*filepath", http.Dir("css"))
	r.ServeFiles("/js/*filepath", http.Dir("js"))
	log.Fatal(http.ListenAndServe("localhost:8090", r))

}

// indexTemplate is the main site template.
// The default template includes two template blocks ("sidebar" and "content")
// that may be replaced in templates derived from this one.
//var indexTemplate = template.Must(template.ParseFiles("templates/index.tmpl"))

type DisplayList struct {
	Link  string
	Title string
}

func (song DisplayList) MatchTitle(title string) bool {
	return song.Title == title
}

type IndexPage struct {
	Title        string
	Songs        []DisplayList
	Books        []DisplayList
	ShowIndigo   bool
	SelectedSong string
	SelectedBook string
}

type SongPage struct {
	Song Song
	IndexPage
}

type BookPage struct {
	Songbook Songbook
	BookLink string
	IndexPage
}

var loadedSongs = make([]DisplayList, 0)
var loadedBooks = make([]DisplayList, 0)

// indexHandler is an HTTP handler that serves the index page.
func indexHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	t, err := template.ParseFiles("templates/index.tmpl")
	if err != nil {
		panic(err)
	}

	data := getBasicIndexData()
	data.ShowIndigo = true

	if err := t.ExecuteTemplate(w, "index.tmpl", data); err != nil {
		log.Println(err)
	}
}

// imageTemplate is a clone of indexTemplate that provides
// alternate "sidebar" and "content" templates.
//var songTemplate = template.Must(template.Must(indexTemplate.Clone()).ParseFiles("templates/song.tmpl"))

func loadSongFile(title string, transpose int) (*Song, error) {
	filename := songs_root + "/" + title + ".song"

	song, err := ParseSongFile(filename, transpose)

	if err != nil {
		return nil, err
	}

	return song, nil
}

func getBasicIndexData() IndexPage {
	index_data := IndexPage{
		Title:        "Indigo Song Book",
		Songs:        loadedSongs,
		Books:        loadedBooks,
		ShowIndigo:   false,
		SelectedSong: "",
		SelectedBook: ""}

	return index_data
}

func bookIndexHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	sbook, err := ParseSongbookFile(books_root+"/"+p.ByName("book")+".songlist", songs_root)

	if err != nil {
		fmt.Println(err)
	}

	index := getBasicIndexData()
	index.SelectedBook = sbook.Title

	book_data := &BookPage{
		Songbook:  *sbook,
		BookLink:  sbook.Filename[0 : len(sbook.Filename)-len(".songlist")],
		IndexPage: index}

	temp, err := template.ParseFiles("templates/index.tmpl", "templates/book.tmpl")
	if err != nil {
		panic(err)
	}

	if err := temp.ExecuteTemplate(w, "book.tmpl", book_data); err != nil {
		log.Println(err)
	}
}

func bookHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	num := p.ByName("number")
	n, _ := strconv.Atoi(num)

	var transpose = r.FormValue("transpose")
	if len(transpose) == 0 {
		transpose = "0"
	}
	t, err := strconv.Atoi(transpose)

	sbook, err := ParseSongbookFile(books_root+"/"+p.ByName("book")+".songlist", songs_root)

	if err != nil {
		fmt.Println(err)
	}

	song := sbook.Songs[n]
	song.Transpose = t

	index := getBasicIndexData()
	index.SelectedSong = song.Title
	index.SelectedBook = sbook.Title

	page_data := &SongPage{
		Song:      song,
		IndexPage: index}

	temp, err := template.ParseGlob("templates/*.tmpl")
	if err != nil {
		panic(err)
	}

	if err := temp.ExecuteTemplate(w, "song.tmpl", page_data); err != nil {
		log.Println(err)
	}
}

func songHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	var transpose = r.FormValue("transpose")
	if len(transpose) == 0 {
		transpose = "0"
	}
	t, err := strconv.Atoi(transpose)

	data, err := loadSongFile(p.ByName("song"), t)
	if err != nil {
		log.Println(err)
		http.NotFound(w, r)
		return
	}

	index := getBasicIndexData()
	index.SelectedSong = data.Title

	page_data := &SongPage{
		Song:      *data,
		IndexPage: index}

	temp, err := template.ParseGlob("templates/*.tmpl")
	if err != nil {
		panic(err)
	}

	if err := temp.ExecuteTemplate(w, "song.tmpl", page_data); err != nil {
		log.Println(err)
	}
}

func pdfHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	var transpose = r.FormValue("transpose")
	if len(transpose) == 0 {
		transpose = "0"
	}
	t, err := strconv.Atoi(transpose)

	song, err := loadSongFile(p.ByName("song"), t)
	if err != nil {
		log.Println(err)
		http.NotFound(w, r)
		return
	}

	pdf, err := WriteSongPDF(song)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// stream straight to client(browser)
	w.Header().Set("Content-type", "application/pdf")

	if _, err := pdf.WriteTo(w); err != nil {
		fmt.Fprintf(w, "%s", err)
	}

	w.Write([]byte("PDF Generated"))
}

func loadSongs(path string, f os.FileInfo, err error) error {
	if f.IsDir() && f.Name() != songs_root[2:] {
		return filepath.SkipDir
	} else if f.IsDir() {
		return nil
	}

	if strings.HasSuffix(strings.ToLower(f.Name()), ".song") {
		song, err := ParseSongFile(songs_root+"/"+f.Name(), 0)
		link := song.Filename[0 : len(song.Filename)-len(".song")]
		loadedSongs = append(loadedSongs, DisplayList{Link: link, Title: song.Title})

		return err
	}

	return nil
}

func loadBooks(path string, f os.FileInfo, err error) error {
	if f.IsDir() && f.Name() != books_root[2:] {
		return filepath.SkipDir
	} else if f.IsDir() {
		return nil
	}

	if strings.HasSuffix(strings.ToLower(f.Name()), ".songlist") {
		book, err := ParseSongbookFile(books_root+"/"+f.Name(), songs_root)
		link := book.Filename[0 : len(book.Filename)-len(".songlist")]
		loadedBooks = append(loadedBooks, DisplayList{Link: link, Title: book.Title})

		return err
	}

	return nil
}

// readLines reads a whole file into memory
// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}
<commit_msg>Fixing 404 problem on live server<commit_after>/*
Copyright 2016 Google Inc.

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

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

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

// Command template is a trivial web server that uses the text/template (and
// html/template) package's "block" feature to implement a kind of template
// inheritance.
//
// It should be executed from the directory in which the source resides,
// as it will look for its template files in the current directory.
package main

import (
	"bufio"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/julienschmidt/httprouter"
)

var (
	songs_root = "./songs"
	books_root = "./books"
)

func main() {
	filepath.Walk(songs_root, loadSongs)
	filepath.Walk(books_root, loadBooks)

	fmt.Printf("%d Songs loaded.\n", len(loadedSongs))
	fmt.Printf("%d Books loaded.\n", len(loadedBooks))

	r := httprouter.New()

	r.GET("/", indexHandler)
	r.GET("/index.php", indexHandler)
	r.GET("/index.html", indexHandler)
	r.GET("/song/:song", songHandler)
	r.GET("/pdf/:song", pdfHandler)
	r.GET("/book/:book", bookIndexHandler)
	r.GET("/book/:book/:number", bookHandler)
	r.ServeFiles("/css/*filepath", http.Dir("css"))
	r.ServeFiles("/js/*filepath", http.Dir("js"))

	log.Fatal(http.ListenAndServe(":8090", r))

}

// indexTemplate is the main site template.
// The default template includes two template blocks ("sidebar" and "content")
// that may be replaced in templates derived from this one.
//var indexTemplate = template.Must(template.ParseFiles("templates/index.tmpl"))

type DisplayList struct {
	Link  string
	Title string
}

func (song DisplayList) MatchTitle(title string) bool {
	return song.Title == title
}

type IndexPage struct {
	Title        string
	Songs        []DisplayList
	Books        []DisplayList
	ShowIndigo   bool
	SelectedSong string
	SelectedBook string
}

type SongPage struct {
	Song Song
	IndexPage
}

type BookPage struct {
	Songbook Songbook
	BookLink string
	IndexPage
}

var loadedSongs = make([]DisplayList, 0)
var loadedBooks = make([]DisplayList, 0)

// indexHandler is an HTTP handler that serves the index page.
func indexHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	t, err := template.ParseFiles("templates/index.tmpl")
	if err != nil {
		panic(err)
	}

	data := getBasicIndexData()
	data.ShowIndigo = true

	if err := t.ExecuteTemplate(w, "index.tmpl", data); err != nil {
		log.Println(err)
	}
}

// imageTemplate is a clone of indexTemplate that provides
// alternate "sidebar" and "content" templates.
//var songTemplate = template.Must(template.Must(indexTemplate.Clone()).ParseFiles("templates/song.tmpl"))

func loadSongFile(title string, transpose int) (*Song, error) {
	filename := songs_root + "/" + title + ".song"

	song, err := ParseSongFile(filename, transpose)

	if err != nil {
		return nil, err
	}

	return song, nil
}

func getBasicIndexData() IndexPage {
	index_data := IndexPage{
		Title:        "Indigo Song Book",
		Songs:        loadedSongs,
		Books:        loadedBooks,
		ShowIndigo:   false,
		SelectedSong: "",
		SelectedBook: ""}

	return index_data
}

func bookIndexHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	sbook, err := ParseSongbookFile(books_root+"/"+p.ByName("book")+".songlist", songs_root)

	if err != nil {
		fmt.Println(err)
	}

	index := getBasicIndexData()
	index.SelectedBook = sbook.Title

	book_data := &BookPage{
		Songbook:  *sbook,
		BookLink:  sbook.Filename[0 : len(sbook.Filename)-len(".songlist")],
		IndexPage: index}

	temp, err := template.ParseFiles("templates/index.tmpl", "templates/book.tmpl")
	if err != nil {
		panic(err)
	}

	if err := temp.ExecuteTemplate(w, "book.tmpl", book_data); err != nil {
		log.Println(err)
	}
}

func bookHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	num := p.ByName("number")
	n, _ := strconv.Atoi(num)

	var transpose = r.FormValue("transpose")
	if len(transpose) == 0 {
		transpose = "0"
	}
	t, err := strconv.Atoi(transpose)

	sbook, err := ParseSongbookFile(books_root+"/"+p.ByName("book")+".songlist", songs_root)

	if err != nil {
		fmt.Println(err)
	}

	song := sbook.Songs[n]
	song.Transpose = t

	index := getBasicIndexData()
	index.SelectedSong = song.Title
	index.SelectedBook = sbook.Title

	page_data := &SongPage{
		Song:      song,
		IndexPage: index}

	temp, err := template.ParseGlob("templates/*.tmpl")
	if err != nil {
		panic(err)
	}

	if err := temp.ExecuteTemplate(w, "song.tmpl", page_data); err != nil {
		log.Println(err)
	}
}

func songHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	var transpose = r.FormValue("transpose")
	if len(transpose) == 0 {
		transpose = "0"
	}
	t, err := strconv.Atoi(transpose)

	data, err := loadSongFile(p.ByName("song"), t)
	if err != nil {
		log.Println(err)
		http.NotFound(w, r)
		return
	}

	index := getBasicIndexData()
	index.SelectedSong = data.Title

	page_data := &SongPage{
		Song:      *data,
		IndexPage: index}

	temp, err := template.ParseGlob("templates/*.tmpl")
	if err != nil {
		panic(err)
	}

	if err := temp.ExecuteTemplate(w, "song.tmpl", page_data); err != nil {
		log.Println(err)
	}
}

func pdfHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	var transpose = r.FormValue("transpose")
	if len(transpose) == 0 {
		transpose = "0"
	}
	t, err := strconv.Atoi(transpose)

	song, err := loadSongFile(p.ByName("song"), t)
	if err != nil {
		log.Println(err)
		http.NotFound(w, r)
		return
	}

	pdf, err := WriteSongPDF(song)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// stream straight to client(browser)
	w.Header().Set("Content-type", "application/pdf")

	if _, err := pdf.WriteTo(w); err != nil {
		fmt.Fprintf(w, "%s", err)
	}

	w.Write([]byte("PDF Generated"))
}

func loadSongs(path string, f os.FileInfo, err error) error {
	if f.IsDir() && f.Name() != songs_root[2:] {
		return filepath.SkipDir
	} else if f.IsDir() {
		return nil
	}

	if strings.HasSuffix(strings.ToLower(f.Name()), ".song") {
		song, err := ParseSongFile(songs_root+"/"+f.Name(), 0)
		link := song.Filename[0 : len(song.Filename)-len(".song")]
		loadedSongs = append(loadedSongs, DisplayList{Link: link, Title: song.Title})

		return err
	}

	return nil
}

func loadBooks(path string, f os.FileInfo, err error) error {
	if f.IsDir() && f.Name() != books_root[2:] {
		return filepath.SkipDir
	} else if f.IsDir() {
		return nil
	}

	if strings.HasSuffix(strings.ToLower(f.Name()), ".songlist") {
		book, err := ParseSongbookFile(books_root+"/"+f.Name(), songs_root)
		link := book.Filename[0 : len(book.Filename)-len(".songlist")]
		loadedBooks = append(loadedBooks, DisplayList{Link: link, Title: book.Title})

		return err
	}

	return nil
}

// readLines reads a whole file into memory
// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}
<|endoftext|>