<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package config

import (
	"fmt"
	stdLog "log"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/admpub/confl"
	"github.com/admpub/log"
	"github.com/admpub/mysql-schema-sync/sync"
	"github.com/admpub/nging/v4/application/cmd/event"
	"github.com/admpub/nging/v4/application/library/common"
	"github.com/admpub/nging/v4/application/library/config/subconfig/sdb"
	"github.com/admpub/nging/v4/application/library/config/subconfig/ssystem"
	"github.com/admpub/nging/v4/application/library/cron"
	cronSend "github.com/admpub/nging/v4/application/library/cron/send"
	"github.com/webx-top/com"
	"github.com/webx-top/db/lib/factory"
	"github.com/webx-top/db/lib/sqlbuilder"
	"github.com/webx-top/db/mongo"
	"github.com/webx-top/db/mysql"
	"github.com/webx-top/echo"
)

func MustGetConfig() *Config {
	if DefaultConfig == nil {
		DefaultCLIConfig.ParseConfig()
	}
	return DefaultConfig
}

func InitConfig() (*Config, error) {
	configFiles := []string{
		DefaultCLIConfig.Conf,
		filepath.Join(echo.Wd(), `config/config.yaml.sample`),
	}
	var (
		configFile      string
		err             error
		temporaryConfig = NewConfig()
	)
	temporaryConfig.Debug = event.Develop
	for key, conf := range configFiles {
		if !filepath.IsAbs(conf) {
			conf = filepath.Join(echo.Wd(), conf)
			configFiles[key] = conf
			if key == 0 {
				DefaultCLIConfig.Conf = conf
			}
		}
		_, err = os.Stat(conf)
		if err == nil {
			configFile = conf
			break
		}
		if !os.IsNotExist(err) {
			return temporaryConfig, err
		}
	}
	if err != nil {
		return temporaryConfig, err
	}
	_, err = confl.DecodeFile(configFile, temporaryConfig)
	if err != nil {
		return temporaryConfig, err
	}
	temporaryConfig.SetDefaults(configFile)

	return temporaryConfig, nil
}

func ParseConfig() error {
	if false {
		b, err := confl.Marshal(DefaultConfig)
		if err != nil {
			return err
		}
		err = os.WriteFile(DefaultCLIConfig.Conf, b, os.ModePerm)
		if err != nil {
			return err
		}
	}
	conf, err := InitConfig()
	if err != nil {
		return err
	}
	InitSessionOptions(conf)
	if conf.Cron.PoolSize > 0 {
		cron.PoolSize = conf.Cron.PoolSize
	}
	cronSend.DefaultEmailConfig.Template = conf.Cron.Template
	if IsInstalled() {
		err = conf.connectDB()
		if err != nil {
			return err
		}
		if DefaultConfig != nil {
			err = DefaultConfig.Reload(conf)
			if err != nil {
				return err
			}
		}
	}
	conf.AsDefault()
	return err
}

var (
	DBConnecters = map[string]func(*Config) error{
		`mysql`: ConnectMySQL,
		`mongo`: ConnectMongoDB,
	}
	DBInstallers = map[string]func(string) error{
		`mysql`: ExecMySQL,
	}
	DBCreaters = map[string]func(error, *Config) error{
		`mysql`: CreaterMySQL,
	}
	DBUpgraders = map[string]func(string, *sync.Config, *Config) (DBOperators, error){
		`mysql`: UpgradeMySQL,
	}
	DBEngines     = echo.NewKVData().Add(`mysql`, `MySQL`)
	ParseDuration = ssystem.ParseTimeDuration
	ParseBytes    = ssystem.ParseBytes
)

type DBOperators struct {
	Source      sync.DBOperator
	Destination sync.DBOperator
}

func CreaterMySQL(err error, c *Config) error {
	if strings.Contains(err.Error(), `Unknown database`) {
		dbName := c.DB.Database
		c.DB.Database = ``
		err2 := ConnectDB(c)
		if err2 != nil {
			return err
		}
		charset := c.DB.Charset()
		if len(charset) == 0 {
			charset = sdb.MySQLDefaultCharset
		}
		sqlStr := "CREATE DATABASE `" + dbName + "` COLLATE '" + charset + "_general_ci'"
		_, err = factory.NewParam().SetCollection(sqlStr).Exec()
		if err != nil {
			return err
		}
		c.DB.Database = dbName
		err = ConnectDB(c)
	}
	return err
}

func UpgradeMySQL(schema string, syncConfig *sync.Config, cfg *Config) (DBOperators, error) {
	syncConfig.DestDSN = cfg.DB.User + `:` + cfg.DB.Password + `@(` + cfg.DB.Host + `)/` + cfg.DB.Database
	t := `?`
	for key, value := range cfg.DB.Options {
		syncConfig.DestDSN += t + fmt.Sprintf("%s=%s", key, url.QueryEscape(value))
		t = `&`
	}
	syncConfig.SQLPreprocessor = func() func(string) string {
		charset := cfg.DB.Charset()
		if len(charset) == 0 {
			charset = sdb.MySQLDefaultCharset
		}
		return func(sqlStr string) string {
			return common.ReplaceCharset(sqlStr, charset)
		}
	}()
	return DBOperators{Source: sync.NewMySchemaData(schema, `source`)}, nil
}

func ConnectMySQL(c *Config) error {
	settings := c.DB.ToMySQL()
	database, err := mysql.Open(settings)
	if err != nil {
		return err
	}
	c.DB.SetConn(database)
	cluster := factory.NewCluster().AddMaster(database)
	factory.SetCluster(0, cluster)
	factory.SetDebug(c.DB.Debug)
	return nil
}

func ConnectMongoDB(c *Config) error {
	settings := c.DB.ToMongoDB()
	database, err := mongo.Open(settings)
	if err != nil {
		return err
	}
	c.DB.SetConn(database)
	cluster := factory.NewCluster().AddMaster(database)
	factory.SetCluster(0, cluster)
	factory.SetDebug(c.DB.Debug)
	return nil
}

func ExecMySQL(sqlStr string) error {
	_, err := factory.NewParam().SetCollection(sqlStr).Exec()
	if err != nil {
		stdLog.Println(err.Error(), `->SQL:`, sqlStr)
	}
	return err
}

func QueryTo(sqlStr string, result interface{}) (sqlbuilder.Iterator, error) {
	return factory.NewParam().SetRecv(result).SetCollection(sqlStr).QueryTo()
}

func ConnectDB(c *Config) error {
	factory.CloseAll()
	if fn, ok := DBConnecters[c.DB.Type]; ok {
		return fn(c)
	}
	return ErrUnknowDatabaseType
}

func MustOK(err error) {
	if err != nil {
		log.Fatal(err.Error())
	}
}

var CmdIsRunning = com.CmdIsRunning

func Table(table string) string {
	return DefaultConfig.DB.Table(table)
}

func ToTable(m sqlbuilder.Name_) string {
	return DefaultConfig.DB.ToTable(m)
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package config

import (
	"fmt"
	stdLog "log"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/admpub/confl"
	"github.com/admpub/log"
	"github.com/admpub/mysql-schema-sync/sync"
	"github.com/admpub/nging/v4/application/cmd/event"
	"github.com/admpub/nging/v4/application/library/common"
	"github.com/admpub/nging/v4/application/library/config/subconfig/sdb"
	"github.com/admpub/nging/v4/application/library/config/subconfig/ssystem"
	"github.com/admpub/nging/v4/application/library/cron"
	cronSend "github.com/admpub/nging/v4/application/library/cron/send"
	"github.com/webx-top/com"
	"github.com/webx-top/db/lib/factory"
	"github.com/webx-top/db/lib/sqlbuilder"
	"github.com/webx-top/db/mongo"
	"github.com/webx-top/db/mysql"
	"github.com/webx-top/echo"
)

func MustGetConfig() *Config {
	if DefaultConfig == nil {
		DefaultCLIConfig.ParseConfig()
	}
	return DefaultConfig
}

func InitConfig() (*Config, error) {
	configFiles := []string{
		DefaultCLIConfig.Conf,
		filepath.Join(echo.Wd(), `config/config.yaml.sample`),
	}
	var (
		configFile      string
		err             error
		temporaryConfig = NewConfig()
	)
	temporaryConfig.Debug = event.Develop
	for key, conf := range configFiles {
		if !filepath.IsAbs(conf) {
			conf = filepath.Join(echo.Wd(), conf)
			configFiles[key] = conf
			if key == 0 {
				DefaultCLIConfig.Conf = conf
			}
		}
		_, err = os.Stat(conf)
		if err == nil {
			configFile = conf
			break
		}
		if !os.IsNotExist(err) {
			return temporaryConfig, err
		}
	}
	if err != nil {
		return temporaryConfig, err
	}
	_, err = confl.DecodeFile(configFile, temporaryConfig)
	if err != nil {
		return temporaryConfig, err
	}
	temporaryConfig.SetDefaults(configFile)

	return temporaryConfig, nil
}

func ParseConfig() error {
	if false {
		b, err := confl.Marshal(DefaultConfig)
		if err != nil {
			return err
		}
		err = os.WriteFile(DefaultCLIConfig.Conf, b, os.ModePerm)
		if err != nil {
			return err
		}
	}
	conf, err := InitConfig()
	if err != nil {
		return err
	}
	InitSessionOptions(conf)
	if conf.Cron.PoolSize > 0 {
		cron.PoolSize = conf.Cron.PoolSize
	}
	cronSend.DefaultEmailConfig.Template = conf.Cron.Template
	if IsInstalled() {
		err = conf.connectDB()
		if err != nil {
			return err
		}
		if DefaultConfig != nil {
			err = DefaultConfig.Reload(conf)
			if err != nil {
				return err
			}
		}
	}
	conf.AsDefault()
	return err
}

var (
	DBConnecters = map[string]func(*Config) error{
		`mysql`: ConnectMySQL,
		`mongo`: ConnectMongoDB,
	}
	DBInstallers = map[string]func(string) error{
		`mysql`: ExecMySQL,
	}
	DBCreaters = map[string]func(error, *Config) error{
		`mysql`: CreaterMySQL,
	}
	DBUpgraders = map[string]func(string, *sync.Config, *Config) (DBOperators, error){
		`mysql`: UpgradeMySQL,
	}
	DBEngines         = echo.NewKVData().Add(`mysql`, `MySQL`)
	ParseTimeDuration = ssystem.ParseTimeDuration
	ParseBytes        = ssystem.ParseBytes
)

type DBOperators struct {
	Source      sync.DBOperator
	Destination sync.DBOperator
}

func CreaterMySQL(err error, c *Config) error {
	if strings.Contains(err.Error(), `Unknown database`) {
		dbName := c.DB.Database
		c.DB.Database = ``
		err2 := ConnectDB(c)
		if err2 != nil {
			return err
		}
		charset := c.DB.Charset()
		if len(charset) == 0 {
			charset = sdb.MySQLDefaultCharset
		}
		sqlStr := "CREATE DATABASE `" + dbName + "` COLLATE '" + charset + "_general_ci'"
		_, err = factory.NewParam().SetCollection(sqlStr).Exec()
		if err != nil {
			return err
		}
		c.DB.Database = dbName
		err = ConnectDB(c)
	}
	return err
}

func UpgradeMySQL(schema string, syncConfig *sync.Config, cfg *Config) (DBOperators, error) {
	syncConfig.DestDSN = cfg.DB.User + `:` + cfg.DB.Password + `@(` + cfg.DB.Host + `)/` + cfg.DB.Database
	t := `?`
	for key, value := range cfg.DB.Options {
		syncConfig.DestDSN += t + fmt.Sprintf("%s=%s", key, url.QueryEscape(value))
		t = `&`
	}
	syncConfig.SQLPreprocessor = func() func(string) string {
		charset := cfg.DB.Charset()
		if len(charset) == 0 {
			charset = sdb.MySQLDefaultCharset
		}
		return func(sqlStr string) string {
			return common.ReplaceCharset(sqlStr, charset)
		}
	}()
	return DBOperators{Source: sync.NewMySchemaData(schema, `source`)}, nil
}

func ConnectMySQL(c *Config) error {
	settings := c.DB.ToMySQL()
	database, err := mysql.Open(settings)
	if err != nil {
		return err
	}
	c.DB.SetConn(database)
	cluster := factory.NewCluster().AddMaster(database)
	factory.SetCluster(0, cluster)
	factory.SetDebug(c.DB.Debug)
	return nil
}

func ConnectMongoDB(c *Config) error {
	settings := c.DB.ToMongoDB()
	database, err := mongo.Open(settings)
	if err != nil {
		return err
	}
	c.DB.SetConn(database)
	cluster := factory.NewCluster().AddMaster(database)
	factory.SetCluster(0, cluster)
	factory.SetDebug(c.DB.Debug)
	return nil
}

func ExecMySQL(sqlStr string) error {
	_, err := factory.NewParam().SetCollection(sqlStr).Exec()
	if err != nil {
		stdLog.Println(err.Error(), `->SQL:`, sqlStr)
	}
	return err
}

func QueryTo(sqlStr string, result interface{}) (sqlbuilder.Iterator, error) {
	return factory.NewParam().SetRecv(result).SetCollection(sqlStr).QueryTo()
}

func ConnectDB(c *Config) error {
	factory.CloseAll()
	if fn, ok := DBConnecters[c.DB.Type]; ok {
		return fn(c)
	}
	return ErrUnknowDatabaseType
}

func MustOK(err error) {
	if err != nil {
		log.Fatal(err.Error())
	}
}

var CmdIsRunning = com.CmdIsRunning

func Table(table string) string {
	return DefaultConfig.DB.Table(table)
}

func ToTable(m sqlbuilder.Name_) string {
	return DefaultConfig.DB.ToTable(m)
}
<|endoftext|>


<commit_before>// Copyright 2011 Xing Xing <mikespook@gmail.com> All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

/*
This module is Gearman API for golang.
The protocol was implemented by native way.
*/

package gearman

import (
    "time"
    "testing"
    "strings"
    "github.com/mikespook/gearman-go/client"
    "github.com/mikespook/gearman-go/worker"
)

const(
    STR = "The gearman-go is a pure go implemented library."
    GEARMAND = "127.0.0.1:4730"
)

func ToUpper(job *worker.Job) ([]byte, error) {
    data := []byte(strings.ToUpper(string(job.Data)))
    return data, nil
}

func Sleep(job *worker.Job) ([]byte, error) {
    time.Sleep(time.Second * 5)
    return nil, nil
}

func TestJobs(t *testing.T) {
    w := worker.New(worker.Unlimited)
    if err := w.AddServer(GEARMAND); err != nil {
        t.Error(err)
        return
    }
    defer w.Close()
    if err := w.AddFunc("ToUpper", ToUpper, 0); err != nil {
        t.Error(err)
        return
    }
    if err := w.AddFunc("Sleep", Sleep, 0); err != nil {
        t.Error(err)
        return
    }

    w.ErrHandler = func(e error) {
         t.Error(e)
    }
    go w.Work()

    c, err := client.New(GEARMAND)
    if err != nil {
        t.Error(err)
        return
    }
    defer c.Close()

    c.ErrHandler = func(e error) {
        t.Error(e)
    }

    {
        jobHandler := func(job *client.Job) {
            upper := strings.ToUpper(STR)
            if (string(job.Data) != upper) {
                t.Error("%s expected, got %s", []byte(upper), job.Data)
            }
        }

        handle := c.Do("ToUpper", []byte(STR), client.JOB_NORMAL, jobHandler)
        status, err := c.Status(handle, time.Second)
        if err != nil {
            t.Error(err)
            return
        }

        if !status.Known {
            t.Errorf("%s should be known", status.Handle)
            return
        }
    }
    {
        handle := c.DoBg("Sleep", nil, client.JOB_NORMAL)
        time.Sleep(time.Second)
        status, err := c.Status(handle, time.Second)
        if err != nil {
            t.Error(err)
            return
        }

        if !status.Known {
            t.Errorf("%s should be known", status.Handle)
            return
        }

        if !status.Running {
            t.Errorf("%s should be running", status.Handle)
        }
    }
    {
        status, err := c.Status("not exists handle", time.Second)
        if err != nil {
            t.Error(err)
            return
        }

        if status.Known {
            t.Errorf("%s shouldn't be known", status.Handle)
            return
        }

        if status.Running {
            t.Errorf("%s shouldn't be running", status.Handle)
        }
    }
}
<commit_msg>tweaking<commit_after>// Copyright 2011 Xing Xing <mikespook@gmail.com> All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

/*
This module is Gearman API for golang.
The protocol was implemented by native way.
*/

package gearman

import (
    "time"
    "sync"
    "testing"
    "strings"
    "github.com/mikespook/gearman-go/client"
    "github.com/mikespook/gearman-go/worker"
)

const(
    STR = "The gearman-go is a pure go implemented library."
    GEARMAND = "127.0.0.1:4730"
)

func ToUpper(job *worker.Job) ([]byte, error) {
    data := []byte(strings.ToUpper(string(job.Data)))
    return data, nil
}

func Sleep(job *worker.Job) ([]byte, error) {
    time.Sleep(time.Second * 5)
    return nil, nil
}

func TestJobs(t *testing.T) {
    w := worker.New(worker.Unlimited)
    if err := w.AddServer(GEARMAND); err != nil {
        t.Error(err)
        return
    }
    defer w.Close()
    if err := w.AddFunc("ToUpper", ToUpper, 0); err != nil {
        t.Error(err)
        return
    }
    if err := w.AddFunc("Sleep", Sleep, 0); err != nil {
        t.Error(err)
        return
    }

    w.ErrHandler = func(e error) {
         t.Error(e)
    }
    go w.Work()

    c, err := client.New(GEARMAND)
    if err != nil {
        t.Error(err)
        return
    }
    defer c.Close()

    c.ErrHandler = func(e error) {
        t.Error(e)
    }

    {
        var w sync.WaitGroup
        jobHandler := func(job *client.Job) {
            upper := strings.ToUpper(STR)
            if (string(job.Data) != upper) {
                t.Error("%s expected, got %s", []byte(upper), job.Data)
            }
            w.Done()
        }

        w.Add(1)
        handle := c.Do("ToUpper", []byte(STR), client.JOB_NORMAL, jobHandler)
        w.Wait()
        status, err := c.Status(handle, time.Second)
        if err != nil {
            t.Error(err)
            return
        }
        if status.Known {
            t.Errorf("%s shouldn't be known", status.Handle)
            return
        }

        if status.Running {
            t.Errorf("%s shouldn't be running", status.Handle)
        }
    }
    {
        handle := c.DoBg("Sleep", nil, client.JOB_NORMAL)
        time.Sleep(time.Second)
        status, err := c.Status(handle, time.Second)
        if err != nil {
            t.Error(err)
            return
        }

        if !status.Known {
            t.Errorf("%s should be known", status.Handle)
            return
        }

        if !status.Running {
            t.Errorf("%s should be running", status.Handle)
        }
    }
    {
        status, err := c.Status("not exists handle", time.Second)
        if err != nil {
            t.Error(err)
            return
        }

        if status.Known {
            t.Errorf("%s shouldn't be known", status.Handle)
            return
        }

        if status.Running {
            t.Errorf("%s shouldn't be running", status.Handle)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, 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 etcdserver

import (
	"bytes"
	"fmt"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	dstorage "github.com/coreos/etcd/storage"
	"github.com/coreos/etcd/storage/storagepb"
)

type RaftKV interface {
	Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error)
	Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error)
	DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error)
	Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error)
	Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error)
}

func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Range: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.RangeResponse), result.err
}

func (s *EtcdServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Put: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.PutResponse), result.err
}

func (s *EtcdServer) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{DeleteRange: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.DeleteRangeResponse), result.err
}

func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Txn: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.TxnResponse), result.err
}

func (s *EtcdServer) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Compaction: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.CompactionResponse), result.err
}

type applyResult struct {
	resp proto.Message
	err  error
}

func (s *EtcdServer) processInternalRaftRequest(ctx context.Context, r pb.InternalRaftRequest) (*applyResult, error) {
	r.ID = s.reqIDGen.Next()

	data, err := r.Marshal()
	if err != nil {
		return nil, err
	}
	ch := s.w.Register(r.ID)

	s.r.Propose(ctx, data)

	select {
	case x := <-ch:
		return x.(*applyResult), nil
	case <-ctx.Done():
		s.w.Trigger(r.ID, nil) // GC wait
		return nil, ctx.Err()
	case <-s.done:
		return nil, ErrStopped
	}
}

// Watcable returns a watchable interface attached to the etcdserver.
func (s *EtcdServer) Watchable() dstorage.Watchable {
	return s.kv
}

const (
	// noTxn is an invalid txn ID.
	// To apply with independent Range, Put, Delete, you can pass noTxn
	// to apply functions instead of a valid txn ID.
	noTxn = -1
)

func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} {
	ar := &applyResult{}

	switch {
	case r.Range != nil:
		ar.resp, ar.err = applyRange(noTxn, s.kv, r.Range)
	case r.Put != nil:
		ar.resp, ar.err = applyPut(noTxn, s.kv, r.Put)
	case r.DeleteRange != nil:
		ar.resp, ar.err = applyDeleteRange(noTxn, s.kv, r.DeleteRange)
	case r.Txn != nil:
		ar.resp, ar.err = applyTxn(s.kv, r.Txn)
	case r.Compaction != nil:
		ar.resp, ar.err = applyCompaction(s.kv, r.Compaction)
	default:
		panic("not implemented")
	}

	return ar
}

func applyPut(txnID int64, kv dstorage.KV, p *pb.PutRequest) (*pb.PutResponse, error) {
	resp := &pb.PutResponse{}
	resp.Header = &pb.ResponseHeader{}
	var (
		rev int64
		err error
	)
	if txnID != noTxn {
		rev, err = kv.TxnPut(txnID, p.Key, p.Value)
		if err != nil {
			return nil, err
		}
	} else {
		rev = kv.Put(p.Key, p.Value)
	}
	resp.Header.Revision = rev
	return resp, nil
}

func applyRange(txnID int64, kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeResponse, error) {
	resp := &pb.RangeResponse{}
	resp.Header = &pb.ResponseHeader{}

	var (
		kvs []storagepb.KeyValue
		rev int64
		err error
	)

	if txnID != noTxn {
		kvs, rev, err = kv.TxnRange(txnID, r.Key, r.RangeEnd, r.Limit, 0)
		if err != nil {
			return nil, err
		}
	} else {
		kvs, rev, err = kv.Range(r.Key, r.RangeEnd, r.Limit, 0)
		if err != nil {
			return nil, err
		}
	}

	resp.Header.Revision = rev
	for i := range kvs {
		resp.Kvs = append(resp.Kvs, &kvs[i])
	}
	return resp, nil
}

func applyDeleteRange(txnID int64, kv dstorage.KV, dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
	resp := &pb.DeleteRangeResponse{}
	resp.Header = &pb.ResponseHeader{}

	var (
		rev int64
		err error
	)

	if txnID != noTxn {
		_, rev, err = kv.TxnDeleteRange(txnID, dr.Key, dr.RangeEnd)
		if err != nil {
			return nil, err
		}
	} else {
		_, rev = kv.DeleteRange(dr.Key, dr.RangeEnd)
	}

	resp.Header.Revision = rev
	return resp, nil
}

func applyTxn(kv dstorage.KV, rt *pb.TxnRequest) (*pb.TxnResponse, error) {
	var revision int64

	txnID := kv.TxnBegin()
	defer func() {
		err := kv.TxnEnd(txnID)
		if err != nil {
			panic(fmt.Sprint("unexpected error when closing txn", txnID))
		}
	}()

	ok := true
	for _, c := range rt.Compare {
		if revision, ok = applyCompare(txnID, kv, c); !ok {
			break
		}
	}

	// TODO: check potential errors before actually applying anything

	var reqs []*pb.RequestUnion
	if ok {
		reqs = rt.Success
	} else {
		reqs = rt.Failure
	}

	resps := make([]*pb.ResponseUnion, len(reqs))
	for i := range reqs {
		resps[i] = applyUnion(txnID, kv, reqs[i])
	}

	if len(resps) != 0 {
		revision += 1
	}

	txnResp := &pb.TxnResponse{}
	txnResp.Header = &pb.ResponseHeader{}
	txnResp.Header.Revision = revision
	txnResp.Responses = resps
	txnResp.Succeeded = ok
	return txnResp, nil
}

func applyCompaction(kv dstorage.KV, compaction *pb.CompactionRequest) (*pb.CompactionResponse, error) {
	resp := &pb.CompactionResponse{}
	resp.Header = &pb.ResponseHeader{}
	err := kv.Compact(compaction.Revision)
	if err != nil {
		return nil, err
	}
	// get the current revision. which key to get is not important.
	_, resp.Header.Revision, _ = kv.Range([]byte("compaction"), nil, 1, 0)
	return resp, err
}

func applyUnion(txnID int64, kv dstorage.KV, union *pb.RequestUnion) *pb.ResponseUnion {
	switch {
	case union.RequestRange != nil:
		resp, err := applyRange(txnID, kv, union.RequestRange)
		if err != nil {
			panic("unexpected error during txn")
		}
		return &pb.ResponseUnion{ResponseRange: resp}
	case union.RequestPut != nil:
		resp, err := applyPut(txnID, kv, union.RequestPut)
		if err != nil {
			panic("unexpected error during txn")
		}
		return &pb.ResponseUnion{ResponsePut: resp}
	case union.RequestDeleteRange != nil:
		resp, err := applyDeleteRange(txnID, kv, union.RequestDeleteRange)
		if err != nil {
			panic("unexpected error during txn")
		}
		return &pb.ResponseUnion{ResponseDeleteRange: resp}
	default:
		// empty union
		return nil
	}
}

// applyCompare applies the compare request.
// applyCompare should only be called within a txn request and an valid txn ID must
// be presented. Or applyCompare panics.
// It returns the revision at which the comparison happens. If the comparison
// succeeds, the it returns true. Otherwise it returns false.
func applyCompare(txnID int64, kv dstorage.KV, c *pb.Compare) (int64, bool) {
	if txnID == noTxn {
		panic("applyCompare called with noTxn")
	}
	ckvs, rev, err := kv.TxnRange(txnID, c.Key, nil, 1, 0)
	if err != nil {
		if err == dstorage.ErrTxnIDMismatch {
			panic("unexpected txn ID mismatch error")
		}
		return rev, false
	}

	ckv := ckvs[0]

	// -1 is less, 0 is equal, 1 is greater
	var result int
	switch c.Target {
	case pb.Compare_VALUE:
		result = bytes.Compare(ckv.Value, c.Value)
	case pb.Compare_CREATE:
		result = compareInt64(ckv.CreateRevision, c.CreateRevision)
	case pb.Compare_MOD:
		result = compareInt64(ckv.ModRevision, c.ModRevision)
	case pb.Compare_VERSION:
		result = compareInt64(ckv.Version, c.Version)
	}

	switch c.Result {
	case pb.Compare_EQUAL:
		if result != 0 {
			return rev, false
		}
	case pb.Compare_GREATER:
		if result != 1 {
			return rev, false
		}
	case pb.Compare_LESS:
		if result != -1 {
			return rev, false
		}
	}
	return rev, true
}

func compareInt64(a, b int64) int {
	switch {
	case a < b:
		return -1
	case a > b:
		return 1
	default:
		return 0
	}
}
<commit_msg>etcdserver: Fix panic for v3 transaction compares on non-existent keys<commit_after>// Copyright 2015 CoreOS, 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 etcdserver

import (
	"bytes"
	"fmt"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	dstorage "github.com/coreos/etcd/storage"
	"github.com/coreos/etcd/storage/storagepb"
)

type RaftKV interface {
	Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error)
	Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error)
	DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error)
	Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error)
	Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error)
}

func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Range: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.RangeResponse), result.err
}

func (s *EtcdServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Put: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.PutResponse), result.err
}

func (s *EtcdServer) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{DeleteRange: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.DeleteRangeResponse), result.err
}

func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Txn: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.TxnResponse), result.err
}

func (s *EtcdServer) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error) {
	result, err := s.processInternalRaftRequest(ctx, pb.InternalRaftRequest{Compaction: r})
	if err != nil {
		return nil, err
	}
	return result.resp.(*pb.CompactionResponse), result.err
}

type applyResult struct {
	resp proto.Message
	err  error
}

func (s *EtcdServer) processInternalRaftRequest(ctx context.Context, r pb.InternalRaftRequest) (*applyResult, error) {
	r.ID = s.reqIDGen.Next()

	data, err := r.Marshal()
	if err != nil {
		return nil, err
	}
	ch := s.w.Register(r.ID)

	s.r.Propose(ctx, data)

	select {
	case x := <-ch:
		return x.(*applyResult), nil
	case <-ctx.Done():
		s.w.Trigger(r.ID, nil) // GC wait
		return nil, ctx.Err()
	case <-s.done:
		return nil, ErrStopped
	}
}

// Watcable returns a watchable interface attached to the etcdserver.
func (s *EtcdServer) Watchable() dstorage.Watchable {
	return s.kv
}

const (
	// noTxn is an invalid txn ID.
	// To apply with independent Range, Put, Delete, you can pass noTxn
	// to apply functions instead of a valid txn ID.
	noTxn = -1
)

func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} {
	ar := &applyResult{}

	switch {
	case r.Range != nil:
		ar.resp, ar.err = applyRange(noTxn, s.kv, r.Range)
	case r.Put != nil:
		ar.resp, ar.err = applyPut(noTxn, s.kv, r.Put)
	case r.DeleteRange != nil:
		ar.resp, ar.err = applyDeleteRange(noTxn, s.kv, r.DeleteRange)
	case r.Txn != nil:
		ar.resp, ar.err = applyTxn(s.kv, r.Txn)
	case r.Compaction != nil:
		ar.resp, ar.err = applyCompaction(s.kv, r.Compaction)
	default:
		panic("not implemented")
	}

	return ar
}

func applyPut(txnID int64, kv dstorage.KV, p *pb.PutRequest) (*pb.PutResponse, error) {
	resp := &pb.PutResponse{}
	resp.Header = &pb.ResponseHeader{}
	var (
		rev int64
		err error
	)
	if txnID != noTxn {
		rev, err = kv.TxnPut(txnID, p.Key, p.Value)
		if err != nil {
			return nil, err
		}
	} else {
		rev = kv.Put(p.Key, p.Value)
	}
	resp.Header.Revision = rev
	return resp, nil
}

func applyRange(txnID int64, kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeResponse, error) {
	resp := &pb.RangeResponse{}
	resp.Header = &pb.ResponseHeader{}

	var (
		kvs []storagepb.KeyValue
		rev int64
		err error
	)

	if txnID != noTxn {
		kvs, rev, err = kv.TxnRange(txnID, r.Key, r.RangeEnd, r.Limit, 0)
		if err != nil {
			return nil, err
		}
	} else {
		kvs, rev, err = kv.Range(r.Key, r.RangeEnd, r.Limit, 0)
		if err != nil {
			return nil, err
		}
	}

	resp.Header.Revision = rev
	for i := range kvs {
		resp.Kvs = append(resp.Kvs, &kvs[i])
	}
	return resp, nil
}

func applyDeleteRange(txnID int64, kv dstorage.KV, dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
	resp := &pb.DeleteRangeResponse{}
	resp.Header = &pb.ResponseHeader{}

	var (
		rev int64
		err error
	)

	if txnID != noTxn {
		_, rev, err = kv.TxnDeleteRange(txnID, dr.Key, dr.RangeEnd)
		if err != nil {
			return nil, err
		}
	} else {
		_, rev = kv.DeleteRange(dr.Key, dr.RangeEnd)
	}

	resp.Header.Revision = rev
	return resp, nil
}

func applyTxn(kv dstorage.KV, rt *pb.TxnRequest) (*pb.TxnResponse, error) {
	var revision int64

	txnID := kv.TxnBegin()
	defer func() {
		err := kv.TxnEnd(txnID)
		if err != nil {
			panic(fmt.Sprint("unexpected error when closing txn", txnID))
		}
	}()

	ok := true
	for _, c := range rt.Compare {
		if revision, ok = applyCompare(txnID, kv, c); !ok {
			break
		}
	}

	// TODO: check potential errors before actually applying anything

	var reqs []*pb.RequestUnion
	if ok {
		reqs = rt.Success
	} else {
		reqs = rt.Failure
	}

	resps := make([]*pb.ResponseUnion, len(reqs))
	for i := range reqs {
		resps[i] = applyUnion(txnID, kv, reqs[i])
	}

	if len(resps) != 0 {
		revision += 1
	}

	txnResp := &pb.TxnResponse{}
	txnResp.Header = &pb.ResponseHeader{}
	txnResp.Header.Revision = revision
	txnResp.Responses = resps
	txnResp.Succeeded = ok
	return txnResp, nil
}

func applyCompaction(kv dstorage.KV, compaction *pb.CompactionRequest) (*pb.CompactionResponse, error) {
	resp := &pb.CompactionResponse{}
	resp.Header = &pb.ResponseHeader{}
	err := kv.Compact(compaction.Revision)
	if err != nil {
		return nil, err
	}
	// get the current revision. which key to get is not important.
	_, resp.Header.Revision, _ = kv.Range([]byte("compaction"), nil, 1, 0)
	return resp, err
}

func applyUnion(txnID int64, kv dstorage.KV, union *pb.RequestUnion) *pb.ResponseUnion {
	switch {
	case union.RequestRange != nil:
		resp, err := applyRange(txnID, kv, union.RequestRange)
		if err != nil {
			panic("unexpected error during txn")
		}
		return &pb.ResponseUnion{ResponseRange: resp}
	case union.RequestPut != nil:
		resp, err := applyPut(txnID, kv, union.RequestPut)
		if err != nil {
			panic("unexpected error during txn")
		}
		return &pb.ResponseUnion{ResponsePut: resp}
	case union.RequestDeleteRange != nil:
		resp, err := applyDeleteRange(txnID, kv, union.RequestDeleteRange)
		if err != nil {
			panic("unexpected error during txn")
		}
		return &pb.ResponseUnion{ResponseDeleteRange: resp}
	default:
		// empty union
		return nil
	}
}

// applyCompare applies the compare request.
// applyCompare should only be called within a txn request and an valid txn ID must
// be presented. Or applyCompare panics.
// It returns the revision at which the comparison happens. If the comparison
// succeeds, the it returns true. Otherwise it returns false.
func applyCompare(txnID int64, kv dstorage.KV, c *pb.Compare) (int64, bool) {
	if txnID == noTxn {
		panic("applyCompare called with noTxn")
	}
	ckvs, rev, err := kv.TxnRange(txnID, c.Key, nil, 1, 0)
	if err != nil {
		if err == dstorage.ErrTxnIDMismatch {
			panic("unexpected txn ID mismatch error")
		}
		return rev, false
	}
	var ckv storagepb.KeyValue
	if len(ckvs) != 0 {
		ckv = ckvs[0]
	} else {
		// Use the zero value of ckv normally. However...
		if c.Target == pb.Compare_VALUE {
			// Always fail if we're comparing a value on a key that doesn't exist.
			// We can treat non-existence as the empty set explicitly, such that
			// even a key with a value of length 0 bytes is still a real key
			// that was written that way
			return rev, false
		}
	}

	// -1 is less, 0 is equal, 1 is greater
	var result int
	switch c.Target {
	case pb.Compare_VALUE:
		result = bytes.Compare(ckv.Value, c.Value)
	case pb.Compare_CREATE:
		result = compareInt64(ckv.CreateRevision, c.CreateRevision)
	case pb.Compare_MOD:
		result = compareInt64(ckv.ModRevision, c.ModRevision)
	case pb.Compare_VERSION:
		result = compareInt64(ckv.Version, c.Version)
	}

	switch c.Result {
	case pb.Compare_EQUAL:
		if result != 0 {
			return rev, false
		}
	case pb.Compare_GREATER:
		if result != 1 {
			return rev, false
		}
	case pb.Compare_LESS:
		if result != -1 {
			return rev, false
		}
	}
	return rev, true
}

func compareInt64(a, b int64) int {
	switch {
	case a < b:
		return -1
	case a > b:
		return 1
	default:
		return 0
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"fmt"
	"html/template"
	"log"
	"net"
	"os"
	"strconv"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"code.cloudfoundry.org/lager"
	flags "github.com/jessevdk/go-flags"
	"github.com/robdimsdale/honeylager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"github.com/tedsuo/rata"

	"cred-alert/ccp/api"
	"cred-alert/ccp/web"
	"cred-alert/config"
	"cred-alert/revokpb"
)

const (
	// Required.
	portEnvKey = "PORT"

	// Passphrase for the client private key. Required if the key is encrypted.
	clientKeyPassphraseEnvKey = "CLIENT_KEY_PASSPHRASE"

	// Address for RPC server. Required.
	rpcServerAddressEnvKey = "RPC_SERVER_ADDRESS"

	// Port for RPC server. Required.
	rpcServerPortEnvKey = "RPC_SERVER_PORT"

	// Required.
	caCertEnvKey = "SERVER_CA_CERT"

	// Optional
	honeycombWriteKeyEnvKey = "HONEYCOMB_WRITE_KEY"
	honeycombDatasetEnvKey  = "HONEYCOMB_DATASET"
)

type Opts struct {
	ClientCertPath string `long:"client-cert-path" description:"Path to the client certificate" required:"true"`
	ClientKeyPath  string `long:"client-key-path" description:"Path to the client private key" required:"true"`
}

var (
	indexLayout        *template.Template
	organizationLayout *template.Template
	repositoryLayout   *template.Template
	logger             lager.Logger
)

func init() {
	bs, err := web.Asset("web/templates/index.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	indexLayout = template.Must(template.New("index.html").Parse(string(bs)))

	bs, err = web.Asset("web/templates/organization.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	organizationLayout = template.Must(template.New("organization.html").Parse(string(bs)))

	bs, err = web.Asset("web/templates/repository.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	repositoryLayout = template.Must(template.New("repository.html").Parse(string(bs)))

	logger = lager.NewLogger("credential-count-publisher")
	logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.INFO))
}

func main() {
	var opts Opts

	_, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
		os.Exit(1)
	}

	honeycombWriteKey := os.Getenv(honeycombWriteKeyEnvKey)
	honeycombDataset := os.Getenv(honeycombDatasetEnvKey)
	if honeycombWriteKey != "" && honeycombDataset != "" {
		s := honeylager.NewSink(honeycombWriteKey, honeycombDataset, lager.DEBUG)
		defer s.Close()
		logger.RegisterSink(s)
	} else {
		logger.Info(fmt.Sprintf(
			"Honeycomb not configured - need %s and %s",
			honeycombWriteKeyEnvKey,
			honeycombDatasetEnvKey,
		))
	}

	logger.Info("starting")

	portStr := mustGetEnv(logger, portEnvKey)
	port, err := strconv.Atoi(portStr)
	if err != nil {
		logger.Fatal("failed-to-parse-port", err)
	}

	rpcServerAddress := mustGetEnv(logger, rpcServerAddressEnvKey)

	rpcServerPortStr := mustGetEnv(logger, rpcServerPortEnvKey)
	rpcServerPort, err := strconv.Atoi(rpcServerPortStr)
	if err != nil {
		logger.Fatal("failed-to-parse-rpc-server-port", err)
	}

	serverAddr := fmt.Sprintf("%s:%d", rpcServerAddress, rpcServerPort)
	listenAddr := fmt.Sprintf(":%d", port)

	clientCert, err := config.LoadCertificate(
		opts.ClientCertPath,
		opts.ClientKeyPath,
		os.Getenv(clientKeyPassphraseEnvKey),
	)
	if err != nil {
		log.Fatalln(err)
	}

	caCert := mustGetEnv(logger, caCertEnvKey)

	rootCertPool, err := config.LoadCertificatePool(caCert)
	if err != nil {
		log.Fatalln(err)
	}

	transportCreds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      rootCertPool,
	})

	conn, err := grpc.Dial(
		serverAddr,
		grpc.WithTransportCredentials(transportCreds),
		grpc.WithDialer(keepAliveDial),
	)
	if err != nil {
		log.Fatalf("failed to create handler: %s", err.Error())
	}
	defer conn.Close()

	revokClient := revokpb.NewRevokAPIClient(conn)

	handler, err := rata.NewRouter(web.Routes, rata.Handlers{
		web.Index:        api.NewIndexHandler(logger, indexLayout, revokClient),
		web.Organization: api.NewOrganizationHandler(logger, organizationLayout, revokClient),
		web.Repository:   api.NewRepositoryHandler(logger, repositoryLayout, revokClient),
	})

	if err != nil {
		log.Fatalf("failed to create handler: %s", err.Error())
	}

	runner := sigmon.New(http_server.New(listenAddr, handler))

	err = <-ifrit.Invoke(runner).Wait()
	if err != nil {
		logger.Error("running-server-failed", err)
	}
}

func keepAliveDial(addr string, timeout time.Duration) (net.Conn, error) {
	d := net.Dialer{
		Timeout:   timeout,
		KeepAlive: 60 * time.Second,
	}
	return d.Dial("tcp", addr)
}

func mustGetEnv(logger lager.Logger, key string) string {
	val := os.Getenv(key)
	err := fmt.Errorf("failed-to-get-env-key")
	if val == "" {
		logger.Fatal("failed-to-get-env-key", err, lager.Data{"missing-env-key": key})
	}

	return val
}
<commit_msg>Revert "Revert "Refactor mustGetEnv to use existing logger.""<commit_after>package main

import (
	"crypto/tls"
	"fmt"
	"html/template"
	"log"
	"net"
	"os"
	"strconv"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"code.cloudfoundry.org/lager"
	flags "github.com/jessevdk/go-flags"
	"github.com/robdimsdale/honeylager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"github.com/tedsuo/rata"

	"cred-alert/ccp/api"
	"cred-alert/ccp/web"
	"cred-alert/config"
	"cred-alert/revokpb"
)

const (
	// Required.
	portEnvKey = "PORT"

	// Passphrase for the client private key. Required if the key is encrypted.
	clientKeyPassphraseEnvKey = "CLIENT_KEY_PASSPHRASE"

	// Address for RPC server. Required.
	rpcServerAddressEnvKey = "RPC_SERVER_ADDRESS"

	// Port for RPC server. Required.
	rpcServerPortEnvKey = "RPC_SERVER_PORT"

	// Required.
	caCertEnvKey = "SERVER_CA_CERT"

	// Optional
	honeycombWriteKeyEnvKey = "HONEYCOMB_WRITE_KEY"
	honeycombDatasetEnvKey  = "HONEYCOMB_DATASET"
)

type Opts struct {
	ClientCertPath string `long:"client-cert-path" description:"Path to the client certificate" required:"true"`
	ClientKeyPath  string `long:"client-key-path" description:"Path to the client private key" required:"true"`
}

var (
	indexLayout        *template.Template
	organizationLayout *template.Template
	repositoryLayout   *template.Template
	logger             lager.Logger
)

func init() {
	bs, err := web.Asset("web/templates/index.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	indexLayout = template.Must(template.New("index.html").Parse(string(bs)))

	bs, err = web.Asset("web/templates/organization.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	organizationLayout = template.Must(template.New("organization.html").Parse(string(bs)))

	bs, err = web.Asset("web/templates/repository.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	repositoryLayout = template.Must(template.New("repository.html").Parse(string(bs)))

	logger = lager.NewLogger("credential-count-publisher")
	logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.INFO))
}

func main() {
	var opts Opts

	_, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
		os.Exit(1)
	}

	honeycombWriteKey := os.Getenv(honeycombWriteKeyEnvKey)
	honeycombDataset := os.Getenv(honeycombDatasetEnvKey)
	if honeycombWriteKey != "" && honeycombDataset != "" {
		s := honeylager.NewSink(honeycombWriteKey, honeycombDataset, lager.DEBUG)
		defer s.Close()
		logger.RegisterSink(s)
	} else {
		logger.Info(fmt.Sprintf(
			"Honeycomb not configured - need %s and %s",
			honeycombWriteKeyEnvKey,
			honeycombDatasetEnvKey,
		))
	}

	logger.Info("starting")

	portStr := mustGetEnv(portEnvKey)
	port, err := strconv.Atoi(portStr)
	if err != nil {
		logger.Fatal("failed-to-parse-port", err)
	}

	rpcServerAddress := mustGetEnv(rpcServerAddressEnvKey)

	rpcServerPortStr := mustGetEnv(rpcServerPortEnvKey)
	rpcServerPort, err := strconv.Atoi(rpcServerPortStr)
	if err != nil {
		logger.Fatal("failed-to-parse-rpc-server-port", err)
	}

	serverAddr := fmt.Sprintf("%s:%d", rpcServerAddress, rpcServerPort)
	listenAddr := fmt.Sprintf(":%d", port)

	clientCert, err := config.LoadCertificate(
		opts.ClientCertPath,
		opts.ClientKeyPath,
		os.Getenv(clientKeyPassphraseEnvKey),
	)
	if err != nil {
		log.Fatalln(err)
	}

	caCert := mustGetEnv(caCertEnvKey)

	rootCertPool, err := config.LoadCertificatePool(caCert)
	if err != nil {
		log.Fatalln(err)
	}

	transportCreds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      rootCertPool,
	})

	conn, err := grpc.Dial(
		serverAddr,
		grpc.WithTransportCredentials(transportCreds),
		grpc.WithDialer(keepAliveDial),
	)
	if err != nil {
		log.Fatalf("failed to create handler: %s", err.Error())
	}
	defer conn.Close()

	revokClient := revokpb.NewRevokAPIClient(conn)

	handler, err := rata.NewRouter(web.Routes, rata.Handlers{
		web.Index:        api.NewIndexHandler(logger, indexLayout, revokClient),
		web.Organization: api.NewOrganizationHandler(logger, organizationLayout, revokClient),
		web.Repository:   api.NewRepositoryHandler(logger, repositoryLayout, revokClient),
	})

	if err != nil {
		log.Fatalf("failed to create handler: %s", err.Error())
	}

	runner := sigmon.New(http_server.New(listenAddr, handler))

	err = <-ifrit.Invoke(runner).Wait()
	if err != nil {
		logger.Error("running-server-failed", err)
	}
}

func keepAliveDial(addr string, timeout time.Duration) (net.Conn, error) {
	d := net.Dialer{
		Timeout:   timeout,
		KeepAlive: 60 * time.Second,
	}
	return d.Dial("tcp", addr)
}

func mustGetEnv(key string) string {
	val := os.Getenv(key)
	err := fmt.Errorf("failed-to-get-env-key")
	if val == "" {
		logger.Fatal("failed-to-get-env-key", err, lager.Data{"missing-env-key": key})
	}

	return val
}
<|endoftext|>


<commit_before>package kontena

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/jakubknejzlik/kontena-git-cli/model"
	"github.com/jakubknejzlik/kontena-git-cli/utils"
)

// ServiceCreate ...
func (c *Client) ServiceCreate(name string, service model.KontenaService) error {
	return c.ServiceCreateInGrid(c.CurrentGrid().Name, name, service)
}

// ServiceCreateInGrid ...
func (c *Client) ServiceCreateInGrid(grid, name string, service model.KontenaService) error {
	cmd := []string{`kontena service create`}
	if service.Instances != nil && *service.Instances > 0 {
		cmd = append(cmd, `--instances `+string(*service.Instances))
	}
	if service.Command != "" {
		cmd = append(cmd, `--cmd `+service.Command)
	}
	for _, value := range service.Environment {
		cmd = append(cmd, `-e "`+value+`"`)
	}
	for _, value := range service.Links {
		cmd = append(cmd, `-l "`+value+`"`)
	}
	for _, value := range service.Volumes {
		cmd = append(cmd, `-v "`+value+`"`)
	}
	for _, value := range service.Ports {
		cmd = append(cmd, `-p "`+value+`"`)
	}
	if service.Deploy.Strategy != "" {
		cmd = append(cmd, `--deploy `+service.Deploy.Strategy)
	}
	cmd = append(cmd, `--grid `+grid)
	cmd = append(cmd, name)
	cmd = append(cmd, service.Image)

	utils.Log("creating service", name, "in grid", grid)
	return utils.RunInteractive(strings.Join(cmd, " "))
}

// ServiceDeploy ...
func (c *Client) ServiceDeploy(service string) error {
	utils.Log("deploying service", service)
	return utils.RunInteractive(fmt.Sprintf("kontena service deploy %s", service))
}

// ServiceDeployInGrid ...
func (c *Client) ServiceDeployInGrid(grid, service string) error {
	utils.Log("deploying service", service, "in grid", grid)
	return utils.RunInteractive(fmt.Sprintf("kontena service deploy --grid %s %s", grid, service))
}

// ServiceInStackDeploy ...
func (c *Client) ServiceInStackDeploy(stack, service string) error {
	return c.ServiceDeploy(stack + "/" + service)
}

// ServiceInStackInGridDeploy ...
func (c *Client) ServiceInStackInGridDeploy(grid, stack, service string) error {
	return c.ServiceDeployInGrid(grid, stack+"/"+service)
}

// ServiceExec ...
func (c *Client) ServiceExec(service, command string) ([]byte, error) {
	return utils.Run(fmt.Sprintf("kontena service exec %s %s", service, command))
}

// ServiceExecInGrid ...
func (c *Client) ServiceExecInGrid(grid, service, command string) ([]byte, error) {
	return utils.Run(fmt.Sprintf("kontena service exec --grid %s %s %s", grid, service, command))
}

// ServiceExecCommand ...
func (c *Client) ServiceExecCommand(service, command string) *exec.Cmd {
	return utils.RunCommand(fmt.Sprintf("kontena service exec %s %s", service, command))
}

// ServiceExecInGridCommand ...
func (c *Client) ServiceExecInGridCommand(grid, service, command string) *exec.Cmd {
	return utils.RunCommand(fmt.Sprintf("kontena service exec --grid %s %s %s", grid, service, command))
}

// ServiceInStackExec ...
func (c *Client) ServiceInStackExec(stack, service, command string) ([]byte, error) {
	return c.ServiceExec(stack+"/"+service, command)
}

// ServiceInStackInGridExec ...
func (c *Client) ServiceInStackInGridExec(grid, stack, service, command string) ([]byte, error) {
	return c.ServiceExecInGrid(grid, stack+"/"+service, command)
}

// ServiceInStackExecCommand ...
func (c *Client) ServiceInStackExecCommand(stack, service, command string) *exec.Cmd {
	return c.ServiceExecCommand(stack+"/"+service, command)
}

// ServiceInStackInGridExecCommand ...
func (c *Client) ServiceInStackInGridExecCommand(grid, stack, service, command string) *exec.Cmd {
	return c.ServiceExecInGridCommand(grid, stack+"/"+service, command)
}

// ServiceRemove ...
func (c *Client) ServiceRemove(service string) error {
	utils.Log("removing service", service)
	return utils.RunInteractive(fmt.Sprintf("kontena service rm --force %s", service))
}

// ServiceRemoveFromGrid ...
func (c *Client) ServiceRemoveFromGrid(grid, service string) error {
	utils.Log("removing service", service)
	return utils.RunInteractive(fmt.Sprintf("kontena service rm --grid %s --force %s", grid, service))
}

// ServiceList ...
func (c *Client) ServiceList() ([]string, error) {
	data, err := utils.Run("kontena service ls -q")
	if err != nil {
		return []string{}, err
	}
	return utils.SplitString(string(data), "\n"), nil
}

// ServiceListInGrid ...
func (c *Client) ServiceListInGrid(grid string) ([]string, error) {
	data, err := utils.Run(fmt.Sprintf("kontena service ls --grid %s -q", grid))
	if err != nil {
		return []string{}, err
	}
	return utils.SplitString(string(data), "\n"), nil
}

// ServiceExists ...
func (c *Client) ServiceExists(service string) (bool, error) {
	services, err := c.ServiceList()
	if err != nil {
		return false, err
	}
	return utils.ArrayOfStringsContains(services, service), nil
}

// ServiceExistsInGrid ...
func (c *Client) ServiceExistsInGrid(grid, service string) (bool, error) {
	services, err := c.ServiceListInGrid(grid)
	if err != nil {
		return false, err
	}
	return utils.ArrayOfStringsContains(services, service), nil
}
<commit_msg>Add service logs commands<commit_after>package kontena

import (
	"fmt"
	"os/exec"
	"strings"

	"github.com/jakubknejzlik/kontena-git-cli/model"
	"github.com/jakubknejzlik/kontena-git-cli/utils"
)

// ServiceCreate ...
func (c *Client) ServiceCreate(name string, service model.KontenaService) error {
	return c.ServiceCreateInGrid(c.CurrentGrid().Name, name, service)
}

// ServiceCreateInGrid ...
func (c *Client) ServiceCreateInGrid(grid, name string, service model.KontenaService) error {
	cmd := []string{`kontena service create`}
	if service.Instances != nil && *service.Instances > 0 {
		cmd = append(cmd, `--instances `+string(*service.Instances))
	}
	if service.Command != "" {
		cmd = append(cmd, `--cmd `+service.Command)
	}
	for _, value := range service.Environment {
		cmd = append(cmd, `-e "`+value+`"`)
	}
	for _, value := range service.Links {
		cmd = append(cmd, `-l "`+value+`"`)
	}
	for _, value := range service.Volumes {
		cmd = append(cmd, `-v "`+value+`"`)
	}
	for _, value := range service.Ports {
		cmd = append(cmd, `-p "`+value+`"`)
	}
	if service.Deploy.Strategy != "" {
		cmd = append(cmd, `--deploy `+service.Deploy.Strategy)
	}
	cmd = append(cmd, `--grid `+grid)
	cmd = append(cmd, name)
	cmd = append(cmd, service.Image)

	utils.Log("creating service", name, "in grid", grid)
	return utils.RunInteractive(strings.Join(cmd, " "))
}

// ServiceDeploy ...
func (c *Client) ServiceDeploy(service string) error {
	utils.Log("deploying service", service)
	return utils.RunInteractive(fmt.Sprintf("kontena service deploy %s", service))
}

// ServiceDeployInGrid ...
func (c *Client) ServiceDeployInGrid(grid, service string) error {
	utils.Log("deploying service", service, "in grid", grid)
	return utils.RunInteractive(fmt.Sprintf("kontena service deploy --grid %s %s", grid, service))
}

// ServiceInStackDeploy ...
func (c *Client) ServiceInStackDeploy(stack, service string) error {
	return c.ServiceDeploy(stack + "/" + service)
}

// ServiceInStackInGridDeploy ...
func (c *Client) ServiceInStackInGridDeploy(grid, stack, service string) error {
	return c.ServiceDeployInGrid(grid, stack+"/"+service)
}

// ServiceExec ...
func (c *Client) ServiceExec(service, command string) ([]byte, error) {
	return utils.Run(fmt.Sprintf("kontena service exec %s %s", service, command))
}

// ServiceExecInGrid ...
func (c *Client) ServiceExecInGrid(grid, service, command string) ([]byte, error) {
	return utils.Run(fmt.Sprintf("kontena service exec --grid %s %s %s", grid, service, command))
}

// ServiceExecCommand ...
func (c *Client) ServiceExecCommand(service, command string) *exec.Cmd {
	return utils.RunCommand(fmt.Sprintf("kontena service exec %s %s", service, command))
}

// ServiceExecInGridCommand ...
func (c *Client) ServiceExecInGridCommand(grid, service, command string) *exec.Cmd {
	return utils.RunCommand(fmt.Sprintf("kontena service exec --grid %s %s %s", grid, service, command))
}

// ServiceInStackExec ...
func (c *Client) ServiceInStackExec(stack, service, command string) ([]byte, error) {
	return c.ServiceExec(stack+"/"+service, command)
}

// ServiceInStackInGridExec ...
func (c *Client) ServiceInStackInGridExec(grid, stack, service, command string) ([]byte, error) {
	return c.ServiceExecInGrid(grid, stack+"/"+service, command)
}

// ServiceInStackExecCommand ...
func (c *Client) ServiceInStackExecCommand(stack, service, command string) *exec.Cmd {
	return c.ServiceExecCommand(stack+"/"+service, command)
}

// ServiceInStackInGridExecCommand ...
func (c *Client) ServiceInStackInGridExecCommand(grid, stack, service, command string) *exec.Cmd {
	return c.ServiceExecInGridCommand(grid, stack+"/"+service, command)
}

// ServiceRemove ...
func (c *Client) ServiceRemove(service string) error {
	utils.Log("removing service", service)
	return utils.RunInteractive(fmt.Sprintf("kontena service rm --force %s", service))
}

// ServiceRemoveFromGrid ...
func (c *Client) ServiceRemoveFromGrid(grid, service string) error {
	utils.Log("removing service", service)
	return utils.RunInteractive(fmt.Sprintf("kontena service rm --grid %s --force %s", grid, service))
}

// ServiceList ...
func (c *Client) ServiceList() ([]string, error) {
	data, err := utils.Run("kontena service ls -q")
	if err != nil {
		return []string{}, err
	}
	return utils.SplitString(string(data), "\n"), nil
}

// ServiceListInGrid ...
func (c *Client) ServiceListInGrid(grid string) ([]string, error) {
	data, err := utils.Run(fmt.Sprintf("kontena service ls --grid %s -q", grid))
	if err != nil {
		return []string{}, err
	}
	return utils.SplitString(string(data), "\n"), nil
}

// ServiceExists ...
func (c *Client) ServiceExists(service string) (bool, error) {
	services, err := c.ServiceList()
	if err != nil {
		return false, err
	}
	return utils.ArrayOfStringsContains(services, service), nil
}

// ServiceExistsInGrid ...
func (c *Client) ServiceExistsInGrid(grid, service string) (bool, error) {
	services, err := c.ServiceListInGrid(grid)
	if err != nil {
		return false, err
	}
	return utils.ArrayOfStringsContains(services, service), nil
}

// ServiceLogs ...
func (c *Client) ServiceLogs(service string) (string, error) {
	data, err := utils.Run(fmt.Sprintf("kontena service logs %s", service))
	return string(data), err
}

// ServiceInStackLogs ...
func (c *Client) ServiceInStackLogs(stack, service string) (string, error) {
	data, err := utils.Run(fmt.Sprintf("kontena service logs %s/%s", stack, service))
	return string(data), err
}

// ServiceInStackInGridLogs ...
func (c *Client) ServiceInStackInGridLogs(grid, stack, service string) (string, error) {
	data, err := utils.Run(fmt.Sprintf("kontena service logs --grid %s %s/%s", grid, stack, service))
	return string(data), err
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"fmt"
	"time"
)

// GistsService handles communication with the Gist related
// methods of the GitHub API.
//
// GitHub API docs: http://developer.github.com/v3/gists/
type GistsService struct {
	client *Client
}

// Gist represents a GitHub's gist.
type Gist struct {
	ID          *string                   `json:"id,omitempty"`
	Description *string                   `json:"description,omitempty"`
	Public      *bool                     `json:"public,omitempty"`
	User        *User                     `json:"user,omitempty"`
	Files       map[GistFilename]GistFile `json:"files,omitempty"`
	Comments    *int                      `json:"comments,omitempty"`
	HTMLURL     *string                   `json:"html_url,omitempty"`
	GitPullURL  *string                   `json:"git_pull_url,omitempty"`
	GitPushURL  *string                   `json:"git_push_url,omitempty"`
	CreatedAt   *time.Time                `json:"created_at,omitempty"`
}

func (g Gist) String() string {
	return Stringify(g)
}

// GistFilename represents filename on a gist.
type GistFilename string

// GistFile represents a file on a gist.
type GistFile struct {
	Size     *int    `json:"size,omitempty"`
	Filename *string `json:"filename,omitempty"`
	RawURL   *string `json:"raw_url,omitempty"`
	Content  *string `json:"content,omitempty"`
}

func (g GistFile) String() string {
	return Stringify(g)
}

// GistListOptions specifies the optional parameters to the
// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods.
type GistListOptions struct {
	// Since filters Gists by time.
	Since time.Time `url:"since,omitempty"`
}

// List gists for a user. Passing the empty string will list
// all public gists if called anonymously. However, if the call
// is authenticated, it will returns all gists for the authenticated
// user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
func (s *GistsService) List(user string, opt *GistListOptions) ([]Gist, *Response, error) {
	var u string
	if user != "" {
		u = fmt.Sprintf("users/%v/gists", user)
	} else {
		u = "gists"
	}
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	gists := new([]Gist)
	resp, err := s.client.Do(req, gists)
	if err != nil {
		return nil, resp, err
	}

	return *gists, resp, err
}

// ListAll lists all public gists.
//
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
func (s *GistsService) ListAll(opt *GistListOptions) ([]Gist, *Response, error) {
	u, err := addOptions("gists/public", opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	gists := new([]Gist)
	resp, err := s.client.Do(req, gists)
	if err != nil {
		return nil, resp, err
	}

	return *gists, resp, err
}

// ListStarred lists starred gists of authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
func (s *GistsService) ListStarred(opt *GistListOptions) ([]Gist, *Response, error) {
	u, err := addOptions("gists/starred", opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	gists := new([]Gist)
	resp, err := s.client.Do(req, gists)
	if err != nil {
		return nil, resp, err
	}

	return *gists, resp, err
}

// Get a single gist.
//
// GitHub API docs: http://developer.github.com/v3/gists/#get-a-single-gist
func (s *GistsService) Get(id string) (*Gist, *Response, error) {
	u := fmt.Sprintf("gists/%v", id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}
	gist := new(Gist)
	resp, err := s.client.Do(req, gist)
	if err != nil {
		return nil, resp, err
	}

	return gist, resp, err
}

// Create a gist for authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#create-a-gist
func (s *GistsService) Create(gist *Gist) (*Gist, *Response, error) {
	u := "gists"
	req, err := s.client.NewRequest("POST", u, gist)
	if err != nil {
		return nil, nil, err
	}
	g := new(Gist)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, err
}

// Edit a gist.
//
// GitHub API docs: http://developer.github.com/v3/gists/#edit-a-gist
func (s *GistsService) Edit(id string, gist *Gist) (*Gist, *Response, error) {
	u := fmt.Sprintf("gists/%v", id)
	req, err := s.client.NewRequest("PATCH", u, gist)
	if err != nil {
		return nil, nil, err
	}
	g := new(Gist)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, err
}

// Delete a gist.
//
// GitHub API docs: http://developer.github.com/v3/gists/#delete-a-gist
func (s *GistsService) Delete(id string) (*Response, error) {
	u := fmt.Sprintf("gists/%v", id)
	req, err := s.client.NewRequest("DELETE", u, nil)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}

// Star a gist on behalf of authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#star-a-gist
func (s *GistsService) Star(id string) (*Response, error) {
	u := fmt.Sprintf("gists/%v/star", id)
	req, err := s.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}

// Unstar a gist on a behalf of authenticated user.
//
// Github API docs: http://developer.github.com/v3/gists/#unstar-a-gist
func (s *GistsService) Unstar(id string) (*Response, error) {
	u := fmt.Sprintf("gists/%v/star", id)
	req, err := s.client.NewRequest("DELETE", u, nil)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}

// IsStarred checks if a gist is starred by authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#check-if-a-gist-is-starred
func (s *GistsService) IsStarred(id string) (bool, *Response, error) {
	u := fmt.Sprintf("gists/%v/star", id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return false, nil, err
	}
	resp, err := s.client.Do(req, nil)
	starred, err := parseBoolResponse(err)
	return starred, resp, err
}

// Fork a gist.
//
// GitHub API docs: http://developer.github.com/v3/gists/#fork-a-gist
func (s *GistsService) Fork(id string) (*Gist, *Response, error) {
	u := fmt.Sprintf("gists/%v/forks", id)
	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return nil, nil, err
	}

	g := new(Gist)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, err
}
<commit_msg>support pagination for listing gists<commit_after>// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"fmt"
	"time"
)

// GistsService handles communication with the Gist related
// methods of the GitHub API.
//
// GitHub API docs: http://developer.github.com/v3/gists/
type GistsService struct {
	client *Client
}

// Gist represents a GitHub's gist.
type Gist struct {
	ID          *string                   `json:"id,omitempty"`
	Description *string                   `json:"description,omitempty"`
	Public      *bool                     `json:"public,omitempty"`
	User        *User                     `json:"user,omitempty"`
	Files       map[GistFilename]GistFile `json:"files,omitempty"`
	Comments    *int                      `json:"comments,omitempty"`
	HTMLURL     *string                   `json:"html_url,omitempty"`
	GitPullURL  *string                   `json:"git_pull_url,omitempty"`
	GitPushURL  *string                   `json:"git_push_url,omitempty"`
	CreatedAt   *time.Time                `json:"created_at,omitempty"`
}

func (g Gist) String() string {
	return Stringify(g)
}

// GistFilename represents filename on a gist.
type GistFilename string

// GistFile represents a file on a gist.
type GistFile struct {
	Size     *int    `json:"size,omitempty"`
	Filename *string `json:"filename,omitempty"`
	RawURL   *string `json:"raw_url,omitempty"`
	Content  *string `json:"content,omitempty"`
}

func (g GistFile) String() string {
	return Stringify(g)
}

// GistListOptions specifies the optional parameters to the
// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods.
type GistListOptions struct {
	// Since filters Gists by time.
	Since time.Time `url:"since,omitempty"`

	ListOptions
}

// List gists for a user. Passing the empty string will list
// all public gists if called anonymously. However, if the call
// is authenticated, it will returns all gists for the authenticated
// user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
func (s *GistsService) List(user string, opt *GistListOptions) ([]Gist, *Response, error) {
	var u string
	if user != "" {
		u = fmt.Sprintf("users/%v/gists", user)
	} else {
		u = "gists"
	}
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	gists := new([]Gist)
	resp, err := s.client.Do(req, gists)
	if err != nil {
		return nil, resp, err
	}

	return *gists, resp, err
}

// ListAll lists all public gists.
//
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
func (s *GistsService) ListAll(opt *GistListOptions) ([]Gist, *Response, error) {
	u, err := addOptions("gists/public", opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	gists := new([]Gist)
	resp, err := s.client.Do(req, gists)
	if err != nil {
		return nil, resp, err
	}

	return *gists, resp, err
}

// ListStarred lists starred gists of authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
func (s *GistsService) ListStarred(opt *GistListOptions) ([]Gist, *Response, error) {
	u, err := addOptions("gists/starred", opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	gists := new([]Gist)
	resp, err := s.client.Do(req, gists)
	if err != nil {
		return nil, resp, err
	}

	return *gists, resp, err
}

// Get a single gist.
//
// GitHub API docs: http://developer.github.com/v3/gists/#get-a-single-gist
func (s *GistsService) Get(id string) (*Gist, *Response, error) {
	u := fmt.Sprintf("gists/%v", id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}
	gist := new(Gist)
	resp, err := s.client.Do(req, gist)
	if err != nil {
		return nil, resp, err
	}

	return gist, resp, err
}

// Create a gist for authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#create-a-gist
func (s *GistsService) Create(gist *Gist) (*Gist, *Response, error) {
	u := "gists"
	req, err := s.client.NewRequest("POST", u, gist)
	if err != nil {
		return nil, nil, err
	}
	g := new(Gist)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, err
}

// Edit a gist.
//
// GitHub API docs: http://developer.github.com/v3/gists/#edit-a-gist
func (s *GistsService) Edit(id string, gist *Gist) (*Gist, *Response, error) {
	u := fmt.Sprintf("gists/%v", id)
	req, err := s.client.NewRequest("PATCH", u, gist)
	if err != nil {
		return nil, nil, err
	}
	g := new(Gist)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, err
}

// Delete a gist.
//
// GitHub API docs: http://developer.github.com/v3/gists/#delete-a-gist
func (s *GistsService) Delete(id string) (*Response, error) {
	u := fmt.Sprintf("gists/%v", id)
	req, err := s.client.NewRequest("DELETE", u, nil)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}

// Star a gist on behalf of authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#star-a-gist
func (s *GistsService) Star(id string) (*Response, error) {
	u := fmt.Sprintf("gists/%v/star", id)
	req, err := s.client.NewRequest("PUT", u, nil)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}

// Unstar a gist on a behalf of authenticated user.
//
// Github API docs: http://developer.github.com/v3/gists/#unstar-a-gist
func (s *GistsService) Unstar(id string) (*Response, error) {
	u := fmt.Sprintf("gists/%v/star", id)
	req, err := s.client.NewRequest("DELETE", u, nil)
	if err != nil {
		return nil, err
	}
	return s.client.Do(req, nil)
}

// IsStarred checks if a gist is starred by authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/gists/#check-if-a-gist-is-starred
func (s *GistsService) IsStarred(id string) (bool, *Response, error) {
	u := fmt.Sprintf("gists/%v/star", id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return false, nil, err
	}
	resp, err := s.client.Do(req, nil)
	starred, err := parseBoolResponse(err)
	return starred, resp, err
}

// Fork a gist.
//
// GitHub API docs: http://developer.github.com/v3/gists/#fork-a-gist
func (s *GistsService) Fork(id string) (*Gist, *Response, error) {
	u := fmt.Sprintf("gists/%v/forks", id)
	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return nil, nil, err
	}

	g := new(Gist)
	resp, err := s.client.Do(req, g)
	if err != nil {
		return nil, resp, err
	}

	return g, resp, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"errors"
	"strconv"
	"os"
	"io/ioutil"
	"net/http"
	"github.com/MadAppGang/kdbush"
	"github.com/paulmach/go.geojson"
)

type Record struct {
	Feature *geojson.Feature
}

func (p Record) Coordinates() (float64, float64) {
	if !p.Feature.Geometry.IsPoint() {
		panic("Only Point features are supported")
	}

	return p.Feature.Geometry.Point[0], p.Feature.Geometry.Point[1]
}

type BoundingBox struct {
	MinX, MinY, MaxX, MaxY float64
}

func UnmarshalBoundingBox(str string) (BoundingBox, error) {
	components := strings.Split(str, ",")
	if len(components) != 4 {
		return BoundingBox{}, errors.New("bbox string is not 4 components long")
	}

	minX, err := strconv.ParseFloat(components[0], 64)
	if err != nil {
		return BoundingBox{}, fmt.Errorf("Could not decode first component:", err)
	}

	minY, err := strconv.ParseFloat(components[1], 64)
	if err != nil {
		return BoundingBox{}, fmt.Errorf("Could not decode first component:", err)
	}

	maxX, err := strconv.ParseFloat(components[2], 64)
	if err != nil {
		return BoundingBox{}, fmt.Errorf("Could not decode first component:", err)
	}

	maxY, err := strconv.ParseFloat(components[3], 64)
	if err != nil {
		return BoundingBox{}, fmt.Errorf("Could not decode first component:", err)
	}

	return BoundingBox{minX, minY, maxX, maxY}, nil
}

func UnmarshalPoint(str string) (kdbush.Point, error) {
	components := strings.Split(str, ",")

	if len(components) != 2 {
		return nil, errors.New("point string is not 2 components long")
	}

	x, err := strconv.ParseFloat(components[0], 64);
	if err != nil {
		return nil, fmt.Errorf("Could not decode first component:", err)
	}

	y, err := strconv.ParseFloat(components[1], 64);
	if err != nil {
		return nil, fmt.Errorf("Could not decode second component:", err)
	}

	return &kdbush.SimplePoint{x, y}, nil
}

func makeJSONHandler(fn func(*http.Request) ([]byte, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		bytes, err := fn(r)

		if err != nil {
			w.WriteHeader(400)
			fmt.Fprintf(w, "Error: %s", err)
		} else {
			w.Header().Add("Content-Type", "application/json; charset=utf8")
			w.Header().Add("Content-Length", strconv.Itoa(len(bytes)))
			w.WriteHeader(200)
			w.Write(bytes)
		}
	}
}

func makeFeatureCollectionHandler(fc *geojson.FeatureCollection, fn func(*http.Request) ([]int, error)) http.HandlerFunc {
	return makeJSONHandler(func(r *http.Request) ([]byte, error) {
		results, err := fn(r)
		if err != nil {
			return nil, err
		}

		rfc := geojson.NewFeatureCollection()

		for _, i := range results {
			rfc.AddFeature(fc.Features[i])
		}

		bytes, err := rfc.MarshalJSON()
		if err != nil {
			return nil, err
		}

		return bytes, nil
	})
}

func main() {
	fc := geojson.NewFeatureCollection()

	/* Read each GeoJSON file passed as argument */
	for _, path := range os.Args[1:] {
		dat, err := ioutil.ReadFile(path)
		if err != nil {
			panic(err)
		}

		ffc, err := geojson.UnmarshalFeatureCollection(dat)
		if err != nil {
			panic(err)
		}

		fc.Features = append(fc.Features, ffc.Features...)
	}

	/* Create the index */

	points := make([]kdbush.Point, len(fc.Features))

	for i, v := range fc.Features {
		points[i] = Record{v}
	}

	fmt.Printf("Building index for %d records...\n", len(points))

	bush := kdbush.NewBush(points, 10)

	featureHandler := func(r *http.Request) ([]int, error) {
		bbox, err := UnmarshalBoundingBox(r.FormValue("bbox"))

		if err != nil {
			return nil, err
		}

		return bush.Range(bbox.MinX, bbox.MinY, bbox.MaxX, bbox.MaxY), nil
	}

	http.HandleFunc("/features", makeFeatureCollectionHandler(fc, featureHandler));

	nearestHandler := func(r *http.Request) ([]int, error) {
		point, err := UnmarshalPoint(r.FormValue("point"))
		if err != nil {
			return nil, err
		}

		radius, err := strconv.ParseFloat(r.FormValue("radius"), 10)
		if err != nil {
			return nil, err
		}

		return bush.Within(point, radius), nil
	}

	http.HandleFunc("/nearest", makeFeatureCollectionHandler(fc, nearestHandler));

	http.ListenAndServe(":8000", nil);
}
<commit_msg>Partial implementation of live reloading<commit_after>package main

import (
	"fmt"
	"strings"
	"errors"
	"strconv"
	"os"
	"io/ioutil"
	"net/http"
	"github.com/MadAppGang/kdbush"
	"github.com/paulmach/go.geojson"
	"github.com/fsnotify/fsnotify"
)

type Record struct {
	Feature *geojson.Feature
}

type DataSet struct {
	FeatureCollection *geojson.FeatureCollection
	Index *kdbush.KDBush
}

func (p Record) Coordinates() (float64, float64) {
	if !p.Feature.Geometry.IsPoint() {
		panic("Only Point features are supported")
	}

	return p.Feature.Geometry.Point[0], p.Feature.Geometry.Point[1]
}

type BoundingBox struct {
	MinX, MinY, MaxX, MaxY float64
}

func UnmarshalBoundingBox(str string) (BoundingBox, error) {
	components := strings.Split(str, ",")
	if len(components) != 4 {
		return BoundingBox{}, errors.New("bbox string is not 4 components long")
	}

	minX, err := strconv.ParseFloat(components[0], 64)
	if err != nil {
		return BoundingBox{}, fmt.Errorf("Could not decode first component:", err)
	}

	minY, err := strconv.ParseFloat(components[1], 64)
	if err != nil {
		return BoundingBox{}, fmt.Errorf("Could not decode first component:", err)
	}

	maxX, err := strconv.ParseFloat(components[2], 64)
	if err != nil {
		return BoundingBox{}, fmt.Errorf("Could not decode first component:", err)
	}

	maxY, err := strconv.ParseFloat(components[3], 64)
	if err != nil {
		return BoundingBox{}, fmt.Errorf("Could not decode first component:", err)
	}

	return BoundingBox{minX, minY, maxX, maxY}, nil
}

func UnmarshalPoint(str string) (kdbush.Point, error) {
	components := strings.Split(str, ",")

	if len(components) != 2 {
		return nil, errors.New("point string is not 2 components long")
	}

	x, err := strconv.ParseFloat(components[0], 64);
	if err != nil {
		return nil, fmt.Errorf("Could not decode first component:", err)
	}

	y, err := strconv.ParseFloat(components[1], 64);
	if err != nil {
		return nil, fmt.Errorf("Could not decode second component:", err)
	}

	return &kdbush.SimplePoint{x, y}, nil
}

func makeJSONHandler(fn func(*http.Request) ([]byte, error)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		bytes, err := fn(r)

		if err != nil {
			w.WriteHeader(400)
			fmt.Fprintf(w, "Error: %s", err)
		} else {
			w.Header().Add("Content-Type", "application/json; charset=utf8")
			w.Header().Add("Content-Length", strconv.Itoa(len(bytes)))
			w.WriteHeader(200)
			w.Write(bytes)
		}
	}
}

func makeFeatureCollectionHandler(ds *DataSet, fn func(*DataSet, *http.Request) ([]int, error)) http.HandlerFunc {
	return makeJSONHandler(func(r *http.Request) ([]byte, error) {
		results, err := fn(ds, r)
		if err != nil {
			return nil, err
		}

		rfc := geojson.NewFeatureCollection()

		for _, i := range results {
			rfc.AddFeature(ds.GetFeature(i))
		}

		bytes, err := rfc.MarshalJSON()
		if err != nil {
			return nil, err
		}

		return bytes, nil
	})
}

func NewDataSet(paths []string) *DataSet {
	ds := new(DataSet)

	ds.FeatureCollection = geojson.NewFeatureCollection()

	/* Try to load the features from each file */
	for _, path := range paths {
		dat, err := ioutil.ReadFile(path)
		if err != nil {
			fmt.Printf("Skipped %s: %s", path, err)
		}

		ffc, err := geojson.UnmarshalFeatureCollection(dat)
		if err != nil {
			fmt.Printf("Skipped %s: %s", path, err)
		}

		ds.AddFeatures(ffc.Features)
	}

	/* Create the index */
	points := make([]kdbush.Point, len(ds.FeatureCollection.Features))

	for i, v := range ds.FeatureCollection.Features {
		points[i] = Record{v}
	}

	ds.Index = kdbush.NewBush(points, 10)

	return ds
}

func (ds *DataSet) AddFeatures(features []*geojson.Feature) {
	ds.FeatureCollection.Features = append(ds.FeatureCollection.Features, features...)
}

func (ds *DataSet) GetFeature(i int) *geojson.Feature {
	return ds.FeatureCollection.Features[i];
}

func featureHandler(ds *DataSet, r *http.Request) ([]int, error) {
	bbox, err := UnmarshalBoundingBox(r.FormValue("bbox"))

	if err != nil {
		return nil, err
	}

	return ds.Index.Range(bbox.MinX, bbox.MinY, bbox.MaxX, bbox.MaxY), nil
}

func nearestHandler(ds *DataSet, r *http.Request) ([]int, error) {
	point, err := UnmarshalPoint(r.FormValue("point"))
	if err != nil {
		return nil, err
	}

	radius, err := strconv.ParseFloat(r.FormValue("radius"), 10)
	if err != nil {
		return nil, err
	}

	return ds.Index.Within(point, radius), nil
}

func main() {
	files := make([]string, len(os.Args) - 1)
	copy(files, os.Args[1:])

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}
	defer watcher.Close()

	/* Read each GeoJSON file passed as argument */
	ds := NewDataSet(files)

	/* Watch files for reloading */
	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				fmt.Println("event:", event)
				if event.Op&fsnotify.Write == fsnotify.Write {
					fmt.Println("modified file:", event.Name)
					ds = NewDataSet(files)
					fmt.Printf("Dataset contains %d features\n", len(ds.FeatureCollection.Features))
				}
			case err := <-watcher.Errors:
				fmt.Println("error:", err)
			}
		}
	}()

	/* Watch files */
	for _, path := range files {
		err = watcher.Add(path)
		if err != nil {
			fmt.Println(err)
		}
	}

	http.HandleFunc("/features", makeFeatureCollectionHandler(ds, featureHandler));

	http.HandleFunc("/nearest", makeFeatureCollectionHandler(ds, nearestHandler));

	http.ListenAndServe(":8000", nil);

	<- done
}
<|endoftext|>


<commit_before>/*
Package dsl implements the Goa DSL.

The Goa DSL consists of Go functions that can be composed to describe a remote
service API. The functions are composed using anonymous function arguments, for
example:

    var Person = Type("Person", func() {
        Attribute("name", String)
    })

The package defines a set of "top level" DSL functions - functions that do not
appear within other functions such as Type above and a number of functions that
are meant to be used within others such as Attribute above.

The comments for each function describe the intent, parameters and usage of the
function. A number of DSL functions leverage variadic arguments to emulate
optional arguments, for example these are all valid use of Attribute:

    Attribute("name", String)
    Attribute("name", String, "The name of the person")
    Attribute("name", String, "The name of the person", func() {
        Meta("struct:field:type", "json.RawMessage")
    })

It is recommended to use "dot" import when importing the DSL package to improve
the readability of designs:

    import . "goa.design/goa/v3/dsl"

Importing the DSL package this way makes it possible to write the designs as
shown in the examples above instead of having to prefix each DSL function call
with "dsl." (note: the authors are aware that using "dot" imports is bad
practice in general when writing standard Go code and Goa in particular makes no
use of them outside of writing DSLs. However they DO make designs much easier to
read and maintain).

The general structure of the DSL is shown below (partial list):

    API                 Service          Type            ResultType
    ├── Title           ├── Description  ├── Extend      ├── TypeName
    ├── Description     ├── Docs         ├── Reference   ├── ContentType
    ├── Version         ├── Security     ├── ConvertTo   ├── Extend
    ├── Docs            ├── Error        ├── CreateFrom  ├── Reference
    ├── License         ├── GRPC         ├── Attribute   ├── ConvertTo
    ├── TermsOfService  ├── HTTP         ├── Field       ├── CreateFrom
    ├── Contact         ├── Method       └── Required    ├── Attributes
    ├── Server          │   ├── Payload                  └── View
    └── HTTP            │   ├── Result
                        │   ├── Error
                        │   ├── GRPC
                        │   └── HTTP
                        └── Files
*/
package dsl
<commit_msg>Add codegen dependencies (#2903)<commit_after>/*
Package dsl implements the Goa DSL.

The Goa DSL consists of Go functions that can be composed to describe a remote
service API. The functions are composed using anonymous function arguments, for
example:

    var Person = Type("Person", func() {
        Attribute("name", String)
    })

The package defines a set of "top level" DSL functions - functions that do not
appear within other functions such as Type above and a number of functions that
are meant to be used within others such as Attribute above.

The comments for each function describe the intent, parameters and usage of the
function. A number of DSL functions leverage variadic arguments to emulate
optional arguments, for example these are all valid use of Attribute:

    Attribute("name", String)
    Attribute("name", String, "The name of the person")
    Attribute("name", String, "The name of the person", func() {
        Meta("struct:field:type", "json.RawMessage")
    })

It is recommended to use "dot" import when importing the DSL package to improve
the readability of designs:

    import . "goa.design/goa/v3/dsl"

Importing the DSL package this way makes it possible to write the designs as
shown in the examples above instead of having to prefix each DSL function call
with "dsl." (note: the authors are aware that using "dot" imports is bad
practice in general when writing standard Go code and Goa in particular makes no
use of them outside of writing DSLs. However they DO make designs much easier to
read and maintain).

The general structure of the DSL is shown below (partial list):

    API                 Service          Type            ResultType
    ├── Title           ├── Description  ├── Extend      ├── TypeName
    ├── Description     ├── Docs         ├── Reference   ├── ContentType
    ├── Version         ├── Security     ├── ConvertTo   ├── Extend
    ├── Docs            ├── Error        ├── CreateFrom  ├── Reference
    ├── License         ├── GRPC         ├── Attribute   ├── ConvertTo
    ├── TermsOfService  ├── HTTP         ├── Field       ├── CreateFrom
    ├── Contact         ├── Method       └── Required    ├── Attributes
    ├── Server          │   ├── Payload                  └── View
    └── HTTP            │   ├── Result
                        │   ├── Error
                        │   ├── GRPC
                        │   └── HTTP
                        └── Files
*/
package dsl

import (
	// The imports below add dependencies needed by the generated temporary
	// code generation tool. Go cannot detect these dependencies so we must
	// add them explicitly.
	_ "golang.org/x/tools/go/ast/astutil"
	_ "golang.org/x/tools/go/packages"
	_ "golang.org/x/tools/imports"
	_ "gopkg.in/yaml.v2"
)
<|endoftext|>


<commit_before>package duktape

/*
#cgo linux LDFLAGS: -lm
#cgo freebsd LDFLAGS: -lm

# include "duktape.h"
extern duk_ret_t goFunctionCall(duk_context *ctx);
extern void goFinalizeCall(duk_context *ctx);

*/
import "C"
import (
	"errors"
	"fmt"
	"regexp"
	"sync"
	"unsafe"
)

var reFuncName = regexp.MustCompile("^[a-z_][a-z0-9_]*([A-Z_][a-z0-9_]*)*$")

const (
	goFunctionPtrProp = "\xff" + "goFunctionPtrProp"
	goContextPtrProp  = "\xff" + "goContextPtrProp"
)

type Context struct {
	*context
}

// transmute replaces the value from Context with the value of pointer
func (c *Context) transmute(p unsafe.Pointer) {
	*c = *(*Context)(p)
}

// this is a pojo containing only the values of the Context
type context struct {
	sync.Mutex
	duk_context *C.duk_context
	fnIndex     *functionIndex
	timerIndex  *timerIndex
}

// New returns plain initialized duktape context object
// See: http://duktape.org/api.html#duk_create_heap_default
func New() *Context {
	return &Context{
		&context{
			duk_context: C.duk_create_heap(nil, nil, nil, nil, nil),
			fnIndex:     newFunctionIndex(),
			timerIndex:  &timerIndex{},
		},
	}
}

func contextFromPointer(ctx *C.duk_context) *Context {
	return &Context{&context{duk_context: ctx}}
}

// PushGlobalGoFunction push the given function into duktape global object
// Returns non-negative index (relative to stack bottom) of the pushed function
// also returns error if the function name is invalid
func (d *Context) PushGlobalGoFunction(name string, fn func(*Context) int) (int, error) {
	if !reFuncName.MatchString(name) {
		return -1, errors.New("Malformed function name '" + name + "'")
	}

	d.PushGlobalObject()
	idx := d.PushGoFunction(fn)
	d.PutPropString(-2, name)
	d.Pop()

	return idx, nil
}

// PushGoFunction push the given function into duktape stack, returns non-negative
// index (relative to stack bottom) of the pushed function
func (d *Context) PushGoFunction(fn func(*Context) int) int {
	funPtr := d.fnIndex.add(fn)
	ctxPtr := contexts.add(d)

	idx := d.PushCFunction((*[0]byte)(C.goFunctionCall), C.DUK_VARARGS)
	d.PushCFunction((*[0]byte)(C.goFinalizeCall), 1)
	d.PushPointer(funPtr)
	d.PutPropString(-2, goFunctionPtrProp)
	d.PushPointer(ctxPtr)
	d.PutPropString(-2, goContextPtrProp)
	d.SetFinalizer(-2)

	d.PushPointer(funPtr)
	d.PutPropString(-2, goFunctionPtrProp)
	d.PushPointer(ctxPtr)
	d.PutPropString(-2, goContextPtrProp)

	return idx
}

//export goFunctionCall
func goFunctionCall(cCtx *C.duk_context) C.duk_ret_t {
	d := contextFromPointer(cCtx)

	funPtr, ctx := d.getFunctionPtrs()
	d.transmute(unsafe.Pointer(ctx))

	result := d.fnIndex.get(funPtr)(d)

	return C.duk_ret_t(result)
}

//export goFinalizeCall
func goFinalizeCall(cCtx *C.duk_context) {
	d := contextFromPointer(cCtx)

	funPtr, ctx := d.getFunctionPtrs()
	d.transmute(unsafe.Pointer(ctx))

	d.fnIndex.delete(funPtr)
}

func (d *Context) getFunctionPtrs() (unsafe.Pointer, *Context) {
	d.PushCurrentFunction()
	d.GetPropString(-1, goFunctionPtrProp)
	funPtr := d.GetPointer(-1)

	d.Pop()

	d.GetPropString(-1, goContextPtrProp)
	ctx := contexts.get(d.GetPointer(-1))
	d.Pop2()
	return funPtr, ctx
}

// Destroy destroy all the references to the functions and freed the pointers
func (d *Context) Destroy() {
	d.fnIndex.destroy()
	contexts.delete(d)
}

type Error struct {
	Type       string
	Message    string
	FileName   string
	LineNumber int
	Stack      string
}

func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s", e.Type, e.Message)
}

type Type int

func (t Type) IsNone() bool      { return t == TypeNone }
func (t Type) IsUndefined() bool { return t == TypeUndefined }
func (t Type) IsNull() bool      { return t == TypeNull }
func (t Type) IsBool() bool      { return t == TypeBoolean }
func (t Type) IsNumber() bool    { return t == TypeNumber }
func (t Type) IsString() bool    { return t == TypeString }
func (t Type) IsObject() bool    { return t == TypeObject }
func (t Type) IsBuffer() bool    { return t == TypeBuffer }
func (t Type) IsPointer() bool   { return t == TypePointer }
func (t Type) IsLightFunc() bool { return t == TypeLightFunc }

func (t Type) String() string {
	switch t {
	case TypeNone:
		return "None"
	case TypeUndefined:
		return "Undefined"
	case TypeNull:
		return "Null"
	case TypeBoolean:
		return "Boolean"
	case TypeNumber:
		return "Number"
	case TypeString:
		return "String"
	case TypeObject:
		return "Object"
	case TypeBuffer:
		return "Buffer"
	case TypePointer:
		return "Pointer"
	case TypeLightFunc:
		return "LightFunc"
	default:
		return "Unknown"
	}
}

type functionIndex struct {
	functions map[unsafe.Pointer]func(*Context) int
	sync.RWMutex
}

type timerIndex struct {
	c float64
	sync.Mutex
}

func (t *timerIndex) get() float64 {
	t.Lock()
	defer t.Unlock()
	t.c++
	return t.c
}

func newFunctionIndex() *functionIndex {
	return &functionIndex{
		functions: make(map[unsafe.Pointer]func(*Context) int, 0),
	}
}

func (i *functionIndex) add(fn func(*Context) int) unsafe.Pointer {
	ptr := C.malloc(1)

	i.Lock()
	i.functions[ptr] = fn
	i.Unlock()

	return ptr
}

func (i *functionIndex) get(ptr unsafe.Pointer) func(*Context) int {
	i.RLock()
	fn := i.functions[ptr]
	i.RUnlock()

	return fn
}

func (i *functionIndex) delete(ptr unsafe.Pointer) {
	i.Lock()
	delete(i.functions, ptr)
	i.Unlock()

	C.free(ptr)
}

func (i *functionIndex) destroy() {
	i.Lock()

	for ptr, _ := range i.functions {
		delete(i.functions, ptr)
		C.free(ptr)
	}
	i.Unlock()
}

type ctxIndex struct {
	sync.RWMutex
	ctxs map[unsafe.Pointer]*Context
}

func (ci *ctxIndex) add(ctx *Context) unsafe.Pointer {

	ci.RLock()
	for ptr, ctxPtr := range ci.ctxs {
		if ctxPtr == ctx {
			ci.RUnlock()
			return ptr
		}
	}
	ci.RUnlock()

	ptr := C.malloc(1)

	ci.Lock()
	ci.ctxs[ptr] = ctx
	ci.Unlock()

	return ptr
}

func (ci *ctxIndex) get(ptr unsafe.Pointer) *Context {
	ci.RLock()
	ctx := ci.ctxs[ptr]
	ci.RUnlock()
	return ctx
}

func (ci *ctxIndex) delete(ctx *Context) {
	ci.Lock()
	for ptr, ctxPtr := range ci.ctxs {
		if ctxPtr == ctx {
			delete(ci.ctxs, ptr)
			C.free(ptr)
			ci.Unlock()
			return
		}
	}
	panic(fmt.Sprintf("context (%p) doesn't exist", ctx))
}

var contexts *ctxIndex

func init() {
	contexts = &ctxIndex{
		ctxs: make(map[unsafe.Pointer]*Context),
	}
}
<commit_msg>Bugfix: race condition in *ctxIndex.add. Close #36<commit_after>package duktape

/*
#cgo linux LDFLAGS: -lm
#cgo freebsd LDFLAGS: -lm

# include "duktape.h"
extern duk_ret_t goFunctionCall(duk_context *ctx);
extern void goFinalizeCall(duk_context *ctx);

*/
import "C"
import (
	"errors"
	"fmt"
	"regexp"
	"sync"
	"unsafe"
)

var reFuncName = regexp.MustCompile("^[a-z_][a-z0-9_]*([A-Z_][a-z0-9_]*)*$")

const (
	goFunctionPtrProp = "\xff" + "goFunctionPtrProp"
	goContextPtrProp  = "\xff" + "goContextPtrProp"
)

type Context struct {
	*context
}

// transmute replaces the value from Context with the value of pointer
func (c *Context) transmute(p unsafe.Pointer) {
	*c = *(*Context)(p)
}

// this is a pojo containing only the values of the Context
type context struct {
	sync.Mutex
	duk_context *C.duk_context
	fnIndex     *functionIndex
	timerIndex  *timerIndex
}

// New returns plain initialized duktape context object
// See: http://duktape.org/api.html#duk_create_heap_default
func New() *Context {
	return &Context{
		&context{
			duk_context: C.duk_create_heap(nil, nil, nil, nil, nil),
			fnIndex:     newFunctionIndex(),
			timerIndex:  &timerIndex{},
		},
	}
}

func contextFromPointer(ctx *C.duk_context) *Context {
	return &Context{&context{duk_context: ctx}}
}

// PushGlobalGoFunction push the given function into duktape global object
// Returns non-negative index (relative to stack bottom) of the pushed function
// also returns error if the function name is invalid
func (d *Context) PushGlobalGoFunction(name string, fn func(*Context) int) (int, error) {
	if !reFuncName.MatchString(name) {
		return -1, errors.New("Malformed function name '" + name + "'")
	}

	d.PushGlobalObject()
	idx := d.PushGoFunction(fn)
	d.PutPropString(-2, name)
	d.Pop()

	return idx, nil
}

// PushGoFunction push the given function into duktape stack, returns non-negative
// index (relative to stack bottom) of the pushed function
func (d *Context) PushGoFunction(fn func(*Context) int) int {
	funPtr := d.fnIndex.add(fn)
	ctxPtr := contexts.add(d)

	idx := d.PushCFunction((*[0]byte)(C.goFunctionCall), C.DUK_VARARGS)
	d.PushCFunction((*[0]byte)(C.goFinalizeCall), 1)
	d.PushPointer(funPtr)
	d.PutPropString(-2, goFunctionPtrProp)
	d.PushPointer(ctxPtr)
	d.PutPropString(-2, goContextPtrProp)
	d.SetFinalizer(-2)

	d.PushPointer(funPtr)
	d.PutPropString(-2, goFunctionPtrProp)
	d.PushPointer(ctxPtr)
	d.PutPropString(-2, goContextPtrProp)

	return idx
}

//export goFunctionCall
func goFunctionCall(cCtx *C.duk_context) C.duk_ret_t {
	d := contextFromPointer(cCtx)

	funPtr, ctx := d.getFunctionPtrs()
	d.transmute(unsafe.Pointer(ctx))

	result := d.fnIndex.get(funPtr)(d)

	return C.duk_ret_t(result)
}

//export goFinalizeCall
func goFinalizeCall(cCtx *C.duk_context) {
	d := contextFromPointer(cCtx)

	funPtr, ctx := d.getFunctionPtrs()
	d.transmute(unsafe.Pointer(ctx))

	d.fnIndex.delete(funPtr)
}

func (d *Context) getFunctionPtrs() (unsafe.Pointer, *Context) {
	d.PushCurrentFunction()
	d.GetPropString(-1, goFunctionPtrProp)
	funPtr := d.GetPointer(-1)

	d.Pop()

	d.GetPropString(-1, goContextPtrProp)
	ctx := contexts.get(d.GetPointer(-1))
	d.Pop2()
	return funPtr, ctx
}

// Destroy destroy all the references to the functions and freed the pointers
func (d *Context) Destroy() {
	d.fnIndex.destroy()
	contexts.delete(d)
}

type Error struct {
	Type       string
	Message    string
	FileName   string
	LineNumber int
	Stack      string
}

func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s", e.Type, e.Message)
}

type Type int

func (t Type) IsNone() bool      { return t == TypeNone }
func (t Type) IsUndefined() bool { return t == TypeUndefined }
func (t Type) IsNull() bool      { return t == TypeNull }
func (t Type) IsBool() bool      { return t == TypeBoolean }
func (t Type) IsNumber() bool    { return t == TypeNumber }
func (t Type) IsString() bool    { return t == TypeString }
func (t Type) IsObject() bool    { return t == TypeObject }
func (t Type) IsBuffer() bool    { return t == TypeBuffer }
func (t Type) IsPointer() bool   { return t == TypePointer }
func (t Type) IsLightFunc() bool { return t == TypeLightFunc }

func (t Type) String() string {
	switch t {
	case TypeNone:
		return "None"
	case TypeUndefined:
		return "Undefined"
	case TypeNull:
		return "Null"
	case TypeBoolean:
		return "Boolean"
	case TypeNumber:
		return "Number"
	case TypeString:
		return "String"
	case TypeObject:
		return "Object"
	case TypeBuffer:
		return "Buffer"
	case TypePointer:
		return "Pointer"
	case TypeLightFunc:
		return "LightFunc"
	default:
		return "Unknown"
	}
}

type functionIndex struct {
	functions map[unsafe.Pointer]func(*Context) int
	sync.RWMutex
}

type timerIndex struct {
	c float64
	sync.Mutex
}

func (t *timerIndex) get() float64 {
	t.Lock()
	defer t.Unlock()
	t.c++
	return t.c
}

func newFunctionIndex() *functionIndex {
	return &functionIndex{
		functions: make(map[unsafe.Pointer]func(*Context) int, 0),
	}
}

func (i *functionIndex) add(fn func(*Context) int) unsafe.Pointer {
	ptr := C.malloc(1)

	i.Lock()
	i.functions[ptr] = fn
	i.Unlock()

	return ptr
}

func (i *functionIndex) get(ptr unsafe.Pointer) func(*Context) int {
	i.RLock()
	fn := i.functions[ptr]
	i.RUnlock()

	return fn
}

func (i *functionIndex) delete(ptr unsafe.Pointer) {
	i.Lock()
	delete(i.functions, ptr)
	i.Unlock()

	C.free(ptr)
}

func (i *functionIndex) destroy() {
	i.Lock()

	for ptr, _ := range i.functions {
		delete(i.functions, ptr)
		C.free(ptr)
	}
	i.Unlock()
}

type ctxIndex struct {
	sync.RWMutex
	ctxs map[unsafe.Pointer]*Context
}

func (ci *ctxIndex) add(ctx *Context) unsafe.Pointer {

	ci.RLock()
	for ptr, ctxPtr := range ci.ctxs {
		if ctxPtr == ctx {
			ci.RUnlock()
			return ptr
		}
	}
	ci.RUnlock()

	ci.Lock()
	for ptr, ctxPtr := range ci.ctxs {
		if ctxPtr == ctx {
			ci.Unlock()
			return ptr
		}
	}
	ptr := C.malloc(1)
	ci.ctxs[ptr] = ctx
	ci.Unlock()

	return ptr
}

func (ci *ctxIndex) get(ptr unsafe.Pointer) *Context {
	ci.RLock()
	ctx := ci.ctxs[ptr]
	ci.RUnlock()
	return ctx
}

func (ci *ctxIndex) delete(ctx *Context) {
	ci.Lock()
	for ptr, ctxPtr := range ci.ctxs {
		if ctxPtr == ctx {
			delete(ci.ctxs, ptr)
			C.free(ptr)
			ci.Unlock()
			return
		}
	}
	panic(fmt.Sprintf("context (%p) doesn't exist", ctx))
}

var contexts *ctxIndex

func init() {
	contexts = &ctxIndex{
		ctxs: make(map[unsafe.Pointer]*Context),
	}
}
<|endoftext|>