<commit_before>package contractcourt

import (
	"encoding/binary"
	"io"

	"github.com/btcsuite/btcd/wire"
	"github.com/lightningnetwork/lnd/input"
	"github.com/lightningnetwork/lnd/lnwallet"
	"github.com/lightningnetwork/lnd/sweep"
)

const (
	// commitOutputConfTarget is the default confirmation target we'll use
	// for sweeps of commit outputs that belong to us.
	commitOutputConfTarget = 6
)

// commitSweepResolver is a resolver that will attempt to sweep the commitment
// output paying to us, in the case that the remote party broadcasts their
// version of the commitment transaction. We can sweep this output immediately,
// as it doesn't have a time-lock delay.
type commitSweepResolver struct {
	// commitResolution contains all data required to successfully sweep
	// this HTLC on-chain.
	commitResolution lnwallet.CommitOutputResolution

	// resolved reflects if the contract has been fully resolved or not.
	resolved bool

	// broadcastHeight is the height that the original contract was
	// broadcast to the main-chain at. We'll use this value to bound any
	// historical queries to the chain for spends/confirmations.
	broadcastHeight uint32

	// chanPoint is the channel point of the original contract.
	chanPoint wire.OutPoint

	ResolverKit
}

// ResolverKey returns an identifier which should be globally unique for this
// particular resolver within the chain the original contract resides within.
func (c *commitSweepResolver) ResolverKey() []byte {
	key := newResolverID(c.commitResolution.SelfOutPoint)
	return key[:]
}

// Resolve instructs the contract resolver to resolve the output on-chain. Once
// the output has been *fully* resolved, the function should return immediately
// with a nil ContractResolver value for the first return value.  In the case
// that the contract requires further resolution, then another resolve is
// returned.
//
// NOTE: This function MUST be run as a goroutine.
func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
	// If we're already resolved, then we can exit early.
	if c.resolved {
		return nil, nil
	}

	// First, we'll register for a notification once the commitment output
	// itself has been confirmed.
	//
	// TODO(roasbeef): instead sweep asap if remote commit? yeh
	commitTXID := c.commitResolution.SelfOutPoint.Hash
	sweepScript := c.commitResolution.SelfOutputSignDesc.Output.PkScript
	confNtfn, err := c.Notifier.RegisterConfirmationsNtfn(
		&commitTXID, sweepScript, 1, c.broadcastHeight,
	)
	if err != nil {
		return nil, err
	}

	log.Debugf("%T(%v): waiting for commit tx to confirm", c, c.chanPoint)

	select {
	case _, ok := <-confNtfn.Confirmed:
		if !ok {
			return nil, errResolverShuttingDown
		}

	case <-c.Quit:
		return nil, errResolverShuttingDown
	}

	// We're dealing with our commitment transaction if the delay on the
	// resolution isn't zero.
	isLocalCommitTx := c.commitResolution.MaturityDelay != 0

	if !isLocalCommitTx {
		// We'll craft an input with all the information required for
		// the sweeper to create a fully valid sweeping transaction to
		// recover these coins.
		inp := input.MakeBaseInput(
			&c.commitResolution.SelfOutPoint,
			input.CommitmentNoDelay,
			&c.commitResolution.SelfOutputSignDesc,
			c.broadcastHeight,
		)

		// With our input constructed, we'll now offer it to the
		// sweeper.
		log.Infof("%T(%v): sweeping commit output", c, c.chanPoint)

		feePref := sweep.FeePreference{ConfTarget: commitOutputConfTarget}
		resultChan, err := c.Sweeper.SweepInput(&inp, feePref)
		if err != nil {
			log.Errorf("%T(%v): unable to sweep input: %v",
				c, c.chanPoint, err)

			return nil, err
		}

		// Sweeper is going to join this input with other inputs if
		// possible and publish the sweep tx. When the sweep tx
		// confirms, it signals us through the result channel with the
		// outcome. Wait for this to happen.
		select {
		case sweepResult := <-resultChan:
			if sweepResult.Err != nil {
				log.Errorf("%T(%v): unable to sweep input: %v",
					c, c.chanPoint, sweepResult.Err)

				return nil, sweepResult.Err
			}

			log.Infof("ChannelPoint(%v) commit tx is fully resolved by "+
				"sweep tx: %v", c.chanPoint, sweepResult.Tx.TxHash())
		case <-c.Quit:
			return nil, errResolverShuttingDown
		}

		c.resolved = true
		return nil, c.Checkpoint(c)
	}

	// Otherwise we are dealing with a local commitment transaction and the
	// output we need to sweep has been sent to the nursery for incubation.
	// In this case, we'll wait until the commitment output has been spent.
	spendNtfn, err := c.Notifier.RegisterSpendNtfn(
		&c.commitResolution.SelfOutPoint,
		c.commitResolution.SelfOutputSignDesc.Output.PkScript,
		c.broadcastHeight,
	)
	if err != nil {
		return nil, err
	}

	log.Infof("%T(%v): waiting for commit output to be swept", c,
		c.chanPoint)

	var sweepTx *wire.MsgTx
	select {
	case commitSpend, ok := <-spendNtfn.Spend:
		if !ok {
			return nil, errResolverShuttingDown
		}

		// Once we detect the commitment output has been spent,
		// we'll extract the spending transaction itself, as we
		// now consider this to be our sweep transaction.
		sweepTx = commitSpend.SpendingTx

		log.Infof("%T(%v): commit output swept by txid=%v",
			c, c.chanPoint, sweepTx.TxHash())

		if err := c.Checkpoint(c); err != nil {
			log.Errorf("unable to Checkpoint: %v", err)
			return nil, err
		}
	case <-c.Quit:
		return nil, errResolverShuttingDown
	}

	log.Infof("%T(%v): waiting for commit sweep txid=%v conf", c, c.chanPoint,
		sweepTx.TxHash())

	// Now we'll wait until the sweeping transaction has been fully
	// confirmed.  Once it's confirmed, we can mark this contract resolved.
	sweepTXID := sweepTx.TxHash()
	sweepingScript := sweepTx.TxOut[0].PkScript
	confNtfn, err = c.Notifier.RegisterConfirmationsNtfn(
		&sweepTXID, sweepingScript, 1, c.broadcastHeight,
	)
	if err != nil {
		return nil, err
	}
	select {
	case confInfo, ok := <-confNtfn.Confirmed:
		if !ok {
			return nil, errResolverShuttingDown
		}

		log.Infof("ChannelPoint(%v) commit tx is fully resolved, at height: %v",
			c.chanPoint, confInfo.BlockHeight)

	case <-c.Quit:
		return nil, errResolverShuttingDown
	}

	// Once the transaction has received a sufficient number of
	// confirmations, we'll mark ourselves as fully resolved and exit.
	c.resolved = true
	return nil, c.Checkpoint(c)
}

// Stop signals the resolver to cancel any current resolution processes, and
// suspend.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) Stop() {
	close(c.Quit)
}

// IsResolved returns true if the stored state in the resolve is fully
// resolved. In this case the target output can be forgotten.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) IsResolved() bool {
	return c.resolved
}

// Encode writes an encoded version of the ContractResolver into the passed
// Writer.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) Encode(w io.Writer) error {
	if err := encodeCommitResolution(w, &c.commitResolution); err != nil {
		return err
	}

	if err := binary.Write(w, endian, c.resolved); err != nil {
		return err
	}
	if err := binary.Write(w, endian, c.broadcastHeight); err != nil {
		return err
	}
	if _, err := w.Write(c.chanPoint.Hash[:]); err != nil {
		return err
	}
	err := binary.Write(w, endian, c.chanPoint.Index)
	if err != nil {
		return err
	}

	// Previously a sweep tx was serialized at this point. Refactoring
	// removed this, but keep in mind that this data may still be present in
	// the database.

	return nil
}

// Decode attempts to decode an encoded ContractResolver from the passed Reader
// instance, returning an active ContractResolver instance.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) Decode(r io.Reader) error {
	if err := decodeCommitResolution(r, &c.commitResolution); err != nil {
		return err
	}

	if err := binary.Read(r, endian, &c.resolved); err != nil {
		return err
	}
	if err := binary.Read(r, endian, &c.broadcastHeight); err != nil {
		return err
	}
	_, err := io.ReadFull(r, c.chanPoint.Hash[:])
	if err != nil {
		return err
	}
	err = binary.Read(r, endian, &c.chanPoint.Index)
	if err != nil {
		return err
	}

	// Previously a sweep tx was deserialized at this point. Refactoring
	// removed this, but keep in mind that this data may still be present in
	// the database.

	return nil
}

// AttachResolverKit should be called once a resolved is successfully decoded
// from its stored format. This struct delivers a generic tool kit that
// resolvers need to complete their duty.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) AttachResolverKit(r ResolverKit) {
	c.ResolverKit = r
}

// A compile time assertion to ensure commitSweepResolver meets the
// ContractResolver interface.
var _ ContractResolver = (*commitSweepResolver)(nil)
<commit_msg>contractcourt: update the commitSweepResolver to be aware of tweakless commits<commit_after>package contractcourt

import (
	"encoding/binary"
	"io"

	"github.com/btcsuite/btcd/wire"
	"github.com/lightningnetwork/lnd/input"
	"github.com/lightningnetwork/lnd/lnwallet"
	"github.com/lightningnetwork/lnd/sweep"
)

const (
	// commitOutputConfTarget is the default confirmation target we'll use
	// for sweeps of commit outputs that belong to us.
	commitOutputConfTarget = 6
)

// commitSweepResolver is a resolver that will attempt to sweep the commitment
// output paying to us, in the case that the remote party broadcasts their
// version of the commitment transaction. We can sweep this output immediately,
// as it doesn't have a time-lock delay.
type commitSweepResolver struct {
	// commitResolution contains all data required to successfully sweep
	// this HTLC on-chain.
	commitResolution lnwallet.CommitOutputResolution

	// resolved reflects if the contract has been fully resolved or not.
	resolved bool

	// broadcastHeight is the height that the original contract was
	// broadcast to the main-chain at. We'll use this value to bound any
	// historical queries to the chain for spends/confirmations.
	broadcastHeight uint32

	// chanPoint is the channel point of the original contract.
	chanPoint wire.OutPoint

	ResolverKit
}

// ResolverKey returns an identifier which should be globally unique for this
// particular resolver within the chain the original contract resides within.
func (c *commitSweepResolver) ResolverKey() []byte {
	key := newResolverID(c.commitResolution.SelfOutPoint)
	return key[:]
}

// Resolve instructs the contract resolver to resolve the output on-chain. Once
// the output has been *fully* resolved, the function should return immediately
// with a nil ContractResolver value for the first return value.  In the case
// that the contract requires further resolution, then another resolve is
// returned.
//
// NOTE: This function MUST be run as a goroutine.
func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
	// If we're already resolved, then we can exit early.
	if c.resolved {
		return nil, nil
	}

	// First, we'll register for a notification once the commitment output
	// itself has been confirmed.
	//
	// TODO(roasbeef): instead sweep asap if remote commit? yeh
	commitTXID := c.commitResolution.SelfOutPoint.Hash
	sweepScript := c.commitResolution.SelfOutputSignDesc.Output.PkScript
	confNtfn, err := c.Notifier.RegisterConfirmationsNtfn(
		&commitTXID, sweepScript, 1, c.broadcastHeight,
	)
	if err != nil {
		return nil, err
	}

	log.Debugf("%T(%v): waiting for commit tx to confirm", c, c.chanPoint)

	select {
	case _, ok := <-confNtfn.Confirmed:
		if !ok {
			return nil, errResolverShuttingDown
		}

	case <-c.Quit:
		return nil, errResolverShuttingDown
	}

	// We're dealing with our commitment transaction if the delay on the
	// resolution isn't zero.
	isLocalCommitTx := c.commitResolution.MaturityDelay != 0

	if !isLocalCommitTx {
		// There're two types of commitments, those that have tweaks
		// for the remote key (us in this case), and those that don't.
		// We'll rely on the presence of the commitment tweak to to
		// discern which type of commitment this is.
		var witnessType input.WitnessType
		if c.commitResolution.SelfOutputSignDesc.SingleTweak == nil {
			witnessType = input.CommitSpendNoDelayTweakless
		} else {
			witnessType = input.CommitmentNoDelay
		}

		// We'll craft an input with all the information required for
		// the sweeper to create a fully valid sweeping transaction to
		// recover these coins.
		inp := input.MakeBaseInput(
			&c.commitResolution.SelfOutPoint,
			witnessType,
			&c.commitResolution.SelfOutputSignDesc,
			c.broadcastHeight,
		)

		// With our input constructed, we'll now offer it to the
		// sweeper.
		log.Infof("%T(%v): sweeping commit output", c, c.chanPoint)

		feePref := sweep.FeePreference{ConfTarget: commitOutputConfTarget}
		resultChan, err := c.Sweeper.SweepInput(&inp, feePref)
		if err != nil {
			log.Errorf("%T(%v): unable to sweep input: %v",
				c, c.chanPoint, err)

			return nil, err
		}

		// Sweeper is going to join this input with other inputs if
		// possible and publish the sweep tx. When the sweep tx
		// confirms, it signals us through the result channel with the
		// outcome. Wait for this to happen.
		select {
		case sweepResult := <-resultChan:
			if sweepResult.Err != nil {
				log.Errorf("%T(%v): unable to sweep input: %v",
					c, c.chanPoint, sweepResult.Err)

				return nil, sweepResult.Err
			}

			log.Infof("ChannelPoint(%v) commit tx is fully resolved by "+
				"sweep tx: %v", c.chanPoint, sweepResult.Tx.TxHash())
		case <-c.Quit:
			return nil, errResolverShuttingDown
		}

		c.resolved = true
		return nil, c.Checkpoint(c)
	}

	// Otherwise we are dealing with a local commitment transaction and the
	// output we need to sweep has been sent to the nursery for incubation.
	// In this case, we'll wait until the commitment output has been spent.
	spendNtfn, err := c.Notifier.RegisterSpendNtfn(
		&c.commitResolution.SelfOutPoint,
		c.commitResolution.SelfOutputSignDesc.Output.PkScript,
		c.broadcastHeight,
	)
	if err != nil {
		return nil, err
	}

	log.Infof("%T(%v): waiting for commit output to be swept", c,
		c.chanPoint)

	var sweepTx *wire.MsgTx
	select {
	case commitSpend, ok := <-spendNtfn.Spend:
		if !ok {
			return nil, errResolverShuttingDown
		}

		// Once we detect the commitment output has been spent,
		// we'll extract the spending transaction itself, as we
		// now consider this to be our sweep transaction.
		sweepTx = commitSpend.SpendingTx

		log.Infof("%T(%v): commit output swept by txid=%v",
			c, c.chanPoint, sweepTx.TxHash())

		if err := c.Checkpoint(c); err != nil {
			log.Errorf("unable to Checkpoint: %v", err)
			return nil, err
		}
	case <-c.Quit:
		return nil, errResolverShuttingDown
	}

	log.Infof("%T(%v): waiting for commit sweep txid=%v conf", c, c.chanPoint,
		sweepTx.TxHash())

	// Now we'll wait until the sweeping transaction has been fully
	// confirmed.  Once it's confirmed, we can mark this contract resolved.
	sweepTXID := sweepTx.TxHash()
	sweepingScript := sweepTx.TxOut[0].PkScript
	confNtfn, err = c.Notifier.RegisterConfirmationsNtfn(
		&sweepTXID, sweepingScript, 1, c.broadcastHeight,
	)
	if err != nil {
		return nil, err
	}
	select {
	case confInfo, ok := <-confNtfn.Confirmed:
		if !ok {
			return nil, errResolverShuttingDown
		}

		log.Infof("ChannelPoint(%v) commit tx is fully resolved, at height: %v",
			c.chanPoint, confInfo.BlockHeight)

	case <-c.Quit:
		return nil, errResolverShuttingDown
	}

	// Once the transaction has received a sufficient number of
	// confirmations, we'll mark ourselves as fully resolved and exit.
	c.resolved = true
	return nil, c.Checkpoint(c)
}

// Stop signals the resolver to cancel any current resolution processes, and
// suspend.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) Stop() {
	close(c.Quit)
}

// IsResolved returns true if the stored state in the resolve is fully
// resolved. In this case the target output can be forgotten.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) IsResolved() bool {
	return c.resolved
}

// Encode writes an encoded version of the ContractResolver into the passed
// Writer.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) Encode(w io.Writer) error {
	if err := encodeCommitResolution(w, &c.commitResolution); err != nil {
		return err
	}

	if err := binary.Write(w, endian, c.resolved); err != nil {
		return err
	}
	if err := binary.Write(w, endian, c.broadcastHeight); err != nil {
		return err
	}
	if _, err := w.Write(c.chanPoint.Hash[:]); err != nil {
		return err
	}
	err := binary.Write(w, endian, c.chanPoint.Index)
	if err != nil {
		return err
	}

	// Previously a sweep tx was serialized at this point. Refactoring
	// removed this, but keep in mind that this data may still be present in
	// the database.

	return nil
}

// Decode attempts to decode an encoded ContractResolver from the passed Reader
// instance, returning an active ContractResolver instance.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) Decode(r io.Reader) error {
	if err := decodeCommitResolution(r, &c.commitResolution); err != nil {
		return err
	}

	if err := binary.Read(r, endian, &c.resolved); err != nil {
		return err
	}
	if err := binary.Read(r, endian, &c.broadcastHeight); err != nil {
		return err
	}
	_, err := io.ReadFull(r, c.chanPoint.Hash[:])
	if err != nil {
		return err
	}
	err = binary.Read(r, endian, &c.chanPoint.Index)
	if err != nil {
		return err
	}

	// Previously a sweep tx was deserialized at this point. Refactoring
	// removed this, but keep in mind that this data may still be present in
	// the database.

	return nil
}

// AttachResolverKit should be called once a resolved is successfully decoded
// from its stored format. This struct delivers a generic tool kit that
// resolvers need to complete their duty.
//
// NOTE: Part of the ContractResolver interface.
func (c *commitSweepResolver) AttachResolverKit(r ResolverKit) {
	c.ResolverKit = r
}

// A compile time assertion to ensure commitSweepResolver meets the
// ContractResolver interface.
var _ ContractResolver = (*commitSweepResolver)(nil)
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"os"
	"sort"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise/bitrise"
	"github.com/bitrise-io/bitrise/configs"
	"github.com/bitrise-io/bitrise/models"
	"github.com/bitrise-io/bitrise/version"
	envmanModels "github.com/bitrise-io/envman/models"
	"github.com/bitrise-io/go-utils/colorstring"
	"github.com/bitrise-io/go-utils/pointers"
	"github.com/urfave/cli"
)

const (
	// DefaultBitriseConfigFileName ...
	DefaultBitriseConfigFileName = "bitrise.yml"
	// DefaultSecretsFileName ...
	DefaultSecretsFileName = ".bitrise.secrets.yml"

	depManagerBrew      = "brew"
	depManagerTryCheck  = "_"
	secretFilteringFlag = "secret-filtering"
)

var runCommand = cli.Command{
	Name:    "run",
	Aliases: []string{"r"},
	Usage:   "Runs a specified Workflow.",
	Action:  run,
	Flags: []cli.Flag{
		// cli params
		cli.StringFlag{Name: WorkflowKey, Usage: "workflow id to run."},
		cli.StringFlag{Name: ConfigKey + ", " + configShortKey, Usage: "Path where the workflow config file is located."},
		cli.StringFlag{Name: InventoryKey + ", " + inventoryShortKey, Usage: "Path of the inventory file."},
		cli.BoolFlag{Name: secretFilteringFlag, Usage: "Hide secret values from the log."},

		// cli params used in CI mode
		cli.StringFlag{Name: JSONParamsKey, Usage: "Specify command flags with json string-string hash."},
		cli.StringFlag{Name: JSONParamsBase64Key, Usage: "Specify command flags with base64 encoded json string-string hash."},

		// deprecated
		flPath,

		// should deprecate
		cli.StringFlag{Name: ConfigBase64Key, Usage: "base64 encoded config data."},
		cli.StringFlag{Name: InventoryBase64Key, Usage: "base64 encoded inventory data."},
	},
}

func printAboutUtilityWorkflowsText() {
	fmt.Println("Note about utility workflows:")
	fmt.Println(" Utility workflow names start with '_' (example: _my_utility_workflow).")
	fmt.Println(" These workflows can't be triggered directly, but can be used by other workflows")
	fmt.Println(" in the before_run and after_run lists.")
}

func printAvailableWorkflows(config models.BitriseDataModel) {
	workflowNames := []string{}
	utilityWorkflowNames := []string{}

	for wfName := range config.Workflows {
		if strings.HasPrefix(wfName, "_") {
			utilityWorkflowNames = append(utilityWorkflowNames, wfName)
		} else {
			workflowNames = append(workflowNames, wfName)
		}
	}
	sort.Strings(workflowNames)
	sort.Strings(utilityWorkflowNames)

	if len(workflowNames) > 0 {
		fmt.Println("The following workflows are available:")
		for _, wfName := range workflowNames {
			fmt.Println(" * " + wfName)
		}

		fmt.Println()
		fmt.Println("You can run a selected workflow with:")
		fmt.Println("$ bitrise run WORKFLOW-ID")
		fmt.Println()
	} else {
		fmt.Println("No workflows are available!")
	}

	if len(utilityWorkflowNames) > 0 {
		fmt.Println()
		fmt.Println("The following utility workflows are defined:")
		for _, wfName := range utilityWorkflowNames {
			fmt.Println(" * " + wfName)
		}

		fmt.Println()
		printAboutUtilityWorkflowsText()
		fmt.Println()
	}
}

func runAndExit(bitriseConfig models.BitriseDataModel, inventoryEnvironments []envmanModels.EnvironmentItemModel, workflowToRunID string) {
	if workflowToRunID == "" {
		log.Fatal("No workflow id specified")
	}

	if err := bitrise.RunSetupIfNeeded(version.VERSION, false); err != nil {
		log.Fatalf("Setup failed, error: %s", err)
	}

	startTime := time.Now()

	// Run selected configuration
	if buildRunResults, err := runWorkflowWithConfiguration(startTime, workflowToRunID, bitriseConfig, inventoryEnvironments); err != nil {
		log.Fatalf("Failed to run workflow, error: %s", err)
	} else if buildRunResults.IsBuildFailed() {
		os.Exit(1)
	}
	if err := checkUpdate(); err != nil {
		log.Warnf("failed to check for update, error: %s", err)
	}
	os.Exit(0)
}

func printRunningWorkflow(bitriseConfig models.BitriseDataModel, targetWorkflowToRunID string) {
	beforeWorkflowIDs := bitriseConfig.Workflows[targetWorkflowToRunID].BeforeRun
	afterWorkflowIDs := bitriseConfig.Workflows[targetWorkflowToRunID].AfterRun
	workflowsString := ""
	if len(beforeWorkflowIDs) == 0 && len(afterWorkflowIDs) == 0 {
		workflowsString = "Running workflow: "
	} else {
		workflowsString = "Running workflows: "
	}

	if len(beforeWorkflowIDs) != 0 {
		for _, workflowName := range beforeWorkflowIDs {
			workflowsString = workflowsString + workflowName + " --> "
		}
	}

	workflowsString = workflowsString + colorstring.Green(targetWorkflowToRunID)

	if len(afterWorkflowIDs) != 0 {
		for _, workflowName := range afterWorkflowIDs {
			workflowsString = workflowsString + " --> " + workflowName
		}
	}

	log.Infof(workflowsString)
}

func run(c *cli.Context) error {
	PrintBitriseHeaderASCIIArt(version.VERSION)

	//
	// Expand cli.Context
	var prGlobalFlagPtr *bool
	if c.GlobalIsSet(PRKey) {
		prGlobalFlagPtr = pointers.NewBoolPtr(c.GlobalBool(PRKey))
	}

	var ciGlobalFlagPtr *bool
	if c.GlobalIsSet(CIKey) {
		ciGlobalFlagPtr = pointers.NewBoolPtr(c.GlobalBool(CIKey))
	}

	var secretFiltering *bool
	if c.IsSet(secretFilteringFlag) {
		secretFiltering = pointers.NewBoolPtr(c.Bool(secretFilteringFlag))
	} else if os.Getenv(configs.IsSecretFilteringKey) == "true" {
		secretFiltering = pointers.NewBoolPtr(true)
	} else if os.Getenv(configs.IsSecretFilteringKey) == "false" {
		secretFiltering = pointers.NewBoolPtr(false)
	}

	workflowToRunID := c.String(WorkflowKey)
	if workflowToRunID == "" && len(c.Args()) > 0 {
		workflowToRunID = c.Args()[0]
	}

	bitriseConfigBase64Data := c.String(ConfigBase64Key)
	bitriseConfigPath := c.String(ConfigKey)
	deprecatedBitriseConfigPath := c.String(PathKey)
	if bitriseConfigPath == "" && deprecatedBitriseConfigPath != "" {
		log.Warn("'path' key is deprecated, use 'config' instead!")
		bitriseConfigPath = deprecatedBitriseConfigPath
	}

	inventoryBase64Data := c.String(InventoryBase64Key)
	inventoryPath := c.String(InventoryKey)

	jsonParams := c.String(JSONParamsKey)
	jsonParamsBase64 := c.String(JSONParamsBase64Key)

	runParams, err := parseRunParams(
		workflowToRunID,
		bitriseConfigPath, bitriseConfigBase64Data,
		inventoryPath, inventoryBase64Data,
		jsonParams, jsonParamsBase64)
	if err != nil {
		return fmt.Errorf("Failed to parse command params, error: %s", err)
	}
	//

	// Inventory validation
	inventoryEnvironments, err := CreateInventoryFromCLIParams(runParams.InventoryBase64Data, runParams.InventoryPath)
	if err != nil {
		log.Fatalf("Failed to create inventory, error: %s", err)
	}

	// Config validation
	bitriseConfig, warnings, err := CreateBitriseConfigFromCLIParams(runParams.BitriseConfigBase64Data, runParams.BitriseConfigPath)
	for _, warning := range warnings {
		log.Warnf("warning: %s", warning)
	}
	if err != nil {
		log.Fatalf("Failed to create bitrise config, error: %s", err)
	}

	// Workflow id validation
	if runParams.WorkflowToRunID == "" {
		// no workflow specified
		//  list all the available ones and then exit
		log.Error("No workfow specified!")
		fmt.Println()
		printAvailableWorkflows(bitriseConfig)
		os.Exit(1)
	}
	if strings.HasPrefix(runParams.WorkflowToRunID, "_") {
		// util workflow specified
		//  print about util workflows and then exit
		log.Error("Utility workflows can't be triggered directly")
		fmt.Println()
		printAboutUtilityWorkflowsText()
		os.Exit(1)
	}
	//

	//
	// Main
	enabledFiltering, err := isSecretFiltering(secretFiltering, inventoryEnvironments)
	if err != nil {
		log.Fatalf("Failed to check Secret Filtering mode, error: %s", err)
	}

	if err := registerSecretFiltering(enabledFiltering); err != nil {
		log.Fatalf("Failed to register Secret Filtering mode, error: %s", err)
	}

	isPRMode, err := isPRMode(prGlobalFlagPtr, inventoryEnvironments)
	if err != nil {
		log.Fatalf("Failed to check PR mode, error: %s", err)
	}

	if err := registerPrMode(isPRMode); err != nil {
		log.Fatalf("Failed to register PR mode, error: %s", err)
	}

	isCIMode, err := isCIMode(ciGlobalFlagPtr, inventoryEnvironments)
	if err != nil {
		log.Fatalf("Failed to check CI mode, error: %s", err)
	}

	if err := registerCIMode(isCIMode); err != nil {
		log.Fatalf("Failed to register CI mode, error: %s", err)
	}

	printRunningWorkflow(bitriseConfig, runParams.WorkflowToRunID)

	runAndExit(bitriseConfig, inventoryEnvironments, runParams.WorkflowToRunID)
	//
	return nil
}
<commit_msg>Fix typo (#724)<commit_after>package cli

import (
	"fmt"
	"os"
	"sort"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise/bitrise"
	"github.com/bitrise-io/bitrise/configs"
	"github.com/bitrise-io/bitrise/models"
	"github.com/bitrise-io/bitrise/version"
	envmanModels "github.com/bitrise-io/envman/models"
	"github.com/bitrise-io/go-utils/colorstring"
	"github.com/bitrise-io/go-utils/pointers"
	"github.com/urfave/cli"
)

const (
	// DefaultBitriseConfigFileName ...
	DefaultBitriseConfigFileName = "bitrise.yml"
	// DefaultSecretsFileName ...
	DefaultSecretsFileName = ".bitrise.secrets.yml"

	depManagerBrew      = "brew"
	depManagerTryCheck  = "_"
	secretFilteringFlag = "secret-filtering"
)

var runCommand = cli.Command{
	Name:    "run",
	Aliases: []string{"r"},
	Usage:   "Runs a specified Workflow.",
	Action:  run,
	Flags: []cli.Flag{
		// cli params
		cli.StringFlag{Name: WorkflowKey, Usage: "workflow id to run."},
		cli.StringFlag{Name: ConfigKey + ", " + configShortKey, Usage: "Path where the workflow config file is located."},
		cli.StringFlag{Name: InventoryKey + ", " + inventoryShortKey, Usage: "Path of the inventory file."},
		cli.BoolFlag{Name: secretFilteringFlag, Usage: "Hide secret values from the log."},

		// cli params used in CI mode
		cli.StringFlag{Name: JSONParamsKey, Usage: "Specify command flags with json string-string hash."},
		cli.StringFlag{Name: JSONParamsBase64Key, Usage: "Specify command flags with base64 encoded json string-string hash."},

		// deprecated
		flPath,

		// should deprecate
		cli.StringFlag{Name: ConfigBase64Key, Usage: "base64 encoded config data."},
		cli.StringFlag{Name: InventoryBase64Key, Usage: "base64 encoded inventory data."},
	},
}

func printAboutUtilityWorkflowsText() {
	fmt.Println("Note about utility workflows:")
	fmt.Println(" Utility workflow names start with '_' (example: _my_utility_workflow).")
	fmt.Println(" These workflows can't be triggered directly, but can be used by other workflows")
	fmt.Println(" in the before_run and after_run lists.")
}

func printAvailableWorkflows(config models.BitriseDataModel) {
	workflowNames := []string{}
	utilityWorkflowNames := []string{}

	for wfName := range config.Workflows {
		if strings.HasPrefix(wfName, "_") {
			utilityWorkflowNames = append(utilityWorkflowNames, wfName)
		} else {
			workflowNames = append(workflowNames, wfName)
		}
	}
	sort.Strings(workflowNames)
	sort.Strings(utilityWorkflowNames)

	if len(workflowNames) > 0 {
		fmt.Println("The following workflows are available:")
		for _, wfName := range workflowNames {
			fmt.Println(" * " + wfName)
		}

		fmt.Println()
		fmt.Println("You can run a selected workflow with:")
		fmt.Println("$ bitrise run WORKFLOW-ID")
		fmt.Println()
	} else {
		fmt.Println("No workflows are available!")
	}

	if len(utilityWorkflowNames) > 0 {
		fmt.Println()
		fmt.Println("The following utility workflows are defined:")
		for _, wfName := range utilityWorkflowNames {
			fmt.Println(" * " + wfName)
		}

		fmt.Println()
		printAboutUtilityWorkflowsText()
		fmt.Println()
	}
}

func runAndExit(bitriseConfig models.BitriseDataModel, inventoryEnvironments []envmanModels.EnvironmentItemModel, workflowToRunID string) {
	if workflowToRunID == "" {
		log.Fatal("No workflow id specified")
	}

	if err := bitrise.RunSetupIfNeeded(version.VERSION, false); err != nil {
		log.Fatalf("Setup failed, error: %s", err)
	}

	startTime := time.Now()

	// Run selected configuration
	if buildRunResults, err := runWorkflowWithConfiguration(startTime, workflowToRunID, bitriseConfig, inventoryEnvironments); err != nil {
		log.Fatalf("Failed to run workflow, error: %s", err)
	} else if buildRunResults.IsBuildFailed() {
		os.Exit(1)
	}
	if err := checkUpdate(); err != nil {
		log.Warnf("failed to check for update, error: %s", err)
	}
	os.Exit(0)
}

func printRunningWorkflow(bitriseConfig models.BitriseDataModel, targetWorkflowToRunID string) {
	beforeWorkflowIDs := bitriseConfig.Workflows[targetWorkflowToRunID].BeforeRun
	afterWorkflowIDs := bitriseConfig.Workflows[targetWorkflowToRunID].AfterRun
	workflowsString := ""
	if len(beforeWorkflowIDs) == 0 && len(afterWorkflowIDs) == 0 {
		workflowsString = "Running workflow: "
	} else {
		workflowsString = "Running workflows: "
	}

	if len(beforeWorkflowIDs) != 0 {
		for _, workflowName := range beforeWorkflowIDs {
			workflowsString = workflowsString + workflowName + " --> "
		}
	}

	workflowsString = workflowsString + colorstring.Green(targetWorkflowToRunID)

	if len(afterWorkflowIDs) != 0 {
		for _, workflowName := range afterWorkflowIDs {
			workflowsString = workflowsString + " --> " + workflowName
		}
	}

	log.Infof(workflowsString)
}

func run(c *cli.Context) error {
	PrintBitriseHeaderASCIIArt(version.VERSION)

	//
	// Expand cli.Context
	var prGlobalFlagPtr *bool
	if c.GlobalIsSet(PRKey) {
		prGlobalFlagPtr = pointers.NewBoolPtr(c.GlobalBool(PRKey))
	}

	var ciGlobalFlagPtr *bool
	if c.GlobalIsSet(CIKey) {
		ciGlobalFlagPtr = pointers.NewBoolPtr(c.GlobalBool(CIKey))
	}

	var secretFiltering *bool
	if c.IsSet(secretFilteringFlag) {
		secretFiltering = pointers.NewBoolPtr(c.Bool(secretFilteringFlag))
	} else if os.Getenv(configs.IsSecretFilteringKey) == "true" {
		secretFiltering = pointers.NewBoolPtr(true)
	} else if os.Getenv(configs.IsSecretFilteringKey) == "false" {
		secretFiltering = pointers.NewBoolPtr(false)
	}

	workflowToRunID := c.String(WorkflowKey)
	if workflowToRunID == "" && len(c.Args()) > 0 {
		workflowToRunID = c.Args()[0]
	}

	bitriseConfigBase64Data := c.String(ConfigBase64Key)
	bitriseConfigPath := c.String(ConfigKey)
	deprecatedBitriseConfigPath := c.String(PathKey)
	if bitriseConfigPath == "" && deprecatedBitriseConfigPath != "" {
		log.Warn("'path' key is deprecated, use 'config' instead!")
		bitriseConfigPath = deprecatedBitriseConfigPath
	}

	inventoryBase64Data := c.String(InventoryBase64Key)
	inventoryPath := c.String(InventoryKey)

	jsonParams := c.String(JSONParamsKey)
	jsonParamsBase64 := c.String(JSONParamsBase64Key)

	runParams, err := parseRunParams(
		workflowToRunID,
		bitriseConfigPath, bitriseConfigBase64Data,
		inventoryPath, inventoryBase64Data,
		jsonParams, jsonParamsBase64)
	if err != nil {
		return fmt.Errorf("Failed to parse command params, error: %s", err)
	}
	//

	// Inventory validation
	inventoryEnvironments, err := CreateInventoryFromCLIParams(runParams.InventoryBase64Data, runParams.InventoryPath)
	if err != nil {
		log.Fatalf("Failed to create inventory, error: %s", err)
	}

	// Config validation
	bitriseConfig, warnings, err := CreateBitriseConfigFromCLIParams(runParams.BitriseConfigBase64Data, runParams.BitriseConfigPath)
	for _, warning := range warnings {
		log.Warnf("warning: %s", warning)
	}
	if err != nil {
		log.Fatalf("Failed to create bitrise config, error: %s", err)
	}

	// Workflow id validation
	if runParams.WorkflowToRunID == "" {
		// no workflow specified
		//  list all the available ones and then exit
		log.Error("No workflow specified!")
		fmt.Println()
		printAvailableWorkflows(bitriseConfig)
		os.Exit(1)
	}
	if strings.HasPrefix(runParams.WorkflowToRunID, "_") {
		// util workflow specified
		//  print about util workflows and then exit
		log.Error("Utility workflows can't be triggered directly")
		fmt.Println()
		printAboutUtilityWorkflowsText()
		os.Exit(1)
	}
	//

	//
	// Main
	enabledFiltering, err := isSecretFiltering(secretFiltering, inventoryEnvironments)
	if err != nil {
		log.Fatalf("Failed to check Secret Filtering mode, error: %s", err)
	}

	if err := registerSecretFiltering(enabledFiltering); err != nil {
		log.Fatalf("Failed to register Secret Filtering mode, error: %s", err)
	}

	isPRMode, err := isPRMode(prGlobalFlagPtr, inventoryEnvironments)
	if err != nil {
		log.Fatalf("Failed to check PR mode, error: %s", err)
	}

	if err := registerPrMode(isPRMode); err != nil {
		log.Fatalf("Failed to register PR mode, error: %s", err)
	}

	isCIMode, err := isCIMode(ciGlobalFlagPtr, inventoryEnvironments)
	if err != nil {
		log.Fatalf("Failed to check CI mode, error: %s", err)
	}

	if err := registerCIMode(isCIMode); err != nil {
		log.Fatalf("Failed to register CI mode, error: %s", err)
	}

	printRunningWorkflow(bitriseConfig, runParams.WorkflowToRunID)

	runAndExit(bitriseConfig, inventoryEnvironments, runParams.WorkflowToRunID)
	//
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package maas

import (
	"io/ioutil"

	gc "launchpad.net/gocheck"
	"launchpad.net/goyaml"

	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/testing"
	jc "launchpad.net/juju-core/testing/checkers"
	"launchpad.net/juju-core/utils"
)

type EnvironProviderSuite struct {
	providerSuite
}

var _ = gc.Suite(&EnvironProviderSuite{})

func (suite *EnvironProviderSuite) TestSecretAttrsReturnsSensitiveMAASAttributes(c *gc.C) {
	testJujuHome := c.MkDir()
	defer config.SetJujuHome(config.SetJujuHome(testJujuHome))
	const oauth = "aa:bb:cc"
	attrs := testing.FakeConfig().Merge(testing.Attrs{
		"type":        "maas",
		"maas-oauth":  oauth,
		"maas-server": "http://maas.testing.invalid/maas/",
	})
	config, err := config.New(config.NoDefaults, attrs)
	c.Assert(err, gc.IsNil)

	secretAttrs, err := suite.makeEnviron().Provider().SecretAttrs(config)
	c.Assert(err, gc.IsNil)

	expectedAttrs := map[string]string{"maas-oauth": oauth}
	c.Check(secretAttrs, gc.DeepEquals, expectedAttrs)
}

func (suite *EnvironProviderSuite) TestUnknownAttrsContainEnvironmentUUID(c *gc.C) {
	defer config.SetJujuHome(config.SetJujuHome(testJujuHome))
	attrs := testing.FakeConfig().Merge(testing.Attrs{
		"type":        "maas",
		"maas-oauth":  "aa:bb:cc",
		"maas-server": "http://maas.testing.invalid/maas/",
	})
	config, err := config.New(config.NoDefaults, attrs)
	c.Assert(err, gc.IsNil)

	environ, err := suite.makeEnviron().Provider().Prepare(config)
	c.Assert(err, gc.IsNil)

	preparedConfig := environ.Config()
	unknownAttrs := preparedConfig.UnknownAttrs()

	uuid, ok := unknownAttrs["maas-agent-name"]

	c.Assert(ok, jc.IsTrue)
	c.Assert(uuid, jc.Satisfies, utils.IsValidUUIDString)
}

func (suite *EnvironProviderSuite) TestAgentNameShouldNotBeSetByHand(c *gc.C) {
	testJujuHome := c.MkDir()
	defer config.SetJujuHome(config.SetJujuHome(testJujuHome))
	attrs := testing.FakeConfig().Merge(testing.Attrs{
		"type":            "maas",
		"maas-oauth":      "aa:bb:cc",
		"maas-server":     "http://maas.testing.invalid/maas/",
		"maas-agent-name": "foobar",
	})
	config, err := config.New(config.NoDefaults, attrs)
	c.Assert(err, gc.IsNil)

	_, err = suite.makeEnviron().Provider().Prepare(config)
	c.Assert(err, gc.Equals, errAgentNameAlreadySet)
}

// create a temporary file with the given content.  The file will be cleaned
// up at the end of the test calling this method.
func createTempFile(c *gc.C, content []byte) string {
	file, err := ioutil.TempFile(c.MkDir(), "")
	c.Assert(err, gc.IsNil)
	filename := file.Name()
	err = ioutil.WriteFile(filename, content, 0644)
	c.Assert(err, gc.IsNil)
	return filename
}

// PublicAddress and PrivateAddress return the hostname of the machine read
// from the file _MAASInstanceFilename.
func (suite *EnvironProviderSuite) TestPrivatePublicAddressReadsHostnameFromMachineFile(c *gc.C) {
	hostname := "myhostname"
	info := machineInfo{hostname}
	yaml, err := goyaml.Marshal(info)
	c.Assert(err, gc.IsNil)
	// Create a temporary file to act as the file where the instanceID
	// is stored.
	filename := createTempFile(c, yaml)
	// "Monkey patch" the value of _MAASInstanceFilename with the path
	// to the temporary file.
	old_MAASInstanceFilename := _MAASInstanceFilename
	_MAASInstanceFilename = filename
	defer func() { _MAASInstanceFilename = old_MAASInstanceFilename }()

	provider := suite.makeEnviron().Provider()
	publicAddress, err := provider.PublicAddress()
	c.Assert(err, gc.IsNil)
	c.Check(publicAddress, gc.Equals, hostname)
	privateAddress, err := provider.PrivateAddress()
	c.Assert(err, gc.IsNil)
	c.Check(privateAddress, gc.Equals, hostname)
}

func (suite *EnvironProviderSuite) TestOpenReturnsNilInterfaceUponFailure(c *gc.C) {
	testJujuHome := c.MkDir()
	defer config.SetJujuHome(config.SetJujuHome(testJujuHome))
	const oauth = "wrongly-formatted-oauth-string"
	attrs := testing.FakeConfig().Merge(testing.Attrs{
		"type":        "maas",
		"maas-oauth":  oauth,
		"maas-server": "http://maas.testing.invalid/maas/",
	})
	config, err := config.New(config.NoDefaults, attrs)
	c.Assert(err, gc.IsNil)
	env, err := suite.makeEnviron().Provider().Open(config)
	// When Open() fails (i.e. returns a non-nil error), it returns an
	// environs.Environ interface object with a nil value and a nil
	// type.
	c.Check(env, gc.Equals, nil)
	c.Check(err, gc.ErrorMatches, ".*malformed maas-oauth.*")
}
<commit_msg>provider/maas: fix merge problems<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package maas

import (
	"io/ioutil"

	gc "launchpad.net/gocheck"
	"launchpad.net/goyaml"

	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/testing"
	jc "launchpad.net/juju-core/testing/checkers"
	"launchpad.net/juju-core/utils"
)

type EnvironProviderSuite struct {
	providerSuite
}

var _ = gc.Suite(&EnvironProviderSuite{})

func (suite *EnvironProviderSuite) TestSecretAttrsReturnsSensitiveMAASAttributes(c *gc.C) {
	testJujuHome := c.MkDir()
	defer config.SetJujuHome(config.SetJujuHome(testJujuHome))
	const oauth = "aa:bb:cc"
	attrs := testing.FakeConfig().Merge(testing.Attrs{
		"type":        "maas",
		"maas-oauth":  oauth,
		"maas-server": "http://maas.testing.invalid/maas/",
	})
	config, err := config.New(config.NoDefaults, attrs)
	c.Assert(err, gc.IsNil)

	secretAttrs, err := suite.makeEnviron().Provider().SecretAttrs(config)
	c.Assert(err, gc.IsNil)

	expectedAttrs := map[string]string{"maas-oauth": oauth}
	c.Check(secretAttrs, gc.DeepEquals, expectedAttrs)
}

func (suite *EnvironProviderSuite) TestUnknownAttrsContainAgentName(c *gc.C) {
	testJujuHome := c.MkDir()
	defer config.SetJujuHome(config.SetJujuHome(testJujuHome))
	attrs := testing.FakeConfig().Merge(testing.Attrs{
		"type":        "maas",
		"maas-oauth":  "aa:bb:cc",
		"maas-server": "http://maas.testing.invalid/maas/",
	})
	config, err := config.New(config.NoDefaults, attrs)
	c.Assert(err, gc.IsNil)

	environ, err := suite.makeEnviron().Provider().Prepare(config)
	c.Assert(err, gc.IsNil)

	preparedConfig := environ.Config()
	unknownAttrs := preparedConfig.UnknownAttrs()

	uuid, ok := unknownAttrs["maas-agent-name"]

	c.Assert(ok, jc.IsTrue)
	c.Assert(uuid, jc.Satisfies, utils.IsValidUUIDString)
}

func (suite *EnvironProviderSuite) TestAgentNameShouldNotBeSetByHand(c *gc.C) {
	testJujuHome := c.MkDir()
	defer config.SetJujuHome(config.SetJujuHome(testJujuHome))
	attrs := testing.FakeConfig().Merge(testing.Attrs{
		"type":            "maas",
		"maas-oauth":      "aa:bb:cc",
		"maas-server":     "http://maas.testing.invalid/maas/",
		"maas-agent-name": "foobar",
	})
	config, err := config.New(config.NoDefaults, attrs)
	c.Assert(err, gc.IsNil)

	_, err = suite.makeEnviron().Provider().Prepare(config)
	c.Assert(err, gc.Equals, errAgentNameAlreadySet)
}

// create a temporary file with the given content.  The file will be cleaned
// up at the end of the test calling this method.
func createTempFile(c *gc.C, content []byte) string {
	file, err := ioutil.TempFile(c.MkDir(), "")
	c.Assert(err, gc.IsNil)
	filename := file.Name()
	err = ioutil.WriteFile(filename, content, 0644)
	c.Assert(err, gc.IsNil)
	return filename
}

// PublicAddress and PrivateAddress return the hostname of the machine read
// from the file _MAASInstanceFilename.
func (suite *EnvironProviderSuite) TestPrivatePublicAddressReadsHostnameFromMachineFile(c *gc.C) {
	hostname := "myhostname"
	info := machineInfo{hostname}
	yaml, err := goyaml.Marshal(info)
	c.Assert(err, gc.IsNil)
	// Create a temporary file to act as the file where the instanceID
	// is stored.
	filename := createTempFile(c, yaml)
	// "Monkey patch" the value of _MAASInstanceFilename with the path
	// to the temporary file.
	old_MAASInstanceFilename := _MAASInstanceFilename
	_MAASInstanceFilename = filename
	defer func() { _MAASInstanceFilename = old_MAASInstanceFilename }()

	provider := suite.makeEnviron().Provider()
	publicAddress, err := provider.PublicAddress()
	c.Assert(err, gc.IsNil)
	c.Check(publicAddress, gc.Equals, hostname)
	privateAddress, err := provider.PrivateAddress()
	c.Assert(err, gc.IsNil)
	c.Check(privateAddress, gc.Equals, hostname)
}

func (suite *EnvironProviderSuite) TestOpenReturnsNilInterfaceUponFailure(c *gc.C) {
	testJujuHome := c.MkDir()
	defer config.SetJujuHome(config.SetJujuHome(testJujuHome))
	const oauth = "wrongly-formatted-oauth-string"
	attrs := testing.FakeConfig().Merge(testing.Attrs{
		"type":        "maas",
		"maas-oauth":  oauth,
		"maas-server": "http://maas.testing.invalid/maas/",
	})
	config, err := config.New(config.NoDefaults, attrs)
	c.Assert(err, gc.IsNil)
	env, err := suite.makeEnviron().Provider().Open(config)
	// When Open() fails (i.e. returns a non-nil error), it returns an
	// environs.Environ interface object with a nil value and a nil
	// type.
	c.Check(env, gc.Equals, nil)
	c.Check(err, gc.ErrorMatches, ".*malformed maas-oauth.*")
}
<|endoftext|>


<commit_before>package redisc

import (
	"errors"
	"math/rand"
	"strconv"
	"sync"
	"time"

	"github.com/gomodule/redigo/redis"
)

const hashSlots = 16384

// Cluster manages a redis cluster. If the CreatePool field is not nil,
// a redis.Pool is used for each node in the cluster to get connections
// via Get. If it is nil or if Dial is called, redis.Dial
// is used to get the connection.
type Cluster struct {
	// StartupNodes is the list of initial nodes that make up
	// the cluster. The values are expected as "address:port"
	// (e.g.: "127.0.0.1:6379"). Only master nodes should be
	// specified.
	StartupNodes []string

	// DialOptions is the list of options to set on each new connection.
	DialOptions []redis.DialOption

	// CreatePool is the function to call to create a redis.Pool for
	// the specified TCP address, using the provided options
	// as set in DialOptions. If this field is not nil, a
	// redis.Pool is created for each node in the cluster and the
	// pool is used to manage the connections returned by Get.
	CreatePool func(address string, options ...redis.DialOption) (*redis.Pool, error)

	mu         sync.RWMutex           // protects following fields
	err        error                  // broken connection error
	pools      map[string]*redis.Pool // created pools per node
	masters    map[string]bool        // set of known active master nodes, kept up-to-date
	replicas   map[string]bool        // set of known active replica nodes, kept up-to-date
	mapping    [hashSlots][]string    // hash slot number to master and replica(s) server addresses, master is always at [0]
	refreshing bool                   // indicates if there's a refresh in progress
}

// Refresh updates the cluster's internal mapping of hash slots
// to redis node. It calls CLUSTER SLOTS on each known node until one
// of them succeeds.
//
// It should typically be called after creating the Cluster and before
// using it. The cluster automatically keeps its mapping up-to-date
// afterwards, based on the redis commands' MOVED responses.
func (c *Cluster) Refresh() error {
	c.mu.Lock()
	err := c.err
	if err == nil {
		c.refreshing = true
	}
	c.mu.Unlock()
	if err != nil {
		return err
	}

	return c.refresh()
}

func (c *Cluster) refresh() error {
	addrs := c.getNodeAddrs(false)
	for _, addr := range addrs {
		m, err := c.getClusterSlots(addr)
		if err == nil {
			// succeeded, save as mapping
			c.mu.Lock()
			// mark all current nodes as false
			for k := range c.masters {
				c.masters[k] = false
			}
			for k := range c.replicas {
				c.replicas[k] = false
			}

			for _, sm := range m {
				for i, node := range sm.nodes {
					if node != "" {
						target := c.masters
						if i > 0 {
							target = c.replicas
						}
						target[node] = true
					}
				}
				for ix := sm.start; ix <= sm.end; ix++ {
					c.mapping[ix] = sm.nodes
				}
			}

			// remove all nodes that are gone from the cluster
			for _, nodes := range []map[string]bool{c.masters, c.replicas} {
				for k, ok := range nodes {
					if !ok {
						delete(nodes, k)

						// close and remove all existing pools for removed nodes
						if p := c.pools[k]; p != nil {
							p.Close()
							delete(c.pools, k)
						}
					}
				}
			}

			// mark that no refresh is needed until another MOVED
			c.refreshing = false
			c.mu.Unlock()

			return nil
		}
	}

	// reset the refreshing flag
	c.mu.Lock()
	c.refreshing = false
	c.mu.Unlock()

	return errors.New("redisc: all nodes failed")
}

// needsRefresh handles automatic update of the mapping.
func (c *Cluster) needsRefresh(re *RedirError) {
	c.mu.Lock()
	if re != nil {
		// update the mapping only if the address has changed, so that if
		// a READONLY replica read returns a MOVED to a master, it doesn't
		// overwrite that slot's replicas by setting just the master (i.e. this
		// is not a MOVED because the cluster is updating, it is a MOVED
		// because the replica cannot serve that key). Same goes for a request
		// to a random connection that gets a MOVED, should not overwrite
		// the moved-to slot's configuration if the master's address is the same.
		if current := c.mapping[re.NewSlot]; len(current) == 0 || current[0] != re.Addr {
			c.mapping[re.NewSlot] = []string{re.Addr}
		}
	}
	if !c.refreshing {
		// refreshing is reset to only once the goroutine has
		// finished updating the mapping, so a new refresh goroutine
		// will only be started if none is running.
		c.refreshing = true
		go c.refresh()
	}
	c.mu.Unlock()
}

type slotMapping struct {
	start, end int
	nodes      []string // master is always at [0]
}

func (c *Cluster) getClusterSlots(addr string) ([]slotMapping, error) {
	conn, err := c.getConnForAddr(addr, false)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	vals, err := redis.Values(conn.Do("CLUSTER", "SLOTS"))
	if err != nil {
		return nil, err
	}

	m := make([]slotMapping, 0, len(vals))
	for len(vals) > 0 {
		var slotRange []interface{}
		vals, err = redis.Scan(vals, &slotRange)
		if err != nil {
			return nil, err
		}

		var start, end int
		slotRange, err = redis.Scan(slotRange, &start, &end)
		if err != nil {
			return nil, err
		}

		sm := slotMapping{start: start, end: end}
		// store the master address and all replicas
		for len(slotRange) > 0 {
			var nodes []interface{}
			slotRange, err = redis.Scan(slotRange, &nodes)
			if err != nil {
				return nil, err
			}

			var addr string
			var port int
			if _, err = redis.Scan(nodes, &addr, &port); err != nil {
				return nil, err
			}
			sm.nodes = append(sm.nodes, addr+":"+strconv.Itoa(port))
		}

		m = append(m, sm)
	}

	return m, nil
}

func (c *Cluster) getConnForAddr(addr string, forceDial bool) (redis.Conn, error) {
	// non-pooled doesn't require a lock
	if c.CreatePool == nil || forceDial {
		return redis.Dial("tcp", addr, c.DialOptions...)
	}

	c.mu.Lock()

	p := c.pools[addr]
	if p == nil {
		c.mu.Unlock()
		pool, err := c.CreatePool(addr, c.DialOptions...)
		if err != nil {
			return nil, err
		}

		c.mu.Lock()
		// check again, concurrent request may have set the pool in the meantime
		if p = c.pools[addr]; p == nil {
			if c.pools == nil {
				c.pools = make(map[string]*redis.Pool, len(c.StartupNodes))
			}
			c.pools[addr] = pool
			p = pool
		} else {
			// Don't assume CreatePool just returned the pool struct, it may have
			// used a connection or something - always match CreatePool with Close.
			// Do it in a defer to keep lock time short.
			defer pool.Close()
		}
	}
	c.mu.Unlock()

	conn := p.Get()
	return conn, conn.Err()
}

var errNoNodeForSlot = errors.New("redisc: no node for slot")

func (c *Cluster) getConnForSlot(slot int, forceDial, readOnly bool) (redis.Conn, string, error) {
	c.mu.Lock()
	addrs := c.mapping[slot]
	c.mu.Unlock()
	if len(addrs) == 0 {
		return nil, "", errNoNodeForSlot
	}

	// mapping slices are never altered, they are replaced when refreshing
	// or on a MOVED response, so it's non-racy to read them outside the lock.
	addr := addrs[0]
	if readOnly && len(addrs) > 1 {
		// get the address of a replica
		if len(addrs) == 2 {
			addr = addrs[1]
		} else {
			rnd.Lock()
			ix := rnd.Intn(len(addrs) - 1)
			rnd.Unlock()
			addr = addrs[ix+1] // +1 because 0 is the master
		}
	} else {
		readOnly = false
	}
	conn, err := c.getConnForAddr(addr, forceDial)
	if err == nil && readOnly {
		conn.Do("READONLY")
	}
	return conn, addr, err
}

// a *rand.Rand is not safe for concurrent access
var rnd = struct {
	sync.Mutex
	*rand.Rand
}{Rand: rand.New(rand.NewSource(time.Now().UnixNano()))}

func (c *Cluster) getRandomConn(forceDial, readOnly bool) (redis.Conn, string, error) {
	addrs := c.getNodeAddrs(readOnly)
	rnd.Lock()
	perms := rnd.Perm(len(addrs))
	rnd.Unlock()

	for _, ix := range perms {
		addr := addrs[ix]
		conn, err := c.getConnForAddr(addr, forceDial)
		if err == nil {
			if readOnly {
				conn.Do("READONLY")
			}
			return conn, addr, nil
		}
	}
	return nil, "", errors.New("redisc: failed to get a connection")
}

func (c *Cluster) getConn(preferredSlot int, forceDial, readOnly bool) (conn redis.Conn, addr string, err error) {
	if preferredSlot >= 0 {
		conn, addr, err = c.getConnForSlot(preferredSlot, forceDial, readOnly)
		if err == errNoNodeForSlot {
			c.needsRefresh(nil)
		}
	}
	if preferredSlot < 0 || err != nil {
		conn, addr, err = c.getRandomConn(forceDial, readOnly)
	}
	return conn, addr, err
}

func (c *Cluster) getNodeAddrs(preferReplicas bool) []string {
	c.mu.Lock()

	// populate nodes lazily, only once
	if c.masters == nil {
		c.masters = make(map[string]bool)
		c.replicas = make(map[string]bool)

		// StartupNodes should be masters
		for _, n := range c.StartupNodes {
			c.masters[n] = true
		}
	}

	from := c.masters
	if preferReplicas && len(c.replicas) > 0 {
		from = c.replicas
	}

	// grab a slice of addresses
	addrs := make([]string, 0, len(from))
	for addr := range from {
		addrs = append(addrs, addr)
	}
	c.mu.Unlock()

	return addrs
}

// Dial returns a connection the same way as Get, but
// it guarantees that the connection will not be managed by the
// pool, even if CreatePool is set. The actual returned
// type is *Conn, see its documentation for details.
func (c *Cluster) Dial() (redis.Conn, error) {
	c.mu.Lock()
	err := c.err
	c.mu.Unlock()

	if err != nil {
		return nil, err
	}

	return &Conn{
		cluster:   c,
		forceDial: true,
	}, nil
}

// Get returns a redis.Conn interface that can be used to call
// redis commands on the cluster. The application must close the
// returned connection. The actual returned type is *Conn,
// see its documentation for details.
func (c *Cluster) Get() redis.Conn {
	c.mu.Lock()
	err := c.err
	c.mu.Unlock()

	return &Conn{
		cluster: c,
		err:     err,
	}
}

// Close releases the resources used by the cluster. It closes all the
// pools that were created, if any.
func (c *Cluster) Close() error {
	c.mu.Lock()
	err := c.err
	if err == nil {
		c.err = errors.New("redisc: closed")
		for _, p := range c.pools {
			if e := p.Close(); e != nil && err == nil {
				err = e
			}
		}
	}
	c.mu.Unlock()

	return err
}

// Stats returns the current statistics for all pools. Keys are node's addresses.
func (c *Cluster) Stats() map[string]redis.PoolStats {
	c.mu.RLock()
	defer c.mu.RUnlock()

	stats := make(map[string]redis.PoolStats, len(c.pools))

	for address, pool := range c.pools {
		stats[address] = pool.Stats()
	}

	return stats
}
<commit_msg>doc: remove mention that StartupNodes must be masters<commit_after>package redisc

import (
	"errors"
	"math/rand"
	"strconv"
	"sync"
	"time"

	"github.com/gomodule/redigo/redis"
)

const hashSlots = 16384

// Cluster manages a redis cluster. If the CreatePool field is not nil,
// a redis.Pool is used for each node in the cluster to get connections
// via Get. If it is nil or if Dial is called, redis.Dial
// is used to get the connection.
type Cluster struct {
	// StartupNodes is the list of initial nodes that make up
	// the cluster. The values are expected as "address:port"
	// (e.g.: "127.0.0.1:6379").
	StartupNodes []string

	// DialOptions is the list of options to set on each new connection.
	DialOptions []redis.DialOption

	// CreatePool is the function to call to create a redis.Pool for
	// the specified TCP address, using the provided options
	// as set in DialOptions. If this field is not nil, a
	// redis.Pool is created for each node in the cluster and the
	// pool is used to manage the connections returned by Get.
	CreatePool func(address string, options ...redis.DialOption) (*redis.Pool, error)

	mu         sync.RWMutex           // protects following fields
	err        error                  // broken connection error
	pools      map[string]*redis.Pool // created pools per node
	masters    map[string]bool        // set of known active master nodes, kept up-to-date
	replicas   map[string]bool        // set of known active replica nodes, kept up-to-date
	mapping    [hashSlots][]string    // hash slot number to master and replica(s) server addresses, master is always at [0]
	refreshing bool                   // indicates if there's a refresh in progress
}

// Refresh updates the cluster's internal mapping of hash slots
// to redis node. It calls CLUSTER SLOTS on each known node until one
// of them succeeds.
//
// It should typically be called after creating the Cluster and before
// using it. The cluster automatically keeps its mapping up-to-date
// afterwards, based on the redis commands' MOVED responses.
func (c *Cluster) Refresh() error {
	c.mu.Lock()
	err := c.err
	if err == nil {
		c.refreshing = true
	}
	c.mu.Unlock()
	if err != nil {
		return err
	}

	return c.refresh()
}

func (c *Cluster) refresh() error {
	addrs := c.getNodeAddrs(false)
	for _, addr := range addrs {
		m, err := c.getClusterSlots(addr)
		if err == nil {
			// succeeded, save as mapping
			c.mu.Lock()
			// mark all current nodes as false
			for k := range c.masters {
				c.masters[k] = false
			}
			for k := range c.replicas {
				c.replicas[k] = false
			}

			for _, sm := range m {
				for i, node := range sm.nodes {
					if node != "" {
						target := c.masters
						if i > 0 {
							target = c.replicas
						}
						target[node] = true
					}
				}
				for ix := sm.start; ix <= sm.end; ix++ {
					c.mapping[ix] = sm.nodes
				}
			}

			// remove all nodes that are gone from the cluster
			for _, nodes := range []map[string]bool{c.masters, c.replicas} {
				for k, ok := range nodes {
					if !ok {
						delete(nodes, k)

						// close and remove all existing pools for removed nodes
						if p := c.pools[k]; p != nil {
							p.Close()
							delete(c.pools, k)
						}
					}
				}
			}

			// mark that no refresh is needed until another MOVED
			c.refreshing = false
			c.mu.Unlock()

			return nil
		}
	}

	// reset the refreshing flag
	c.mu.Lock()
	c.refreshing = false
	c.mu.Unlock()

	return errors.New("redisc: all nodes failed")
}

// needsRefresh handles automatic update of the mapping.
func (c *Cluster) needsRefresh(re *RedirError) {
	c.mu.Lock()
	if re != nil {
		// update the mapping only if the address has changed, so that if
		// a READONLY replica read returns a MOVED to a master, it doesn't
		// overwrite that slot's replicas by setting just the master (i.e. this
		// is not a MOVED because the cluster is updating, it is a MOVED
		// because the replica cannot serve that key). Same goes for a request
		// to a random connection that gets a MOVED, should not overwrite
		// the moved-to slot's configuration if the master's address is the same.
		if current := c.mapping[re.NewSlot]; len(current) == 0 || current[0] != re.Addr {
			c.mapping[re.NewSlot] = []string{re.Addr}
		}
	}
	if !c.refreshing {
		// refreshing is reset to only once the goroutine has
		// finished updating the mapping, so a new refresh goroutine
		// will only be started if none is running.
		c.refreshing = true
		go c.refresh()
	}
	c.mu.Unlock()
}

type slotMapping struct {
	start, end int
	nodes      []string // master is always at [0]
}

func (c *Cluster) getClusterSlots(addr string) ([]slotMapping, error) {
	conn, err := c.getConnForAddr(addr, false)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	vals, err := redis.Values(conn.Do("CLUSTER", "SLOTS"))
	if err != nil {
		return nil, err
	}

	m := make([]slotMapping, 0, len(vals))
	for len(vals) > 0 {
		var slotRange []interface{}
		vals, err = redis.Scan(vals, &slotRange)
		if err != nil {
			return nil, err
		}

		var start, end int
		slotRange, err = redis.Scan(slotRange, &start, &end)
		if err != nil {
			return nil, err
		}

		sm := slotMapping{start: start, end: end}
		// store the master address and all replicas
		for len(slotRange) > 0 {
			var nodes []interface{}
			slotRange, err = redis.Scan(slotRange, &nodes)
			if err != nil {
				return nil, err
			}

			var addr string
			var port int
			if _, err = redis.Scan(nodes, &addr, &port); err != nil {
				return nil, err
			}
			sm.nodes = append(sm.nodes, addr+":"+strconv.Itoa(port))
		}

		m = append(m, sm)
	}

	return m, nil
}

func (c *Cluster) getConnForAddr(addr string, forceDial bool) (redis.Conn, error) {
	// non-pooled doesn't require a lock
	if c.CreatePool == nil || forceDial {
		return redis.Dial("tcp", addr, c.DialOptions...)
	}

	c.mu.Lock()

	p := c.pools[addr]
	if p == nil {
		c.mu.Unlock()
		pool, err := c.CreatePool(addr, c.DialOptions...)
		if err != nil {
			return nil, err
		}

		c.mu.Lock()
		// check again, concurrent request may have set the pool in the meantime
		if p = c.pools[addr]; p == nil {
			if c.pools == nil {
				c.pools = make(map[string]*redis.Pool, len(c.StartupNodes))
			}
			c.pools[addr] = pool
			p = pool
		} else {
			// Don't assume CreatePool just returned the pool struct, it may have
			// used a connection or something - always match CreatePool with Close.
			// Do it in a defer to keep lock time short.
			defer pool.Close()
		}
	}
	c.mu.Unlock()

	conn := p.Get()
	return conn, conn.Err()
}

var errNoNodeForSlot = errors.New("redisc: no node for slot")

func (c *Cluster) getConnForSlot(slot int, forceDial, readOnly bool) (redis.Conn, string, error) {
	c.mu.Lock()
	addrs := c.mapping[slot]
	c.mu.Unlock()
	if len(addrs) == 0 {
		return nil, "", errNoNodeForSlot
	}

	// mapping slices are never altered, they are replaced when refreshing
	// or on a MOVED response, so it's non-racy to read them outside the lock.
	addr := addrs[0]
	if readOnly && len(addrs) > 1 {
		// get the address of a replica
		if len(addrs) == 2 {
			addr = addrs[1]
		} else {
			rnd.Lock()
			ix := rnd.Intn(len(addrs) - 1)
			rnd.Unlock()
			addr = addrs[ix+1] // +1 because 0 is the master
		}
	} else {
		readOnly = false
	}
	conn, err := c.getConnForAddr(addr, forceDial)
	if err == nil && readOnly {
		conn.Do("READONLY")
	}
	return conn, addr, err
}

// a *rand.Rand is not safe for concurrent access
var rnd = struct {
	sync.Mutex
	*rand.Rand
}{Rand: rand.New(rand.NewSource(time.Now().UnixNano()))}

func (c *Cluster) getRandomConn(forceDial, readOnly bool) (redis.Conn, string, error) {
	addrs := c.getNodeAddrs(readOnly)
	rnd.Lock()
	perms := rnd.Perm(len(addrs))
	rnd.Unlock()

	for _, ix := range perms {
		addr := addrs[ix]
		conn, err := c.getConnForAddr(addr, forceDial)
		if err == nil {
			if readOnly {
				conn.Do("READONLY")
			}
			return conn, addr, nil
		}
	}
	return nil, "", errors.New("redisc: failed to get a connection")
}

func (c *Cluster) getConn(preferredSlot int, forceDial, readOnly bool) (conn redis.Conn, addr string, err error) {
	if preferredSlot >= 0 {
		conn, addr, err = c.getConnForSlot(preferredSlot, forceDial, readOnly)
		if err == errNoNodeForSlot {
			c.needsRefresh(nil)
		}
	}
	if preferredSlot < 0 || err != nil {
		conn, addr, err = c.getRandomConn(forceDial, readOnly)
	}
	return conn, addr, err
}

func (c *Cluster) getNodeAddrs(preferReplicas bool) []string {
	c.mu.Lock()

	// populate nodes lazily, only once
	if c.masters == nil {
		c.masters = make(map[string]bool)
		c.replicas = make(map[string]bool)

		// StartupNodes should be masters
		for _, n := range c.StartupNodes {
			c.masters[n] = true
		}
	}

	from := c.masters
	if preferReplicas && len(c.replicas) > 0 {
		from = c.replicas
	}

	// grab a slice of addresses
	addrs := make([]string, 0, len(from))
	for addr := range from {
		addrs = append(addrs, addr)
	}
	c.mu.Unlock()

	return addrs
}

// Dial returns a connection the same way as Get, but
// it guarantees that the connection will not be managed by the
// pool, even if CreatePool is set. The actual returned
// type is *Conn, see its documentation for details.
func (c *Cluster) Dial() (redis.Conn, error) {
	c.mu.Lock()
	err := c.err
	c.mu.Unlock()

	if err != nil {
		return nil, err
	}

	return &Conn{
		cluster:   c,
		forceDial: true,
	}, nil
}

// Get returns a redis.Conn interface that can be used to call
// redis commands on the cluster. The application must close the
// returned connection. The actual returned type is *Conn,
// see its documentation for details.
func (c *Cluster) Get() redis.Conn {
	c.mu.Lock()
	err := c.err
	c.mu.Unlock()

	return &Conn{
		cluster: c,
		err:     err,
	}
}

// Close releases the resources used by the cluster. It closes all the
// pools that were created, if any.
func (c *Cluster) Close() error {
	c.mu.Lock()
	err := c.err
	if err == nil {
		c.err = errors.New("redisc: closed")
		for _, p := range c.pools {
			if e := p.Close(); e != nil && err == nil {
				err = e
			}
		}
	}
	c.mu.Unlock()

	return err
}

// Stats returns the current statistics for all pools. Keys are node's addresses.
func (c *Cluster) Stats() map[string]redis.PoolStats {
	c.mu.RLock()
	defer c.mu.RUnlock()

	stats := make(map[string]redis.PoolStats, len(c.pools))

	for address, pool := range c.pools {
		stats[address] = pool.Stats()
	}

	return stats
}
<|endoftext|>


<commit_before>package cmd

import (
	"strings"

	ccli "github.com/micro/cli"
	"github.com/micro/go-micro/cmd"
	"github.com/micro/micro/api"
	"github.com/micro/micro/bot"
	"github.com/micro/micro/car"
	"github.com/micro/micro/cli"
	"github.com/micro/micro/new"
	"github.com/micro/micro/plugin"
	"github.com/micro/micro/run"
	"github.com/micro/micro/web"
)

var (
	name        = "micro"
	description = "A microservices toolkit"
	version     = "0.5.0"
)

func setup(app *ccli.App) {
	app.Flags = append(app.Flags,
		ccli.BoolFlag{
			Name:   "enable_acme",
			Usage:  "Enables ACME support via Let's Encrypt. ACME hosts should also be specified.",
			EnvVar: "MICRO_ENABLE_ACME",
		},
		ccli.StringFlag{
			Name:   "acme_hosts",
			Usage:  "Comma separated list of hostnames to manage ACME certs for",
			EnvVar: "MICRO_ACME_HOSTS",
		},
		ccli.BoolFlag{
			Name:   "enable_tls",
			Usage:  "Enable TLS support. Expects cert and key file to be specified",
			EnvVar: "MICRO_ENABLE_TLS",
		},
		ccli.StringFlag{
			Name:   "tls_cert_file",
			Usage:  "Path to the TLS Certificate file",
			EnvVar: "MICRO_TLS_CERT_FILE",
		},
		ccli.StringFlag{
			Name:   "tls_key_file",
			Usage:  "Path to the TLS Key file",
			EnvVar: "MICRO_TLS_KEY_FILE",
		},
		ccli.StringFlag{
			Name:   "tls_client_ca_file",
			Usage:  "Path to the TLS CA file to verify clients against",
			EnvVar: "MICRO_TLS_CLIENT_CA_FILE",
		},
		ccli.StringFlag{
			Name:   "api_address",
			Usage:  "Set the api address e.g 0.0.0.0:8080",
			EnvVar: "MICRO_API_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "proxy_address",
			Usage:  "Proxy requests via the HTTP address specified",
			EnvVar: "MICRO_PROXY_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "sidecar_address",
			Usage:  "Set the sidecar address e.g 0.0.0.0:8081",
			EnvVar: "MICRO_SIDECAR_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "web_address",
			Usage:  "Set the web UI address e.g 0.0.0.0:8082",
			EnvVar: "MICRO_WEB_ADDRESS",
		},
		ccli.IntFlag{
			Name:   "register_ttl",
			EnvVar: "MICRO_REGISTER_TTL",
			Usage:  "Register TTL in seconds",
		},
		ccli.IntFlag{
			Name:   "register_interval",
			EnvVar: "MICRO_REGISTER_INTERVAL",
			Usage:  "Register interval in seconds",
		},
		ccli.StringFlag{
			Name:   "api_handler",
			Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {api, proxy, rpc}",
			EnvVar: "MICRO_API_HANDLER",
		},
		ccli.StringFlag{
			Name:   "api_namespace",
			Usage:  "Set the namespace used by the API e.g. com.example.api",
			EnvVar: "MICRO_API_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "sidecar_handler",
			Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {proxy, rpc}",
			EnvVar: "MICRO_SIDECAR_HANDLER",
		},
		ccli.StringFlag{
			Name:   "sidecar_namespace",
			Usage:  "Set the namespace used by the Sidecar e.g. com.example.srv",
			EnvVar: "MICRO_SIDECAR_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "web_namespace",
			Usage:  "Set the namespace used by the Web proxy e.g. com.example.web",
			EnvVar: "MICRO_WEB_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "api_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_API_CORS",
		},
		ccli.StringFlag{
			Name:   "web_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_WEB_CORS",
		},
		ccli.StringFlag{
			Name:   "sidecar_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_SIDECAR_CORS",
		},
		ccli.BoolFlag{
			Name:   "enable_stats",
			Usage:  "Enable stats",
			EnvVar: "MICRO_ENABLE_STATS",
		},
	)

	plugins := plugin.Plugins()

	for _, p := range plugins {
		if flags := p.Flags(); len(flags) > 0 {
			app.Flags = append(app.Flags, flags...)
		}

		if cmds := p.Commands(); len(cmds) > 0 {
			app.Commands = append(app.Commands, cmds...)
		}
	}

	before := app.Before

	app.Before = func(ctx *ccli.Context) error {
		if len(ctx.String("api_handler")) > 0 {
			api.Handler = ctx.String("api_handler")
		}
		if len(ctx.String("api_address")) > 0 {
			api.Address = ctx.String("api_address")
		}
		if len(ctx.String("sidecar_address")) > 0 {
			car.Address = ctx.String("sidecar_address")
		}
		if len(ctx.String("web_address")) > 0 {
			web.Address = ctx.String("web_address")
		}
		if len(ctx.String("api_namespace")) > 0 {
			api.Namespace = ctx.String("api_namespace")
		}
		if len(ctx.String("sidecar_handler")) > 0 {
			car.Handler = ctx.String("sidecar_handler")
		}
		if len(ctx.String("sidecar_namespace")) > 0 {
			car.Namespace = ctx.String("sidecar_namespace")
		}
		if len(ctx.String("web_namespace")) > 0 {
			web.Namespace = ctx.String("web_namespace")
		}

		// origin comma separated string to map
		fn := func(s string) map[string]bool {
			origins := make(map[string]bool)
			for _, origin := range strings.Split(s, ",") {
				origins[origin] = true
			}
			return origins
		}

		if len(ctx.String("api_cors")) > 0 {
			api.CORS = fn(ctx.String("api_cors"))
		}
		if len(ctx.String("sidecar_cors")) > 0 {
			car.CORS = fn(ctx.String("sidecar_cors"))
		}
		if len(ctx.String("web_cors")) > 0 {
			web.CORS = fn(ctx.String("web_cors"))
		}

		for _, p := range plugins {
			p.Init(ctx)
		}

		return before(ctx)
	}
}

func Init() {
	app := cmd.App()
	app.Commands = append(app.Commands, api.Commands()...)
	app.Commands = append(app.Commands, bot.Commands()...)
	app.Commands = append(app.Commands, cli.Commands()...)
	app.Commands = append(app.Commands, car.Commands()...)
	app.Commands = append(app.Commands, new.Commands()...)
	app.Commands = append(app.Commands, run.Commands()...)
	app.Commands = append(app.Commands, web.Commands()...)
	app.Action = func(context *ccli.Context) { ccli.ShowAppHelp(context) }

	setup(app)

	cmd.Init(
		cmd.Name(name),
		cmd.Description(description),
		cmd.Version(version),
	)
}
<commit_msg>version bump<commit_after>package cmd

import (
	"strings"

	ccli "github.com/micro/cli"
	"github.com/micro/go-micro/cmd"
	"github.com/micro/micro/api"
	"github.com/micro/micro/bot"
	"github.com/micro/micro/car"
	"github.com/micro/micro/cli"
	"github.com/micro/micro/new"
	"github.com/micro/micro/plugin"
	"github.com/micro/micro/run"
	"github.com/micro/micro/web"
)

var (
	name        = "micro"
	description = "A microservices toolkit"
	version     = "0.7.1"
)

func setup(app *ccli.App) {
	app.Flags = append(app.Flags,
		ccli.BoolFlag{
			Name:   "enable_acme",
			Usage:  "Enables ACME support via Let's Encrypt. ACME hosts should also be specified.",
			EnvVar: "MICRO_ENABLE_ACME",
		},
		ccli.StringFlag{
			Name:   "acme_hosts",
			Usage:  "Comma separated list of hostnames to manage ACME certs for",
			EnvVar: "MICRO_ACME_HOSTS",
		},
		ccli.BoolFlag{
			Name:   "enable_tls",
			Usage:  "Enable TLS support. Expects cert and key file to be specified",
			EnvVar: "MICRO_ENABLE_TLS",
		},
		ccli.StringFlag{
			Name:   "tls_cert_file",
			Usage:  "Path to the TLS Certificate file",
			EnvVar: "MICRO_TLS_CERT_FILE",
		},
		ccli.StringFlag{
			Name:   "tls_key_file",
			Usage:  "Path to the TLS Key file",
			EnvVar: "MICRO_TLS_KEY_FILE",
		},
		ccli.StringFlag{
			Name:   "tls_client_ca_file",
			Usage:  "Path to the TLS CA file to verify clients against",
			EnvVar: "MICRO_TLS_CLIENT_CA_FILE",
		},
		ccli.StringFlag{
			Name:   "api_address",
			Usage:  "Set the api address e.g 0.0.0.0:8080",
			EnvVar: "MICRO_API_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "proxy_address",
			Usage:  "Proxy requests via the HTTP address specified",
			EnvVar: "MICRO_PROXY_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "sidecar_address",
			Usage:  "Set the sidecar address e.g 0.0.0.0:8081",
			EnvVar: "MICRO_SIDECAR_ADDRESS",
		},
		ccli.StringFlag{
			Name:   "web_address",
			Usage:  "Set the web UI address e.g 0.0.0.0:8082",
			EnvVar: "MICRO_WEB_ADDRESS",
		},
		ccli.IntFlag{
			Name:   "register_ttl",
			EnvVar: "MICRO_REGISTER_TTL",
			Usage:  "Register TTL in seconds",
		},
		ccli.IntFlag{
			Name:   "register_interval",
			EnvVar: "MICRO_REGISTER_INTERVAL",
			Usage:  "Register interval in seconds",
		},
		ccli.StringFlag{
			Name:   "api_handler",
			Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {api, proxy, rpc}",
			EnvVar: "MICRO_API_HANDLER",
		},
		ccli.StringFlag{
			Name:   "api_namespace",
			Usage:  "Set the namespace used by the API e.g. com.example.api",
			EnvVar: "MICRO_API_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "sidecar_handler",
			Usage:  "Specify the request handler to be used for mapping HTTP requests to services; {proxy, rpc}",
			EnvVar: "MICRO_SIDECAR_HANDLER",
		},
		ccli.StringFlag{
			Name:   "sidecar_namespace",
			Usage:  "Set the namespace used by the Sidecar e.g. com.example.srv",
			EnvVar: "MICRO_SIDECAR_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "web_namespace",
			Usage:  "Set the namespace used by the Web proxy e.g. com.example.web",
			EnvVar: "MICRO_WEB_NAMESPACE",
		},
		ccli.StringFlag{
			Name:   "api_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_API_CORS",
		},
		ccli.StringFlag{
			Name:   "web_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_WEB_CORS",
		},
		ccli.StringFlag{
			Name:   "sidecar_cors",
			Usage:  "Comma separated whitelist of allowed origins for CORS",
			EnvVar: "MICRO_SIDECAR_CORS",
		},
		ccli.BoolFlag{
			Name:   "enable_stats",
			Usage:  "Enable stats",
			EnvVar: "MICRO_ENABLE_STATS",
		},
	)

	plugins := plugin.Plugins()

	for _, p := range plugins {
		if flags := p.Flags(); len(flags) > 0 {
			app.Flags = append(app.Flags, flags...)
		}

		if cmds := p.Commands(); len(cmds) > 0 {
			app.Commands = append(app.Commands, cmds...)
		}
	}

	before := app.Before

	app.Before = func(ctx *ccli.Context) error {
		if len(ctx.String("api_handler")) > 0 {
			api.Handler = ctx.String("api_handler")
		}
		if len(ctx.String("api_address")) > 0 {
			api.Address = ctx.String("api_address")
		}
		if len(ctx.String("sidecar_address")) > 0 {
			car.Address = ctx.String("sidecar_address")
		}
		if len(ctx.String("web_address")) > 0 {
			web.Address = ctx.String("web_address")
		}
		if len(ctx.String("api_namespace")) > 0 {
			api.Namespace = ctx.String("api_namespace")
		}
		if len(ctx.String("sidecar_handler")) > 0 {
			car.Handler = ctx.String("sidecar_handler")
		}
		if len(ctx.String("sidecar_namespace")) > 0 {
			car.Namespace = ctx.String("sidecar_namespace")
		}
		if len(ctx.String("web_namespace")) > 0 {
			web.Namespace = ctx.String("web_namespace")
		}

		// origin comma separated string to map
		fn := func(s string) map[string]bool {
			origins := make(map[string]bool)
			for _, origin := range strings.Split(s, ",") {
				origins[origin] = true
			}
			return origins
		}

		if len(ctx.String("api_cors")) > 0 {
			api.CORS = fn(ctx.String("api_cors"))
		}
		if len(ctx.String("sidecar_cors")) > 0 {
			car.CORS = fn(ctx.String("sidecar_cors"))
		}
		if len(ctx.String("web_cors")) > 0 {
			web.CORS = fn(ctx.String("web_cors"))
		}

		for _, p := range plugins {
			p.Init(ctx)
		}

		return before(ctx)
	}
}

func Init() {
	app := cmd.App()
	app.Commands = append(app.Commands, api.Commands()...)
	app.Commands = append(app.Commands, bot.Commands()...)
	app.Commands = append(app.Commands, cli.Commands()...)
	app.Commands = append(app.Commands, car.Commands()...)
	app.Commands = append(app.Commands, new.Commands()...)
	app.Commands = append(app.Commands, run.Commands()...)
	app.Commands = append(app.Commands, web.Commands()...)
	app.Action = func(context *ccli.Context) { ccli.ShowAppHelp(context) }

	setup(app)

	cmd.Init(
		cmd.Name(name),
		cmd.Description(description),
		cmd.Version(version),
	)
}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/mail"
	"net/smtp"
	"os"
	"os/user"
)

import flag "github.com/spf13/pflag"

// Go runs the MailHog sendmail replacement.
func Go() {
	host, err := os.Hostname()
	if err != nil {
		host = "localhost"
	}

	username := "nobody"
	user, err := user.Current()
	if err == nil && user != nil && len(user.Username) > 0 {
		username = user.Username
	}

	fromAddr := username + "@" + host
	smtpAddr := "localhost:1025"
	var recip []string

	// defaults from envars if provided
	if len(os.Getenv("MH_SENDMAIL_SMTP_ADDR")) > 0 {
		smtpAddr = os.Getenv("MH_SENDMAIL_SMTP_ADDR")
	}
	if len(os.Getenv("MH_SENDMAIL_FROM")) > 0 {
		fromAddr = os.Getenv("MH_SENDMAIL_FROM")
	}

	var verbose bool

	// override defaults from cli flags
	flag.StringVar(&smtpAddr, "smtp-addr", smtpAddr, "SMTP server address")
	flag.StringVarP(&fromAddr, "from", "f", fromAddr, "SMTP sender")
	flag.BoolP("long-i", "i", true, "Ignored. This flag exists for sendmail compatibility.")
	flag.BoolP("long-t", "t", true, "Ignored. This flag exists for sendmail compatibility.")
	flag.BoolVarP(&verbose, "verbose", "v", true, "Verbose mode (sends debug output to stderr)")
	flag.Parse()

	// allow recipient to be passed as an argument
	recip = flag.Args()

	if verbose {
		fmt.Fprintln(os.Stderr, smtpAddr, fromAddr)
	}

	body, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, "error reading stdin")
		os.Exit(11)
	}

	msg, err := mail.ReadMessage(bytes.NewReader(body))
	if err != nil {
		fmt.Fprintln(os.Stderr, "error parsing message body")
		os.Exit(11)
	}

	if len(recip) == 0 {
		// We only need to parse the message to get a recipient if none where
		// provided on the command line.
		recip = append(recip, msg.Header.Get("To"))
	}

	err = smtp.SendMail(smtpAddr, nil, fromAddr, recip, body)
	if err != nil {
		fmt.Fprintln(os.Stderr, "error sending mail")
		log.Fatal(err)
	}

}
<commit_msg>fix c&p error<commit_after>package cmd

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/mail"
	"net/smtp"
	"os"
	"os/user"
)

import flag "github.com/spf13/pflag"

// Go runs the MailHog sendmail replacement.
func Go() {
	host, err := os.Hostname()
	if err != nil {
		host = "localhost"
	}

	username := "nobody"
	user, err := user.Current()
	if err == nil && user != nil && len(user.Username) > 0 {
		username = user.Username
	}

	fromAddr := username + "@" + host
	smtpAddr := "localhost:1025"
	var recip []string

	// defaults from envars if provided
	if len(os.Getenv("MH_SENDMAIL_SMTP_ADDR")) > 0 {
		smtpAddr = os.Getenv("MH_SENDMAIL_SMTP_ADDR")
	}
	if len(os.Getenv("MH_SENDMAIL_FROM")) > 0 {
		fromAddr = os.Getenv("MH_SENDMAIL_FROM")
	}

	var verbose bool

	// override defaults from cli flags
	flag.StringVar(&smtpAddr, "smtp-addr", smtpAddr, "SMTP server address")
	flag.StringVarP(&fromAddr, "from", "f", fromAddr, "SMTP sender")
	flag.BoolP("long-i", "i", true, "Ignored. This flag exists for sendmail compatibility.")
	flag.BoolP("long-t", "t", true, "Ignored. This flag exists for sendmail compatibility.")
	flag.BoolVarP(&verbose, "verbose", "v", false, "Verbose mode (sends debug output to stderr)")
	flag.Parse()

	// allow recipient to be passed as an argument
	recip = flag.Args()

	if verbose {
		fmt.Fprintln(os.Stderr, smtpAddr, fromAddr)
	}

	body, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, "error reading stdin")
		os.Exit(11)
	}

	msg, err := mail.ReadMessage(bytes.NewReader(body))
	if err != nil {
		fmt.Fprintln(os.Stderr, "error parsing message body")
		os.Exit(11)
	}

	if len(recip) == 0 {
		// We only need to parse the message to get a recipient if none where
		// provided on the command line.
		recip = append(recip, msg.Header.Get("To"))
	}

	err = smtp.SendMail(smtpAddr, nil, fromAddr, recip, body)
	if err != nil {
		fmt.Fprintln(os.Stderr, "error sending mail")
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/go-playground/ansi"
	"github.com/go-playground/log"
	"github.com/spf13/viper"
	"golang.org/x/crypto/ssh/terminal"
)

var (
	loc              *time.Location
	defaultLogLevels = []log.Level{
		log.InfoLevel,
		log.WarnLevel,
		log.ErrorLevel,
		log.PanicLevel,
		log.AlertLevel,
		log.FatalLevel,
	}
)

const (
	defaultFormat string = "2006-01-02 15:04:05.000ms"
	// defaultTZ     string = "America/Sao_Paulo"
	defaultTZ string = "Brazil/EAST"
)

// TextHandler is a log handdle that print simple text messages
type TextHandler struct{}

// Log accepts log entries to be processed
func (c *TextHandler) Log(e log.Entry) {
	b := new(bytes.Buffer)

	b.Reset()
	b.WriteString(e.Timestamp.In(loc).Format(defaultFormat))
	fmt.Fprint(b, " ")
	fmt.Fprintf(b, "%-6s", e.Level.String())
	fmt.Fprint(b, " ")

	for _, f := range e.Fields {
		fmt.Fprint(b, f.Key)
		fmt.Fprint(b, "=")
		fmt.Fprintf(b, "%-10s", f.Value)
		fmt.Fprint(b, " ")
	}
	b.WriteString(e.Message)
	fmt.Println(b.String())
}

// CollorHandler is a log handdle that print colorfull messages
type CollorHandler struct{}

var defaultColors = [...]ansi.EscSeq{
	log.DebugLevel: ansi.Gray,
	// log.TraceLevel:  ansi.White,
	log.InfoLevel:   ansi.Blue,
	log.NoticeLevel: ansi.LightCyan,
	log.WarnLevel:   ansi.LightYellow,
	log.ErrorLevel:  ansi.LightRed,
	log.PanicLevel:  ansi.Red,
	log.AlertLevel:  ansi.Red + ansi.Underline,
	log.FatalLevel:  ansi.Red + ansi.Underline + ansi.Blink,
}

// Log accepts log entries to be processed
func (c *CollorHandler) Log(e log.Entry) {

	color := defaultColors[e.Level]

	b := new(bytes.Buffer)
	b.Reset()
	b.WriteString(e.Timestamp.In(loc).Format(defaultFormat))
	b.WriteString(" ")
	fmt.Fprintf(b, "%s%-6s%s", ansi.Bold+color, e.Level.String(), ansi.BoldOff+ansi.Reset)

	for _, f := range e.Fields {
		fmt.Fprint(b, ansi.Bold)
		fmt.Fprint(b, f.Key)
		fmt.Fprint(b, ansi.BoldOff)
		fmt.Fprint(b, "=")
		fmt.Fprint(b, ansi.Italics)
		fmt.Fprintf(b, "%-10s", f.Value)
		fmt.Fprint(b, ansi.ItalicsOff)
		fmt.Fprint(b, " ")
	}
	b.WriteString(e.Message)

	fmt.Println(b.String())
}

func setHandler() log.Handler {

	// ugly messages on windows forces me to disable this pretty messages
	if !terminal.IsTerminal(int(os.Stdout.Fd())) || runtime.GOOS == "windows" {
		return new(TextHandler)
	}

	return new(CollorHandler)
}

func setupLogger() {

	var err error
	var defaultTimeZoneErr bool
	loc, err = time.LoadLocation(defaultTZ)
	if err != nil {
		defaultTimeZoneErr = true
		loc = time.Local
	}

	if viper.GetBool("debug") {
		defaultLogLevels = append(defaultLogLevels, log.DebugLevel)
	}

	log.AddHandler(setHandler(), defaultLogLevels...)

	log.Infof("DBView CLI v%s", version)

	if viper.GetBool("debug") {
		log.Info("Enabling DEBUG messages.")
	}

	if defaultTimeZoneErr {
		log.Info("Default TZ", defaultTZ, " Not found. Falling back to local timezone.")
	}
}
<commit_msg>Fix missing space on log output<commit_after>package cmd

import (
	"bytes"
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/go-playground/ansi"
	"github.com/go-playground/log"
	"github.com/spf13/viper"
	"golang.org/x/crypto/ssh/terminal"
)

var (
	loc              *time.Location
	defaultLogLevels = []log.Level{
		log.InfoLevel,
		log.WarnLevel,
		log.ErrorLevel,
		log.PanicLevel,
		log.AlertLevel,
		log.FatalLevel,
	}
)

const (
	defaultFormat string = "2006-01-02 15:04:05.000ms"
	// defaultTZ     string = "America/Sao_Paulo"
	defaultTZ string = "Brazil/EAST"
)

// TextHandler is a log handdle that print simple text messages
type TextHandler struct{}

// Log accepts log entries to be processed
func (c *TextHandler) Log(e log.Entry) {
	b := new(bytes.Buffer)

	b.Reset()
	b.WriteString(e.Timestamp.In(loc).Format(defaultFormat))
	fmt.Fprint(b, " ")
	fmt.Fprintf(b, "%-6s", e.Level.String())
	fmt.Fprint(b, " ")

	for _, f := range e.Fields {
		fmt.Fprint(b, f.Key)
		fmt.Fprint(b, "=")
		fmt.Fprintf(b, "%-10s", f.Value)
		fmt.Fprint(b, " ")
	}
	b.WriteString(e.Message)
	fmt.Println(b.String())
}

// CollorHandler is a log handdle that print colorfull messages
type CollorHandler struct{}

var defaultColors = [...]ansi.EscSeq{
	log.DebugLevel: ansi.Gray,
	// log.TraceLevel:  ansi.White,
	log.InfoLevel:   ansi.Blue,
	log.NoticeLevel: ansi.LightCyan,
	log.WarnLevel:   ansi.LightYellow,
	log.ErrorLevel:  ansi.LightRed,
	log.PanicLevel:  ansi.Red,
	log.AlertLevel:  ansi.Red + ansi.Underline,
	log.FatalLevel:  ansi.Red + ansi.Underline + ansi.Blink,
}

// Log accepts log entries to be processed
func (c *CollorHandler) Log(e log.Entry) {

	color := defaultColors[e.Level]

	b := new(bytes.Buffer)
	b.Reset()
	b.WriteString(e.Timestamp.In(loc).Format(defaultFormat))
	b.WriteString(" ")
	fmt.Fprintf(b, "%s%-6s%s", ansi.Bold+color, e.Level.String(), ansi.BoldOff+ansi.Reset)

	for _, f := range e.Fields {
		fmt.Fprint(b, ansi.Bold)
		fmt.Fprint(b, f.Key)
		fmt.Fprint(b, ansi.BoldOff)
		fmt.Fprint(b, "=")
		fmt.Fprint(b, ansi.Italics)
		fmt.Fprintf(b, "%-10s", f.Value)
		fmt.Fprint(b, ansi.ItalicsOff)
		fmt.Fprint(b, " ")
	}
	b.WriteString(e.Message)

	fmt.Println(b.String())
}

func setHandler() log.Handler {

	// ugly messages on windows forces me to disable this pretty messages
	if !terminal.IsTerminal(int(os.Stdout.Fd())) || runtime.GOOS == "windows" {
		return new(TextHandler)
	}

	return new(CollorHandler)
}

func setupLogger() {

	var err error
	var defaultTimeZoneErr bool
	loc, err = time.LoadLocation(defaultTZ)
	if err != nil {
		defaultTimeZoneErr = true
		loc = time.Local
	}

	if viper.GetBool("debug") {
		defaultLogLevels = append(defaultLogLevels, log.DebugLevel)
	}

	log.AddHandler(setHandler(), defaultLogLevels...)

	log.Infof("DBView CLI v%s", version)

	if viper.GetBool("debug") {
		log.Info("Enabling DEBUG messages.")
	}

	if defaultTimeZoneErr {
		log.Info("Default TZ ", defaultTZ, " Not found. Falling back to local timezone.")
	}
}
<|endoftext|>


<commit_before>package cmd

import (
    "net"
    "strings"
    "encoding/json"
    "periodic/sched"
    "fmt"
    "log"
    "bytes"
    "errors"
    "os/exec"
    "strconv"
)

func Run(entryPoint, Func, cmd string) {
    parts := strings.SplitN(entryPoint, "://", 2)
    c, err := net.Dial(parts[0], parts[1])
    if err != nil {
        log.Fatal(err)
    }
    conn := sched.Conn{Conn: c}
    defer conn.Close()
    err = conn.Send(sched.PackCmd(sched.TYPE_WORKER))
    if err != nil {
        log.Fatal(err)
    }
    buf := bytes.NewBuffer(nil)
    buf.WriteByte(byte(sched.CAN_DO))
    buf.Write(sched.NULL_CHAR)
    buf.WriteString(Func)
    err = conn.Send(buf.Bytes())
    if err != nil {
        log.Fatal(err)
    }

    var payload []byte
    var job sched.Job
    var jobHandle []byte
    for {
        err = conn.Send(sched.PackCmd(sched.GRAB_JOB))
        if err != nil {
            log.Fatal(err)
        }
        payload, err = conn.Receive()
        if err != nil {
            log.Fatal(err)
        }
        job, jobHandle, err = extraJob(payload)
        realCmd := strings.Split(cmd, " ")
        realCmd = append(realCmd, job.Name)
        c := exec.Command(realCmd[0], realCmd[1:]...)
        c.Stdin = strings.NewReader(job.Args)
        var out bytes.Buffer
        var stderr bytes.Buffer
        c.Stdout = &out
        c.Stderr = &stderr
        err = c.Run()
        var schedLater int
        for {
            line, err := out.ReadString([]byte("\n")[0])
            if err != nil {
                break
            }
            if strings.HasPrefix(line, "SCHED_LATER") {
                parts = strings.SplitN(line[:len(line) - 1], " ", 2)
                later := strings.Trim(parts[1], " ")
                schedLater, _ = strconv.Atoi(later)
            } else {
                fmt.Print(line)
            }
        }
        fmt.Print(stderr.String())
        buf := bytes.NewBuffer(nil)
        if err != nil {
            buf.WriteByte(byte(sched.JOB_FAIL))
        } else if schedLater > 0 {
            buf.WriteByte(byte(sched.SCHED_LATER))
        } else {
            buf.WriteByte(byte(sched.JOB_DONE))
        }
        buf.Write(sched.NULL_CHAR)
        buf.Write(jobHandle)
        if schedLater > 0 {
            buf.Write(sched.NULL_CHAR)
            buf.WriteString(strconv.Itoa(schedLater))
        }
        err = conn.Send(buf.Bytes())
    }
}

func extraJob(payload []byte) (job sched.Job, jobHandle []byte, err error) {
    parts := bytes.SplitN(payload, sched.NULL_CHAR, 2)
    if len(parts) != 2 {
        err = errors.New("Invalid payload " + string(payload))
        return
    }
    err = json.Unmarshal(parts[0], &job)
    jobHandle = parts[1]
    return
}
<commit_msg>Add cmd FAIL on result<commit_after>package cmd

import (
    "net"
    "strings"
    "encoding/json"
    "periodic/sched"
    "fmt"
    "log"
    "bytes"
    "errors"
    "os/exec"
    "strconv"
)

func Run(entryPoint, Func, cmd string) {
    parts := strings.SplitN(entryPoint, "://", 2)
    c, err := net.Dial(parts[0], parts[1])
    if err != nil {
        log.Fatal(err)
    }
    conn := sched.Conn{Conn: c}
    defer conn.Close()
    err = conn.Send(sched.PackCmd(sched.TYPE_WORKER))
    if err != nil {
        log.Fatal(err)
    }
    buf := bytes.NewBuffer(nil)
    buf.WriteByte(byte(sched.CAN_DO))
    buf.Write(sched.NULL_CHAR)
    buf.WriteString(Func)
    err = conn.Send(buf.Bytes())
    if err != nil {
        log.Fatal(err)
    }

    var payload []byte
    var job sched.Job
    var jobHandle []byte
    for {
        err = conn.Send(sched.PackCmd(sched.GRAB_JOB))
        if err != nil {
            log.Fatal(err)
        }
        payload, err = conn.Receive()
        if err != nil {
            log.Fatal(err)
        }
        job, jobHandle, err = extraJob(payload)
        realCmd := strings.Split(cmd, " ")
        realCmd = append(realCmd, job.Name)
        c := exec.Command(realCmd[0], realCmd[1:]...)
        c.Stdin = strings.NewReader(job.Args)
        var out bytes.Buffer
        var stderr bytes.Buffer
        c.Stdout = &out
        c.Stderr = &stderr
        err = c.Run()
        var schedLater int
        var fail = false
        for {
            line, err := out.ReadString([]byte("\n")[0])
            if err != nil {
                break
            }
            if strings.HasPrefix(line, "SCHED_LATER") {
                parts = strings.SplitN(line[:len(line) - 1], " ", 2)
                later := strings.Trim(parts[1], " ")
                schedLater, _ = strconv.Atoi(later)
            } else if strings.HasPrefix(line, "FAIL") {
                fail = true
            } else {
                fmt.Print(line)
            }
        }
        fmt.Print(stderr.String())
        buf := bytes.NewBuffer(nil)
        if err != nil || fail {
            buf.WriteByte(byte(sched.JOB_FAIL))
        } else if schedLater > 0 {
            buf.WriteByte(byte(sched.SCHED_LATER))
        } else {
            buf.WriteByte(byte(sched.JOB_DONE))
        }
        buf.Write(sched.NULL_CHAR)
        buf.Write(jobHandle)
        if schedLater > 0 {
            buf.Write(sched.NULL_CHAR)
            buf.WriteString(strconv.Itoa(schedLater))
        }
        err = conn.Send(buf.Bytes())
    }
}

func extraJob(payload []byte) (job sched.Job, jobHandle []byte, err error) {
    parts := bytes.SplitN(payload, sched.NULL_CHAR, 2)
    if len(parts) != 2 {
        err = errors.New("Invalid payload " + string(payload))
        return
    }
    err = json.Unmarshal(parts[0], &job)
    jobHandle = parts[1]
    return
}
<|endoftext|>