<commit_before>package gateway

import (
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
)

// managedPeerManagerConnect is a blocking function which tries to connect to
// the input addreess as a peer.
func (g *Gateway) managedPeerManagerConnect(addr modules.NetAddress) {
	g.log.Debugf("[PMC] [%v] Attempting connection", addr)
	err := g.managedConnect(addr)
	if err == errPeerExists {
		// This peer is already connected to us. Safety around the
		// oubound peers relates to the fact that we have picked out
		// the outbound peers instead of allow the attacker to pick out
		// the peers for us. Because we have made the selection, it is
		// okay to set the peer as an outbound peer.
		//
		// The nodelist size check ensures that an attacker can't flood
		// a new node with a bunch of inbound requests. Doing so would
		// result in a nodelist that's entirely full of attacker nodes.
		// There's not much we can do about that anyway, but at least
		// we can hold off making attacker nodes 'outbound' peers until
		// our nodelist has had time to fill up naturally.
		g.mu.Lock()
		p, exists := g.peers[addr]
		if exists {
			// Have to check it exists because we released the lock, a
			// race condition could mean that the peer was disconnected
			// before this code block was reached.
			p.Inbound = false
			g.log.Debugf("[PMC] [SUCCESS] [%v] existing peer has been converted to outbound peer", addr)
		}
		g.mu.Unlock()
	} else if err != nil {
		g.log.Debugf("[PMC] [ERROR] [%v] WARN: removing peer because automatic connect failed: %v\n", addr, err)
	} else {
		g.log.Debugf("[PMC] [SUCCESS] [%v] peer successfully added", addr)
	}
}

// numOutboundPeers returns the number of outbound peers in the gateway.
func (g *Gateway) numOutboundPeers() (numOutboundPeers int) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	for _, p := range g.peers {
		if !p.Inbound {
			numOutboundPeers++
		}
	}
	return numOutboundPeers
}

// permanentPeerManager tries to keep the Gateway well-connected. As long as
// the Gateway is not well-connected, it tries to connect to random nodes.
func (g *Gateway) permanentPeerManager(closedChan chan struct{}) {
	// Send a signal upon shutdown.
	defer close(closedChan)
	defer g.log.Debugln("INFO: [PPM] Permanent peer manager is shutting down")

	// permanentPeerManager will attempt to connect to peers asynchronously,
	// such that multiple connection attempts can be open at once, but a
	// limited number.
	connectionLimiterChan := make(chan struct{}, maxConcurrentOutboundPeerRequests)

	g.log.Debugln("INFO: [PPM] Permanent peer manager has started")
	for {
		// If the gateway is well connected, sleep for a while and then try
		// again.
		numOutboundPeers := g.numOutboundPeers()
		if numOutboundPeers >= wellConnectedThreshold {
			g.log.Debugln("INFO: [PPM] Gateway has enough peers, sleeping.")
			if !g.managedSleep(wellConnectedDelay) {
				return
			}
			continue
		}

		// Fetch a random node.
		g.mu.RLock()
		addr, err := g.randomNode()
		g.mu.RUnlock()
		// If there was an error, log the error and then wait a while before
		// trying again.
		g.log.Debugln("[PPM] Fetched a random node:", addr)
		if err != nil {
			g.log.Debugln("[PPM] Unable to acquire selected peer:", err)
			if !g.managedSleep(noNodesDelay) {
				return
			}
			continue
		}
		// We need at least some of our outbound peers to be remote peers. If
		// we already have reached a certain threshold of outbound peers and
		// this peer is a local peer, do not consider it for an outbound peer.
		// Sleep breifly to prevent the gateway from hogging the CPU if all
		// peers are local.
		if numOutboundPeers >= maxLocalOutboundPeers && addr.IsLocal() && build.Release != "testing" {
			g.log.Debugln("[PPM] Ignorning selected peer; this peer is local and we already have multiple outbound peers:", addr)
			if !g.managedSleep(unwantedLocalPeerDelay) {
				return
			}
			continue
		}

		// Try connecting to that peer in a goroutine. Do not block unless
		// there are currently 3 or more peer connection attempts open at once.
		// Before spawning the thread, make sure that there is enough room by
		// throwing a struct into the buffered channel.
		g.log.Debugln("[PPM] Trying to connect to a node:", addr)
		connectionLimiterChan <- struct{}{}
		go func(addr modules.NetAddress) {
			// After completion, take the struct out of the channel so that the
			// next thread may proceed.
			defer func() {
				<-connectionLimiterChan
			}()

			if err := g.threads.Add(); err != nil {
				return
			}
			defer g.threads.Done()
			// peerManagerConnect will handle all of its own logging.
			g.managedPeerManagerConnect(addr)
		}(addr)

		// Wait a bit before trying the next peer. The peer connections are
		// non-blocking, so they should be spaced out to avoid spinning up an
		// uncontrolled number of threads and therefore peer connections.
		if !g.managedSleep(acquiringPeersDelay) {
			return
		}
	}
}
<commit_msg>toss node if connection attempt fails<commit_after>package gateway

import (
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
)

// managedPeerManagerConnect is a blocking function which tries to connect to
// the input addreess as a peer.
func (g *Gateway) managedPeerManagerConnect(addr modules.NetAddress) {
	g.log.Debugf("[PMC] [%v] Attempting connection", addr)
	err := g.managedConnect(addr)
	if err == errPeerExists {
		// This peer is already connected to us. Safety around the
		// oubound peers relates to the fact that we have picked out
		// the outbound peers instead of allow the attacker to pick out
		// the peers for us. Because we have made the selection, it is
		// okay to set the peer as an outbound peer.
		//
		// The nodelist size check ensures that an attacker can't flood
		// a new node with a bunch of inbound requests. Doing so would
		// result in a nodelist that's entirely full of attacker nodes.
		// There's not much we can do about that anyway, but at least
		// we can hold off making attacker nodes 'outbound' peers until
		// our nodelist has had time to fill up naturally.
		g.mu.Lock()
		p, exists := g.peers[addr]
		if exists {
			// Have to check it exists because we released the lock, a
			// race condition could mean that the peer was disconnected
			// before this code block was reached.
			p.Inbound = false
			g.log.Debugf("[PMC] [SUCCESS] [%v] existing peer has been converted to outbound peer", addr)
		}
		g.mu.Unlock()
	} else if err != nil {
		g.log.Debugf("[PMC] [ERROR] [%v] WARN: removing peer because automatic connect failed: %v\n", addr, err)
		g.mu.Lock()
		g.removeNode(addr)
		g.mu.Unlock()
	} else {
		g.log.Debugf("[PMC] [SUCCESS] [%v] peer successfully added", addr)
	}
}

// numOutboundPeers returns the number of outbound peers in the gateway.
func (g *Gateway) numOutboundPeers() (numOutboundPeers int) {
	g.mu.RLock()
	defer g.mu.RUnlock()

	for _, p := range g.peers {
		if !p.Inbound {
			numOutboundPeers++
		}
	}
	return numOutboundPeers
}

// permanentPeerManager tries to keep the Gateway well-connected. As long as
// the Gateway is not well-connected, it tries to connect to random nodes.
func (g *Gateway) permanentPeerManager(closedChan chan struct{}) {
	// Send a signal upon shutdown.
	defer close(closedChan)
	defer g.log.Debugln("INFO: [PPM] Permanent peer manager is shutting down")

	// permanentPeerManager will attempt to connect to peers asynchronously,
	// such that multiple connection attempts can be open at once, but a
	// limited number.
	connectionLimiterChan := make(chan struct{}, maxConcurrentOutboundPeerRequests)

	g.log.Debugln("INFO: [PPM] Permanent peer manager has started")
	for {
		// If the gateway is well connected, sleep for a while and then try
		// again.
		numOutboundPeers := g.numOutboundPeers()
		if numOutboundPeers >= wellConnectedThreshold {
			g.log.Debugln("INFO: [PPM] Gateway has enough peers, sleeping.")
			if !g.managedSleep(wellConnectedDelay) {
				return
			}
			continue
		}

		// Fetch a random node.
		g.mu.RLock()
		addr, err := g.randomNode()
		g.mu.RUnlock()
		// If there was an error, log the error and then wait a while before
		// trying again.
		g.log.Debugln("[PPM] Fetched a random node:", addr)
		if err != nil {
			g.log.Debugln("[PPM] Unable to acquire selected peer:", err)
			if !g.managedSleep(noNodesDelay) {
				return
			}
			continue
		}
		// We need at least some of our outbound peers to be remote peers. If
		// we already have reached a certain threshold of outbound peers and
		// this peer is a local peer, do not consider it for an outbound peer.
		// Sleep breifly to prevent the gateway from hogging the CPU if all
		// peers are local.
		if numOutboundPeers >= maxLocalOutboundPeers && addr.IsLocal() && build.Release != "testing" {
			g.log.Debugln("[PPM] Ignorning selected peer; this peer is local and we already have multiple outbound peers:", addr)
			if !g.managedSleep(unwantedLocalPeerDelay) {
				return
			}
			continue
		}

		// Try connecting to that peer in a goroutine. Do not block unless
		// there are currently 3 or more peer connection attempts open at once.
		// Before spawning the thread, make sure that there is enough room by
		// throwing a struct into the buffered channel.
		g.log.Debugln("[PPM] Trying to connect to a node:", addr)
		connectionLimiterChan <- struct{}{}
		go func(addr modules.NetAddress) {
			// After completion, take the struct out of the channel so that the
			// next thread may proceed.
			defer func() {
				<-connectionLimiterChan
			}()

			if err := g.threads.Add(); err != nil {
				return
			}
			defer g.threads.Done()
			// peerManagerConnect will handle all of its own logging.
			g.managedPeerManagerConnect(addr)
		}(addr)

		// Wait a bit before trying the next peer. The peer connections are
		// non-blocking, so they should be spaced out to avoid spinning up an
		// uncontrolled number of threads and therefore peer connections.
		if !g.managedSleep(acquiringPeersDelay) {
			return
		}
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io/ioutil"
	"time"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/tendermint/tendermint/node"
	"github.com/tendermint/tendermint/types"
	cmn "github.com/tendermint/tmlibs/common"
)

var runNodeCmd = &cobra.Command{
	Use:   "node",
	Short: "Run the tendermint node",
	RunE:  runNode,
}

func init() {
	AddNodeFlags(runNodeCmd)
	RootCmd.AddCommand(runNodeCmd)
}

// AddNodeFlags exposes some common configuration options on the command-line
// These are exposed for convenience of commands embedding a tendermint node
func AddNodeFlags(cmd *cobra.Command) {
	// bind flags
	cmd.Flags().String("moniker", config.Moniker, "Node Name")

	// node flags
	cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")

	// abci flags
	cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.")
	cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")

	// rpc flags
	cmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required")
	cmd.Flags().String("rpc.grpc_laddr", config.RPC.GRPCListenAddress, "GRPC listen address (BroadcastTx only). Port required")
	cmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "Enabled unsafe rpc methods")

	// p2p flags
	cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)")
	cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes")
	cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration")
	cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable Peer-Exchange (dev feature)")
}

func ParseGenesisFile() (*types.GenesisDoc, error) {
	genDocFile := config.GenesisFile()
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		return nil, errors.Wrap(err, "Couldn't read GenesisDoc file")
	}
	genDoc, err := types.GenesisDocFromJSON(jsonBlob)
	if err != nil {
		return nil, errors.Wrap(err, "Error reading GenesisDoc")
	}
	if genDoc.ChainID == "" {
		return nil, errors.Errorf("Genesis doc %v must include non-empty chain_id", genDocFile)
	}
	return genDoc, nil
}

// Users wishing to:
//	* Use an external signer for their validators
//	* Supply an in-proc abci app
// should import github.com/tendermint/tendermint/node and implement
// their own run_node to call node.NewNode (instead of node.NewNodeDefault)
// with their custom priv validator and/or custom proxy.ClientCreator
func runNode(cmd *cobra.Command, args []string) error {

	// Wait until the genesis doc becomes available
	// This is for Mintnet compatibility.
	// TODO: If Mintnet gets deprecated or genesis_file is
	// always available, remove.
	genDocFile := config.GenesisFile()
	for !cmn.FileExists(genDocFile) {
		logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile))
		time.Sleep(time.Second)
	}

	genDoc, err := ParseGenesisFile()
	if err != nil {
		return err
	}
	config.ChainID = genDoc.ChainID

	// Create & start node
	n := node.NewNodeDefault(config, logger.With("module", "node"))
	if _, err := n.Start(); err != nil {
		return fmt.Errorf("Failed to start node: %v", err)
	} else {
		logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo())
	}

	// Trap signal, run forever.
	n.RunForever()

	return nil
}
<commit_msg>Add argument to ParseGenesis to use in light-client<commit_after>package commands

import (
	"fmt"
	"io/ioutil"
	"time"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/tendermint/tendermint/node"
	"github.com/tendermint/tendermint/types"
	cmn "github.com/tendermint/tmlibs/common"
)

var runNodeCmd = &cobra.Command{
	Use:   "node",
	Short: "Run the tendermint node",
	RunE:  runNode,
}

func init() {
	AddNodeFlags(runNodeCmd)
	RootCmd.AddCommand(runNodeCmd)
}

// AddNodeFlags exposes some common configuration options on the command-line
// These are exposed for convenience of commands embedding a tendermint node
func AddNodeFlags(cmd *cobra.Command) {
	// bind flags
	cmd.Flags().String("moniker", config.Moniker, "Node Name")

	// node flags
	cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")

	// abci flags
	cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.")
	cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")

	// rpc flags
	cmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required")
	cmd.Flags().String("rpc.grpc_laddr", config.RPC.GRPCListenAddress, "GRPC listen address (BroadcastTx only). Port required")
	cmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "Enabled unsafe rpc methods")

	// p2p flags
	cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)")
	cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes")
	cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration")
	cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable Peer-Exchange (dev feature)")
}

func ParseGenesisFile(genDocFile string) (*types.GenesisDoc, error) {
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		return nil, errors.Wrap(err, "Couldn't read GenesisDoc file")
	}
	genDoc, err := types.GenesisDocFromJSON(jsonBlob)
	if err != nil {
		return nil, errors.Wrap(err, "Error reading GenesisDoc")
	}
	if genDoc.ChainID == "" {
		return nil, errors.Errorf("Genesis doc %v must include non-empty chain_id", genDocFile)
	}
	return genDoc, nil
}

// Users wishing to:
//	* Use an external signer for their validators
//	* Supply an in-proc abci app
// should import github.com/tendermint/tendermint/node and implement
// their own run_node to call node.NewNode (instead of node.NewNodeDefault)
// with their custom priv validator and/or custom proxy.ClientCreator
func runNode(cmd *cobra.Command, args []string) error {

	// Wait until the genesis doc becomes available
	// This is for Mintnet compatibility.
	// TODO: If Mintnet gets deprecated or genesis_file is
	// always available, remove.
	genDocFile := config.GenesisFile()
	for !cmn.FileExists(genDocFile) {
		logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile))
		time.Sleep(time.Second)
	}

	genDoc, err := ParseGenesisFile(genDocFile)
	if err != nil {
		return err
	}
	config.ChainID = genDoc.ChainID

	// Create & start node
	n := node.NewNodeDefault(config, logger.With("module", "node"))
	if _, err := n.Start(); err != nil {
		return fmt.Errorf("Failed to start node: %v", err)
	} else {
		logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo())
	}

	// Trap signal, run forever.
	n.RunForever()

	return nil
}
<|endoftext|>


<commit_before>package config

import "github.com/hashicorp/vault/internalshared/configutil"

func IsValidListener(listener *configutil.Listener) error {
	return nil
}
<commit_msg>fix build tag (#12588)<commit_after>// +build !fips_140_3

package config

import "github.com/hashicorp/vault/internalshared/configutil"

func IsValidListener(listener *configutil.Listener) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/stealthycoin/rhynock"
	"net/http"
)

// Super amazing database of users
var (
	people = map[string]string{
		"Steve": "password1",
		"Joe": "12345",
		"Anna": "hunter2",
	}
)

// Profile object to keep track of login status
type Profile struct {
	authed bool // Is this person authenticated yet?
	name string // Who is this person?
}

type Router struct {
	// Kind of sorta keep track of who is connected
	connections map[*rhynock.Conn]*Profile

	// Channel to recieve bottles through
	bottle     chan *rhynock.Bottle
}

// Satisfy the BottleDst interface
func (r *Router) GetBottleChan() (chan *rhynock.Bottle) {
	return r.bottle
}

// Satisfy the BottleDst interface
func (r *Router) ConnectionClosed(c *rhynock.Conn) {
	delete(r.connections, c)
}

// Satisfy the BottleDst interface
func (r *Router) ConnectionOpened(c *rhynock.Conn) {
	// Create an empty profile for them
	r.connections[c] = &Profile{}

	// Send them a login prompt
	c.Send <- []byte("Enter your username")
}

// Function to manage login process
func (r *Router) login(b *rhynock.Bottle) {
	// For ease of use
	profile := r.connections[b.Sender]

	// Is the message a username or a password?
	// Username comes first so check if its empty
	if profile.name == "" {
		// Dont know their name yet so this message should be their name
		profile.name = string(b.Message)

		// Now tell them to enter their password
		b.Sender.Send <- []byte("Enter password")
	} else {

		// Their name is set so this is their password
		// Check our super super database to see if they
		// authenticated correctly
		pass := string(b.Message)
		if db_pass, ok := people[profile.name]; ok {
			// The name was in the list now lets check their password
			if pass == db_pass {
				// Huzzah they logged in successfully
				profile.authed = true

				// Alert them they are logged in
				b.Sender.Send <- []byte("Logged in as " + profile.name)
			} else {
				// They typed the password wrong
				b.Sender.Quit <- []byte("Invalid password.")
			}
		} else {
			// The name wasn't in the list so they fail
			b.Sender.Quit <- []byte("Invalid username.")
		}
	}
}

func main() {
	router := &Router{
		connections: make(map[*rhynock.Conn]*Profile),
		bottle: make(chan *rhynock.Bottle),
	}

	// Register the route to rhynock handler function and pass in our BottleDst
	http.HandleFunc("/socket/", func (w http.ResponseWriter, r *http.Request) {
		rhynock.ConnectionHandler(w, r, router)
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "index.html")
	})

	// Start router listening routine
	go func() {
		for {
			// Listen for a bottle
			btl := <- router.bottle

			// If this person is not authenticated forward the bottle to the login function
			if !router.connections[btl.Sender].authed {
				router.login(btl)
			} else {
				// If they are logged in...
				// reconstruct the message to include the username
				message := []byte(router.connections[btl.Sender].name + ": " + string(btl.Message))

				// Loop through all active connections
				for c, _ := range router.connections {
					// Send everyone the message
					c.Send <- message
				}
			}
		}
	}()

	http.ListenAndServe("127.0.0.1:8000", nil)
}
<commit_msg>Comment<commit_after>package main

import (
	"github.com/stealthycoin/rhynock"
	"net/http"
)

// Super amazing database of users
var (
	people = map[string]string{
		"Steve": "password1",
		"Joe": "12345",
		"Anna": "hunter2",
	}
)

// Profile object to keep track of login status
type Profile struct {
	authed bool // Is this person authenticated yet?
	name string // Who is this person?
}

type Router struct {
	// Do a decent job keeping track of who is logged in
	connections map[*rhynock.Conn]*Profile

	// Channel to recieve bottles through
	bottle     chan *rhynock.Bottle
}

// Satisfy the BottleDst interface
func (r *Router) GetBottleChan() (chan *rhynock.Bottle) {
	return r.bottle
}

// Satisfy the BottleDst interface
func (r *Router) ConnectionClosed(c *rhynock.Conn) {
	delete(r.connections, c)
}

// Satisfy the BottleDst interface
func (r *Router) ConnectionOpened(c *rhynock.Conn) {
	// Create an empty profile for them
	r.connections[c] = &Profile{}

	// Send them a login prompt
	c.Send <- []byte("Enter your username")
}

// Function to manage login process
func (r *Router) login(b *rhynock.Bottle) {
	// For ease of use
	profile := r.connections[b.Sender]

	// Is the message a username or a password?
	// Username comes first so check if its empty
	if profile.name == "" {
		// Dont know their name yet so this message should be their name
		profile.name = string(b.Message)

		// Now tell them to enter their password
		b.Sender.Send <- []byte("Enter password")
	} else {

		// Their name is set so this is their password
		// Check our super super database to see if they
		// authenticated correctly
		pass := string(b.Message)
		if db_pass, ok := people[profile.name]; ok {
			// The name was in the list now lets check their password
			if pass == db_pass {
				// Huzzah they logged in successfully
				profile.authed = true

				// Alert them they are logged in
				b.Sender.Send <- []byte("Logged in as " + profile.name)
			} else {
				// They typed the password wrong
				b.Sender.Quit <- []byte("Invalid password.")
			}
		} else {
			// The name wasn't in the list so they fail
			b.Sender.Quit <- []byte("Invalid username.")
		}
	}
}

func main() {
	router := &Router{
		connections: make(map[*rhynock.Conn]*Profile),
		bottle: make(chan *rhynock.Bottle),
	}

	// Register the route to rhynock handler function and pass in our BottleDst
	http.HandleFunc("/socket/", func (w http.ResponseWriter, r *http.Request) {
		rhynock.ConnectionHandler(w, r, router)
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "index.html")
	})

	// Start router listening routine
	go func() {
		for {
			// Listen for a bottle
			btl := <- router.bottle

			// If this person is not authenticated forward the bottle to the login function
			if !router.connections[btl.Sender].authed {
				router.login(btl)
			} else {
				// If they are logged in...
				// reconstruct the message to include the username
				message := []byte(router.connections[btl.Sender].name + ": " + string(btl.Message))

				// Loop through all active connections
				for c, _ := range router.connections {
					// Send everyone the message
					c.Send <- message
				}
			}
		}
	}()

	http.ListenAndServe("127.0.0.1:8000", nil)
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "io"
    "os"
    "net/http"
    "archive/zip"
    "path/filepath"
    "github.com/urfave/cli"
)

// Uses Go CLI package - please see here for documentation: https://github.com/urfave/cli

func main() {
    // set some variables
    var vagrant string
    var provision string
    var editorconfig bool
    var git bool
    var github bool

    // read in the source server (i.e. where to get packages from). Expect to be in an environment variable
    var sourceServer string
    sourceServer = os.Getenv("SHOELACE_SERVER")

    // if we don't have a server then error and bail
    if sourceServer == "" {
        fmt.Println("No SHOELACE_SERVER environment variable found, please add one and run shoelace again")
        return
    }

    // create a new CLI app
    app := cli.NewApp()
    app.Version = "0.0.1"
    app.Name = "Shoelace - Project environment initialisation for cool dudes :)"

    // add command(s) to the CLI app
    app.Commands = []cli.Command{
        {
            Name: "init",
            Usage: "Initialise a project with given settings",
            // define flags that the "init" command can use. E.g. shoelace init --flag1=foo --flag2=bar
            Flags: []cli.Flag{
                cli.StringFlag{
                    Name: "vagrant",
                    Usage: "The Vagrant machine to use",
                    Destination: &vagrant,  // the variable to pop this value in
                },
                cli.StringFlag{
                    Name: "provision",
                    Usage: "The provisioner to use. TOOL/CONFIG, e.g. ansible/lamp",
                    Destination: &provision,
                },
                cli.BoolFlag{
                    Name:  "editorconfig",
                    Usage: "Whether to include the .editorconfig or not",
                    Destination: &editorconfig,
                },
                cli.BoolFlag{
                    Name:  "git",
                    Usage: "Whether to include sample .gitignore and .gitattributes files or not",
                    Destination: &git,
                },
                cli.BoolFlag{
                    Name:  "github",
                    Usage: "Whether to include Github files (pull request template)",
                    Destination: &github,
                },
            },
            // define tha actual work to do when "init" is used
            Action:  func(c *cli.Context) error {
                var url string;

                // build the URL to the package server and pass arguments
                url = fmt.Sprintf("%spackager.php?vagrant=%s&provision=%s&editorconfig=%t&git=%t&github=%t", sourceServer, vagrant, provision, editorconfig, git, github)
                fmt.Println(url)

                // make the HTTP request to the URL (just an HTTP GET request)
                response, err := http.Get(url)

                if err != nil {
                    // if there was an error handle it (or not)
                } else {
                    // once we're done with the response body close it
                    defer response.Body.Close()

                    // create a file to store the response from the package server
                    out, err := os.Create("filename.zip")
                    if err != nil {
                        // handle file creation error
                        // panic?
                    }

                    // defer closing the output file until the function we're in has completed
                    defer out.Close()

                    // set the content of the output file with the response from the package server
                    io.Copy(out, response.Body)

                    // unzip the file we got into the current directory
                    Unzip("filename.zip", "")
                }

                return nil
            },
        },
    }

    // when the command(s) above have completed, remove the downloaded package from the client
    defer func () {
        os.Remove("filename.zip");
    }()

    // run the CLI app
    app.Run(os.Args)
}

/*
    Unzip the file in src to dest
    @see: http://stackoverflow.com/questions/20357223/easy-way-to-unzip-file-with-golang
 */
func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer func() {
        if err := r.Close(); err != nil {
            panic(err)
        }
    }()

    os.MkdirAll(dest, 0755)

    // Closure to address file descriptors issue with all the deferred .Close() methods
    extractAndWriteFile := func(f *zip.File) error {
        rc, err := f.Open()
        if err != nil {
            return err
        }
        defer func() {
            if err := rc.Close(); err != nil {
                panic(err)
            }
        }()

        path := filepath.Join(dest, f.Name)

        if f.FileInfo().IsDir() {
            os.MkdirAll(path, f.Mode())
        } else {
            os.MkdirAll(filepath.Dir(path), f.Mode())
            f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer func() {
                if err := f.Close(); err != nil {
                    panic(err)
                }
            }()

            _, err = io.Copy(f, rc)
            if err != nil {
                return err
            }
        }
        return nil
    }

    for _, f := range r.File {
        err := extractAndWriteFile(f)
        if err != nil {
            return err
        }
    }

    return nil
}
<commit_msg>Version bump<commit_after>package main

import (
    "fmt"
    "io"
    "os"
    "net/http"
    "archive/zip"
    "path/filepath"
    "github.com/urfave/cli"
)

// Uses Go CLI package - please see here for documentation: https://github.com/urfave/cli

func main() {
    // set some variables
    var vagrant string
    var provision string
    var editorconfig bool
    var git bool
    var github bool

    // read in the source server (i.e. where to get packages from). Expect to be in an environment variable
    var sourceServer string
    sourceServer = os.Getenv("SHOELACE_SERVER")

    // if we don't have a server then error and bail
    if sourceServer == "" {
        fmt.Println("No SHOELACE_SERVER environment variable found, please add one and run shoelace again")
        return
    }

    // create a new CLI app
    app := cli.NewApp()
    app.Version = "0.3.0"
    app.Name = "Shoelace - Project environment initialisation for cool dudes :)"

    // add command(s) to the CLI app
    app.Commands = []cli.Command{
        {
            Name: "init",
            Usage: "Initialise a project with given settings",
            // define flags that the "init" command can use. E.g. shoelace init --flag1=foo --flag2=bar
            Flags: []cli.Flag{
                cli.StringFlag{
                    Name: "vagrant",
                    Usage: "The Vagrant machine to use",
                    Destination: &vagrant,  // the variable to pop this value in
                },
                cli.StringFlag{
                    Name: "provision",
                    Usage: "The provisioner to use. TOOL/CONFIG, e.g. ansible/lamp",
                    Destination: &provision,
                },
                cli.BoolFlag{
                    Name:  "editorconfig",
                    Usage: "Whether to include the .editorconfig or not",
                    Destination: &editorconfig,
                },
                cli.BoolFlag{
                    Name:  "git",
                    Usage: "Whether to include sample .gitignore and .gitattributes files or not",
                    Destination: &git,
                },
                cli.BoolFlag{
                    Name:  "github",
                    Usage: "Whether to include Github files (pull request template)",
                    Destination: &github,
                },
            },
            // define tha actual work to do when "init" is used
            Action:  func(c *cli.Context) error {
                var url string;

                // build the URL to the package server and pass arguments
                url = fmt.Sprintf("%spackager.php?vagrant=%s&provision=%s&editorconfig=%t&git=%t&github=%t", sourceServer, vagrant, provision, editorconfig, git, github)
                fmt.Println(url)

                // make the HTTP request to the URL (just an HTTP GET request)
                response, err := http.Get(url)

                if err != nil {
                    // if there was an error handle it (or not)
                } else {
                    // once we're done with the response body close it
                    defer response.Body.Close()

                    // create a file to store the response from the package server
                    out, err := os.Create("filename.zip")
                    if err != nil {
                        // handle file creation error
                        // panic?
                    }

                    // defer closing the output file until the function we're in has completed
                    defer out.Close()

                    // set the content of the output file with the response from the package server
                    io.Copy(out, response.Body)

                    // unzip the file we got into the current directory
                    Unzip("filename.zip", "")
                }

                return nil
            },
        },
    }

    // when the command(s) above have completed, remove the downloaded package from the client
    defer func () {
        os.Remove("filename.zip");
    }()

    // run the CLI app
    app.Run(os.Args)
}

/*
    Unzip the file in src to dest
    @see: http://stackoverflow.com/questions/20357223/easy-way-to-unzip-file-with-golang
 */
func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer func() {
        if err := r.Close(); err != nil {
            panic(err)
        }
    }()

    os.MkdirAll(dest, 0755)

    // Closure to address file descriptors issue with all the deferred .Close() methods
    extractAndWriteFile := func(f *zip.File) error {
        rc, err := f.Open()
        if err != nil {
            return err
        }
        defer func() {
            if err := rc.Close(); err != nil {
                panic(err)
            }
        }()

        path := filepath.Join(dest, f.Name)

        if f.FileInfo().IsDir() {
            os.MkdirAll(path, f.Mode())
        } else {
            os.MkdirAll(filepath.Dir(path), f.Mode())
            f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer func() {
                if err := f.Close(); err != nil {
                    panic(err)
                }
            }()

            _, err = io.Copy(f, rc)
            if err != nil {
                return err
            }
        }
        return nil
    }

    for _, f := range r.File {
        err := extractAndWriteFile(f)
        if err != nil {
            return err
        }
    }

    return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"go.pedge.io/env"

	"github.com/pachyderm/pachyderm/src/cmd/pfs/cmds"
	"github.com/spf13/cobra"
)

type appEnv struct {
	PachydermPfsd1Port string `env:"PACHYDERM_PFSD_1_PORT"`
	Address            string `env:"PFS_ADDRESS,default=0.0.0.0:650"`
}

func main() {
	env.Main(do, &appEnv{})
}

func do(appEnvObj interface{}) error {
	appEnv := appEnvObj.(*appEnv)
	address := appEnv.PachydermPfsd1Port
	if address == "" {
		address = appEnv.Address
	} else {
		address = strings.Replace(address, "tcp://", "", -1)
	}
	rootCmd := &cobra.Command{
		Use: "pfs",
		Long: `Access the PFS API.

Note that this CLI is experimental and does not even check for common errors.
The environment variable PFS_ADDRESS controls what server the CLI connects to, the default is 0.0.0.0:650.`,
	}
	cmds, err := cmds.Cmds(address)
	if err != nil {
		return err
	}
	for _, cmd := range cmds {
		rootCmd.AddCommand(cmd)
	}

	return rootCmd.Execute()
}

func getPfsdAddress() string {
	pfsdAddr := os.Getenv("PFSD_PORT_650_TCP_ADDR")
	if pfsdAddr == "" {
		return "0.0.0.0:650"
	}
	return fmt.Sprintf("%s:650", pfsdAddr)
}
<commit_msg>Remove unused getPfsdAddress() func.<commit_after>package main

import (
	"strings"

	"go.pedge.io/env"

	"github.com/pachyderm/pachyderm/src/cmd/pfs/cmds"
	"github.com/spf13/cobra"
)

type appEnv struct {
	PachydermPfsd1Port string `env:"PACHYDERM_PFSD_1_PORT"`
	Address            string `env:"PFS_ADDRESS,default=0.0.0.0:650"`
}

func main() {
	env.Main(do, &appEnv{})
}

func do(appEnvObj interface{}) error {
	appEnv := appEnvObj.(*appEnv)
	address := appEnv.PachydermPfsd1Port
	if address == "" {
		address = appEnv.Address
	} else {
		address = strings.Replace(address, "tcp://", "", -1)
	}
	rootCmd := &cobra.Command{
		Use: "pfs",
		Long: `Access the PFS API.

Note that this CLI is experimental and does not even check for common errors.
The environment variable PFS_ADDRESS controls what server the CLI connects to, the default is 0.0.0.0:650.`,
	}
	cmds, err := cmds.Cmds(address)
	if err != nil {
		return err
	}
	for _, cmd := range cmds {
		rootCmd.AddCommand(cmd)
	}

	return rootCmd.Execute()
}
<|endoftext|>


<commit_before>// Copyright 2019 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"fmt"
	"sync"
	"time"

	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type onlineStatusTracker struct {
	cancel   func()
	config   Config
	onChange func()
	log      logger.Logger

	lock          sync.RWMutex
	currentStatus keybase1.KbfsOnlineStatus
	userIsLooking map[string]bool

	userIn  chan struct{}
	userOut chan struct{}

	wg *sync.WaitGroup
}

const ostTryingStateTimeout = 4 * time.Second

type ostState int

const (
	_ ostState = iota
	// We are connected to the mdserver, and user is looking at the Fs tab.
	ostOnlineUserIn
	// We are connected to the mdserver, and user is not looking at the Fs tab.
	ostOnlineUserOut
	// User is looking at the Fs tab. We are not connected to the mdserver, but
	// we are showing a "trying" state in GUI. This usually lasts for
	// ostTryingStateTimeout.
	ostTryingUserIn
	// User is not looking at the Fs tab. We are not connected to the mdserver,
	// but we are telling GUI a "trying" state.
	ostTryingUserOut
	// User is looking at the Fs tab. We are disconnected from the mdserver and
	// are telling GUI so.
	ostOfflineUserIn
	// User is not looking at the Fs tab. We are disconnected from the mdserver
	// and are telling GUI so.
	//
	// Note that we can only go to ostOfflineUserOut from ostOfflineUserIn, but
	// not from any other state. This is because when user is out we don't fast
	// forward. Even if user has got good connection, we might still show as
	// offline until user navigates into the Fs tab which triggers a fast
	// forward and get us connected. If we were to show this state, user would
	// see an offline screen flash for a second before actually getting
	// connected every time they come back to the Fs tab with a previous bad
	// (or lack of) connection, or even from backgrounded app.  So instead, in
	// this case we just use the trying state which shows a slim (less
	// invasive) banner saying we are trying to reconnect.  On the other hand,
	// if user has seen the transition into offline, and user has remained
	// disconnected, it'd be weird for them to see a "trying" state every time
	// they switch away and back into the Fs tab. So in this case just keep the
	// offline state, which is what ostOfflineUserOut is for.
	ostOfflineUserOut
)

func (s ostState) String() string {
	switch s {
	case ostOnlineUserIn:
		return "online-userIn"
	case ostOnlineUserOut:
		return "online-userOut"
	case ostTryingUserIn:
		return "trying-userIn"
	case ostTryingUserOut:
		return "trying-userOut"
	case ostOfflineUserIn:
		return "offline-userIn"
	case ostOfflineUserOut:
		return "offline-userOut"
	default:
		panic("unknown state")
	}
}

func (s ostState) getOnlineStatus() keybase1.KbfsOnlineStatus {
	switch s {
	case ostOnlineUserIn:
		return keybase1.KbfsOnlineStatus_ONLINE
	case ostOnlineUserOut:
		return keybase1.KbfsOnlineStatus_ONLINE
	case ostTryingUserIn:
		return keybase1.KbfsOnlineStatus_TRYING
	case ostTryingUserOut:
		return keybase1.KbfsOnlineStatus_TRYING
	case ostOfflineUserIn:
		return keybase1.KbfsOnlineStatus_OFFLINE
	case ostOfflineUserOut:
		return keybase1.KbfsOnlineStatus_OFFLINE
	default:
		panic("unknown state")
	}
}

// ostSideEffect is a type for side effects that happens as a result of
// transitions happening inside the FSM. These side effects describe what
// should happen, but the FSM doesn't directly do them. The caller of outFsm
// should make sure those actions are carried out.
type ostSideEffect int

const (
	// ostResetTimer describes a side effect where the timer for transitioning
	// from a "trying" state into a "offline" state should be reset and
	// started.
	ostResetTimer ostSideEffect = iota
	// ostStopTimer describes a side effect where the timer for transitioning
	// from a "trying" state into a "offline" state should be stopped.
	ostStopTimer
	// ostFastForward describes a side effect where we should fast forward the
	// reconnecting backoff timer and attempt to connect to the mdserver right
	// away.
	ostFastForward
)

func ostFsm(
	ctx context.Context,
	wg *sync.WaitGroup,
	log logger.Logger,
	initialState ostState,
	// sideEffects carries events about side effects caused by the FSM
	// transitions. Caller should handle these effects and make things actually
	// happen.
	sideEffects chan<- ostSideEffect,
	// onlineStatusUpdates carries a special side effect for the caller to know
	// when the onlineStatus changes.
	onlineStatusUpdates chan<- keybase1.KbfsOnlineStatus,
	// userIn is used to signify the FSM that user has just started looking at
	// the Fs tab.
	userIn <-chan struct{},
	// userOut is used to signify the FSM that user has just switched away from
	// the Fs tab.
	userOut <-chan struct{},
	// tryingTimerUp is used to signify the FSM that the timer for
	// transitioning from a "trying" state to "offline" state is up.
	tryingTimerUp <-chan struct{},
	// connected is used to signify the FSM that we've just connected to the
	// mdserver.
	connected <-chan struct{},
	// disconnected is used to signify the FSM that we've just lost connection to
	// the mdserver.
	disconnected <-chan struct{},
) {
	defer wg.Done()

	select {
	case <-ctx.Done():
		return
	default:
	}
	log.CDebugf(ctx, "ostFsm initialState=%s", initialState)

	state := initialState
	for {
		previousState := state

		switch state {
		case ostOnlineUserIn:
			select {
			case <-userIn:
			case <-userOut:
				state = ostOnlineUserOut
			case <-tryingTimerUp:
			case <-connected:
			case <-disconnected:
				state = ostTryingUserIn
				sideEffects <- ostFastForward
				sideEffects <- ostResetTimer

			case <-ctx.Done():
				return
			}
		case ostOnlineUserOut:
			select {
			case <-userIn:
				state = ostOnlineUserIn
			case <-userOut:
			case <-tryingTimerUp:
			case <-connected:
			case <-disconnected:
				state = ostTryingUserOut
				// Don't start a timer as we don't want to transition into
				// offline from trying when user is out. See comment for
				// ostOfflineUserOut above.

			case <-ctx.Done():
				return
			}
		case ostTryingUserIn:
			select {
			case <-userIn:
			case <-userOut:
				state = ostTryingUserOut
				// Stop the timer as we don't transition into offline when
				// user is not looking.
				sideEffects <- ostStopTimer
			case <-tryingTimerUp:
				state = ostOfflineUserIn
			case <-connected:
				state = ostOnlineUserIn
			case <-disconnected:

			case <-ctx.Done():
				return
			}
		case ostTryingUserOut:
			select {
			case <-userIn:
				state = ostTryingUserIn
				sideEffects <- ostFastForward
				sideEffects <- ostResetTimer
			case <-userOut:
			case <-tryingTimerUp:
				// Don't transition into ostOfflineUserOut. See comment for
				// offlienUserOut above.
			case <-connected:
				state = ostOnlineUserOut
			case <-disconnected:

			case <-ctx.Done():
				return
			}
		case ostOfflineUserIn:
			select {
			case <-userIn:
			case <-userOut:
				state = ostOfflineUserOut
			case <-tryingTimerUp:
			case <-connected:
				state = ostOnlineUserIn
			case <-disconnected:

			case <-ctx.Done():
				return
			}
		case ostOfflineUserOut:
			select {
			case <-userIn:
				state = ostOfflineUserIn
				// Trigger fast forward but don't transition into "trying", to
				// avoid flip-flopping.
				sideEffects <- ostFastForward
			case <-userOut:
			case <-tryingTimerUp:
			case <-connected:
				state = ostOnlineUserOut
			case <-disconnected:

			case <-ctx.Done():
				return
			}

		}

		if previousState != state {
			log.CDebugf(ctx, "ostFsm state=%s", state)
			onlineStatus := state.getOnlineStatus()
			if previousState.getOnlineStatus() != onlineStatus {
				select {
				case onlineStatusUpdates <- onlineStatus:
				case <-ctx.Done():
					return
				}
			}
		}
	}
}

func (ost *onlineStatusTracker) updateOnlineStatus(onlineStatus keybase1.KbfsOnlineStatus) {
	ost.lock.Lock()
	ost.currentStatus = onlineStatus
	ost.lock.Unlock()
	ost.onChange()
}

func (ost *onlineStatusTracker) run(ctx context.Context) {
	defer ost.wg.Done()

	for ost.config.KBFSOps() == nil {
		time.Sleep(100 * time.Millisecond)
	}

	tryingStateTimer := time.NewTimer(time.Hour)
	tryingStateTimer.Stop()

	sideEffects := make(chan ostSideEffect)
	onlineStatusUpdates := make(chan keybase1.KbfsOnlineStatus)
	tryingTimerUp := make(chan struct{})
	connected := make(chan struct{})
	disconnected := make(chan struct{})

	serviceErrors, invalidateChan := ost.config.KBFSOps().
		StatusOfServices()

	initialState := ostOfflineUserOut
	if serviceErrors[MDServiceName] == nil {
		initialState = ostOnlineUserOut
	}

	ost.wg.Add(1)
	go ostFsm(ctx, ost.wg, ost.log,
		initialState, sideEffects, onlineStatusUpdates,
		ost.userIn, ost.userOut, tryingTimerUp, connected, disconnected)

	ost.wg.Add(1)
	// mdserver connection status watch routine
	go func() {
		defer ost.wg.Done()
		invalidateChan := invalidateChan
		var serviceErrors map[string]error
		for {
			select {
			case <-invalidateChan:
				serviceErrors, invalidateChan = ost.config.KBFSOps().
					StatusOfServices()
				if serviceErrors[MDServiceName] == nil {
					connected <- struct{}{}
				} else {
					disconnected <- struct{}{}
				}
			case <-ctx.Done():
				return
			}
		}
	}()

	for {
		select {
		case <-tryingStateTimer.C:
			tryingTimerUp <- struct{}{}
		case sideEffect := <-sideEffects:
			switch sideEffect {
			case ostResetTimer:
				if !tryingStateTimer.Stop() {
					select {
					case <-tryingStateTimer.C:
					default:
					}
				}
				tryingStateTimer.Reset(ostTryingStateTimeout)
			case ostStopTimer:
				if !tryingStateTimer.Stop() {
					<-tryingStateTimer.C
					select {
					case <-tryingStateTimer.C:
					default:
					}
				}
			case ostFastForward:
				// This requires holding a lock and may block sometimes.
				go ost.config.MDServer().FastForwardBackoff()
			default:
				panic(fmt.Sprintf("unknown side effect %d", sideEffect))
			}
		case onlineStatus := <-onlineStatusUpdates:
			ost.updateOnlineStatus(onlineStatus)
			ost.log.CDebugf(ctx, "ost onlineStatus=%d", onlineStatus)
		case <-ctx.Done():
			return
		}
	}
}

func (ost *onlineStatusTracker) userInOut(clientID string, clientIsIn bool) {
	ost.lock.Lock()
	wasIn := len(ost.userIsLooking) != 0
	if clientIsIn {
		ost.userIsLooking[clientID] = true
	} else {
		delete(ost.userIsLooking, clientID)
	}
	isIn := len(ost.userIsLooking) != 0
	ost.lock.Unlock()

	if wasIn && !isIn {
		ost.userOut <- struct{}{}
	}

	if !wasIn && isIn {
		ost.userIn <- struct{}{}
	}
}

// UserIn tells the onlineStatusTracker that user is looking at the Fs tab in
// GUI. When user is looking at the Fs tab, the underlying RPC fast forwards
// any backoff timer for reconnecting to the mdserver.
func (ost *onlineStatusTracker) UserIn(ctx context.Context, clientID string) {
	ost.userInOut(clientID, true)
	ost.log.CDebugf(ctx, "UserIn clientID=%s", clientID)
}

// UserOut tells the onlineStatusTracker that user is not looking at the Fs
// tab in GUI anymore.  GUI.
func (ost *onlineStatusTracker) UserOut(ctx context.Context, clientID string) {
	ost.userInOut(clientID, false)
	ost.log.CDebugf(ctx, "UserOut clientID=%s", clientID)
}

// GetOnlineStatus implements the OnlineStatusTracker interface.
func (ost *onlineStatusTracker) GetOnlineStatus() keybase1.KbfsOnlineStatus {
	ost.lock.RLock()
	defer ost.lock.RUnlock()
	return ost.currentStatus
}

func newOnlineStatusTracker(
	config Config, onChange func()) *onlineStatusTracker {
	ctx, cancel := context.WithCancel(context.Background())
	ost := &onlineStatusTracker{
		cancel:        cancel,
		config:        config,
		onChange:      onChange,
		currentStatus: keybase1.KbfsOnlineStatus_ONLINE,
		log:           config.MakeLogger("onlineStatusTracker"),
		userIsLooking: make(map[string]bool),
		userIn:        make(chan struct{}),
		userOut:       make(chan struct{}),
		wg:            &sync.WaitGroup{},
	}

	ost.wg.Add(1)
	go ost.run(ctx)

	return ost
}

func (ost *onlineStatusTracker) shutdown() {
	ost.cancel()
	ost.wg.Wait()
}
<commit_msg>remove debug (#22359)<commit_after>// Copyright 2019 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"fmt"
	"sync"
	"time"

	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type onlineStatusTracker struct {
	cancel   func()
	config   Config
	onChange func()
	log      logger.Logger

	lock          sync.RWMutex
	currentStatus keybase1.KbfsOnlineStatus
	userIsLooking map[string]bool

	userIn  chan struct{}
	userOut chan struct{}

	wg *sync.WaitGroup
}

const ostTryingStateTimeout = 4 * time.Second

type ostState int

const (
	_ ostState = iota
	// We are connected to the mdserver, and user is looking at the Fs tab.
	ostOnlineUserIn
	// We are connected to the mdserver, and user is not looking at the Fs tab.
	ostOnlineUserOut
	// User is looking at the Fs tab. We are not connected to the mdserver, but
	// we are showing a "trying" state in GUI. This usually lasts for
	// ostTryingStateTimeout.
	ostTryingUserIn
	// User is not looking at the Fs tab. We are not connected to the mdserver,
	// but we are telling GUI a "trying" state.
	ostTryingUserOut
	// User is looking at the Fs tab. We are disconnected from the mdserver and
	// are telling GUI so.
	ostOfflineUserIn
	// User is not looking at the Fs tab. We are disconnected from the mdserver
	// and are telling GUI so.
	//
	// Note that we can only go to ostOfflineUserOut from ostOfflineUserIn, but
	// not from any other state. This is because when user is out we don't fast
	// forward. Even if user has got good connection, we might still show as
	// offline until user navigates into the Fs tab which triggers a fast
	// forward and get us connected. If we were to show this state, user would
	// see an offline screen flash for a second before actually getting
	// connected every time they come back to the Fs tab with a previous bad
	// (or lack of) connection, or even from backgrounded app.  So instead, in
	// this case we just use the trying state which shows a slim (less
	// invasive) banner saying we are trying to reconnect.  On the other hand,
	// if user has seen the transition into offline, and user has remained
	// disconnected, it'd be weird for them to see a "trying" state every time
	// they switch away and back into the Fs tab. So in this case just keep the
	// offline state, which is what ostOfflineUserOut is for.
	ostOfflineUserOut
)

func (s ostState) String() string {
	switch s {
	case ostOnlineUserIn:
		return "online-userIn"
	case ostOnlineUserOut:
		return "online-userOut"
	case ostTryingUserIn:
		return "trying-userIn"
	case ostTryingUserOut:
		return "trying-userOut"
	case ostOfflineUserIn:
		return "offline-userIn"
	case ostOfflineUserOut:
		return "offline-userOut"
	default:
		panic("unknown state")
	}
}

func (s ostState) getOnlineStatus() keybase1.KbfsOnlineStatus {
	switch s {
	case ostOnlineUserIn:
		return keybase1.KbfsOnlineStatus_ONLINE
	case ostOnlineUserOut:
		return keybase1.KbfsOnlineStatus_ONLINE
	case ostTryingUserIn:
		return keybase1.KbfsOnlineStatus_TRYING
	case ostTryingUserOut:
		return keybase1.KbfsOnlineStatus_TRYING
	case ostOfflineUserIn:
		return keybase1.KbfsOnlineStatus_OFFLINE
	case ostOfflineUserOut:
		return keybase1.KbfsOnlineStatus_OFFLINE
	default:
		panic("unknown state")
	}
}

// ostSideEffect is a type for side effects that happens as a result of
// transitions happening inside the FSM. These side effects describe what
// should happen, but the FSM doesn't directly do them. The caller of outFsm
// should make sure those actions are carried out.
type ostSideEffect int

const (
	// ostResetTimer describes a side effect where the timer for transitioning
	// from a "trying" state into a "offline" state should be reset and
	// started.
	ostResetTimer ostSideEffect = iota
	// ostStopTimer describes a side effect where the timer for transitioning
	// from a "trying" state into a "offline" state should be stopped.
	ostStopTimer
	// ostFastForward describes a side effect where we should fast forward the
	// reconnecting backoff timer and attempt to connect to the mdserver right
	// away.
	ostFastForward
)

func ostFsm(
	ctx context.Context,
	wg *sync.WaitGroup,
	log logger.Logger,
	initialState ostState,
	// sideEffects carries events about side effects caused by the FSM
	// transitions. Caller should handle these effects and make things actually
	// happen.
	sideEffects chan<- ostSideEffect,
	// onlineStatusUpdates carries a special side effect for the caller to know
	// when the onlineStatus changes.
	onlineStatusUpdates chan<- keybase1.KbfsOnlineStatus,
	// userIn is used to signify the FSM that user has just started looking at
	// the Fs tab.
	userIn <-chan struct{},
	// userOut is used to signify the FSM that user has just switched away from
	// the Fs tab.
	userOut <-chan struct{},
	// tryingTimerUp is used to signify the FSM that the timer for
	// transitioning from a "trying" state to "offline" state is up.
	tryingTimerUp <-chan struct{},
	// connected is used to signify the FSM that we've just connected to the
	// mdserver.
	connected <-chan struct{},
	// disconnected is used to signify the FSM that we've just lost connection to
	// the mdserver.
	disconnected <-chan struct{},
) {
	defer wg.Done()

	select {
	case <-ctx.Done():
		return
	default:
	}
	// log.CDebugf(ctx, "ostFsm initialState=%s", initialState)

	state := initialState
	for {
		previousState := state

		switch state {
		case ostOnlineUserIn:
			select {
			case <-userIn:
			case <-userOut:
				state = ostOnlineUserOut
			case <-tryingTimerUp:
			case <-connected:
			case <-disconnected:
				state = ostTryingUserIn
				sideEffects <- ostFastForward
				sideEffects <- ostResetTimer

			case <-ctx.Done():
				return
			}
		case ostOnlineUserOut:
			select {
			case <-userIn:
				state = ostOnlineUserIn
			case <-userOut:
			case <-tryingTimerUp:
			case <-connected:
			case <-disconnected:
				state = ostTryingUserOut
				// Don't start a timer as we don't want to transition into
				// offline from trying when user is out. See comment for
				// ostOfflineUserOut above.

			case <-ctx.Done():
				return
			}
		case ostTryingUserIn:
			select {
			case <-userIn:
			case <-userOut:
				state = ostTryingUserOut
				// Stop the timer as we don't transition into offline when
				// user is not looking.
				sideEffects <- ostStopTimer
			case <-tryingTimerUp:
				state = ostOfflineUserIn
			case <-connected:
				state = ostOnlineUserIn
			case <-disconnected:

			case <-ctx.Done():
				return
			}
		case ostTryingUserOut:
			select {
			case <-userIn:
				state = ostTryingUserIn
				sideEffects <- ostFastForward
				sideEffects <- ostResetTimer
			case <-userOut:
			case <-tryingTimerUp:
				// Don't transition into ostOfflineUserOut. See comment for
				// offlienUserOut above.
			case <-connected:
				state = ostOnlineUserOut
			case <-disconnected:

			case <-ctx.Done():
				return
			}
		case ostOfflineUserIn:
			select {
			case <-userIn:
			case <-userOut:
				state = ostOfflineUserOut
			case <-tryingTimerUp:
			case <-connected:
				state = ostOnlineUserIn
			case <-disconnected:

			case <-ctx.Done():
				return
			}
		case ostOfflineUserOut:
			select {
			case <-userIn:
				state = ostOfflineUserIn
				// Trigger fast forward but don't transition into "trying", to
				// avoid flip-flopping.
				sideEffects <- ostFastForward
			case <-userOut:
			case <-tryingTimerUp:
			case <-connected:
				state = ostOnlineUserOut
			case <-disconnected:

			case <-ctx.Done():
				return
			}

		}

		if previousState != state {
			log.CDebugf(ctx, "ostFsm state=%s", state)
			onlineStatus := state.getOnlineStatus()
			if previousState.getOnlineStatus() != onlineStatus {
				select {
				case onlineStatusUpdates <- onlineStatus:
				case <-ctx.Done():
					return
				}
			}
		}
	}
}

func (ost *onlineStatusTracker) updateOnlineStatus(onlineStatus keybase1.KbfsOnlineStatus) {
	ost.lock.Lock()
	ost.currentStatus = onlineStatus
	ost.lock.Unlock()
	ost.onChange()
}

func (ost *onlineStatusTracker) run(ctx context.Context) {
	defer ost.wg.Done()

	for ost.config.KBFSOps() == nil {
		time.Sleep(100 * time.Millisecond)
	}

	tryingStateTimer := time.NewTimer(time.Hour)
	tryingStateTimer.Stop()

	sideEffects := make(chan ostSideEffect)
	onlineStatusUpdates := make(chan keybase1.KbfsOnlineStatus)
	tryingTimerUp := make(chan struct{})
	connected := make(chan struct{})
	disconnected := make(chan struct{})

	serviceErrors, invalidateChan := ost.config.KBFSOps().
		StatusOfServices()

	initialState := ostOfflineUserOut
	if serviceErrors[MDServiceName] == nil {
		initialState = ostOnlineUserOut
	}

	ost.wg.Add(1)
	go ostFsm(ctx, ost.wg, ost.log,
		initialState, sideEffects, onlineStatusUpdates,
		ost.userIn, ost.userOut, tryingTimerUp, connected, disconnected)

	ost.wg.Add(1)
	// mdserver connection status watch routine
	go func() {
		defer ost.wg.Done()
		invalidateChan := invalidateChan
		var serviceErrors map[string]error
		for {
			select {
			case <-invalidateChan:
				serviceErrors, invalidateChan = ost.config.KBFSOps().
					StatusOfServices()
				if serviceErrors[MDServiceName] == nil {
					connected <- struct{}{}
				} else {
					disconnected <- struct{}{}
				}
			case <-ctx.Done():
				return
			}
		}
	}()

	for {
		select {
		case <-tryingStateTimer.C:
			tryingTimerUp <- struct{}{}
		case sideEffect := <-sideEffects:
			switch sideEffect {
			case ostResetTimer:
				if !tryingStateTimer.Stop() {
					select {
					case <-tryingStateTimer.C:
					default:
					}
				}
				tryingStateTimer.Reset(ostTryingStateTimeout)
			case ostStopTimer:
				if !tryingStateTimer.Stop() {
					<-tryingStateTimer.C
					select {
					case <-tryingStateTimer.C:
					default:
					}
				}
			case ostFastForward:
				// This requires holding a lock and may block sometimes.
				go ost.config.MDServer().FastForwardBackoff()
			default:
				panic(fmt.Sprintf("unknown side effect %d", sideEffect))
			}
		case onlineStatus := <-onlineStatusUpdates:
			ost.updateOnlineStatus(onlineStatus)
			ost.log.CDebugf(ctx, "ost onlineStatus=%d", onlineStatus)
		case <-ctx.Done():
			return
		}
	}
}

func (ost *onlineStatusTracker) userInOut(clientID string, clientIsIn bool) {
	ost.lock.Lock()
	wasIn := len(ost.userIsLooking) != 0
	if clientIsIn {
		ost.userIsLooking[clientID] = true
	} else {
		delete(ost.userIsLooking, clientID)
	}
	isIn := len(ost.userIsLooking) != 0
	ost.lock.Unlock()

	if wasIn && !isIn {
		ost.userOut <- struct{}{}
	}

	if !wasIn && isIn {
		ost.userIn <- struct{}{}
	}
}

// UserIn tells the onlineStatusTracker that user is looking at the Fs tab in
// GUI. When user is looking at the Fs tab, the underlying RPC fast forwards
// any backoff timer for reconnecting to the mdserver.
func (ost *onlineStatusTracker) UserIn(ctx context.Context, clientID string) {
	ost.userInOut(clientID, true)
	ost.log.CDebugf(ctx, "UserIn clientID=%s", clientID)
}

// UserOut tells the onlineStatusTracker that user is not looking at the Fs
// tab in GUI anymore.  GUI.
func (ost *onlineStatusTracker) UserOut(ctx context.Context, clientID string) {
	ost.userInOut(clientID, false)
	ost.log.CDebugf(ctx, "UserOut clientID=%s", clientID)
}

// GetOnlineStatus implements the OnlineStatusTracker interface.
func (ost *onlineStatusTracker) GetOnlineStatus() keybase1.KbfsOnlineStatus {
	ost.lock.RLock()
	defer ost.lock.RUnlock()
	return ost.currentStatus
}

func newOnlineStatusTracker(
	config Config, onChange func()) *onlineStatusTracker {
	ctx, cancel := context.WithCancel(context.Background())
	ost := &onlineStatusTracker{
		cancel:        cancel,
		config:        config,
		onChange:      onChange,
		currentStatus: keybase1.KbfsOnlineStatus_ONLINE,
		log:           config.MakeLogger("onlineStatusTracker"),
		userIsLooking: make(map[string]bool),
		userIn:        make(chan struct{}),
		userOut:       make(chan struct{}),
		wg:            &sync.WaitGroup{},
	}

	ost.wg.Add(1)
	go ost.run(ctx)

	return ost
}

func (ost *onlineStatusTracker) shutdown() {
	ost.cancel()
	ost.wg.Wait()
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors 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 chartutil

import (
	"errors"
	"log"
	"strings"
	"time"

	"github.com/ghodss/yaml"
	"k8s.io/helm/pkg/proto/hapi/chart"
)

const (
	requirementsName = "requirements.yaml"
	lockfileName     = "requirements.lock"
)

var (
	// ErrRequirementsNotFound indicates that a requirements.yaml is not found.
	ErrRequirementsNotFound = errors.New(requirementsName + " not found")
	// ErrLockfileNotFound indicates that a requirements.lock is not found.
	ErrLockfileNotFound = errors.New(lockfileName + " not found")
)

// Dependency describes a chart upon which another chart depends.
//
// Dependencies can be used to express developer intent, or to capture the state
// of a chart.
type Dependency struct {
	// Name is the name of the dependency.
	//
	// This must mach the name in the dependency's Chart.yaml.
	Name string `json:"name"`
	// Version is the version (range) of this chart.
	//
	// A lock file will always produce a single version, while a dependency
	// may contain a semantic version range.
	Version string `json:"version,omitempty"`
	// The URL to the repository.
	//
	// Appending `index.yaml` to this string should result in a URL that can be
	// used to fetch the repository index.
	Repository string `json:"repository"`
	// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
	Condition string `json:"condition"`
	// Tags can be used to group charts for enabling/disabling together
	Tags []string `json:"tags"`
	// Enabled bool determines if chart should be loaded
	Enabled bool `json:"enabled"`
	// ImportValues holds the mapping of source values to parent key to be imported
	ImportValues []interface{} `json:"import-values"`
}

// ErrNoRequirementsFile to detect error condition
type ErrNoRequirementsFile error

// Requirements is a list of requirements for a chart.
//
// Requirements are charts upon which this chart depends. This expresses
// developer intent.
type Requirements struct {
	Dependencies []*Dependency `json:"dependencies"`
}

// RequirementsLock is a lock file for requirements.
//
// It represents the state that the dependencies should be in.
type RequirementsLock struct {
	// Genderated is the date the lock file was last generated.
	Generated time.Time `json:"generated"`
	// Digest is a hash of the requirements file used to generate it.
	Digest string `json:"digest"`
	// Dependencies is the list of dependencies that this lock file has locked.
	Dependencies []*Dependency `json:"dependencies"`
}

// LoadRequirements loads a requirements file from an in-memory chart.
func LoadRequirements(c *chart.Chart) (*Requirements, error) {
	var data []byte
	for _, f := range c.Files {
		if f.TypeUrl == requirementsName {
			data = f.Value
		}
	}
	if len(data) == 0 {
		return nil, ErrRequirementsNotFound
	}
	r := &Requirements{}
	return r, yaml.Unmarshal(data, r)
}

// LoadRequirementsLock loads a requirements lock file.
func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
	var data []byte
	for _, f := range c.Files {
		if f.TypeUrl == lockfileName {
			data = f.Value
		}
	}
	if len(data) == 0 {
		return nil, ErrLockfileNotFound
	}
	r := &RequirementsLock{}
	return r, yaml.Unmarshal(data, r)
}

// ProcessRequirementsConditions disables charts based on condition path value in values
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
	var cond string
	var conds []string
	if reqs == nil || len(reqs.Dependencies) == 0 {
		return
	}
	for _, r := range reqs.Dependencies {
		var hasTrue, hasFalse bool
		cond = string(r.Condition)
		// check for list
		if len(cond) > 0 {
			if strings.Contains(cond, ",") {
				conds = strings.Split(strings.TrimSpace(cond), ",")
			} else {
				conds = []string{strings.TrimSpace(cond)}
			}
			for _, c := range conds {
				if len(c) > 0 {
					// retrieve value
					vv, err := cvals.PathValue(c)
					if err == nil {
						// if not bool, warn
						if bv, ok := vv.(bool); ok {
							if bv {
								hasTrue = true
							} else {
								hasFalse = true
							}
						} else {
							log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name)
						}
					} else if _, ok := err.(ErrNoValue); !ok {
						// this is a real error
						log.Printf("Warning: PathValue returned error %v", err)

					}
					if vv != nil {
						// got first value, break loop
						break
					}
				}
			}
			if !hasTrue && hasFalse {
				r.Enabled = false
			} else if hasTrue {
				r.Enabled = true

			}
		}

	}

}

// ProcessRequirementsTags disables charts based on tags in values
func ProcessRequirementsTags(reqs *Requirements, cvals Values) {
	vt, err := cvals.Table("tags")
	if err != nil {
		return

	}
	if reqs == nil || len(reqs.Dependencies) == 0 {
		return
	}
	for _, r := range reqs.Dependencies {
		if len(r.Tags) > 0 {
			tags := r.Tags

			var hasTrue, hasFalse bool
			for _, k := range tags {
				if b, ok := vt[k]; ok {
					// if not bool, warn
					if bv, ok := b.(bool); ok {
						if bv {
							hasTrue = true
						} else {
							hasFalse = true
						}
					} else {
						log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name)
					}
				}
			}
			if !hasTrue && hasFalse {
				r.Enabled = false
			} else if hasTrue || !hasTrue && !hasFalse {
				r.Enabled = true

			}

		}
	}

}

// ProcessRequirementsEnabled removes disabled charts from dependencies
func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
	reqs, err := LoadRequirements(c)
	if err != nil {
		// if not just missing requirements file, return error
		if nerr, ok := err.(ErrNoRequirementsFile); !ok {
			return nerr
		}

		// no requirements to process
		return nil
	}
	// set all to true
	for _, lr := range reqs.Dependencies {
		lr.Enabled = true
	}
	cvals, err := CoalesceValues(c, v)
	if err != nil {
		return err
	}
	// flag dependencies as enabled/disabled
	ProcessRequirementsTags(reqs, cvals)
	ProcessRequirementsConditions(reqs, cvals)

	// make a map of charts to remove
	rm := map[string]bool{}
	for _, r := range reqs.Dependencies {
		if !r.Enabled {
			// remove disabled chart
			rm[r.Name] = true
		}
	}
	// don't keep disabled charts in new slice
	cd := []*chart.Chart{}
	copy(cd, c.Dependencies[:0])
	for _, n := range c.Dependencies {
		if _, ok := rm[n.Metadata.Name]; !ok {
			cd = append(cd, n)
		}

	}
	// recursively call self to process sub dependencies
	for _, t := range cd {
		err := ProcessRequirementsEnabled(t, v)
		// if its not just missing requirements file, return error
		if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil {
			return nerr
		}
	}
	c.Dependencies = cd

	return nil
}

// pathToMap creates a nested map given a YAML path in dot notation
func pathToMap(path string, data map[string]interface{}) map[string]interface{} {
	ap := strings.Split(path, ".")
	n := []map[string]interface{}{}
	for _, v := range ap {
		nm := make(map[string]interface{})
		nm[v] = make(map[string]interface{})
		n = append(n, nm)
	}
	for i, d := range n {
		for k := range d {
			z := i + 1
			if z == len(n) {
				n[i][k] = data
				break
			}
			n[i][k] = n[z]
		}
	}

	return n[0]
}

// getParents returns a slice of parent charts in reverse order
func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart {
	if len(out) == 0 {
		out = []*chart.Chart{}
		out = append(out, c)
	}
	for _, ch := range c.Dependencies {
		if len(ch.Dependencies) > 0 {
			out = append(out, ch)
			out = getParents(ch, out)
		}
	}

	return out
}

// processImportValues merges values from child to parent based on ImportValues field
func processImportValues(c *chart.Chart, v *chart.Config) error {
	reqs, err := LoadRequirements(c)
	if err != nil {
		log.Printf("Warning: ImportValues cannot load requirements for %s", c.Metadata.Name)
		return nil
	}
	cvals, err := CoalesceValues(c, v)
	if err != nil {
		log.Fatalf("Error coalescing values for ImportValues %s", err)
	}
	nv := v.GetValues()
	b := make(map[string]interface{})
	for kk, v3 := range nv {
		b[kk] = v3
	}
	for _, r := range reqs.Dependencies {
		if len(r.ImportValues) > 0 {
			var outiv []interface{}
			for _, riv := range r.ImportValues {
				switch riv.(type) {
				case map[string]interface{}:
					if m, ok := riv.(map[string]interface{}); ok {
						nm := make(map[string]string)
						nm["child"] = m["child"].(string)
						nm["parent"] = m["parent"].(string)
						outiv = append(outiv, nm)
						s := r.Name + "." + nm["child"]
						vv, err := cvals.Table(s)
						if err != nil {
							log.Printf("Warning: ImportValues missing table %v", err)
							continue
						}
						if nm["parent"] == "." {
							coalesceTables(b, vv.AsMap())
						} else {

							vm := pathToMap(nm["parent"], vv.AsMap())
							coalesceTables(b, vm)
						}
					}
				case string:
					nm := make(map[string]string)
					nm["child"] = "exports." + riv.(string)
					nm["parent"] = "."
					outiv = append(outiv, nm)
					s := r.Name + "." + nm["child"]
					vv, err := cvals.Table(s)
					if err != nil {
						log.Printf("Warning: ImportValues missing table %v", err)
						continue
					}
					coalesceTables(b, vv.AsMap())
				}
			}
			// set our formatted import values
			r.ImportValues = outiv
		}
	}

	cv, err := coalesceValues(c, b)
	if err != nil {
		log.Fatalf("Error coalescing processed values for ImportValues %s", err)
	}
	y, err := yaml.Marshal(cv)
	if err != nil {
		log.Printf("Warning: ImportValues could not marshall %v", err)
	}
	bb := &chart.Config{Raw: string(y)}
	v = bb
	c.Values = bb

	return nil
}

// ProcessRequirementsImportValues imports specified chart values from child to parent
func ProcessRequirementsImportValues(c *chart.Chart, v *chart.Config) error {
	pc := getParents(c, nil)
	for i := len(pc) - 1; i >= 0; i-- {
		processImportValues(pc[i], v)

	}

	return nil
}
<commit_msg>Fixup style and errors<commit_after>/*
Copyright 2016 The Kubernetes Authors 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 chartutil

import (
	"errors"
	"log"
	"strings"
	"time"

	"github.com/ghodss/yaml"
	"k8s.io/helm/pkg/proto/hapi/chart"
)

const (
	requirementsName = "requirements.yaml"
	lockfileName     = "requirements.lock"
)

var (
	// ErrRequirementsNotFound indicates that a requirements.yaml is not found.
	ErrRequirementsNotFound = errors.New(requirementsName + " not found")
	// ErrLockfileNotFound indicates that a requirements.lock is not found.
	ErrLockfileNotFound = errors.New(lockfileName + " not found")
)

// Dependency describes a chart upon which another chart depends.
//
// Dependencies can be used to express developer intent, or to capture the state
// of a chart.
type Dependency struct {
	// Name is the name of the dependency.
	//
	// This must mach the name in the dependency's Chart.yaml.
	Name string `json:"name"`
	// Version is the version (range) of this chart.
	//
	// A lock file will always produce a single version, while a dependency
	// may contain a semantic version range.
	Version string `json:"version,omitempty"`
	// The URL to the repository.
	//
	// Appending `index.yaml` to this string should result in a URL that can be
	// used to fetch the repository index.
	Repository string `json:"repository"`
	// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
	Condition string `json:"condition"`
	// Tags can be used to group charts for enabling/disabling together
	Tags []string `json:"tags"`
	// Enabled bool determines if chart should be loaded
	Enabled bool `json:"enabled"`
	// ImportValues holds the mapping of source values to parent key to be imported
	ImportValues []interface{} `json:"import-values"`
}

// ErrNoRequirementsFile to detect error condition
type ErrNoRequirementsFile error

// Requirements is a list of requirements for a chart.
//
// Requirements are charts upon which this chart depends. This expresses
// developer intent.
type Requirements struct {
	Dependencies []*Dependency `json:"dependencies"`
}

// RequirementsLock is a lock file for requirements.
//
// It represents the state that the dependencies should be in.
type RequirementsLock struct {
	// Genderated is the date the lock file was last generated.
	Generated time.Time `json:"generated"`
	// Digest is a hash of the requirements file used to generate it.
	Digest string `json:"digest"`
	// Dependencies is the list of dependencies that this lock file has locked.
	Dependencies []*Dependency `json:"dependencies"`
}

// LoadRequirements loads a requirements file from an in-memory chart.
func LoadRequirements(c *chart.Chart) (*Requirements, error) {
	var data []byte
	for _, f := range c.Files {
		if f.TypeUrl == requirementsName {
			data = f.Value
		}
	}
	if len(data) == 0 {
		return nil, ErrRequirementsNotFound
	}
	r := &Requirements{}
	return r, yaml.Unmarshal(data, r)
}

// LoadRequirementsLock loads a requirements lock file.
func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
	var data []byte
	for _, f := range c.Files {
		if f.TypeUrl == lockfileName {
			data = f.Value
		}
	}
	if len(data) == 0 {
		return nil, ErrLockfileNotFound
	}
	r := &RequirementsLock{}
	return r, yaml.Unmarshal(data, r)
}

// ProcessRequirementsConditions disables charts based on condition path value in values
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
	var cond string
	var conds []string
	if reqs == nil || len(reqs.Dependencies) == 0 {
		return
	}
	for _, r := range reqs.Dependencies {
		var hasTrue, hasFalse bool
		cond = string(r.Condition)
		// check for list
		if len(cond) > 0 {
			if strings.Contains(cond, ",") {
				conds = strings.Split(strings.TrimSpace(cond), ",")
			} else {
				conds = []string{strings.TrimSpace(cond)}
			}
			for _, c := range conds {
				if len(c) > 0 {
					// retrieve value
					vv, err := cvals.PathValue(c)
					if err == nil {
						// if not bool, warn
						if bv, ok := vv.(bool); ok {
							if bv {
								hasTrue = true
							} else {
								hasFalse = true
							}
						} else {
							log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name)
						}
					} else if _, ok := err.(ErrNoValue); !ok {
						// this is a real error
						log.Printf("Warning: PathValue returned error %v", err)

					}
					if vv != nil {
						// got first value, break loop
						break
					}
				}
			}
			if !hasTrue && hasFalse {
				r.Enabled = false
			} else if hasTrue {
				r.Enabled = true

			}
		}

	}

}

// ProcessRequirementsTags disables charts based on tags in values
func ProcessRequirementsTags(reqs *Requirements, cvals Values) {
	vt, err := cvals.Table("tags")
	if err != nil {
		return

	}
	if reqs == nil || len(reqs.Dependencies) == 0 {
		return
	}
	for _, r := range reqs.Dependencies {
		if len(r.Tags) > 0 {
			tags := r.Tags

			var hasTrue, hasFalse bool
			for _, k := range tags {
				if b, ok := vt[k]; ok {
					// if not bool, warn
					if bv, ok := b.(bool); ok {
						if bv {
							hasTrue = true
						} else {
							hasFalse = true
						}
					} else {
						log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name)
					}
				}
			}
			if !hasTrue && hasFalse {
				r.Enabled = false
			} else if hasTrue || !hasTrue && !hasFalse {
				r.Enabled = true

			}

		}
	}

}

// ProcessRequirementsEnabled removes disabled charts from dependencies
func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
	reqs, err := LoadRequirements(c)
	if err != nil {
		// if not just missing requirements file, return error
		if nerr, ok := err.(ErrNoRequirementsFile); !ok {
			return nerr
		}

		// no requirements to process
		return nil
	}
	// set all to true
	for _, lr := range reqs.Dependencies {
		lr.Enabled = true
	}
	cvals, err := CoalesceValues(c, v)
	if err != nil {
		return err
	}
	// flag dependencies as enabled/disabled
	ProcessRequirementsTags(reqs, cvals)
	ProcessRequirementsConditions(reqs, cvals)

	// make a map of charts to remove
	rm := map[string]bool{}
	for _, r := range reqs.Dependencies {
		if !r.Enabled {
			// remove disabled chart
			rm[r.Name] = true
		}
	}
	// don't keep disabled charts in new slice
	cd := []*chart.Chart{}
	copy(cd, c.Dependencies[:0])
	for _, n := range c.Dependencies {
		if _, ok := rm[n.Metadata.Name]; !ok {
			cd = append(cd, n)
		}

	}
	// recursively call self to process sub dependencies
	for _, t := range cd {
		err := ProcessRequirementsEnabled(t, v)
		// if its not just missing requirements file, return error
		if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil {
			return nerr
		}
	}
	c.Dependencies = cd

	return nil
}

// pathToMap creates a nested map given a YAML path in dot notation.
func pathToMap(path string, data map[string]interface{}) map[string]interface{} {
	ap := strings.Split(path, ".")
	if len(ap) == 0 {
		return nil
	}
	n := []map[string]interface{}{}
	// created nested map for each key, adding to slice
	for _, v := range ap {
		nm := make(map[string]interface{})
		nm[v] = make(map[string]interface{})
		n = append(n, nm)
	}
	// find the last key (map) and set our data
	for i, d := range n {
		for k := range d {
			z := i + 1
			if z == len(n) {
				n[i][k] = data
				break
			}
			n[i][k] = n[z]
		}
	}

	return n[0]
}

// getParents returns a slice of parent charts in reverse order.
func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart {
	if len(out) == 0 {
		out = []*chart.Chart{c}
	}
	for _, ch := range c.Dependencies {
		if len(ch.Dependencies) > 0 {
			out = append(out, ch)
			out = getParents(ch, out)
		}
	}

	return out
}

// processImportValues merges values from child to parent based on ImportValues field.
func processImportValues(c *chart.Chart, v *chart.Config) error {
	reqs, err := LoadRequirements(c)
	if err != nil {
		return err
	}
	cvals, err := CoalesceValues(c, v)
	if err != nil {
		return err
	}
	nv := v.GetValues()
	b := make(map[string]interface{})
	for kk, v3 := range nv {
		b[kk] = v3
	}
	for _, r := range reqs.Dependencies {
		if len(r.ImportValues) > 0 {
			var outiv []interface{}
			for _, riv := range r.ImportValues {
				switch iv := riv.(type) {
				case map[string]interface{}:
					nm := map[string]string{
						"child":  iv["child"].(string),
						"parent": iv["parent"].(string),
					}
					outiv = append(outiv, nm)
					s := r.Name + "." + nm["child"]
					vv, err := cvals.Table(s)
					if err != nil {
						log.Printf("Warning: ImportValues missing table %v", err)
						continue
					}
					if nm["parent"] == "." {
						coalesceTables(b, vv.AsMap())
					} else {

						vm := pathToMap(nm["parent"], vv.AsMap())
						coalesceTables(b, vm)
					}
				case string:
					nm := make(map[string]string)
					nm["child"] = "exports." + iv
					nm["parent"] = "."
					outiv = append(outiv, nm)
					s := r.Name + "." + nm["child"]
					vv, err := cvals.Table(s)
					if err != nil {
						log.Printf("Warning: ImportValues missing table %v", err)
						continue
					}
					coalesceTables(b, vv.AsMap())
				}
			}
			// set our formatted import values
			r.ImportValues = outiv
		}
	}
	cv, err := coalesceValues(c, b)
	if err != nil {
		return err
	}
	y, err := yaml.Marshal(cv)
	if err != nil {
		return err
	}
	bb := &chart.Config{Raw: string(y)}
	v = bb
	c.Values = bb

	return nil
}

// ProcessRequirementsImportValues imports specified chart values from child to parent.
func ProcessRequirementsImportValues(c *chart.Chart, v *chart.Config) error {
	pc := getParents(c, nil)
	for i := len(pc) - 1; i >= 0; i-- {
		processImportValues(pc[i], v)
	}

	return nil
}
<|endoftext|>


<commit_before>// +build linux

package clipboard

import (
	"context"
	"strings"

	"github.com/godbus/dbus"
)

func clearClipboardHistory(ctx context.Context) error {
	conn, err := dbus.SessionBus()
	if err != nil {
		return err
	}

	obj := conn.Object("org.kde.klipper", "/klipper")
	call := obj.Call("org.kde.klipper.klipper.clearClipboardHistory", 0)
	if call.Err != nil {
		if strings.HasPrefix(call.Err.Error(), "The name org.kde.klipper was not provided") {
			return nil
		}
		return call.Err
	}

	return nil
}
<commit_msg>Catch 'The name is not activatable' DBus error when unclipping (#1247)<commit_after>// +build linux

package clipboard

import (
	"context"
	"strings"

	"github.com/godbus/dbus"
)

func clearClipboardHistory(ctx context.Context) error {
	conn, err := dbus.SessionBus()
	if err != nil {
		return err
	}

	obj := conn.Object("org.kde.klipper", "/klipper")
	call := obj.Call("org.kde.klipper.klipper.clearClipboardHistory", 0)
	if call.Err != nil {
		if strings.HasPrefix(call.Err.Error(), "The name org.kde.klipper was not provided") {
			return nil
		}
		if strings.HasPrefix(call.Err.Error(), "The name is not activatable") {
			return nil
		}
		return call.Err
	}

	return nil
}
<|endoftext|>


<commit_before>package crasher

import (
	"os"
	foo "sync"
)

// Crasher just crashes.
func Crasher() {
	b := false
	bs := []bool{b}
	var f *os.File
	_, _ = foo.Once{}, foo.Once{}
	println(f)
	_ = false || bs[12345678987654321]
}
<commit_msg>test: use lighter std imports<commit_after>package crasher

import (
	"unsafe"
	foo "errors"
)

// Crasher just crashes.
func Crasher() {
	b := false
	bs := []bool{b}
	var p unsafe.Pointer
	_, _ = foo.New, foo.New("")
	println(p)
	_ = false || bs[12345678987654321]
}
<|endoftext|>


<commit_before>package logger

import (
	"fmt"
	stdlog "log"
	"os"
	"runtime"

	"github.com/op/go-logging"
)

var modules []string

func init() {
	modules = []string{}
}

// Default Log implementation.
type GoLogger struct {
	log *logging.Logger
}

func NewGoLog(name string) *GoLogger {
	logging.SetFormatter(logging.MustStringFormatter("[%{level:.8s}] - %{message}"))

	// Send log to stdout
	var logBackend = logging.NewLogBackend(os.Stderr, "", stdlog.LstdFlags|stdlog.Lshortfile)
	logBackend.Color = true

	// Send log to syslog
	var syslogBackend, err = logging.NewSyslogBackend("")
	if err != nil {
		panic(err)
	}

	logging.SetBackend(logBackend, syslogBackend)

	loggingLevel = getLoggingLevelFromConfig(name)

	// go-logging calls Reset() each time it is imported. So if this
	// pkg is imported in a library and then in a worker, the library
	// defaults back to DEBUG logging level. This fixes that by
	// re-setting the log level for already set modules.
	modules = append(modules, name)
	for _, mod := range modules {
		logging.SetLevel(loggingLevel, mod)
	}

	var goLog = &GoLogger{logging.MustGetLogger(name)}

	return goLog
}

func (g *GoLogger) Fatal(args ...interface{}) {
	g.log.Fatal(args...)
}

func (g *GoLogger) Panic(format string, args ...interface{}) {
	g.log.Panicf(format, args...)
}

func (g *GoLogger) Critical(format string, args ...interface{}) {
	g.log.Critical(format, args...)
}

func (g *GoLogger) Error(format string, args ...interface{}) {
	g.log.Error(format, args...)
}

func (g *GoLogger) Warning(format string, args ...interface{}) {
	g.log.Warning(format, args...)
}

func (g *GoLogger) Notice(format string, args ...interface{}) {
	g.log.Notice(format, args...)
}

func (g *GoLogger) Info(format string, args ...interface{}) {
	g.log.Info(format, args...)
}

func (g *GoLogger) Debug(format string, args ...interface{}) {
	g.log.Debug(format, args...)
}

func (g *GoLogger) Name() string {
	return g.log.Module
}

//----------------------------------------------------------
// Originally from koding/tools/log
//----------------------------------------------------------

func (g *GoLogger) RecoverAndLog() {
	if err := recover(); err != nil {
		g.Critical("Panicked %v", err)
	}
}

func (g *GoLogger) LogError(err interface{}, stackOffset int, additionalData ...interface{}) {
	data = append(data, fmt.Sprintln(err))
	data := make([]interface{}, 0)
	for i := 1 + stackOffset; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		name := "<unknown>"
		if fn := runtime.FuncForPC(pc); fn != nil {
			name = fn.Name()
		}
		data = append(data, fmt.Sprintf("at %s (%s:%d)\n", name, file, line))
	}
	data = append(data, additionalData...)
	g.Error("LogError %v", data)
}
<commit_msg>tools/logger:  append should be called later.<commit_after>package logger

import (
	"fmt"
	stdlog "log"
	"os"
	"runtime"

	"github.com/op/go-logging"
)

var modules []string

func init() {
	modules = []string{}
}

// Default Log implementation.
type GoLogger struct {
	log *logging.Logger
}

func NewGoLog(name string) *GoLogger {
	logging.SetFormatter(logging.MustStringFormatter("[%{level:.8s}] - %{message}"))

	// Send log to stdout
	var logBackend = logging.NewLogBackend(os.Stderr, "", stdlog.LstdFlags|stdlog.Lshortfile)
	logBackend.Color = true

	// Send log to syslog
	var syslogBackend, err = logging.NewSyslogBackend("")
	if err != nil {
		panic(err)
	}

	logging.SetBackend(logBackend, syslogBackend)

	loggingLevel = getLoggingLevelFromConfig(name)

	// go-logging calls Reset() each time it is imported. So if this
	// pkg is imported in a library and then in a worker, the library
	// defaults back to DEBUG logging level. This fixes that by
	// re-setting the log level for already set modules.
	modules = append(modules, name)
	for _, mod := range modules {
		logging.SetLevel(loggingLevel, mod)
	}

	var goLog = &GoLogger{logging.MustGetLogger(name)}

	return goLog
}

func (g *GoLogger) Fatal(args ...interface{}) {
	g.log.Fatal(args...)
}

func (g *GoLogger) Panic(format string, args ...interface{}) {
	g.log.Panicf(format, args...)
}

func (g *GoLogger) Critical(format string, args ...interface{}) {
	g.log.Critical(format, args...)
}

func (g *GoLogger) Error(format string, args ...interface{}) {
	g.log.Error(format, args...)
}

func (g *GoLogger) Warning(format string, args ...interface{}) {
	g.log.Warning(format, args...)
}

func (g *GoLogger) Notice(format string, args ...interface{}) {
	g.log.Notice(format, args...)
}

func (g *GoLogger) Info(format string, args ...interface{}) {
	g.log.Info(format, args...)
}

func (g *GoLogger) Debug(format string, args ...interface{}) {
	g.log.Debug(format, args...)
}

func (g *GoLogger) Name() string {
	return g.log.Module
}

//----------------------------------------------------------
// Originally from koding/tools/log
//----------------------------------------------------------

func (g *GoLogger) RecoverAndLog() {
	if err := recover(); err != nil {
		g.Critical("Panicked %v", err)
	}
}

func (g *GoLogger) LogError(err interface{}, stackOffset int, additionalData ...interface{}) {
	data := make([]interface{}, 0)
	data = append(data, fmt.Sprintln(err))
	for i := 1 + stackOffset; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		name := "<unknown>"
		if fn := runtime.FuncForPC(pc); fn != nil {
			name = fn.Name()
		}
		data = append(data, fmt.Sprintf("at %s (%s:%d)\n", name, file, line))
	}
	data = append(data, additionalData...)
	g.Error("LogError %v", data)
}
<|endoftext|>