<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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 node

import (
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig"
	"github.com/hyperledger/fabric/common/configtx/tool/provisional"
	"github.com/hyperledger/fabric/common/flogging"
	"github.com/hyperledger/fabric/common/localmsp"
	"github.com/hyperledger/fabric/common/util"
	"github.com/hyperledger/fabric/core"
	"github.com/hyperledger/fabric/core/chaincode"
	"github.com/hyperledger/fabric/core/comm"
	"github.com/hyperledger/fabric/core/config"
	"github.com/hyperledger/fabric/core/endorser"
	"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
	"github.com/hyperledger/fabric/core/peer"
	"github.com/hyperledger/fabric/core/scc"
	"github.com/hyperledger/fabric/events/producer"
	"github.com/hyperledger/fabric/gossip/service"
	"github.com/hyperledger/fabric/msp/mgmt"
	"github.com/hyperledger/fabric/peer/common"
	"github.com/hyperledger/fabric/peer/gossip/mcs"
	cb "github.com/hyperledger/fabric/protos/common"
	pb "github.com/hyperledger/fabric/protos/peer"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

var chaincodeDevMode bool
var peerDefaultChain bool
var orderingEndpoint string

// XXXDefaultChannelMSPID should not be defined in production code
// It should only be referenced in tests.  However, it is necessary
// to support the 'default chain' setup so temporarilly adding until
// this concept can be removed to testing scenarios only
const XXXDefaultChannelMSPID = "DEFAULT"

func startCmd() *cobra.Command {
	// Set the flags on the node start command.
	flags := nodeStartCmd.Flags()
	flags.BoolVarP(&chaincodeDevMode, "peer-chaincodedev", "", false,
		"Whether peer in chaincode development mode")
	flags.BoolVarP(&peerDefaultChain, "peer-defaultchain", "", true,
		"Whether to start peer with chain testchainid")
	flags.StringVarP(&orderingEndpoint, "orderer", "o", "orderer:7050", "Ordering service endpoint")

	return nodeStartCmd
}

var nodeStartCmd = &cobra.Command{
	Use:   "start",
	Short: "Starts the node.",
	Long:  `Starts a node that interacts with the network.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		// initialize the log level for the "error" module to the value of
		// `logging.error` in core.yaml. this is necessary to ensure that the stack
		// is automatically appended to error messages (if set to debug)
		common.SetLogLevelFromViper("error")
		return serve(args)
	},
}

//start chaincodes
func initSysCCs() {
	//deploy system chaincodes
	scc.DeploySysCCs("")
	logger.Infof("Deployed system chaincodess")
}

func serve(args []string) error {
	ledgermgmt.Initialize()
	// Parameter overrides must be processed before any paramaters are
	// cached. Failures to cache cause the server to terminate immediately.
	if chaincodeDevMode {
		logger.Info("Running in chaincode development mode")
		logger.Info("Disable loading validity system chaincode")

		viper.Set("chaincode.mode", chaincode.DevModeUserRunsChaincode)

	}

	if err := peer.CacheConfiguration(); err != nil {
		return err
	}

	peerEndpoint, err := peer.GetPeerEndpoint()
	if err != nil {
		err = fmt.Errorf("Failed to get Peer Endpoint: %s", err)
		return err
	}

	listenAddr := viper.GetString("peer.listenAddress")

	secureConfig, err := peer.GetSecureConfig()
	if err != nil {
		logger.Fatalf("Error loading secure config for peer (%s)", err)
	}
	peerServer, err := peer.CreatePeerServer(listenAddr, secureConfig)
	if err != nil {
		logger.Fatalf("Failed to create peer server (%s)", err)
	}

	if secureConfig.UseTLS {
		logger.Info("Starting peer with TLS enabled")
		// set up CA support
		caSupport := comm.GetCASupport()
		caSupport.ServerRootCAs = secureConfig.ServerRootCAs
	}

	//TODO - do we need different SSL material for events ?
	ehubGrpcServer, err := createEventHubServer(secureConfig)
	if err != nil {
		grpclog.Fatalf("Failed to create ehub server: %v", err)
	}

	registerChaincodeSupport(peerServer.Server())

	logger.Debugf("Running peer")

	// Register the Admin server
	pb.RegisterAdminServer(peerServer.Server(), core.NewAdminServer())

	// Register the Endorser server
	serverEndorser := endorser.NewEndorserServer()
	pb.RegisterEndorserServer(peerServer.Server(), serverEndorser)

	// Initialize gossip component
	bootstrap := viper.GetStringSlice("peer.gossip.bootstrap")

	serializedIdentity, err := mgmt.GetLocalSigningIdentityOrPanic().Serialize()
	if err != nil {
		logger.Panicf("Failed serializing self identity: %v", err)
	}

	messageCryptoService := mcs.New(
		peer.NewChannelPolicyManagerGetter(),
		localmsp.NewSigner(),
		mgmt.NewDeserializersManager())
	service.InitGossipService(serializedIdentity, peerEndpoint.Address, peerServer.Server(), messageCryptoService, bootstrap...)
	defer service.GetGossipService().Stop()

	//initialize system chaincodes
	initSysCCs()

	// Begin startup of default chain
	if peerDefaultChain {
		if orderingEndpoint == "" {
			logger.Panic("No ordering service endpoint provided, please use -o option.")
		}

		if len(strings.Split(orderingEndpoint, ":")) != 2 {
			logger.Panicf("Invalid format of ordering service endpoint, %s.", orderingEndpoint)
		}

		chainID := util.GetTestChainID()

		var block *cb.Block

		func() {
			defer func() {
				if err := recover(); err != nil {
					logger.Fatalf("Peer configured to start with the default test chain, but supporting configuration files did not match.  Please ensure that configtx.yaml contains the unmodified SampleSingleMSPSolo profile and that sampleconfig/msp is present.\n%s", err)
				}
			}()

			genConf := genesisconfig.Load(genesisconfig.SampleSingleMSPSoloProfile)
			genConf.Orderer.Addresses = []string{orderingEndpoint}
			genConf.Application.Organizations[0].Name = XXXDefaultChannelMSPID
			genConf.Application.Organizations[0].ID = XXXDefaultChannelMSPID
			block = provisional.New(genConf).GenesisBlockForChannel(chainID)
		}()

		//this creates testchainid and sets up gossip
		if err = peer.CreateChainFromBlock(block); err == nil {
			fmt.Printf("create chain [%s]", chainID)
			scc.DeploySysCCs(chainID)
			logger.Infof("Deployed system chaincodes on %s", chainID)
		} else {
			fmt.Printf("create default chain [%s] failed with %s", chainID, err)
		}
	}

	//this brings up all the chains (including testchainid)
	peer.Initialize(func(cid string) {
		logger.Debugf("Deploying system CC, for chain <%s>", cid)
		scc.DeploySysCCs(cid)
	})

	logger.Infof("Starting peer with ID=[%s], network ID=[%s], address=[%s]",
		peerEndpoint.Id, viper.GetString("peer.networkId"), peerEndpoint.Address)

	// Start the grpc server. Done in a goroutine so we can deploy the
	// genesis block if needed.
	serve := make(chan error)

	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		sig := <-sigs
		fmt.Println()
		fmt.Println(sig)
		serve <- nil
	}()

	go func() {
		var grpcErr error
		if grpcErr = peerServer.Start(); grpcErr != nil {
			grpcErr = fmt.Errorf("grpc server exited with error: %s", grpcErr)
		} else {
			logger.Info("peer server exited")
		}
		serve <- grpcErr
	}()

	if err := writePid(config.GetPath("peer.fileSystemPath")+"/peer.pid", os.Getpid()); err != nil {
		return err
	}

	// Start the event hub server
	if ehubGrpcServer != nil {
		go ehubGrpcServer.Start()
	}

	// Start profiling http endpoint if enabled
	if viper.GetBool("peer.profile.enabled") {
		go func() {
			profileListenAddress := viper.GetString("peer.profile.listenAddress")
			logger.Infof("Starting profiling server with listenAddress = %s", profileListenAddress)
			if profileErr := http.ListenAndServe(profileListenAddress, nil); profileErr != nil {
				logger.Errorf("Error starting profiler: %s", profileErr)
			}
		}()
	}

	logger.Infof("Started peer with ID=[%s], network ID=[%s], address=[%s]",
		peerEndpoint.Id, viper.GetString("peer.networkId"), peerEndpoint.Address)

	// set the logging level for specific modules defined in core.yaml.
	// TODO Add calls to set 'ledger' module loggers
	overrideLogModules := []string{"msp", "gossip"}
	for _, module := range overrideLogModules {
		err = common.SetLogLevelFromViper(module)
		if err != nil {
			logger.Warningf("Error setting log level for module '%s': %s", module, err.Error())
		}
	}

	flogging.SetPeerStartupModulesMap()

	// Block until grpc server exits
	return <-serve
}

//NOTE - when we implment JOIN we will no longer pass the chainID as param
//The chaincode support will come up without registering system chaincodes
//which will be registered only during join phase.
func registerChaincodeSupport(grpcServer *grpc.Server) {
	//get user mode
	userRunsCC := chaincode.IsDevMode()

	//get chaincode startup timeout
	tOut, err := strconv.Atoi(viper.GetString("chaincode.startuptimeout"))
	if err != nil { //what went wrong ?
		fmt.Printf("could not retrive timeout var...setting to 5secs\n")
		tOut = 5000
	}
	ccStartupTimeout := time.Duration(tOut) * time.Millisecond

	ccSrv := chaincode.NewChaincodeSupport(peer.GetPeerEndpoint, userRunsCC, ccStartupTimeout)

	//Now that chaincode is initialized, register all system chaincodes.
	scc.RegisterSysCCs()

	pb.RegisterChaincodeSupportServer(grpcServer, ccSrv)
}

func createEventHubServer(secureConfig comm.SecureServerConfig) (comm.GRPCServer, error) {
	var lis net.Listener
	var err error
	lis, err = net.Listen("tcp", viper.GetString("peer.events.address"))
	if err != nil {
		return nil, fmt.Errorf("failed to listen: %v", err)
	}

	grpcServer, err := comm.NewGRPCServerFromListener(lis, secureConfig)
	if err != nil {
		fmt.Println("Failed to return new GRPC server: ", err)
		return nil, err
	}
	ehServer := producer.NewEventsServer(
		uint(viper.GetInt("peer.events.buffersize")),
		viper.GetInt("peer.events.timeout"))

	pb.RegisterEventsServer(grpcServer.Server(), ehServer)
	return grpcServer, nil
}

func writePid(fileName string, pid int) error {
	err := os.MkdirAll(filepath.Dir(fileName), 0755)
	if err != nil {
		return err
	}

	fd, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer fd.Close()
	if err := syscall.Flock(int(fd.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
		return fmt.Errorf("can't lock '%s', lock is held", fd.Name())
	}

	if _, err := fd.Seek(0, 0); err != nil {
		return err
	}

	if err := fd.Truncate(0); err != nil {
		return err
	}

	if _, err := fmt.Fprintf(fd, "%d", pid); err != nil {
		return err
	}

	if err := fd.Sync(); err != nil {
		return err
	}

	if err := syscall.Flock(int(fd.Fd()), syscall.LOCK_UN); err != nil {
		return fmt.Errorf("can't release lock '%s', lock is held", fd.Name())
	}
	return nil
}
<commit_msg>[FAB-1870] Logging cleanup for peer start<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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 node

import (
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig"
	"github.com/hyperledger/fabric/common/configtx/tool/provisional"
	"github.com/hyperledger/fabric/common/flogging"
	"github.com/hyperledger/fabric/common/localmsp"
	"github.com/hyperledger/fabric/common/util"
	"github.com/hyperledger/fabric/core"
	"github.com/hyperledger/fabric/core/chaincode"
	"github.com/hyperledger/fabric/core/comm"
	"github.com/hyperledger/fabric/core/config"
	"github.com/hyperledger/fabric/core/endorser"
	"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
	"github.com/hyperledger/fabric/core/peer"
	"github.com/hyperledger/fabric/core/scc"
	"github.com/hyperledger/fabric/events/producer"
	"github.com/hyperledger/fabric/gossip/service"
	"github.com/hyperledger/fabric/msp/mgmt"
	"github.com/hyperledger/fabric/peer/common"
	"github.com/hyperledger/fabric/peer/gossip/mcs"
	cb "github.com/hyperledger/fabric/protos/common"
	pb "github.com/hyperledger/fabric/protos/peer"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

var chaincodeDevMode bool
var peerDefaultChain bool
var orderingEndpoint string

// XXXDefaultChannelMSPID should not be defined in production code
// It should only be referenced in tests.  However, it is necessary
// to support the 'default chain' setup so temporarilly adding until
// this concept can be removed to testing scenarios only
const XXXDefaultChannelMSPID = "DEFAULT"

func startCmd() *cobra.Command {
	// Set the flags on the node start command.
	flags := nodeStartCmd.Flags()
	flags.BoolVarP(&chaincodeDevMode, "peer-chaincodedev", "", false,
		"Whether peer in chaincode development mode")
	flags.BoolVarP(&peerDefaultChain, "peer-defaultchain", "", true,
		"Whether to start peer with chain testchainid")
	flags.StringVarP(&orderingEndpoint, "orderer", "o", "orderer:7050", "Ordering service endpoint")

	return nodeStartCmd
}

var nodeStartCmd = &cobra.Command{
	Use:   "start",
	Short: "Starts the node.",
	Long:  `Starts a node that interacts with the network.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		// initialize the log level for the "error" module to the value of
		// `logging.error` in core.yaml. this is necessary to ensure that the stack
		// is automatically appended to error messages (if set to debug)
		common.SetLogLevelFromViper("error")
		return serve(args)
	},
}

//start chaincodes
func initSysCCs() {
	//deploy system chaincodes
	scc.DeploySysCCs("")
	logger.Infof("Deployed system chaincodess")
}

func serve(args []string) error {
	ledgermgmt.Initialize()
	// Parameter overrides must be processed before any paramaters are
	// cached. Failures to cache cause the server to terminate immediately.
	if chaincodeDevMode {
		logger.Info("Running in chaincode development mode")
		logger.Info("Disable loading validity system chaincode")

		viper.Set("chaincode.mode", chaincode.DevModeUserRunsChaincode)

	}

	if err := peer.CacheConfiguration(); err != nil {
		return err
	}

	peerEndpoint, err := peer.GetPeerEndpoint()
	if err != nil {
		err = fmt.Errorf("Failed to get Peer Endpoint: %s", err)
		return err
	}

	listenAddr := viper.GetString("peer.listenAddress")

	secureConfig, err := peer.GetSecureConfig()
	if err != nil {
		logger.Fatalf("Error loading secure config for peer (%s)", err)
	}
	peerServer, err := peer.CreatePeerServer(listenAddr, secureConfig)
	if err != nil {
		logger.Fatalf("Failed to create peer server (%s)", err)
	}

	if secureConfig.UseTLS {
		logger.Info("Starting peer with TLS enabled")
		// set up CA support
		caSupport := comm.GetCASupport()
		caSupport.ServerRootCAs = secureConfig.ServerRootCAs
	}

	//TODO - do we need different SSL material for events ?
	ehubGrpcServer, err := createEventHubServer(secureConfig)
	if err != nil {
		grpclog.Fatalf("Failed to create ehub server: %v", err)
	}

	registerChaincodeSupport(peerServer.Server())

	logger.Debugf("Running peer")

	// Register the Admin server
	pb.RegisterAdminServer(peerServer.Server(), core.NewAdminServer())

	// Register the Endorser server
	serverEndorser := endorser.NewEndorserServer()
	pb.RegisterEndorserServer(peerServer.Server(), serverEndorser)

	// Initialize gossip component
	bootstrap := viper.GetStringSlice("peer.gossip.bootstrap")

	serializedIdentity, err := mgmt.GetLocalSigningIdentityOrPanic().Serialize()
	if err != nil {
		logger.Panicf("Failed serializing self identity: %v", err)
	}

	messageCryptoService := mcs.New(
		peer.NewChannelPolicyManagerGetter(),
		localmsp.NewSigner(),
		mgmt.NewDeserializersManager())
	service.InitGossipService(serializedIdentity, peerEndpoint.Address, peerServer.Server(), messageCryptoService, bootstrap...)
	defer service.GetGossipService().Stop()

	//initialize system chaincodes
	initSysCCs()

	// Begin startup of default chain
	if peerDefaultChain {
		if orderingEndpoint == "" {
			logger.Panic("No ordering service endpoint provided, please use -o option.")
		}

		if len(strings.Split(orderingEndpoint, ":")) != 2 {
			logger.Panicf("Invalid format of ordering service endpoint, %s.", orderingEndpoint)
		}

		chainID := util.GetTestChainID()

		var block *cb.Block

		func() {
			defer func() {
				if err := recover(); err != nil {
					logger.Fatalf("Peer configured to start with the default test chain, but supporting configuration files did not match.  Please ensure that configtx.yaml contains the unmodified SampleSingleMSPSolo profile and that sampleconfig/msp is present.\n%s", err)
				}
			}()

			genConf := genesisconfig.Load(genesisconfig.SampleSingleMSPSoloProfile)
			genConf.Orderer.Addresses = []string{orderingEndpoint}
			genConf.Application.Organizations[0].Name = XXXDefaultChannelMSPID
			genConf.Application.Organizations[0].ID = XXXDefaultChannelMSPID
			block = provisional.New(genConf).GenesisBlockForChannel(chainID)
		}()

		//this creates testchainid and sets up gossip
		if err = peer.CreateChainFromBlock(block); err == nil {
			logger.Infof("create chain [%s]", chainID)
			scc.DeploySysCCs(chainID)
			logger.Infof("Deployed system chaincodes on %s", chainID)
		} else {
			logger.Errorf("create default chain [%s] failed with %s", chainID, err)
		}
	}

	//this brings up all the chains (including testchainid)
	peer.Initialize(func(cid string) {
		logger.Debugf("Deploying system CC, for chain <%s>", cid)
		scc.DeploySysCCs(cid)
	})

	logger.Infof("Starting peer with ID=[%s], network ID=[%s], address=[%s]",
		peerEndpoint.Id, viper.GetString("peer.networkId"), peerEndpoint.Address)

	// Start the grpc server. Done in a goroutine so we can deploy the
	// genesis block if needed.
	serve := make(chan error)

	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		sig := <-sigs
		logger.Debugf("sig: %s", sig)
		serve <- nil
	}()

	go func() {
		var grpcErr error
		if grpcErr = peerServer.Start(); grpcErr != nil {
			grpcErr = fmt.Errorf("grpc server exited with error: %s", grpcErr)
		} else {
			logger.Info("peer server exited")
		}
		serve <- grpcErr
	}()

	if err := writePid(config.GetPath("peer.fileSystemPath")+"/peer.pid", os.Getpid()); err != nil {
		return err
	}

	// Start the event hub server
	if ehubGrpcServer != nil {
		go ehubGrpcServer.Start()
	}

	// Start profiling http endpoint if enabled
	if viper.GetBool("peer.profile.enabled") {
		go func() {
			profileListenAddress := viper.GetString("peer.profile.listenAddress")
			logger.Infof("Starting profiling server with listenAddress = %s", profileListenAddress)
			if profileErr := http.ListenAndServe(profileListenAddress, nil); profileErr != nil {
				logger.Errorf("Error starting profiler: %s", profileErr)
			}
		}()
	}

	logger.Infof("Started peer with ID=[%s], network ID=[%s], address=[%s]",
		peerEndpoint.Id, viper.GetString("peer.networkId"), peerEndpoint.Address)

	// set the logging level for specific modules defined in core.yaml.
	// TODO Add calls to set 'ledger' module loggers
	overrideLogModules := []string{"msp", "gossip"}
	for _, module := range overrideLogModules {
		err = common.SetLogLevelFromViper(module)
		if err != nil {
			logger.Warningf("Error setting log level for module '%s': %s", module, err.Error())
		}
	}

	flogging.SetPeerStartupModulesMap()

	// Block until grpc server exits
	return <-serve
}

//NOTE - when we implment JOIN we will no longer pass the chainID as param
//The chaincode support will come up without registering system chaincodes
//which will be registered only during join phase.
func registerChaincodeSupport(grpcServer *grpc.Server) {
	//get user mode
	userRunsCC := chaincode.IsDevMode()

	//get chaincode startup timeout
	tOut, err := strconv.Atoi(viper.GetString("chaincode.startuptimeout"))
	if err != nil { //what went wrong ?
		logger.Warning("could not retrieve timeout var...setting to 5secs")
		tOut = 5000
	}
	ccStartupTimeout := time.Duration(tOut) * time.Millisecond

	ccSrv := chaincode.NewChaincodeSupport(peer.GetPeerEndpoint, userRunsCC, ccStartupTimeout)

	//Now that chaincode is initialized, register all system chaincodes.
	scc.RegisterSysCCs()

	pb.RegisterChaincodeSupportServer(grpcServer, ccSrv)
}

func createEventHubServer(secureConfig comm.SecureServerConfig) (comm.GRPCServer, error) {
	var lis net.Listener
	var err error
	lis, err = net.Listen("tcp", viper.GetString("peer.events.address"))
	if err != nil {
		return nil, fmt.Errorf("failed to listen: %v", err)
	}

	grpcServer, err := comm.NewGRPCServerFromListener(lis, secureConfig)
	if err != nil {
		logger.Errorf("Failed to return new GRPC server: %s", err)
		return nil, err
	}
	ehServer := producer.NewEventsServer(
		uint(viper.GetInt("peer.events.buffersize")),
		viper.GetInt("peer.events.timeout"))

	pb.RegisterEventsServer(grpcServer.Server(), ehServer)
	return grpcServer, nil
}

func writePid(fileName string, pid int) error {
	err := os.MkdirAll(filepath.Dir(fileName), 0755)
	if err != nil {
		return err
	}

	fd, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer fd.Close()
	if err := syscall.Flock(int(fd.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
		return fmt.Errorf("can't lock '%s', lock is held", fd.Name())
	}

	if _, err := fd.Seek(0, 0); err != nil {
		return err
	}

	if err := fd.Truncate(0); err != nil {
		return err
	}

	if _, err := fmt.Fprintf(fd, "%d", pid); err != nil {
		return err
	}

	if err := fd.Sync(); err != nil {
		return err
	}

	if err := syscall.Flock(int(fd.Fd()), syscall.LOCK_UN); err != nil {
		return fmt.Errorf("can't release lock '%s', lock is held", fd.Name())
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp 2016 All Rights Reserved.

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

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

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

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}
	//args[0] = "{\"zabi\":6.13,\"abhi\":23}"
	err := stub.PutState("moneyWorld", []byte(args[0]))
	if err != nil {
		return nil, err
	}
	var dat map[string]int
	if err := json.Unmarshal([]byte(args[0]), &dat); err != nil {
		panic(err)
	}

	for k, v := range dat {
		err := stub.PutState(k, []byte(strconv.Itoa(v)))
		if err != nil {
			return nil, err
		}

	}
	return nil, nil
}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "transferMoney" {
		return t.transferMoney(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Pff...Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) transferMoney(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var sender, recipient, jsonResp string
	var amount int
	var err error
	fmt.Println("running transferMoney()")

	if len(args) != 3 {
		return nil, errors.New("Incorrect number of arguments. Expecting 3. Sender, recipient, amount")
	}

	sender = args[0]    //sender
	recipient = args[1] // recipient
	amount, _ = strconv.Atoi(args[2])

	//first get current state
	currState, err := stub.GetState("moneyWorld")
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + "moneyWorld" + "\"}"
		return nil, errors.New(jsonResp)
	}

	var dat map[string]int
	if err := json.Unmarshal([]byte(currState), &dat); err != nil {
		panic(err)
	}
	dat[sender] = dat[sender] - amount
	dat[recipient] = dat[recipient] + amount
	strB, _ := json.Marshal(dat)
	err1 := stub.PutState("moneyWorld", []byte(strB))
	if err1 != nil {
		return nil, err1
	}

	for k, v := range dat {
		err := stub.PutState(k, []byte(strconv.Itoa(v)))
		if err != nil {
			return nil, err
		}
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error
	fmt.Println("Updated")
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}
<commit_msg>Compiled my code<commit_after>/*
My first chaincode :  Money payment
*/

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}
	//args[0] = "{\"zabi\":6.13,\"abhi\":23}"
	err := stub.PutState("moneyWorld", []byte(args[0]))
	if err != nil {
		return nil, err
	}
	var dat map[string]int
	if err := json.Unmarshal([]byte(args[0]), &dat); err != nil {
		panic(err)
	}

	for k, v := range dat {
		err := stub.PutState(k, []byte(strconv.Itoa(v)))
		if err != nil {
			return nil, err
		}

	}
	return nil, nil
}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "transferMoney" {
		return t.transferMoney(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Pff...Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) transferMoney(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var sender, recipient, jsonResp string
	var amount int
	var err error
	fmt.Println("running transferMoney()")

	if len(args) != 3 {
		return nil, errors.New("Incorrect number of arguments. Expecting 3. Sender, recipient, amount")
	}

	sender = args[0]    //sender
	recipient = args[1] // recipient
	amount, _ = strconv.Atoi(args[2])

	//first get current state
	currState, err := stub.GetState("moneyWorld")
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + "moneyWorld" + "\"}"
		return nil, errors.New(jsonResp)
	}

	var dat map[string]int
	if err := json.Unmarshal([]byte(currState), &dat); err != nil {
		panic(err)
	}
	dat[sender] = dat[sender] - amount
	dat[recipient] = dat[recipient] + amount
	strB, _ := json.Marshal(dat)
	err1 := stub.PutState("moneyWorld", []byte(strB))
	if err1 != nil {
		return nil, err1
	}

	for k, v := range dat {
		err := stub.PutState(k, []byte(strconv.Itoa(v)))
		if err != nil {
			return nil, err
		}
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error
	fmt.Println("Updated")
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp 2016 All Rights Reserved.

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

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

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

package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}

	err := stub.PutState("hello_world", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if strings.EqualFold(function, "read") { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) write(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the key and value to set")
	}

	key = args[0] //rename for funsies
	value = args[1]
	err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}
<commit_msg>Undo change<commit_after>/*
Copyright IBM Corp 2016 All Rights Reserved.

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

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

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

package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init resets all the things
func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting 1")
	}

	err := stub.PutState("hello_world", []byte(args[0]))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// Invoke isur entry point to invoke a chaincode function
func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "init" {
		return t.Init(stub, "init", args)
	} else if function == "write" {
		return t.write(stub, args)
	}
	fmt.Println("invoke did not find func: " + function)

	return nil, errors.New("Received unknown function invocation: " + function)
}

// Query is our entry point for queries
func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
	fmt.Println("query is running " + function)

	// Handle different functions
	if function == "read" { //read a variable
		return t.read(stub, args)
	}
	fmt.Println("query did not find func: " + function)

	return nil, errors.New("Received unknown function query: " + function)
}

// write - invoke function to write key/value pair
func (t *SimpleChaincode) write(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
	var key, value string
	var err error
	fmt.Println("running write()")

	if len(args) != 2 {
		return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the key and value to set")
	}

	key = args[0] //rename for funsies
	value = args[1]
	err = stub.PutState(key, []byte(value)) //write the variable into the chaincode state
	if err != nil {
		return nil, err
	}
	return nil, nil
}

// read - query function to read key/value pair
func (t *SimpleChaincode) read(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
	var key, jsonResp string
	var err error

	if len(args) != 1 {
		return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
	}

	key = args[0]
	valAsbytes, err := stub.GetState(key)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + key + "\"}"
		return nil, errors.New(jsonResp)
	}

	return valAsbytes, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	year          = strconv.FormatInt(int64(time.Now().Year()), 10)
	licenseheader = []byte(`// Copyright ` + year + ` The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.
`)
)

func license(path string, fi os.FileInfo, err error) error {
	if fi.IsDir() {
		switch filepath.Base(path) {
		case "testdata":
			return filepath.SkipDir
		case "vendor":
			return filepath.SkipDir
		}
		return nil
	}

	switch filepath.Ext(path) {
	case ".go":
	default:
		return nil
	}

	changed := false
	cmp, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	lhn := append(licenseheader, '\n')
	if !bytes.Equal([]byte("// Copyright"), cmp[:12]) {
		cmp = append(lhn, cmp...)
		changed = true
	}

	if changed {
		if *check {
			return errors.New(fmt.Sprintf("Missing license in %s", path))
		}
		log.Println("Added license to", path)
		return ioutil.WriteFile(path, cmp, fi.Mode().Perm())
	}

	return nil
}

var (
	scan  = flag.String("scan", "./", "set scan path")
	check = flag.Bool("check", false, "just check if all files have license")
)

func main() {
	flag.Parse()
	if err := filepath.Walk(*scan, license); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>remove build ignore tag<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	year          = strconv.FormatInt(int64(time.Now().Year()), 10)
	licenseheader = []byte(`// Copyright ` + year + ` The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.
`)
)

func license(path string, fi os.FileInfo, err error) error {
	if fi.IsDir() {
		switch filepath.Base(path) {
		case "testdata":
			return filepath.SkipDir
		case "vendor":
			return filepath.SkipDir
		}
		return nil
	}

	switch filepath.Ext(path) {
	case ".go":
	default:
		return nil
	}

	changed := false
	cmp, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	lhn := append(licenseheader, '\n')
	if !bytes.Equal([]byte("// Copyright"), cmp[:12]) {
		cmp = append(lhn, cmp...)
		changed = true
	}

	if changed {
		if *check {
			return errors.New(fmt.Sprintf("Missing license in %s", path))
		}
		log.Println("Added license to", path)
		return ioutil.WriteFile(path, cmp, fi.Mode().Perm())
	}

	return nil
}

var (
	scan  = flag.String("scan", "./", "set scan path")
	check = flag.Bool("check", false, "just check if all files have license")
)

func main() {
	flag.Parse()
	if err := filepath.Walk(*scan, license); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"time"

	"code.google.com/p/goauth2/oauth"
	"github.com/google/go-github/github"
)

var githubToken string = os.Getenv("FX_DEVTOOLS_BOT_GITHUB_TOKEN")

const BRANCH = "fx-team"
const REPO_OWNER = "mozilla"
const REPO_NAME = "gecko-dev"
const CLIENT_PATH = "browser/devtools"
const SERVER_PATH = "toolkit/devtools"
const STYLES_PATH = "browser/themes/shared/devtools"

func GetCommits(since time.Time) ([]github.RepositoryCommit, error) {
	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: githubToken},
	}
	client := github.NewClient(t.Client())

	allCommits := []github.RepositoryCommit{}

	for _, path := range []string{CLIENT_PATH, SERVER_PATH, STYLES_PATH} {
		opts := &github.CommitsListOptions{SHA: BRANCH, Since: since, Path: path}
		commits, res, err := client.Repositories.ListCommits(REPO_OWNER, REPO_NAME, opts)

		if err != nil {
			return nil, err
		}

		// ensure that the SHA hasn't already been appended, in the case
		// where both server and client sides are in the same commit
		for _, commit := range commits {
			isDuplicate := false
			for _, storedCommit := range allCommits {
				if *storedCommit.SHA == *commit.SHA {
					isDuplicate = true
				}
			}
			if !isDuplicate && IsValidCommit(*commit.Commit.Message) {
				allCommits = append(allCommits, commit)
			}
		}
	}

	return FilterCommits(DB_NAME, allCommits), nil
}

func FilterCommits(dbName string, commits []github.RepositoryCommit) []github.RepositoryCommit {
	filtered := []github.RepositoryCommit{}

	for _, commit := range commits {
		if GetSHA(dbName, *commit.SHA) == false {
			filtered = append(filtered, commit)
		}
	}

	return filtered
}
<commit_msg>Fix debugging, tests pass<commit_after>package main

import (
	"os"
	"time"

	"code.google.com/p/goauth2/oauth"
	"github.com/google/go-github/github"
)

var githubToken string = os.Getenv("FX_DEVTOOLS_BOT_GITHUB_TOKEN")

const BRANCH = "fx-team"
const REPO_OWNER = "mozilla"
const REPO_NAME = "gecko-dev"
const CLIENT_PATH = "browser/devtools"
const SERVER_PATH = "toolkit/devtools"
const STYLES_PATH = "browser/themes/shared/devtools"

func GetCommits(since time.Time) ([]github.RepositoryCommit, error) {
	t := &oauth.Transport{
		Token: &oauth.Token{AccessToken: githubToken},
	}
	client := github.NewClient(t.Client())

	allCommits := []github.RepositoryCommit{}

	for _, path := range []string{CLIENT_PATH, SERVER_PATH, STYLES_PATH} {
		opts := &github.CommitsListOptions{SHA: BRANCH, Since: since, Path: path}
		commits, _, err := client.Repositories.ListCommits(REPO_OWNER, REPO_NAME, opts)

		if err != nil {
			return nil, err
		}

		// ensure that the SHA hasn't already been appended, in the case
		// where both server and client sides are in the same commit
		for _, commit := range commits {
			isDuplicate := false
			for _, storedCommit := range allCommits {
				if *storedCommit.SHA == *commit.SHA {
					isDuplicate = true
				}
			}
			if !isDuplicate && IsValidCommit(*commit.Commit.Message) {
				allCommits = append(allCommits, commit)
			}
		}
	}

	return FilterCommits(DB_NAME, allCommits), nil
}

func FilterCommits(dbName string, commits []github.RepositoryCommit) []github.RepositoryCommit {
	filtered := []github.RepositoryCommit{}

	for _, commit := range commits {
		if GetSHA(dbName, *commit.SHA) == false {
			filtered = append(filtered, commit)
		}
	}

	return filtered
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/base64"
	log "github.com/sirupsen/logrus"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/mssola/user_agent"
	"github.com/usefathom/fathom/pkg/aggregator"
	"github.com/usefathom/fathom/pkg/datastore"
	"github.com/usefathom/fathom/pkg/models"
)

func shouldCollect(r *http.Request) bool {
	// abort if this is a bot.
	ua := user_agent.New(r.UserAgent())
	if ua.Bot() {
		return false
	}

	// abort if DNT header is set to "1" (these should have been filtered client-side already)
	if r.Header.Get("DNT") == "1" {
		return false
	}

	return true
}

func parsePathname(p string) string {
	return "/" + strings.TrimLeft(p, "/")
}

// TODO: Move this to aggregator, as we need this endpoint to be as fast as possible
func parseReferrer(r string) string {
	u, err := url.Parse(r)
	if err != nil {
		return ""
	}

	// remove AMP & UTM vars
	q := u.Query()
	keys := []string{"amp", "utm_campaign", "utm_medium", "utm_source"}
	for _, k := range keys {
		q.Del(k)
	}
	u.RawQuery = q.Encode()

	// remove /amp/
	if strings.HasSuffix(u.Path, "/amp/") {
		u.Path = u.Path[0:(len(u.Path) - 5)]
	}

	return u.String()
}

func parseHostname(r string) string {
	u, err := url.Parse(r)
	if err != nil {
		return ""
	}

	return u.Scheme + "://" + u.Host
}

func (api *API) NewCollectHandler() http.Handler {
	pageviews := make(chan *models.Pageview, 10)
	go aggregate(api.database)
	go collect(api.database, pageviews)

	return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
		if !shouldCollect(r) {
			return nil
		}

		q := r.URL.Query()
		now := time.Now()

		// get pageview details
		pageview := &models.Pageview{
			ID:           q.Get("id"),
			Hostname:     parseHostname(q.Get("h")),
			Pathname:     parsePathname(q.Get("p")),
			IsNewVisitor: q.Get("nv") == "1",
			IsNewSession: q.Get("ns") == "1",
			IsUnique:     q.Get("u") == "1",
			IsBounce:     q.Get("b") != "0",
			Referrer:     parseReferrer(q.Get("r")),
			Duration:     0,
			Timestamp:    now,
		}

		// find previous pageview by same visitor
		previousPageviewID := q.Get("pid")
		if !pageview.IsNewSession && previousPageviewID != "" {
			previousPageview, err := api.database.GetPageview(previousPageviewID)
			if err != nil && err != datastore.ErrNoResults {
				return err
			}

			// if we have a recent pageview that is less than 30 minutes old
			if previousPageview != nil && previousPageview.Timestamp.After(now.Add(-30*time.Minute)) {
				previousPageview.Duration = (now.Unix() - previousPageview.Timestamp.Unix())
				previousPageview.IsBounce = false

				// push onto channel to be updated (in batch) later
				pageviews <- previousPageview
			}
		}

		// push pageview onto channel to be inserted (in batch) later
		pageviews <- pageview

		// indicate that we're not tracking user data, see https://github.com/usefathom/fathom/issues/65
		w.Header().Set("Tk", "N")

		// headers to prevent caching
		w.Header().Set("Content-Type", "image/gif")
		w.Header().Set("Expires", "Mon, 01 Jan 1990 00:00:00 GMT")
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.Header().Set("Pragma", "no-cache")

		// response
		w.WriteHeader(http.StatusOK)

		// 1x1 px transparent GIF
		b, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
		w.Write(b)
		return nil
	})
}

// runs the aggregate func every minute
func aggregate(db datastore.Datastore) {
	agg := aggregator.New(db)
	agg.Run()

	timeout := 1 * time.Minute

	for {
		select {
		case <-time.After(timeout):
			agg.Run()
		}
	}
}

func collect(db datastore.Datastore, pageviews chan *models.Pageview) {
	var buffer []*models.Pageview
	var size = 250
	var timeout = 500 * time.Millisecond

	for {
		select {
		case pageview := <-pageviews:
			buffer = append(buffer, pageview)
			if len(buffer) >= size {
				persist(db, buffer)
				buffer = buffer[:0]
			}
		case <-time.After(timeout):
			if len(buffer) > 0 {
				persist(db, buffer)
				buffer = buffer[:0]
			}
		}
	}
}

func persist(db datastore.Datastore, pageviews []*models.Pageview) {
	n := len(pageviews)
	updates := make([]*models.Pageview, 0, n)
	inserts := make([]*models.Pageview, 0, n)

	for _, p := range pageviews {
		if !p.IsBounce {
			updates = append(updates, p)
		} else {
			inserts = append(inserts, p)
		}
	}

	log.Debugf("persisting %d pageviews (%d inserts, %d updates)", len(pageviews), len(inserts), len(updates))

	var err error
	err = db.InsertPageviews(inserts)
	if err != nil {
		log.Error(err)
	}

	err = db.UpdatePageviews(updates)
	if err != nil {
		log.Error(err)
	}
}
<commit_msg>discard http requests coming from browser prerenders. closes #13<commit_after>package api

import (
	"encoding/base64"
	log "github.com/sirupsen/logrus"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/mssola/user_agent"
	"github.com/usefathom/fathom/pkg/aggregator"
	"github.com/usefathom/fathom/pkg/datastore"
	"github.com/usefathom/fathom/pkg/models"
)

func shouldCollect(r *http.Request) bool {
	// abort if DNT header is set to "1" (these should have been filtered client-side already)
	if r.Header.Get("DNT") == "1" {
		return false
	}

	// don't track prerendered pages, see https://github.com/usefathom/fathom/issues/13
	if r.Header.Get("X-Moz") == "prefetch" || r.Header.Get("X-Purpose") == "preview" {
		return false
	}

	// abort if this is a bot.
	ua := user_agent.New(r.UserAgent())
	if ua.Bot() {
		return false
	}

	return true
}

func parsePathname(p string) string {
	return "/" + strings.TrimLeft(p, "/")
}

// TODO: Move this to aggregator, as we need this endpoint to be as fast as possible
func parseReferrer(r string) string {
	u, err := url.Parse(r)
	if err != nil {
		return ""
	}

	// remove AMP & UTM vars
	q := u.Query()
	keys := []string{"amp", "utm_campaign", "utm_medium", "utm_source"}
	for _, k := range keys {
		q.Del(k)
	}
	u.RawQuery = q.Encode()

	// remove /amp/
	if strings.HasSuffix(u.Path, "/amp/") {
		u.Path = u.Path[0:(len(u.Path) - 5)]
	}

	return u.String()
}

func parseHostname(r string) string {
	u, err := url.Parse(r)
	if err != nil {
		return ""
	}

	return u.Scheme + "://" + u.Host
}

func (api *API) NewCollectHandler() http.Handler {
	pageviews := make(chan *models.Pageview, 10)
	go aggregate(api.database)
	go collect(api.database, pageviews)

	return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
		if !shouldCollect(r) {
			return nil
		}

		q := r.URL.Query()
		now := time.Now()

		// get pageview details
		pageview := &models.Pageview{
			ID:           q.Get("id"),
			Hostname:     parseHostname(q.Get("h")),
			Pathname:     parsePathname(q.Get("p")),
			IsNewVisitor: q.Get("nv") == "1",
			IsNewSession: q.Get("ns") == "1",
			IsUnique:     q.Get("u") == "1",
			IsBounce:     q.Get("b") != "0",
			Referrer:     parseReferrer(q.Get("r")),
			Duration:     0,
			Timestamp:    now,
		}

		// find previous pageview by same visitor
		previousPageviewID := q.Get("pid")
		if !pageview.IsNewSession && previousPageviewID != "" {
			previousPageview, err := api.database.GetPageview(previousPageviewID)
			if err != nil && err != datastore.ErrNoResults {
				return err
			}

			// if we have a recent pageview that is less than 30 minutes old
			if previousPageview != nil && previousPageview.Timestamp.After(now.Add(-30*time.Minute)) {
				previousPageview.Duration = (now.Unix() - previousPageview.Timestamp.Unix())
				previousPageview.IsBounce = false

				// push onto channel to be updated (in batch) later
				pageviews <- previousPageview
			}
		}

		// push pageview onto channel to be inserted (in batch) later
		pageviews <- pageview

		// indicate that we're not tracking user data, see https://github.com/usefathom/fathom/issues/65
		w.Header().Set("Tk", "N")

		// headers to prevent caching
		w.Header().Set("Content-Type", "image/gif")
		w.Header().Set("Expires", "Mon, 01 Jan 1990 00:00:00 GMT")
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.Header().Set("Pragma", "no-cache")

		// response
		w.WriteHeader(http.StatusOK)

		// 1x1 px transparent GIF
		b, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
		w.Write(b)
		return nil
	})
}

// runs the aggregate func every minute
func aggregate(db datastore.Datastore) {
	agg := aggregator.New(db)
	agg.Run()

	timeout := 1 * time.Minute

	for {
		select {
		case <-time.After(timeout):
			agg.Run()
		}
	}
}

func collect(db datastore.Datastore, pageviews chan *models.Pageview) {
	var buffer []*models.Pageview
	var size = 250
	var timeout = 500 * time.Millisecond

	for {
		select {
		case pageview := <-pageviews:
			buffer = append(buffer, pageview)
			if len(buffer) >= size {
				persist(db, buffer)
				buffer = buffer[:0]
			}
		case <-time.After(timeout):
			if len(buffer) > 0 {
				persist(db, buffer)
				buffer = buffer[:0]
			}
		}
	}
}

func persist(db datastore.Datastore, pageviews []*models.Pageview) {
	n := len(pageviews)
	updates := make([]*models.Pageview, 0, n)
	inserts := make([]*models.Pageview, 0, n)

	for _, p := range pageviews {
		if !p.IsBounce {
			updates = append(updates, p)
		} else {
			inserts = append(inserts, p)
		}
	}

	log.Debugf("persisting %d pageviews (%d inserts, %d updates)", len(pageviews), len(inserts), len(updates))

	var err error
	err = db.InsertPageviews(inserts)
	if err != nil {
		log.Error(err)
	}

	err = db.UpdatePageviews(updates)
	if err != nil {
		log.Error(err)
	}
}
<|endoftext|>


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

package github

import (
	"context"
	"fmt"
	"time"
)

// AppsService provides access to the installation related functions
// in the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/apps/
type AppsService service

// App represents a GitHub App.
type App struct {
	ID          *int64                   `json:"id,omitempty"`
	Slug        *string                  `json:"slug,omitempty"`
	NodeID      *string                  `json:"node_id,omitempty"`
	Owner       *User                    `json:"owner,omitempty"`
	Name        *string                  `json:"name,omitempty"`
	Description *string                  `json:"description,omitempty"`
	ExternalURL *string                  `json:"external_url,omitempty"`
	HTMLURL     *string                  `json:"html_url,omitempty"`
	CreatedAt   *Timestamp               `json:"created_at,omitempty"`
	UpdatedAt   *Timestamp               `json:"updated_at,omitempty"`
	Permissions *InstallationPermissions `json:"permissions,omitempty"`
	Events      []*Event                 `json:"events,omitempty"`
}

// InstallationToken represents an installation token.
type InstallationToken struct {
	Token        *string                  `json:"token,omitempty"`
	ExpiresAt    *time.Time               `json:"expires_at,omitempty"`
	Permissions  *InstallationPermissions `json:"permissions,omitempty"`
	Repositories []*Repository            `json:"repositories,omitempty"`
}

// InstallationTokenOptions allow restricting a token's access to specific repositories.
type InstallationTokenOptions struct {
	// The IDs of the repositories that the installation token can access.
	// Providing repository IDs restricts the access of an installation token to specific repositories.
	RepositoryIDs []int64 `json:"repository_ids,omitempty"`

	// The permissions granted to the access token.
	// The permissions object includes the permission names and their access type.
	Permissions *InstallationPermissions `json:"permissions,omitempty"`
}

// InstallationPermissions lists the repository and organization permissions for an installation.
//
// Permission names taken from:
//   https://developer.github.com/v3/apps/permissions/
//   https://developer.github.com/enterprise/v3/apps/permissions/
type InstallationPermissions struct {
	Administration              *string `json:"administration,omitempty"`
	Blocking                    *string `json:"blocking,omitempty"`
	Checks                      *string `json:"checks,omitempty"`
	Contents                    *string `json:"contents,omitempty"`
	ContentReferences           *string `json:"content_references,omitempty"`
	Deployments                 *string `json:"deployments,omitempty"`
	Emails                      *string `json:"emails,omitempty"`
	Followers                   *string `json:"followers,omitempty"`
	Issues                      *string `json:"issues,omitempty"`
	Metadata                    *string `json:"metadata,omitempty"`
	Members                     *string `json:"members,omitempty"`
	OrganizationAdministration  *string `json:"organization_administration,omitempty"`
	OrganizationHooks           *string `json:"organization_hooks,omitempty"`
	OrganizationPlan            *string `json:"organization_plan,omitempty"`
	OrganizationPreReceiveHooks *string `json:"organization_pre_receive_hooks,omitempty"`
	OrganizationProjects        *string `json:"organization_projects,omitempty"`
	OrganizationUserBlocking    *string `json:"organization_user_blocking,omitempty"`
	Packages                    *string `json:"packages,omitempty"`
	Pages                       *string `json:"pages,omitempty"`
	PullRequests                *string `json:"pull_requests,omitempty"`
	RepositoryHooks             *string `json:"repository_hooks,omitempty"`
	RepositoryProjects          *string `json:"repository_projects,omitempty"`
	RepositoryPreReceiveHooks   *string `json:"repository_pre_receive_hooks,omitempty"`
	SingleFile                  *string `json:"single_file,omitempty"`
	Statuses                    *string `json:"statuses,omitempty"`
	TeamDiscussions             *string `json:"team_discussions,omitempty"`
	VulnerabilityAlerts         *string `json:"vulnerability_alerts,omitempty"`
}

// Installation represents a GitHub Apps installation.
type Installation struct {
	ID                  *int64                   `json:"id,omitempty"`
	AppID               *int64                   `json:"app_id,omitempty"`
	TargetID            *int64                   `json:"target_id,omitempty"`
	Account             *User                    `json:"account,omitempty"`
	AccessTokensURL     *string                  `json:"access_tokens_url,omitempty"`
	RepositoriesURL     *string                  `json:"repositories_url,omitempty"`
	HTMLURL             *string                  `json:"html_url,omitempty"`
	TargetType          *string                  `json:"target_type,omitempty"`
	SingleFileName      *string                  `json:"single_file_name,omitempty"`
	RepositorySelection *string                  `json:"repository_selection,omitempty"`
	Events              []string                 `json:"events,omitempty"`
	Permissions         *InstallationPermissions `json:"permissions,omitempty"`
	CreatedAt           *Timestamp               `json:"created_at,omitempty"`
	UpdatedAt           *Timestamp               `json:"updated_at,omitempty"`
}

// Attachment represents a GitHub Apps attachment.
type Attachment struct {
	ID    *int64  `json:"id,omitempty"`
	Title *string `json:"title,omitempty"`
	Body  *string `json:"body,omitempty"`
}

func (i Installation) String() string {
	return Stringify(i)
}

// Get a single GitHub App. Passing the empty string will get
// the authenticated GitHub App.
//
// Note: appSlug is just the URL-friendly name of your GitHub App.
// You can find this on the settings page for your GitHub App
// (e.g., https://github.com/settings/apps/:app_slug).
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app
func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) {
	var u string
	if appSlug != "" {
		u = fmt.Sprintf("apps/%v", appSlug)
	} else {
		u = "app"
	}

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	app := new(App)
	resp, err := s.client.Do(ctx, req, app)
	if err != nil {
		return nil, resp, err
	}

	return app, resp, nil
}

// ListInstallations lists the installations that the current GitHub App has.
//
// GitHub API docs: https://developer.github.com/v3/apps/#list-installations
func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
	u, err := addOptions("app/installations", opt)
	if err != nil {
		return nil, nil, err
	}

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	var i []*Installation
	resp, err := s.client.Do(ctx, req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// GetInstallation returns the specified installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation
func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("app/installations/%v", id))
}

// ListUserInstallations lists installations that are accessible to the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user
func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
	u, err := addOptions("user/installations", opt)
	if err != nil {
		return nil, nil, err
	}

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	var i struct {
		Installations []*Installation `json:"installations"`
	}
	resp, err := s.client.Do(ctx, req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i.Installations, resp, nil
}

// CreateInstallationToken creates a new installation token.
//
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token
func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64, opt *InstallationTokenOptions) (*InstallationToken, *Response, error) {
	u := fmt.Sprintf("app/installations/%v/access_tokens", id)

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	t := new(InstallationToken)
	resp, err := s.client.Do(ctx, req, t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, nil
}

// CreateAttachment creates a new attachment on user comment containing a url.
//
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-content-attachment
func (s *AppsService) CreateAttachment(ctx context.Context, contentReferenceID int64, title, body string) (*Attachment, *Response, error) {
	u := fmt.Sprintf("content_references/%v/attachments", contentReferenceID)
	payload := &Attachment{Title: String(title), Body: String(body)}
	req, err := s.client.NewRequest("POST", u, payload)
	if err != nil {
		return nil, nil, err
	}

	// TODO: remove custom Accept headers when APIs fully launch.
	req.Header.Set("Accept", mediaTypeReactionsPreview)

	m := &Attachment{}
	resp, err := s.client.Do(ctx, req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, nil
}

// FindOrganizationInstallation finds the organization's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-an-organization-installation
func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org))
}

// FindRepositoryInstallation finds the repository's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-repository-installation
func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo))
}

// FindRepositoryInstallationByID finds the repository's installation information.
//
// Note: FindRepositoryInstallationByID uses the undocumented GitHub API endpoint /repositories/:id/installation.
func (s *AppsService) FindRepositoryInstallationByID(ctx context.Context, id int64) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("repositories/%d/installation", id))
}

// FindUserInstallation finds the user's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-user-installation
func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user))
}

func (s *AppsService) getInstallation(ctx context.Context, url string) (*Installation, *Response, error) {
	req, err := s.client.NewRequest("GET", url, nil)
	if err != nil {
		return nil, nil, err
	}

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	i := new(Installation)
	resp, err := s.client.Do(ctx, req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}
<commit_msg>Fix JSON unmarshal error of App.app.events (#1370)<commit_after>// Copyright 2016 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"fmt"
	"time"
)

// AppsService provides access to the installation related functions
// in the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/apps/
type AppsService service

// App represents a GitHub App.
type App struct {
	ID          *int64                   `json:"id,omitempty"`
	Slug        *string                  `json:"slug,omitempty"`
	NodeID      *string                  `json:"node_id,omitempty"`
	Owner       *User                    `json:"owner,omitempty"`
	Name        *string                  `json:"name,omitempty"`
	Description *string                  `json:"description,omitempty"`
	ExternalURL *string                  `json:"external_url,omitempty"`
	HTMLURL     *string                  `json:"html_url,omitempty"`
	CreatedAt   *Timestamp               `json:"created_at,omitempty"`
	UpdatedAt   *Timestamp               `json:"updated_at,omitempty"`
	Permissions *InstallationPermissions `json:"permissions,omitempty"`
	Events      []string                 `json:"events,omitempty"`
}

// InstallationToken represents an installation token.
type InstallationToken struct {
	Token        *string                  `json:"token,omitempty"`
	ExpiresAt    *time.Time               `json:"expires_at,omitempty"`
	Permissions  *InstallationPermissions `json:"permissions,omitempty"`
	Repositories []*Repository            `json:"repositories,omitempty"`
}

// InstallationTokenOptions allow restricting a token's access to specific repositories.
type InstallationTokenOptions struct {
	// The IDs of the repositories that the installation token can access.
	// Providing repository IDs restricts the access of an installation token to specific repositories.
	RepositoryIDs []int64 `json:"repository_ids,omitempty"`

	// The permissions granted to the access token.
	// The permissions object includes the permission names and their access type.
	Permissions *InstallationPermissions `json:"permissions,omitempty"`
}

// InstallationPermissions lists the repository and organization permissions for an installation.
//
// Permission names taken from:
//   https://developer.github.com/v3/apps/permissions/
//   https://developer.github.com/enterprise/v3/apps/permissions/
type InstallationPermissions struct {
	Administration              *string `json:"administration,omitempty"`
	Blocking                    *string `json:"blocking,omitempty"`
	Checks                      *string `json:"checks,omitempty"`
	Contents                    *string `json:"contents,omitempty"`
	ContentReferences           *string `json:"content_references,omitempty"`
	Deployments                 *string `json:"deployments,omitempty"`
	Emails                      *string `json:"emails,omitempty"`
	Followers                   *string `json:"followers,omitempty"`
	Issues                      *string `json:"issues,omitempty"`
	Metadata                    *string `json:"metadata,omitempty"`
	Members                     *string `json:"members,omitempty"`
	OrganizationAdministration  *string `json:"organization_administration,omitempty"`
	OrganizationHooks           *string `json:"organization_hooks,omitempty"`
	OrganizationPlan            *string `json:"organization_plan,omitempty"`
	OrganizationPreReceiveHooks *string `json:"organization_pre_receive_hooks,omitempty"`
	OrganizationProjects        *string `json:"organization_projects,omitempty"`
	OrganizationUserBlocking    *string `json:"organization_user_blocking,omitempty"`
	Packages                    *string `json:"packages,omitempty"`
	Pages                       *string `json:"pages,omitempty"`
	PullRequests                *string `json:"pull_requests,omitempty"`
	RepositoryHooks             *string `json:"repository_hooks,omitempty"`
	RepositoryProjects          *string `json:"repository_projects,omitempty"`
	RepositoryPreReceiveHooks   *string `json:"repository_pre_receive_hooks,omitempty"`
	SingleFile                  *string `json:"single_file,omitempty"`
	Statuses                    *string `json:"statuses,omitempty"`
	TeamDiscussions             *string `json:"team_discussions,omitempty"`
	VulnerabilityAlerts         *string `json:"vulnerability_alerts,omitempty"`
}

// Installation represents a GitHub Apps installation.
type Installation struct {
	ID                  *int64                   `json:"id,omitempty"`
	AppID               *int64                   `json:"app_id,omitempty"`
	TargetID            *int64                   `json:"target_id,omitempty"`
	Account             *User                    `json:"account,omitempty"`
	AccessTokensURL     *string                  `json:"access_tokens_url,omitempty"`
	RepositoriesURL     *string                  `json:"repositories_url,omitempty"`
	HTMLURL             *string                  `json:"html_url,omitempty"`
	TargetType          *string                  `json:"target_type,omitempty"`
	SingleFileName      *string                  `json:"single_file_name,omitempty"`
	RepositorySelection *string                  `json:"repository_selection,omitempty"`
	Events              []string                 `json:"events,omitempty"`
	Permissions         *InstallationPermissions `json:"permissions,omitempty"`
	CreatedAt           *Timestamp               `json:"created_at,omitempty"`
	UpdatedAt           *Timestamp               `json:"updated_at,omitempty"`
}

// Attachment represents a GitHub Apps attachment.
type Attachment struct {
	ID    *int64  `json:"id,omitempty"`
	Title *string `json:"title,omitempty"`
	Body  *string `json:"body,omitempty"`
}

func (i Installation) String() string {
	return Stringify(i)
}

// Get a single GitHub App. Passing the empty string will get
// the authenticated GitHub App.
//
// Note: appSlug is just the URL-friendly name of your GitHub App.
// You can find this on the settings page for your GitHub App
// (e.g., https://github.com/settings/apps/:app_slug).
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app
func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) {
	var u string
	if appSlug != "" {
		u = fmt.Sprintf("apps/%v", appSlug)
	} else {
		u = "app"
	}

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	app := new(App)
	resp, err := s.client.Do(ctx, req, app)
	if err != nil {
		return nil, resp, err
	}

	return app, resp, nil
}

// ListInstallations lists the installations that the current GitHub App has.
//
// GitHub API docs: https://developer.github.com/v3/apps/#list-installations
func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
	u, err := addOptions("app/installations", opt)
	if err != nil {
		return nil, nil, err
	}

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	var i []*Installation
	resp, err := s.client.Do(ctx, req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}

// GetInstallation returns the specified installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation
func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("app/installations/%v", id))
}

// ListUserInstallations lists installations that are accessible to the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user
func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
	u, err := addOptions("user/installations", opt)
	if err != nil {
		return nil, nil, err
	}

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	var i struct {
		Installations []*Installation `json:"installations"`
	}
	resp, err := s.client.Do(ctx, req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i.Installations, resp, nil
}

// CreateInstallationToken creates a new installation token.
//
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token
func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64, opt *InstallationTokenOptions) (*InstallationToken, *Response, error) {
	u := fmt.Sprintf("app/installations/%v/access_tokens", id)

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

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	t := new(InstallationToken)
	resp, err := s.client.Do(ctx, req, t)
	if err != nil {
		return nil, resp, err
	}

	return t, resp, nil
}

// CreateAttachment creates a new attachment on user comment containing a url.
//
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-content-attachment
func (s *AppsService) CreateAttachment(ctx context.Context, contentReferenceID int64, title, body string) (*Attachment, *Response, error) {
	u := fmt.Sprintf("content_references/%v/attachments", contentReferenceID)
	payload := &Attachment{Title: String(title), Body: String(body)}
	req, err := s.client.NewRequest("POST", u, payload)
	if err != nil {
		return nil, nil, err
	}

	// TODO: remove custom Accept headers when APIs fully launch.
	req.Header.Set("Accept", mediaTypeReactionsPreview)

	m := &Attachment{}
	resp, err := s.client.Do(ctx, req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, nil
}

// FindOrganizationInstallation finds the organization's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-an-organization-installation
func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org))
}

// FindRepositoryInstallation finds the repository's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-repository-installation
func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo))
}

// FindRepositoryInstallationByID finds the repository's installation information.
//
// Note: FindRepositoryInstallationByID uses the undocumented GitHub API endpoint /repositories/:id/installation.
func (s *AppsService) FindRepositoryInstallationByID(ctx context.Context, id int64) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("repositories/%d/installation", id))
}

// FindUserInstallation finds the user's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-user-installation
func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) {
	return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user))
}

func (s *AppsService) getInstallation(ctx context.Context, url string) (*Installation, *Response, error) {
	req, err := s.client.NewRequest("GET", url, nil)
	if err != nil {
		return nil, nil, err
	}

	// TODO: remove custom Accept header when this API fully launches.
	req.Header.Set("Accept", mediaTypeIntegrationPreview)

	i := new(Installation)
	resp, err := s.client.Do(ctx, req, i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, nil
}
<|endoftext|>


<commit_before>package main

import (
  iniconf "code.google.com/p/goconf/conf"
  "flag"
  "fmt"
)

const (
  GLCD_CONFIG = "glcd.config"
)

type GLCConfig struct {
  NSQ struct {
    Address        string
    LookupdAddress string
    ReadTopic      string
    PublishTopic   string
  }
  Mongo struct {
    Servers string
    DB      string
  }
}

func setString(category, name string, msg string, ptr *string, conf *iniconf.ConfigFile) {
  // If it's in the config file, read it in.
  if conf != nil {
    str, err := conf.GetString(category, name)
    if err == nil {
      *ptr = str
    }
  }

  // Add it to flag for later parsing.
  flag.StringVar(ptr, fmt.Sprintf("%s-%s", category, name), *ptr, msg)
}

// Configuration priority:
//
// 1) Defaults.
// 2) Config file.
// 3) Flags.
//
// (Each overwrites any such settings in the prior)
func ReadConfiguration() *GLCConfig {
  // Sensible defaults.
  ret := &GLCConfig{}
  ret.NSQ.Address = "localhost:4150"
  ret.NSQ.LookupdAddress = "localhost:4161"
  ret.NSQ.ReadTopic = "glc-daemon"
  ret.NSQ.PublishTopic = "glc-gamestate"

  ret.Mongo.Servers = "localhost"
  ret.Mongo.DB = "test"

  configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
  // It's okay to not have a config file.
  if err != nil {
    configFile = nil
  }

  // NSQ configuration, and set up flags for it as well.
  setString("nsq", "nsqd-address", "host:port of the NSQ daemon (nsq)",
    &ret.NSQ.Address, configFile)
  setString("nsq", "lookupd-address", "host:port of the NSQ Lookup Daemon (http)",
    &ret.NSQ.LookupdAddress, configFile)
  setString("nsq", "server-topic", "Topic GLCD listens on for clients",
    &ret.NSQ.ReadTopic, configFile)
  setString("nsq", "glcd-topic", "Topic GLCD broadcasts to clients via",
    &ret.NSQ.Address, configFile)

  // Mongo configuration
  setString("mongo", "servers", "Server(s) of Mongo daemons", &ret.Mongo.Servers, configFile)
  setString("mongo", "db", "DB that GLCD uses within Mongo", &ret.Mongo.DB, configFile)

  // Parse all flags.
  flag.Parse()

  return ret
}

func (conf *GLCConfig) PrintConfiguration() {
  fmt.Printf("[%s]\n", "nsq")
  fmt.Printf("%s = %s\n", "nsqd-address", conf.NSQ.Address)
  fmt.Printf("%s = %s\n", "lookupd-address", conf.NSQ.LookupdAddress)
  fmt.Printf("%s = %s\n", "server-topic", conf.NSQ.ReadTopic)
  fmt.Printf("%s = %s\n", "glcd-topic", conf.NSQ.PublishTopic)

  fmt.Printf("\n")
  fmt.Printf("[%s]\n", "mongo")
  fmt.Printf("%s: %s\n", "servers", conf.Mongo.Servers)
  fmt.Printf("%s: %s\n", "db", conf.Mongo.DB)
}
<commit_msg>Fix cut-and-paste-po masking PublishTopic<commit_after>package main

import (
  iniconf "code.google.com/p/goconf/conf"
  "flag"
  "fmt"
)

const (
  GLCD_CONFIG = "glcd.config"
)

type GLCConfig struct {
  NSQ struct {
    Address        string
    LookupdAddress string
    ReadTopic      string
    PublishTopic   string
  }
  Mongo struct {
    Servers string
    DB      string
  }
}

func setString(category, name string, msg string, ptr *string, conf *iniconf.ConfigFile) {
  // If it's in the config file, read it in.
  if conf != nil {
    str, err := conf.GetString(category, name)
    if err == nil {
      *ptr = str
    }
  }

  // Add it to flag for later parsing.
  flag.StringVar(ptr, fmt.Sprintf("%s-%s", category, name), *ptr, msg)
}

// Configuration priority:
//
// 1) Defaults.
// 2) Config file.
// 3) Flags.
//
// (Each overwrites any such settings in the prior)
func ReadConfiguration() *GLCConfig {
  // Sensible defaults.
  ret := &GLCConfig{}
  ret.NSQ.Address = "localhost:4150"
  ret.NSQ.LookupdAddress = "localhost:4161"
  ret.NSQ.ReadTopic = "glc-daemon"
  ret.NSQ.PublishTopic = "glc-gamestate"

  ret.Mongo.Servers = "localhost"
  ret.Mongo.DB = "test"

  configFile, err := iniconf.ReadConfigFile(GLCD_CONFIG)
  // It's okay to not have a config file.
  if err != nil {
    configFile = nil
  }

  // NSQ configuration, and set up flags for it as well.
  setString("nsq", "nsqd-address", "host:port of the NSQ daemon (nsq)",
    &ret.NSQ.Address, configFile)
  setString("nsq", "lookupd-address", "host:port of the NSQ Lookup Daemon (http)",
    &ret.NSQ.LookupdAddress, configFile)
  setString("nsq", "server-topic", "Topic GLCD listens on for clients",
    &ret.NSQ.ReadTopic, configFile)
  setString("nsq", "glcd-topic", "Topic GLCD broadcasts to clients via",
    &ret.NSQ.PublishTopic, configFile)

  // Mongo configuration
  setString("mongo", "servers", "Server(s) of Mongo daemons", &ret.Mongo.Servers, configFile)
  setString("mongo", "db", "DB that GLCD uses within Mongo", &ret.Mongo.DB, configFile)

  // Parse all flags.
  flag.Parse()

  return ret
}

func (conf *GLCConfig) PrintConfiguration() {
  fmt.Printf("[%s]\n", "nsq")
  fmt.Printf("%s = %s\n", "nsqd-address", conf.NSQ.Address)
  fmt.Printf("%s = %s\n", "lookupd-address", conf.NSQ.LookupdAddress)
  fmt.Printf("%s = %s\n", "server-topic", conf.NSQ.ReadTopic)
  fmt.Printf("%s = %s\n", "glcd-topic", conf.NSQ.PublishTopic)

  fmt.Printf("\n")
  fmt.Printf("[%s]\n", "mongo")
  fmt.Printf("%s: %s\n", "servers", conf.Mongo.Servers)
  fmt.Printf("%s: %s\n", "db", conf.Mongo.DB)
}
<|endoftext|>


<commit_before>package cli

import (
	"crypto/tls"
	"io"
	"os/user"
	"path/filepath"

	"github.com/dcos/dcos-cli/pkg/config"
	"github.com/dcos/dcos-cli/pkg/httpclient"
	"github.com/dcos/dcos-cli/pkg/login"
	"github.com/dcos/dcos-cli/pkg/open"
	"github.com/dcos/dcos-cli/pkg/prompt"
	"github.com/dcos/dcos-cli/pkg/setup"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
)

// Context provides an implementation of api.Context. It relies on an Environment and is used to create
// various objects across the project and is being passed to every command as a constructor argument.
type Context struct {
	env    *Environment
	logger *logrus.Logger
}

// NewContext creates a new context from a given environment.
func NewContext(env *Environment) *Context {
	return &Context{env: env}
}

// Input returns the reader for CLI input.
func (ctx *Context) Input() io.Reader {
	return ctx.env.Input
}

// Out returns the writer for CLI output.
func (ctx *Context) Out() io.Writer {
	return ctx.env.Out
}

// ErrOut returns the writer for CLI errors, logs, and informational messages.
func (ctx *Context) ErrOut() io.Writer {
	return ctx.env.ErrOut
}

// EnvLookup lookups environment variables.
func (ctx *Context) EnvLookup(key string) (string, bool) {
	return ctx.env.EnvLookup(key)
}

// User returns the current system user.
func (ctx *Context) User() (*user.User, error) {
	return ctx.env.UserLookup()
}

// Fs returns the filesystem.
func (ctx *Context) Fs() afero.Fs {
	return ctx.env.Fs
}

// Logger returns the CLI logger.
func (ctx *Context) Logger() *logrus.Logger {
	if ctx.logger == nil {
		ctx.logger = &logrus.Logger{
			Out:       ctx.env.ErrOut,
			Formatter: new(logrus.TextFormatter),
			Hooks:     make(logrus.LevelHooks),
		}
	}
	return ctx.logger
}

// DCOSDir returns the root directory for the DC/OS CLI.
// It defaults to `~/.dcos` and can be overriden by the `DCOS_DIR` env var.
func (ctx *Context) DCOSDir() string {
	if dcosDir, ok := ctx.env.EnvLookup("DCOS_DIR"); ok {
		return dcosDir
	}
	if usr, err := ctx.env.UserLookup(); err == nil {
		return filepath.Join(usr.HomeDir, ".dcos")
	}

	// Not being able to get the current user is not critical. While it is
	// very unlikely to happen, we can fallback to the current directory.
	return ""
}

// ConfigManager returns the ConfigManager for the context.
func (ctx *Context) ConfigManager() *config.Manager {
	return config.NewManager(config.ManagerOpts{
		Fs:        ctx.env.Fs,
		EnvLookup: ctx.env.EnvLookup,
		Dir:       ctx.DCOSDir(),
	})
}

// Cluster returns the current cluster.
func (ctx *Context) Cluster() (*config.Cluster, error) {
	conf, err := ctx.ConfigManager().Current()
	if err != nil {
		return nil, err
	}
	return config.NewCluster(conf), nil
}

// Clusters returns the clusters.
func (ctx *Context) Clusters() []*config.Cluster {
	confs := ctx.ConfigManager().All()
	var clusters []*config.Cluster
	for _, conf := range confs {
		clusters = append(clusters, config.NewCluster(conf))
	}

	return clusters
}

// HTTPClient creates an httpclient.Client for a given cluster.
func (ctx *Context) HTTPClient(c *config.Cluster, opts ...httpclient.Option) *httpclient.Client {
	var baseOpts []httpclient.Option

	if c.ACSToken() != "" {
		baseOpts = append(baseOpts, httpclient.ACSToken(c.ACSToken()))
	}
	if c.Timeout() > 0 {
		baseOpts = append(baseOpts, httpclient.Timeout(c.Timeout()))
	}
	tlsOpt := httpclient.TLS(&tls.Config{
		InsecureSkipVerify: c.TLS().Insecure,
		RootCAs:            c.TLS().RootCAs,
	})

	baseOpts = append(baseOpts, tlsOpt, httpclient.Logger(ctx.Logger()))
	opts = append(baseOpts, opts...)

	return httpclient.New(c.URL(), opts...)
}

// Prompt is able to prompt for input, password or choices.
func (ctx *Context) Prompt() *prompt.Prompt {
	return prompt.New(ctx.Input(), ctx.Out())
}

// Opener returns a new OS Opener.
func (ctx *Context) Opener() open.Opener {
	return open.NewOsOpener(ctx.Logger())
}

// Login initiates a login based on a set of flags and HTTP client. On success it returns an ACS token.
func (ctx *Context) Login(flags *login.Flags, httpClient *httpclient.Client) (string, error) {
	return ctx.loginFlow().Start(flags, httpClient)
}

// Setup configures a given cluster based on its URL and setup flags.
func (ctx *Context) Setup(flags *setup.Flags, clusterURL string) error {
	return setup.New(setup.Opts{
		Errout:        ctx.ErrOut(),
		Prompt:        ctx.Prompt(),
		Logger:        ctx.Logger(),
		LoginFlow:     ctx.loginFlow(),
		ConfigManager: ctx.ConfigManager(),
	}).Configure(flags, clusterURL)
}

func (ctx *Context) loginFlow() *login.Flow {
	return login.NewFlow(login.FlowOpts{
		Errout: ctx.ErrOut(),
		Prompt: ctx.Prompt(),
		Logger: ctx.Logger(),
		Opener: ctx.Opener(),
	})
}
<commit_msg>Fixed race condition in Logger()<commit_after>package cli

import (
	"crypto/tls"
	"io"
	"os/user"
	"path/filepath"

	"github.com/dcos/dcos-cli/pkg/config"
	"github.com/dcos/dcos-cli/pkg/httpclient"
	"github.com/dcos/dcos-cli/pkg/login"
	"github.com/dcos/dcos-cli/pkg/open"
	"github.com/dcos/dcos-cli/pkg/prompt"
	"github.com/dcos/dcos-cli/pkg/setup"
	"github.com/sirupsen/logrus"
	"github.com/spf13/afero"
)

// Context provides an implementation of api.Context. It relies on an Environment and is used to create
// various objects across the project and is being passed to every command as a constructor argument.
type Context struct {
	env    *Environment
	logger *logrus.Logger
}

// NewContext creates a new context from a given environment.
func NewContext(env *Environment) *Context {
	return &Context{env: env}
}

// Input returns the reader for CLI input.
func (ctx *Context) Input() io.Reader {
	return ctx.env.Input
}

// Out returns the writer for CLI output.
func (ctx *Context) Out() io.Writer {
	return ctx.env.Out
}

// ErrOut returns the writer for CLI errors, logs, and informational messages.
func (ctx *Context) ErrOut() io.Writer {
	return ctx.env.ErrOut
}

// EnvLookup lookups environment variables.
func (ctx *Context) EnvLookup(key string) (string, bool) {
	return ctx.env.EnvLookup(key)
}

// User returns the current system user.
func (ctx *Context) User() (*user.User, error) {
	return ctx.env.UserLookup()
}

// Fs returns the filesystem.
func (ctx *Context) Fs() afero.Fs {
	return ctx.env.Fs
}

// Logger returns the CLI logger.
func (ctx *Context) Logger() *logrus.Logger {
	return &logrus.Logger{
		Out:       ctx.env.ErrOut,
		Formatter: new(logrus.TextFormatter),
		Hooks:     make(logrus.LevelHooks),
	}
}

// DCOSDir returns the root directory for the DC/OS CLI.
// It defaults to `~/.dcos` and can be overriden by the `DCOS_DIR` env var.
func (ctx *Context) DCOSDir() string {
	if dcosDir, ok := ctx.env.EnvLookup("DCOS_DIR"); ok {
		return dcosDir
	}
	if usr, err := ctx.env.UserLookup(); err == nil {
		return filepath.Join(usr.HomeDir, ".dcos")
	}

	// Not being able to get the current user is not critical. While it is
	// very unlikely to happen, we can fallback to the current directory.
	return ""
}

// ConfigManager returns the ConfigManager for the context.
func (ctx *Context) ConfigManager() *config.Manager {
	return config.NewManager(config.ManagerOpts{
		Fs:        ctx.env.Fs,
		EnvLookup: ctx.env.EnvLookup,
		Dir:       ctx.DCOSDir(),
	})
}

// Cluster returns the current cluster.
func (ctx *Context) Cluster() (*config.Cluster, error) {
	conf, err := ctx.ConfigManager().Current()
	if err != nil {
		return nil, err
	}
	return config.NewCluster(conf), nil
}

// Clusters returns the clusters.
func (ctx *Context) Clusters() []*config.Cluster {
	confs := ctx.ConfigManager().All()
	var clusters []*config.Cluster
	for _, conf := range confs {
		clusters = append(clusters, config.NewCluster(conf))
	}

	return clusters
}

// HTTPClient creates an httpclient.Client for a given cluster.
func (ctx *Context) HTTPClient(c *config.Cluster, opts ...httpclient.Option) *httpclient.Client {
	var baseOpts []httpclient.Option

	if c.ACSToken() != "" {
		baseOpts = append(baseOpts, httpclient.ACSToken(c.ACSToken()))
	}
	if c.Timeout() > 0 {
		baseOpts = append(baseOpts, httpclient.Timeout(c.Timeout()))
	}
	tlsOpt := httpclient.TLS(&tls.Config{
		InsecureSkipVerify: c.TLS().Insecure,
		RootCAs:            c.TLS().RootCAs,
	})

	baseOpts = append(baseOpts, tlsOpt, httpclient.Logger(ctx.Logger()))
	opts = append(baseOpts, opts...)

	return httpclient.New(c.URL(), opts...)
}

// Prompt is able to prompt for input, password or choices.
func (ctx *Context) Prompt() *prompt.Prompt {
	return prompt.New(ctx.Input(), ctx.Out())
}

// Opener returns a new OS Opener.
func (ctx *Context) Opener() open.Opener {
	return open.NewOsOpener(ctx.Logger())
}

// Login initiates a login based on a set of flags and HTTP client. On success it returns an ACS token.
func (ctx *Context) Login(flags *login.Flags, httpClient *httpclient.Client) (string, error) {
	return ctx.loginFlow().Start(flags, httpClient)
}

// Setup configures a given cluster based on its URL and setup flags.
func (ctx *Context) Setup(flags *setup.Flags, clusterURL string) error {
	return setup.New(setup.Opts{
		Errout:        ctx.ErrOut(),
		Prompt:        ctx.Prompt(),
		Logger:        ctx.Logger(),
		LoginFlow:     ctx.loginFlow(),
		ConfigManager: ctx.ConfigManager(),
	}).Configure(flags, clusterURL)
}

func (ctx *Context) loginFlow() *login.Flow {
	return login.NewFlow(login.FlowOpts{
		Errout: ctx.ErrOut(),
		Prompt: ctx.Prompt(),
		Logger: ctx.Logger(),
		Opener: ctx.Opener(),
	})
}
<|endoftext|>