<commit_before>package main

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

	"github.com/docker/notary"
	notaryclient "github.com/docker/notary/client"
	"github.com/docker/notary/tuf/data"
	"github.com/docker/notary/tuf/utils"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var cmdDelegationTemplate = usageTemplate{
	Use:   "delegation",
	Short: "Operates on delegations.",
	Long:  `Operations on TUF delegations.`,
}

var cmdDelegationListTemplate = usageTemplate{
	Use:   "list [ GUN ]",
	Short: "Lists delegations for the Global Unique Name.",
	Long:  "Lists all delegations known to notary for a specific Global Unique Name.",
}

var cmdDelegationRemoveTemplate = usageTemplate{
	Use:   "remove [ GUN ] [ Role ] <KeyID 1> ...",
	Short: "Remove KeyID(s) from the specified Role delegation.",
	Long:  "Remove KeyID(s) from the specified Role delegation in a specific Global Unique Name.",
}

var cmdDelegationPurgeKeysTemplate = usageTemplate{
	Use:   "purge [ GUN ]",
	Short: "Remove KeyID(s) from all delegation roles in the given GUN.",
	Long:  "Remove KeyID(s) from all delegation roles in the given GUN, for which the signing keys are available. Warnings will be printed for delegations that cannot be updated.",
}

var cmdDelegationAddTemplate = usageTemplate{
	Use:   "add [ GUN ] [ Role ] <X509 file path 1> ...",
	Short: "Add a keys to delegation using the provided public key X509 certificates.",
	Long:  "Add a keys to delegation using the provided public key PEM encoded X509 certificates in a specific Global Unique Name.",
}

type delegationCommander struct {
	// these need to be set
	configGetter func() (*viper.Viper, error)
	retriever    notary.PassRetriever

	paths                         []string
	allPaths, removeAll, forceYes bool
	keyIDs                        []string

	autoPublish bool
}

func (d *delegationCommander) GetCommand() *cobra.Command {
	cmd := cmdDelegationTemplate.ToCommand(nil)
	cmd.AddCommand(cmdDelegationListTemplate.ToCommand(d.delegationsList))

	cmdPurgeDelgKeys := cmdDelegationPurgeKeysTemplate.ToCommand(d.delegationPurgeKeys)
	cmdPurgeDelgKeys.Flags().StringSliceVar(&d.keyIDs, "key", nil, "Delegation key IDs to be removed from the GUN")
	cmdPurgeDelgKeys.Flags().BoolVarP(&d.autoPublish, "publish", "p", false, htAutoPublish)
	cmd.AddCommand(cmdPurgeDelgKeys)

	cmdRemDelg := cmdDelegationRemoveTemplate.ToCommand(d.delegationRemove)
	cmdRemDelg.Flags().StringSliceVar(&d.paths, "paths", nil, "List of paths to remove")
	cmdRemDelg.Flags().BoolVarP(&d.forceYes, "yes", "y", false, "Answer yes to the removal question (no confirmation)")
	cmdRemDelg.Flags().BoolVar(&d.allPaths, "all-paths", false, "Remove all paths from this delegation")
	cmdRemDelg.Flags().BoolVarP(&d.autoPublish, "publish", "p", false, htAutoPublish)
	cmd.AddCommand(cmdRemDelg)

	cmdAddDelg := cmdDelegationAddTemplate.ToCommand(d.delegationAdd)
	cmdAddDelg.Flags().StringSliceVar(&d.paths, "paths", nil, "List of paths to add")
	cmdAddDelg.Flags().BoolVar(&d.allPaths, "all-paths", false, "Add all paths to this delegation")
	cmdAddDelg.Flags().BoolVarP(&d.autoPublish, "publish", "p", false, htAutoPublish)
	cmd.AddCommand(cmdAddDelg)
	return cmd
}

func (d *delegationCommander) delegationPurgeKeys(cmd *cobra.Command, args []string) error {
	if len(args) != 1 {
		cmd.Usage()
		return fmt.Errorf("Please provide a single Global Unique Name as an argument to remove")
	}

	if len(d.keyIDs) == 0 {
		cmd.Usage()
		return fmt.Errorf("Please provide at least one key ID to be removed using the --key flag")
	}

	gun := args[0]

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"),
		gun,
		getRemoteTrustServer(config),
		nil,
		d.retriever,
		trustPin,
	)
	if err != nil {
		return err
	}

	err = nRepo.RemoveDelegationKeys("targets/*", d.keyIDs)
	if err != nil {
		return fmt.Errorf("failed to remove keys from delegations: %v", err)
	}
	fmt.Printf(
		"Removal of the following keys from all delegations in %s staged for next publish:\n\t- %s\n",
		gun,
		strings.Join(d.keyIDs, "\n\t- "),
	)
	return nil
}

// delegationsList lists all the delegations for a particular GUN
func (d *delegationCommander) delegationsList(cmd *cobra.Command, args []string) error {
	if len(args) != 1 {
		cmd.Usage()
		return fmt.Errorf(
			"Please provide a Global Unique Name as an argument to list")
	}

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	gun := args[0]

	rt, err := getTransport(config, gun, readOnly)
	if err != nil {
		return err
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	// initialize repo with transport to get latest state of the world before listing delegations
	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"), gun, getRemoteTrustServer(config), rt, d.retriever, trustPin)
	if err != nil {
		return err
	}

	delegationRoles, err := nRepo.GetDelegationRoles()
	if err != nil {
		return fmt.Errorf("Error retrieving delegation roles for repository %s: %v", gun, err)
	}

	cmd.Println("")
	prettyPrintRoles(delegationRoles, cmd.Out(), "delegations")
	cmd.Println("")
	return maybeAutoPublish(cmd, d.autoPublish, gun, config, d.retriever)
}

// delegationRemove removes a public key from a specific role in a GUN
func (d *delegationCommander) delegationRemove(cmd *cobra.Command, args []string) error {
	if len(args) < 2 {
		cmd.Usage()
		return fmt.Errorf("must specify the Global Unique Name and the role of the delegation along with optional keyIDs and/or a list of paths to remove")
	}

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	gun := args[0]
	role := args[1]

	// Check if role is valid delegation name before requiring any user input
	if !data.IsDelegation(role) {
		return fmt.Errorf("invalid delegation name %s", role)
	}

	// If we're only given the gun and the role, attempt to remove all data for this delegation
	if len(args) == 2 && d.paths == nil && !d.allPaths {
		d.removeAll = true
	}

	keyIDs := []string{}

	if len(args) > 2 {
		keyIDs = args[2:]
	}

	// If the user passes --all-paths, don't use any of the passed in --paths
	if d.allPaths {
		d.paths = nil
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	// no online operations are performed by add so the transport argument
	// should be nil
	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"), gun, getRemoteTrustServer(config), nil, d.retriever, trustPin)
	if err != nil {
		return err
	}

	if d.removeAll {
		cmd.Println("\nAre you sure you want to remove all data for this delegation? (yes/no)")
		// Ask for confirmation before force removing delegation
		if !d.forceYes {
			confirmed := askConfirm(os.Stdin)
			if !confirmed {
				fatalf("Aborting action.")
			}
		} else {
			cmd.Println("Confirmed `yes` from flag")
		}
		// Delete the entire delegation
		err = nRepo.RemoveDelegationRole(role)
		if err != nil {
			return fmt.Errorf("failed to remove delegation: %v", err)
		}
	} else {
		if d.allPaths {
			err = nRepo.ClearDelegationPaths(role)
			if err != nil {
				return fmt.Errorf("failed to remove delegation: %v", err)
			}
		}
		// Remove any keys or paths that we passed in
		err = nRepo.RemoveDelegationKeysAndPaths(role, keyIDs, d.paths)
		if err != nil {
			return fmt.Errorf("failed to remove delegation: %v", err)
		}
	}

	cmd.Println("")
	if d.removeAll {
		cmd.Printf("Forced removal (including all keys and paths) of delegation role %s to repository \"%s\" staged for next publish.\n", role, gun)
	} else {
		removingItems := ""
		if len(keyIDs) > 0 {
			removingItems = removingItems + fmt.Sprintf("with keys %s, ", keyIDs)
		}
		if d.allPaths {
			removingItems = removingItems + "with all paths, "
		}
		if d.paths != nil {
			removingItems = removingItems + fmt.Sprintf(
				"with paths [%s], ",
				strings.Join(prettyPaths(d.paths), "\n"),
			)
		}
		cmd.Printf("Removal of delegation role %s %sto repository \"%s\" staged for next publish.\n", role, removingItems, gun)
	}
	cmd.Println("")

	return maybeAutoPublish(cmd, d.autoPublish, gun, config, d.retriever)
}

// delegationAdd creates a new delegation by adding a public key from a certificate to a specific role in a GUN
func (d *delegationCommander) delegationAdd(cmd *cobra.Command, args []string) error {
	// We must have at least the gun and role name, and at least one key or path (or the --all-paths flag) to add
	if len(args) < 2 || len(args) < 3 && d.paths == nil && !d.allPaths {
		cmd.Usage()
		return fmt.Errorf("must specify the Global Unique Name and the role of the delegation along with the public key certificate paths and/or a list of paths to add")
	}

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	gun := args[0]
	role := args[1]

	pubKeys := []data.PublicKey{}
	if len(args) > 2 {
		pubKeyPaths := args[2:]
		for _, pubKeyPath := range pubKeyPaths {
			// Read public key bytes from PEM file
			pubKeyBytes, err := ioutil.ReadFile(pubKeyPath)
			if err != nil {
				return fmt.Errorf("unable to read public key from file: %s", pubKeyPath)
			}

			// Parse PEM bytes into type PublicKey
			pubKey, err := utils.ParsePEMPublicKey(pubKeyBytes)
			if err != nil {
				return fmt.Errorf("unable to parse valid public key certificate from PEM file %s: %v", pubKeyPath, err)
			}
			pubKeys = append(pubKeys, pubKey)
		}
	}

	for _, path := range d.paths {
		if path == "" {
			d.allPaths = true
			break
		}
	}

	// If the user passes --all-paths (or gave the "" path in --paths), give the "" path
	if d.allPaths {
		d.paths = []string{""}
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	// no online operations are performed by add so the transport argument
	// should be nil
	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"), gun, getRemoteTrustServer(config), nil, d.retriever, trustPin)
	if err != nil {
		return err
	}

	// Add the delegation to the repository
	err = nRepo.AddDelegation(role, pubKeys, d.paths)
	if err != nil {
		return fmt.Errorf("failed to create delegation: %v", err)
	}

	// Make keyID slice for better CLI print
	pubKeyIDs := []string{}
	for _, pubKey := range pubKeys {
		pubKeyID, err := utils.CanonicalKeyID(pubKey)
		if err != nil {
			return err
		}
		pubKeyIDs = append(pubKeyIDs, pubKeyID)
	}

	cmd.Println("")
	addingItems := ""
	if len(pubKeyIDs) > 0 {
		addingItems = addingItems + fmt.Sprintf("with keys %s, ", pubKeyIDs)
	}
	if d.paths != nil || d.allPaths {
		addingItems = addingItems + fmt.Sprintf(
			"with paths [%s], ",
			strings.Join(prettyPaths(d.paths), "\n"),
		)
	}
	cmd.Printf(
		"Addition of delegation role %s %sto repository \"%s\" staged for next publish.\n",
		role, addingItems, gun)
	cmd.Println("")

	return maybeAutoPublish(cmd, d.autoPublish, gun, config, d.retriever)
}
<commit_msg>make sure autopublish is on purge, not list<commit_after>package main

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

	"github.com/docker/notary"
	notaryclient "github.com/docker/notary/client"
	"github.com/docker/notary/tuf/data"
	"github.com/docker/notary/tuf/utils"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var cmdDelegationTemplate = usageTemplate{
	Use:   "delegation",
	Short: "Operates on delegations.",
	Long:  `Operations on TUF delegations.`,
}

var cmdDelegationListTemplate = usageTemplate{
	Use:   "list [ GUN ]",
	Short: "Lists delegations for the Global Unique Name.",
	Long:  "Lists all delegations known to notary for a specific Global Unique Name.",
}

var cmdDelegationRemoveTemplate = usageTemplate{
	Use:   "remove [ GUN ] [ Role ] <KeyID 1> ...",
	Short: "Remove KeyID(s) from the specified Role delegation.",
	Long:  "Remove KeyID(s) from the specified Role delegation in a specific Global Unique Name.",
}

var cmdDelegationPurgeKeysTemplate = usageTemplate{
	Use:   "purge [ GUN ]",
	Short: "Remove KeyID(s) from all delegation roles in the given GUN.",
	Long:  "Remove KeyID(s) from all delegation roles in the given GUN, for which the signing keys are available. Warnings will be printed for delegations that cannot be updated.",
}

var cmdDelegationAddTemplate = usageTemplate{
	Use:   "add [ GUN ] [ Role ] <X509 file path 1> ...",
	Short: "Add a keys to delegation using the provided public key X509 certificates.",
	Long:  "Add a keys to delegation using the provided public key PEM encoded X509 certificates in a specific Global Unique Name.",
}

type delegationCommander struct {
	// these need to be set
	configGetter func() (*viper.Viper, error)
	retriever    notary.PassRetriever

	paths                         []string
	allPaths, removeAll, forceYes bool
	keyIDs                        []string

	autoPublish bool
}

func (d *delegationCommander) GetCommand() *cobra.Command {
	cmd := cmdDelegationTemplate.ToCommand(nil)
	cmd.AddCommand(cmdDelegationListTemplate.ToCommand(d.delegationsList))

	cmdPurgeDelgKeys := cmdDelegationPurgeKeysTemplate.ToCommand(d.delegationPurgeKeys)
	cmdPurgeDelgKeys.Flags().StringSliceVar(&d.keyIDs, "key", nil, "Delegation key IDs to be removed from the GUN")
	cmdPurgeDelgKeys.Flags().BoolVarP(&d.autoPublish, "publish", "p", false, htAutoPublish)
	cmd.AddCommand(cmdPurgeDelgKeys)

	cmdRemDelg := cmdDelegationRemoveTemplate.ToCommand(d.delegationRemove)
	cmdRemDelg.Flags().StringSliceVar(&d.paths, "paths", nil, "List of paths to remove")
	cmdRemDelg.Flags().BoolVarP(&d.forceYes, "yes", "y", false, "Answer yes to the removal question (no confirmation)")
	cmdRemDelg.Flags().BoolVar(&d.allPaths, "all-paths", false, "Remove all paths from this delegation")
	cmdRemDelg.Flags().BoolVarP(&d.autoPublish, "publish", "p", false, htAutoPublish)
	cmd.AddCommand(cmdRemDelg)

	cmdAddDelg := cmdDelegationAddTemplate.ToCommand(d.delegationAdd)
	cmdAddDelg.Flags().StringSliceVar(&d.paths, "paths", nil, "List of paths to add")
	cmdAddDelg.Flags().BoolVar(&d.allPaths, "all-paths", false, "Add all paths to this delegation")
	cmdAddDelg.Flags().BoolVarP(&d.autoPublish, "publish", "p", false, htAutoPublish)
	cmd.AddCommand(cmdAddDelg)
	return cmd
}

func (d *delegationCommander) delegationPurgeKeys(cmd *cobra.Command, args []string) error {
	if len(args) != 1 {
		cmd.Usage()
		return fmt.Errorf("Please provide a single Global Unique Name as an argument to remove")
	}

	if len(d.keyIDs) == 0 {
		cmd.Usage()
		return fmt.Errorf("Please provide at least one key ID to be removed using the --key flag")
	}

	gun := args[0]

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"),
		gun,
		getRemoteTrustServer(config),
		nil,
		d.retriever,
		trustPin,
	)
	if err != nil {
		return err
	}

	err = nRepo.RemoveDelegationKeys("targets/*", d.keyIDs)
	if err != nil {
		return fmt.Errorf("failed to remove keys from delegations: %v", err)
	}
	fmt.Printf(
		"Removal of the following keys from all delegations in %s staged for next publish:\n\t- %s\n",
		gun,
		strings.Join(d.keyIDs, "\n\t- "),
	)
	return maybeAutoPublish(cmd, d.autoPublish, gun, config, d.retriever)
}

// delegationsList lists all the delegations for a particular GUN
func (d *delegationCommander) delegationsList(cmd *cobra.Command, args []string) error {
	if len(args) != 1 {
		cmd.Usage()
		return fmt.Errorf(
			"Please provide a Global Unique Name as an argument to list")
	}

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	gun := args[0]

	rt, err := getTransport(config, gun, readOnly)
	if err != nil {
		return err
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	// initialize repo with transport to get latest state of the world before listing delegations
	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"), gun, getRemoteTrustServer(config), rt, d.retriever, trustPin)
	if err != nil {
		return err
	}

	delegationRoles, err := nRepo.GetDelegationRoles()
	if err != nil {
		return fmt.Errorf("Error retrieving delegation roles for repository %s: %v", gun, err)
	}

	cmd.Println("")
	prettyPrintRoles(delegationRoles, cmd.Out(), "delegations")
	cmd.Println("")
	return nil
}

// delegationRemove removes a public key from a specific role in a GUN
func (d *delegationCommander) delegationRemove(cmd *cobra.Command, args []string) error {
	if len(args) < 2 {
		cmd.Usage()
		return fmt.Errorf("must specify the Global Unique Name and the role of the delegation along with optional keyIDs and/or a list of paths to remove")
	}

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	gun := args[0]
	role := args[1]

	// Check if role is valid delegation name before requiring any user input
	if !data.IsDelegation(role) {
		return fmt.Errorf("invalid delegation name %s", role)
	}

	// If we're only given the gun and the role, attempt to remove all data for this delegation
	if len(args) == 2 && d.paths == nil && !d.allPaths {
		d.removeAll = true
	}

	keyIDs := []string{}

	if len(args) > 2 {
		keyIDs = args[2:]
	}

	// If the user passes --all-paths, don't use any of the passed in --paths
	if d.allPaths {
		d.paths = nil
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	// no online operations are performed by add so the transport argument
	// should be nil
	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"), gun, getRemoteTrustServer(config), nil, d.retriever, trustPin)
	if err != nil {
		return err
	}

	if d.removeAll {
		cmd.Println("\nAre you sure you want to remove all data for this delegation? (yes/no)")
		// Ask for confirmation before force removing delegation
		if !d.forceYes {
			confirmed := askConfirm(os.Stdin)
			if !confirmed {
				fatalf("Aborting action.")
			}
		} else {
			cmd.Println("Confirmed `yes` from flag")
		}
		// Delete the entire delegation
		err = nRepo.RemoveDelegationRole(role)
		if err != nil {
			return fmt.Errorf("failed to remove delegation: %v", err)
		}
	} else {
		if d.allPaths {
			err = nRepo.ClearDelegationPaths(role)
			if err != nil {
				return fmt.Errorf("failed to remove delegation: %v", err)
			}
		}
		// Remove any keys or paths that we passed in
		err = nRepo.RemoveDelegationKeysAndPaths(role, keyIDs, d.paths)
		if err != nil {
			return fmt.Errorf("failed to remove delegation: %v", err)
		}
	}

	cmd.Println("")
	if d.removeAll {
		cmd.Printf("Forced removal (including all keys and paths) of delegation role %s to repository \"%s\" staged for next publish.\n", role, gun)
	} else {
		removingItems := ""
		if len(keyIDs) > 0 {
			removingItems = removingItems + fmt.Sprintf("with keys %s, ", keyIDs)
		}
		if d.allPaths {
			removingItems = removingItems + "with all paths, "
		}
		if d.paths != nil {
			removingItems = removingItems + fmt.Sprintf(
				"with paths [%s], ",
				strings.Join(prettyPaths(d.paths), "\n"),
			)
		}
		cmd.Printf("Removal of delegation role %s %sto repository \"%s\" staged for next publish.\n", role, removingItems, gun)
	}
	cmd.Println("")

	return maybeAutoPublish(cmd, d.autoPublish, gun, config, d.retriever)
}

// delegationAdd creates a new delegation by adding a public key from a certificate to a specific role in a GUN
func (d *delegationCommander) delegationAdd(cmd *cobra.Command, args []string) error {
	// We must have at least the gun and role name, and at least one key or path (or the --all-paths flag) to add
	if len(args) < 2 || len(args) < 3 && d.paths == nil && !d.allPaths {
		cmd.Usage()
		return fmt.Errorf("must specify the Global Unique Name and the role of the delegation along with the public key certificate paths and/or a list of paths to add")
	}

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	gun := args[0]
	role := args[1]

	pubKeys := []data.PublicKey{}
	if len(args) > 2 {
		pubKeyPaths := args[2:]
		for _, pubKeyPath := range pubKeyPaths {
			// Read public key bytes from PEM file
			pubKeyBytes, err := ioutil.ReadFile(pubKeyPath)
			if err != nil {
				return fmt.Errorf("unable to read public key from file: %s", pubKeyPath)
			}

			// Parse PEM bytes into type PublicKey
			pubKey, err := utils.ParsePEMPublicKey(pubKeyBytes)
			if err != nil {
				return fmt.Errorf("unable to parse valid public key certificate from PEM file %s: %v", pubKeyPath, err)
			}
			pubKeys = append(pubKeys, pubKey)
		}
	}

	for _, path := range d.paths {
		if path == "" {
			d.allPaths = true
			break
		}
	}

	// If the user passes --all-paths (or gave the "" path in --paths), give the "" path
	if d.allPaths {
		d.paths = []string{""}
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	// no online operations are performed by add so the transport argument
	// should be nil
	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"), gun, getRemoteTrustServer(config), nil, d.retriever, trustPin)
	if err != nil {
		return err
	}

	// Add the delegation to the repository
	err = nRepo.AddDelegation(role, pubKeys, d.paths)
	if err != nil {
		return fmt.Errorf("failed to create delegation: %v", err)
	}

	// Make keyID slice for better CLI print
	pubKeyIDs := []string{}
	for _, pubKey := range pubKeys {
		pubKeyID, err := utils.CanonicalKeyID(pubKey)
		if err != nil {
			return err
		}
		pubKeyIDs = append(pubKeyIDs, pubKeyID)
	}

	cmd.Println("")
	addingItems := ""
	if len(pubKeyIDs) > 0 {
		addingItems = addingItems + fmt.Sprintf("with keys %s, ", pubKeyIDs)
	}
	if d.paths != nil || d.allPaths {
		addingItems = addingItems + fmt.Sprintf(
			"with paths [%s], ",
			strings.Join(prettyPaths(d.paths), "\n"),
		)
	}
	cmd.Printf(
		"Addition of delegation role %s %sto repository \"%s\" staged for next publish.\n",
		role, addingItems, gun)
	cmd.Println("")

	return maybeAutoPublish(cmd, d.autoPublish, gun, config, d.retriever)
}
<|endoftext|>


<commit_before>/*
© Copyright IBM Corporation 2018

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

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

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

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

	"github.com/ibm-messaging/mq-container/internal/command"
)

func configureWebTLS(cms *KeyStore) error {
	dir := "/run/runmqdevserver/tls"
	ks := NewJKSKeyStore(filepath.Join(dir, "key.jks"), cms.Password)
	ts := NewJKSKeyStore(filepath.Join(dir, "trust.jks"), cms.Password)

	log.Debug("Creating key store")
	err := ks.Create()
	if err != nil {
		return err
	}
	log.Debug("Creating trust store")
	err = ts.Create()
	if err != nil {
		return err
	}
	log.Debug("Importing keys")
	err = ks.Import(cms.Filename, cms.Password)
	if err != nil {
		return err
	}

	webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
	tlsConfig := filepath.Join(webConfigDir, "tls.xml")
	newTLSConfig := filepath.Join(webConfigDir, "tls-dev.xml")
	err = os.Remove(tlsConfig)
	if err != nil {
		return err
	}
	err = os.Rename(newTLSConfig, tlsConfig)
	if err != nil {
		return err
	}

	return nil
}

func configureTLS(qmName string, inputFile string, passPhrase string) error {
	log.Debug("Configuring TLS")

	_, err := os.Stat(inputFile)
	if err != nil {
		return err
	}

	// TODO: Use a persisted file (on the volume) instead?
	dir := "/run/runmqdevserver/tls"
	keyFile := filepath.Join(dir, "key.kdb")

	_, err = os.Stat(dir)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(dir, 0770)
			if err != nil {
				return err
			}
			mqmUID, mqmGID, err := command.LookupMQM()
			if err != nil {
				log.Error(err)
				return err
			}
			err = os.Chown(dir, mqmUID, mqmGID)
			if err != nil {
				log.Error(err)
				return err
			}
		} else {
			return err
		}
	}

	cms := NewCMSKeyStore(keyFile, passPhrase)

	err = cms.Create()
	if err != nil {
		return err
	}

	err = cms.CreateStash()
	if err != nil {
		return err
	}

	err = cms.Import(inputFile, passPhrase)
	if err != nil {
		return err
	}

	labels, err := cms.GetCertificateLabels()
	if err != nil {
		return err
	}
	if len(labels) == 0 {
		return fmt.Errorf("unable to find certificate label")
	}
	log.Debugf("Renaming certificate from %v", labels[0])
	const newLabel string = "devcert"
	err = cms.RenameCertificate(labels[0], newLabel)
	if err != nil {
		return err
	}

	var sslCipherSpec string
	if os.Getenv("MQ_DEV") == "true" {
		sslCipherSpec = "TLS_RSA_WITH_AES_128_CBC_SHA256"
	} else {
		sslCipherSpec = ""
	}

	const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
	const mqscTemplate string = mqsc + ".tpl"

	err = processTemplateFile(mqscTemplate, mqsc, map[string]string{
		"SSLKeyR":          filepath.Join(dir, "key"),
		"CertificateLabel": newLabel,
		"SSLCipherSpec":    sslCipherSpec,
	})
	if err != nil {
		return err
	}

	err = configureWebTLS(cms)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>ensure we use '' round blank for SSLCIPH<commit_after>/*
© Copyright IBM Corporation 2018

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

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

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

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

	"github.com/ibm-messaging/mq-container/internal/command"
)

func configureWebTLS(cms *KeyStore) error {
	dir := "/run/runmqdevserver/tls"
	ks := NewJKSKeyStore(filepath.Join(dir, "key.jks"), cms.Password)
	ts := NewJKSKeyStore(filepath.Join(dir, "trust.jks"), cms.Password)

	log.Debug("Creating key store")
	err := ks.Create()
	if err != nil {
		return err
	}
	log.Debug("Creating trust store")
	err = ts.Create()
	if err != nil {
		return err
	}
	log.Debug("Importing keys")
	err = ks.Import(cms.Filename, cms.Password)
	if err != nil {
		return err
	}

	webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
	tlsConfig := filepath.Join(webConfigDir, "tls.xml")
	newTLSConfig := filepath.Join(webConfigDir, "tls-dev.xml")
	err = os.Remove(tlsConfig)
	if err != nil {
		return err
	}
	err = os.Rename(newTLSConfig, tlsConfig)
	if err != nil {
		return err
	}

	return nil
}

func configureTLS(qmName string, inputFile string, passPhrase string) error {
	log.Debug("Configuring TLS")

	_, err := os.Stat(inputFile)
	if err != nil {
		return err
	}

	// TODO: Use a persisted file (on the volume) instead?
	dir := "/run/runmqdevserver/tls"
	keyFile := filepath.Join(dir, "key.kdb")

	_, err = os.Stat(dir)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(dir, 0770)
			if err != nil {
				return err
			}
			mqmUID, mqmGID, err := command.LookupMQM()
			if err != nil {
				log.Error(err)
				return err
			}
			err = os.Chown(dir, mqmUID, mqmGID)
			if err != nil {
				log.Error(err)
				return err
			}
		} else {
			return err
		}
	}

	cms := NewCMSKeyStore(keyFile, passPhrase)

	err = cms.Create()
	if err != nil {
		return err
	}

	err = cms.CreateStash()
	if err != nil {
		return err
	}

	err = cms.Import(inputFile, passPhrase)
	if err != nil {
		return err
	}

	labels, err := cms.GetCertificateLabels()
	if err != nil {
		return err
	}
	if len(labels) == 0 {
		return fmt.Errorf("unable to find certificate label")
	}
	log.Debugf("Renaming certificate from %v", labels[0])
	const newLabel string = "devcert"
	err = cms.RenameCertificate(labels[0], newLabel)
	if err != nil {
		return err
	}

	var sslCipherSpec string
	if os.Getenv("MQ_DEV") == "true" {
		sslCipherSpec = "TLS_RSA_WITH_AES_128_CBC_SHA256"
	} else {
		sslCipherSpec = "' '"
	}

	const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
	const mqscTemplate string = mqsc + ".tpl"

	err = processTemplateFile(mqscTemplate, mqsc, map[string]string{
		"SSLKeyR":          filepath.Join(dir, "key"),
		"CertificateLabel": newLabel,
		"SSLCipherSpec":    sslCipherSpec,
	})
	if err != nil {
		return err
	}

	err = configureWebTLS(cms)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"github.com/oleiade/trousseau"
	"log"
	"fmt"
	"strings"
)

func CreateCommand() cli.Command {
	return cli.Command{
		Name:   "create",
		Usage:  "create the trousseau data store",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to create command")
			}

			var recipients []string = strings.Split(c.Args()[0], ",")
			trousseau.CreateAction(recipients)
		},
	}
}

func PushCommand() cli.Command {
	return cli.Command{
		Name:   "push",
		Usage:  "pushes the trousseau to remote storage",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to push command")
			}

			var destination string = c.Args().First()
			trousseau.PushAction(destination, c.String("ssh-private-key"), c.Bool("ask-password"))
		},
		Flags: []cli.Flag{
			OverwriteFlag(),
			AskPassword(),
			VerboseFlag(),
			SshPrivateKeyPathFlag(),
		},
	}
}

func PullCommand() cli.Command {
	return cli.Command{
		Name:   "pull",
		Usage:  "pull the trousseau from remote storage",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to pull command")
			}

			var source string = c.Args().First()
			trousseau.PullAction(source, c.String("ssh-private-key"), c.Bool("ask-password"))
		},
		Flags: []cli.Flag{
			OverwriteFlag(),
			AskPassword(),
			VerboseFlag(),
			SshPrivateKeyPathFlag(),
		},
	}
}

func ExportCommand() cli.Command {
	return cli.Command{
		Name:   "export",
		Usage:  "export the encrypted trousseau to local fs",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to export command")
			}

			var to string = c.Args().First()
			trousseau.ExportAction(to, c.Bool("plain"))
		},
		Flags: []cli.Flag{
			OverwriteFlag(),
			PlainFlag(),
			VerboseFlag(),
		},
	}
}

func ImportCommand() cli.Command {
	return cli.Command{
		Name:   "import",
		Usage:  "import an encrypted trousseau from local fs",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to import command")
			}

			var strategy trousseau.ImportStrategy
			var yours bool = c.Bool("yours")
			var theirs bool = c.Bool("theirs")
			var overwrite bool = c.Bool("overwrite")
			var activated uint = 0

			// Ensure two import strategies were not provided at
			// the same time. Otherwise, throw an error
			for _, flag := range []bool{yours, theirs, overwrite} {
				if flag {
					activated += 1
				}
				if activated >= 2 {
					log.Fatal("--yours, --theirs and --overwrite options are mutually exclusive")
				}
			}

			// Return proper ImportStrategy according to
			// provided flags
			if overwrite == true {
				strategy = trousseau.IMPORT_OVERWRITE
			} else if theirs == true {
				strategy = trousseau.IMPORT_THEIRS
			} else {
				strategy = trousseau.IMPORT_YOURS
			}

			var from string = c.Args().First()
			trousseau.ImportAction(from, strategy, c.Bool("plain"))
		},
		Flags: []cli.Flag{
			VerboseFlag(),
			PlainFlag(),
			OverwriteFlag(),
			TheirsFlag(),
			YoursFlag(),
		},
	}
}

func ListRecipientsCommand() cli.Command {
	return cli.Command{
		Name:   "list-recipients",
		Usage:  "lists trousseau data store recipients",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Incorrect number of arguments provided to 'list-recipients' command")
			}

			trousseau.ListRecipientsAction()
		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func AddRecipientCommand() cli.Command {
	return cli.Command{
		Name:   "add-recipient",
		Usage:  "add a recipient to the encrypted trousseau",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Incorrect number of arguments to 'add-recipient' command")
			}

			trousseau.AddRecipientAction(c.Args().First())

			if c.Bool("verbose") == true {
				trousseau.Logger.Info(fmt.Sprintf("Recipient added to trousseau data store: %s", c.Args().First()))
			}
		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func RemoveRecipientCommand() cli.Command {
	return cli.Command{
		Name:   "remove-recipient",
		Usage:  "remove a recipient of the encrypted trousseau",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Incorrect number of arguments to 'remove-recipient' command")
			}

			trousseau.RemoveRecipientAction(c.Args().First())

			if c.Bool("verbose") == true {
				fmt.Printf("Recipient removed from trousseau data store: %s", c.Args().First())
			}

		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func SetCommand() cli.Command {
	return cli.Command{
		Name:   "set",
		Usage:  "sets a key value pair in the store",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 2) {
				log.Fatal("Invalid number of arguments provided to set command")
			}

			var key string = c.Args().First()
			var value string = c.Args()[1]
			var file string = c.String("file")

			trousseau.SetAction(key, value, file)

			if c.Bool("verbose") == true {
				trousseau.Logger.Info(fmt.Sprintf("%s:%s", key, value))
			}
		},
		Flags: []cli.Flag{
			FileFlag(),
			VerboseFlag(),
		},
	}
}

func GetCommand() cli.Command {
	return cli.Command{
		Name:   "get",
		Usage:  "get a value from the trousseau",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to get command")
			}

			var key string = c.Args().First()
			var file string = c.String("file")
			trousseau.GetAction(key, file)
		},
		Flags: []cli.Flag{
			FileFlag(),
		},
	}
}

func RenameCommand() cli.Command {
	return cli.Command{
		Name:   "rename",
		Usage:  "rename an existing key",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 2) {
				log.Fatal("Invalid number of arguments provided to rename command")
			}

			var src string = c.Args().First()
			var dest string = c.Args()[1]

			trousseau.RenameAction(src, dest, c.Bool("overwrite"))

			if c.Bool("verbose") == true {
				trousseau.Logger.Info(fmt.Sprintf("renamed: %s to %s", src, dest))
			}
		},
		Flags: []cli.Flag{
			OverwriteFlag(),
			VerboseFlag(),
		},
	}
}

func DelCommand() cli.Command {
	return cli.Command{
		Name:   "del",
		Usage:  "delete the point key pair from the store",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to del command")
			}

			var key string = c.Args().First()

			trousseau.DelAction(key)

			if c.Bool("verbose") == true {
				trousseau.Logger.Info(fmt.Sprintf("deleted: %s", c.Args()[0]))
			}
		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func KeysCommand() cli.Command {
	return cli.Command{
		Name:   "keys",
		Usage:  "Lists the store keys",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to keys command")
			}

			trousseau.KeysAction()
		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func ShowCommand() cli.Command {
	return cli.Command{
		Name:   "show",
		Usage:  "shows trousseau content",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to show command")
			}

			trousseau.ShowAction()
		},
	}
}

func MetaCommand() cli.Command {
	return cli.Command{
		Name:   "meta",
		Usage:  "shows trousseau metadata",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to meta command")
			}

			trousseau.MetaAction()
		},
	}
}

func UpgradeCommand() cli.Command {
	return cli.Command{
		Name:   "upgrade",
		Usage:  "Upgrade your data store to a newer version",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to upgrade command")
			}

			trousseau.UpgradeAction(c.Bool("yes"), c.Bool("no-backup"))
		},
		Flags: []cli.Flag{
			YesFlag(),
			NoBackupFlag(),
		},
	}
}

// hasExpectedArgs checks whether the number of args are as expected.
func hasExpectedArgs(args []string, expected int) bool {
	switch expected {
	case -1:
		if len(args) > 0 {
			return true
		} else {
			return false
		}
	default:
		if len(args) == expected {
			return true
		} else {
			return false
		}
	}
}

<commit_msg>Unify errors messages in cli usage<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"github.com/oleiade/trousseau"
	"log"
	"fmt"
	"strings"
)

func CreateCommand() cli.Command {
	return cli.Command{
		Name:   "create",
		Usage:  "create the trousseau data store",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to create command")
			}

			var recipients []string = strings.Split(c.Args()[0], ",")
			trousseau.CreateAction(recipients)
		},
	}
}

func PushCommand() cli.Command {
	return cli.Command{
		Name:   "push",
		Usage:  "pushes the trousseau to remote storage",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to push command")
			}

			var destination string = c.Args().First()
			trousseau.PushAction(destination, c.String("ssh-private-key"), c.Bool("ask-password"))
		},
		Flags: []cli.Flag{
			OverwriteFlag(),
			AskPassword(),
			VerboseFlag(),
			SshPrivateKeyPathFlag(),
		},
	}
}

func PullCommand() cli.Command {
	return cli.Command{
		Name:   "pull",
		Usage:  "pull the trousseau from remote storage",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to pull command")
			}

			var source string = c.Args().First()
			trousseau.PullAction(source, c.String("ssh-private-key"), c.Bool("ask-password"))
		},
		Flags: []cli.Flag{
			OverwriteFlag(),
			AskPassword(),
			VerboseFlag(),
			SshPrivateKeyPathFlag(),
		},
	}
}

func ExportCommand() cli.Command {
	return cli.Command{
		Name:   "export",
		Usage:  "export the encrypted trousseau to local fs",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to export command")
			}

			var to string = c.Args().First()
			trousseau.ExportAction(to, c.Bool("plain"))
		},
		Flags: []cli.Flag{
			OverwriteFlag(),
			PlainFlag(),
			VerboseFlag(),
		},
	}
}

func ImportCommand() cli.Command {
	return cli.Command{
		Name:   "import",
		Usage:  "import an encrypted trousseau from local fs",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to import command")
			}

			var strategy trousseau.ImportStrategy
			var yours bool = c.Bool("yours")
			var theirs bool = c.Bool("theirs")
			var overwrite bool = c.Bool("overwrite")
			var activated uint = 0

			// Ensure two import strategies were not provided at
			// the same time. Otherwise, throw an error
			for _, flag := range []bool{yours, theirs, overwrite} {
				if flag {
					activated += 1
				}
				if activated >= 2 {
					log.Fatal("--yours, --theirs and --overwrite options are mutually exclusive")
				}
			}

			// Return proper ImportStrategy according to
			// provided flags
			if overwrite == true {
				strategy = trousseau.IMPORT_OVERWRITE
			} else if theirs == true {
				strategy = trousseau.IMPORT_THEIRS
			} else {
				strategy = trousseau.IMPORT_YOURS
			}

			var from string = c.Args().First()
			trousseau.ImportAction(from, strategy, c.Bool("plain"))
		},
		Flags: []cli.Flag{
			VerboseFlag(),
			PlainFlag(),
			OverwriteFlag(),
			TheirsFlag(),
			YoursFlag(),
		},
	}
}

func ListRecipientsCommand() cli.Command {
	return cli.Command{
		Name:   "list-recipients",
		Usage:  "lists trousseau data store recipients",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to list-recipients command")
			}

			trousseau.ListRecipientsAction()
		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func AddRecipientCommand() cli.Command {
	return cli.Command{
		Name:   "add-recipient",
		Usage:  "add a recipient to the encrypted trousseau",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to add-recipient command")
			}

			trousseau.AddRecipientAction(c.Args().First())

			if c.Bool("verbose") == true {
				trousseau.Logger.Info(fmt.Sprintf("Recipient added to trousseau data store: %s", c.Args().First()))
			}
		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func RemoveRecipientCommand() cli.Command {
	return cli.Command{
		Name:   "remove-recipient",
		Usage:  "remove a recipient of the encrypted trousseau",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to remove-recipient command")
			}

			trousseau.RemoveRecipientAction(c.Args().First())

			if c.Bool("verbose") == true {
				fmt.Printf("Recipient removed from trousseau data store: %s", c.Args().First())
			}

		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func SetCommand() cli.Command {
	return cli.Command{
		Name:   "set",
		Usage:  "sets a key value pair in the store",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 2) {
				log.Fatal("Invalid number of arguments provided to set command")
			}

			var key string = c.Args().First()
			var value string = c.Args()[1]
			var file string = c.String("file")

			trousseau.SetAction(key, value, file)

			if c.Bool("verbose") == true {
				trousseau.Logger.Info(fmt.Sprintf("%s:%s", key, value))
			}
		},
		Flags: []cli.Flag{
			FileFlag(),
			VerboseFlag(),
		},
	}
}

func GetCommand() cli.Command {
	return cli.Command{
		Name:   "get",
		Usage:  "get a value from the trousseau",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to get command")
			}

			var key string = c.Args().First()
			var file string = c.String("file")
			trousseau.GetAction(key, file)
		},
		Flags: []cli.Flag{
			FileFlag(),
		},
	}
}

func RenameCommand() cli.Command {
	return cli.Command{
		Name:   "rename",
		Usage:  "rename an existing key",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 2) {
				log.Fatal("Invalid number of arguments provided to rename command")
			}

			var src string = c.Args().First()
			var dest string = c.Args()[1]

			trousseau.RenameAction(src, dest, c.Bool("overwrite"))

			if c.Bool("verbose") == true {
				trousseau.Logger.Info(fmt.Sprintf("renamed: %s to %s", src, dest))
			}
		},
		Flags: []cli.Flag{
			OverwriteFlag(),
			VerboseFlag(),
		},
	}
}

func DelCommand() cli.Command {
	return cli.Command{
		Name:   "del",
		Usage:  "delete the point key pair from the store",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 1) {
				log.Fatal("Invalid number of arguments provided to del command")
			}

			var key string = c.Args().First()

			trousseau.DelAction(key)

			if c.Bool("verbose") == true {
				trousseau.Logger.Info(fmt.Sprintf("deleted: %s", c.Args()[0]))
			}
		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func KeysCommand() cli.Command {
	return cli.Command{
		Name:   "keys",
		Usage:  "Lists the store keys",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to keys command")
			}

			trousseau.KeysAction()
		},
		Flags: []cli.Flag{
			VerboseFlag(),
		},
	}
}

func ShowCommand() cli.Command {
	return cli.Command{
		Name:   "show",
		Usage:  "shows trousseau content",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to show command")
			}

			trousseau.ShowAction()
		},
	}
}

func MetaCommand() cli.Command {
	return cli.Command{
		Name:   "meta",
		Usage:  "shows trousseau metadata",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to meta command")
			}

			trousseau.MetaAction()
		},
	}
}

func UpgradeCommand() cli.Command {
	return cli.Command{
		Name:   "upgrade",
		Usage:  "Upgrade your data store to a newer version",
		Action: func(c *cli.Context) {
			if !hasExpectedArgs(c.Args(), 0) {
				log.Fatal("Invalid number of arguments provided to upgrade command")
			}

			trousseau.UpgradeAction(c.Bool("yes"), c.Bool("no-backup"))
		},
		Flags: []cli.Flag{
			YesFlag(),
			NoBackupFlag(),
		},
	}
}

// hasExpectedArgs checks whether the number of args are as expected.
func hasExpectedArgs(args []string, expected int) bool {
	switch expected {
	case -1:
		if len(args) > 0 {
			return true
		} else {
			return false
		}
	default:
		if len(args) == expected {
			return true
		} else {
			return false
		}
	}
}

<|endoftext|>


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

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"upspin.io/bind"
	"upspin.io/factotum"
	"upspin.io/flags"
	"upspin.io/upspin"
)

func (s *State) setupserver(args ...string) {
	const (
		help = `
Setupserver is the final step of setting up an upspinserver.
It assumes that you have run 'setupdomain' and 'setupstorage'.

It registers the user created by 'setupdomain' domain with the key server,
copies the configuration files from $where/$domain to the upspinserver and
restarts it, puts the Writers file, and makes the root for the calling user.

The calling user and the server user are included in the Writers file by
default (giving them write access to the store and directory). You may specify
additional writers with the -writers flag. For instance, if you want all users
@example.com to be able to access storage, specify "-writers=*@example.com".

The calling user must be the same one that ran 'upspin setupdomain'.
`
	)
	fs := flag.NewFlagSet("setupserver", flag.ExitOnError)
	where := fs.String("where", filepath.Join(os.Getenv("HOME"), "upspin", "deploy"), "`directory` to store private configuration files")
	domain := fs.String("domain", "", "domain `name` for this Upspin installation")
	host := fs.String("host", "", "host `name` of upspinserver (empty implies the cluster dir.domain and store.domain)")
	writers := fs.String("writers", "", "additional `users` to be given write access to this server")
	s.parseFlags(fs, args, help, "setupserver -domain=<domain> -host=<host> [-where=$HOME/upspin/deploy] [-writers=user,...]")
	if *domain == "" || *host == "" {
		s.failf("the -domain and -host flags must be provided")
		fs.Usage()
	}

	cfgPath := filepath.Join(*where, *domain)
	cfg := s.readServerConfig(cfgPath)

	if !strings.Contains(*host, ":") {
		*host += ":443"
	}
	_, _, err := net.SplitHostPort(*host)
	if err != nil {
		s.exitf("invalid -host argument %q: %v", *host, err)
	}
	cfg.Addr = upspin.NetAddr(*host)

	s.writeServerConfig(cfgPath, cfg)

	// Put the server user to the key server.
	key, err := bind.KeyServer(s.config, s.config.KeyEndpoint())
	if err != nil {
		s.exit(err)
	}
	local, err := userFor(cfgPath, cfg)
	if err != nil {
		s.exit(err)
	}
	remote, err := key.Lookup(cfg.User)
	if err == nil {
		// TODO(adg): compare local and remote for discrepancies.
		_ = remote
		fmt.Printf("User %q already exists on key server.\n", cfg.User)
	} else {
		if err := key.Put(local); err != nil {
			// TODO(adg): Check whether the TXT record for this
			// domain is in place.
			s.exit(err)
		}
		fmt.Printf("Successfully put %q to the key server.\n", cfg.User)
	}

	// Create Writers file.
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "%s\n%s\n", s.config.UserName(), cfg.User)
	for _, user := range strings.Split(*writers, ",") {
		user = strings.TrimSpace(user)
		if user == "" {
			continue
		}
		fmt.Fprintf(&buf, "%s\n", user)
	}
	if err := ioutil.WriteFile(filepath.Join(cfgPath, "Writers"), buf.Bytes(), 0644); err != nil {
		s.exit(err)
	}

	// Put server config to the remote upspinserver.
	s.configureServer(cfgPath, cfg)
	fmt.Printf("Configured upspinserver at %q.\n", cfg.Addr)

	// Check that the current configuration points to our new server.
	// If not, ask the user to change it and update the key server.
	ep := upspin.Endpoint{
		Transport: upspin.Remote,
		NetAddr:   cfg.Addr,
	}
	if s.config.DirEndpoint() != ep || s.config.StoreEndpoint() != ep {
		fmt.Printf("Your current configuration in %q has these values:\n", flags.Config)
		fmt.Printf("\tdirserver: %v\n\tstoreserver: %v\n\n", s.config.DirEndpoint(), s.config.StoreEndpoint())
		fmt.Printf("To use the server we are setting up now, these values should be\n")
		fmt.Printf("\tdirserver: %v\n\tstoreserver: %v\n\n", ep, ep)
		return
	}

	// Make the current user root.
	root := string(s.config.UserName())
	s.mkdir(root)
	fmt.Printf("Created root %q.\n", root)
}

func userFor(cfgPath string, cfg *ServerConfig) (*upspin.User, error) {
	ep := []upspin.Endpoint{{
		Transport: upspin.Remote,
		NetAddr:   cfg.Addr,
	}}
	fact, err := factotum.NewFromDir(cfgPath)
	if err != nil {
		return nil, err
	}
	return &upspin.User{
		Name:      cfg.User,
		Dirs:      ep,
		Stores:    ep,
		PublicKey: fact.PublicKey(),
	}, nil
}

func (s *State) configureServer(cfgPath string, cfg *ServerConfig) {
	files := map[string][]byte{}
	for _, name := range configureServerFiles {
		b, err := ioutil.ReadFile(filepath.Join(cfgPath, name))
		if err != nil {
			s.exit(err)
		}
		files[name] = b
	}
	b, err := json.Marshal(files)
	if err != nil {
		s.exit(err)
	}

	u := "https://" + string(cfg.Addr) + "/setupserver"
	resp, err := http.Post(u, "application/octet-stream", bytes.NewReader(b))
	if err != nil {
		s.exit(err)
	}
	b, _ = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		s.exitf("upspinserver returned status %v:\n%s", resp.Status, b)
	}
}

func (s *State) readServerConfig(cfgPath string) *ServerConfig {
	cfgFile := filepath.Join(cfgPath, serverConfigFile)
	b, err := ioutil.ReadFile(cfgFile)
	if err != nil {
		if os.IsNotExist(err) {
			s.exitf("No server config file found at %q.\nRun 'upspin setupdomain' first.", cfgFile)
		}
		s.exit(err)
	}
	cfg := &ServerConfig{}
	if err := json.Unmarshal(b, cfg); err != nil {
		s.exit(err)
	}
	return cfg
}

func (s *State) writeServerConfig(cfgPath string, cfg *ServerConfig) {
	cfgFile := filepath.Join(cfgPath, serverConfigFile)
	b, err := json.Marshal(cfg)
	if err != nil {
		s.exit(err)
	}
	err = ioutil.WriteFile(cfgFile, b, 0644)
	if err != nil {
		s.exit(err)
	}
}

// Keep the following declarations in sync with cmd/upspinserver/main.go.
// TODO(adg): move these to their own package if/when there are more users.

type ServerConfig struct {
	Addr   upspin.NetAddr
	User   upspin.UserName
	Bucket string
}

const serverConfigFile = "serverconfig.json"

var configureServerFiles = []string{
	"Writers",
	"public.upspinkey",
	"secret.upspinkey",
	"serverconfig.json",
	"serviceaccount.json",
}
<commit_msg>cmd/upspin: write server user config file on 'upspin setupserver'<commit_after>// Copyright 2017 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"upspin.io/bind"
	"upspin.io/factotum"
	"upspin.io/flags"
	"upspin.io/upspin"
)

func (s *State) setupserver(args ...string) {
	const (
		help = `
Setupserver is the final step of setting up an upspinserver.
It assumes that you have run 'setupdomain' and 'setupstorage'.

It registers the user created by 'setupdomain' domain with the key server,
copies the configuration files from $where/$domain to the upspinserver and
restarts it, puts the Writers file, and makes the root for the calling user.

The calling user and the server user are included in the Writers file by
default (giving them write access to the store and directory). You may specify
additional writers with the -writers flag. For instance, if you want all users
@example.com to be able to access storage, specify "-writers=*@example.com".

The calling user must be the same one that ran 'upspin setupdomain'.
`
	)
	fs := flag.NewFlagSet("setupserver", flag.ExitOnError)
	where := fs.String("where", filepath.Join(os.Getenv("HOME"), "upspin", "deploy"), "`directory` to store private configuration files")
	domain := fs.String("domain", "", "domain `name` for this Upspin installation")
	host := fs.String("host", "", "host `name` of upspinserver (empty implies the cluster dir.domain and store.domain)")
	writers := fs.String("writers", "", "additional `users` to be given write access to this server")
	s.parseFlags(fs, args, help, "setupserver -domain=<domain> -host=<host> [-where=$HOME/upspin/deploy] [-writers=user,...]")
	if *domain == "" || *host == "" {
		s.failf("the -domain and -host flags must be provided")
		fs.Usage()
	}

	cfgPath := filepath.Join(*where, *domain)
	cfg := s.readServerConfig(cfgPath)

	// Stash the provided host name in the server config file.
	if !strings.Contains(*host, ":") {
		*host += ":443"
	}
	_, _, err := net.SplitHostPort(*host)
	if err != nil {
		s.exitf("invalid -host argument %q: %v", *host, err)
	}
	cfg.Addr = upspin.NetAddr(*host)
	s.writeServerConfig(cfgPath, cfg)

	ep := upspin.Endpoint{
		Transport: upspin.Remote,
		NetAddr:   cfg.Addr,
	}

	// Put the server user to the key server.
	key, err := bind.KeyServer(s.config, s.config.KeyEndpoint())
	if err != nil {
		s.exit(err)
	}
	local, err := userFor(cfgPath, cfg)
	if err != nil {
		s.exit(err)
	}
	remote, err := key.Lookup(cfg.User)
	if err == nil {
		// TODO(adg): compare local and remote for discrepancies.
		_ = remote
		fmt.Printf("User %q already exists on key server.\n", cfg.User)
	} else {
		if err := key.Put(local); err != nil {
			// TODO(adg): Check whether the TXT record for this
			// domain is in place.
			s.exit(err)
		}
		fmt.Printf("Successfully put %q to the key server.\n", cfg.User)
	}

	// Create Writers file.
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "%s\n%s\n", s.config.UserName(), cfg.User)
	for _, user := range strings.Split(*writers, ",") {
		user = strings.TrimSpace(user)
		if user == "" {
			continue
		}
		fmt.Fprintf(&buf, "%s\n", user)
	}
	if err := ioutil.WriteFile(filepath.Join(cfgPath, "Writers"), buf.Bytes(), 0644); err != nil {
		s.exit(err)
	}

	// Write Upspin config file for the server user,
	// for convenience when acting as the server user.
	configFile := filepath.Join(cfgPath, "config")
	configBody := new(bytes.Buffer)
	if err := configTemplate.Execute(configBody, configData{
		UserName:  upspin.UserName("upspin@" + *domain),
		Store:     &ep,
		Dir:       &ep,
		SecretDir: cfgPath,
		Packing:   "ee",
	}); err != nil {
		s.exit(err)
	}
	if err := ioutil.WriteFile(configFile, configBody.Bytes(), 0644); err != nil {
		s.exit(err)
	}

	// Put server config to the remote upspinserver.
	s.configureServer(cfgPath, cfg)
	fmt.Printf("Configured upspinserver at %q.\n", cfg.Addr)

	// Check that the current configuration points to our new server.
	// If not, ask the user to change it and update the key server.
	if s.config.DirEndpoint() != ep || s.config.StoreEndpoint() != ep {
		fmt.Printf("Your current configuration in %q has these values:\n", flags.Config)
		fmt.Printf("\tdirserver: %v\n\tstoreserver: %v\n\n", s.config.DirEndpoint(), s.config.StoreEndpoint())
		fmt.Printf("To use the server we are setting up now, these values should be\n")
		fmt.Printf("\tdirserver: %v\n\tstoreserver: %v\n\n", ep, ep)
		return
	}

	// Make the current user root.
	root := string(s.config.UserName())
	s.mkdir(root)
	fmt.Printf("Created root %q.\n", root)
}

func userFor(cfgPath string, cfg *ServerConfig) (*upspin.User, error) {
	ep := []upspin.Endpoint{{
		Transport: upspin.Remote,
		NetAddr:   cfg.Addr,
	}}
	fact, err := factotum.NewFromDir(cfgPath)
	if err != nil {
		return nil, err
	}
	return &upspin.User{
		Name:      cfg.User,
		Dirs:      ep,
		Stores:    ep,
		PublicKey: fact.PublicKey(),
	}, nil
}

func (s *State) configureServer(cfgPath string, cfg *ServerConfig) {
	files := map[string][]byte{}
	for _, name := range configureServerFiles {
		b, err := ioutil.ReadFile(filepath.Join(cfgPath, name))
		if err != nil {
			s.exit(err)
		}
		files[name] = b
	}
	b, err := json.Marshal(files)
	if err != nil {
		s.exit(err)
	}

	u := "https://" + string(cfg.Addr) + "/setupserver"
	resp, err := http.Post(u, "application/octet-stream", bytes.NewReader(b))
	if err != nil {
		s.exit(err)
	}
	b, _ = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		s.exitf("upspinserver returned status %v:\n%s", resp.Status, b)
	}
}

func (s *State) readServerConfig(cfgPath string) *ServerConfig {
	cfgFile := filepath.Join(cfgPath, serverConfigFile)
	b, err := ioutil.ReadFile(cfgFile)
	if err != nil {
		if os.IsNotExist(err) {
			s.exitf("No server config file found at %q.\nRun 'upspin setupdomain' first.", cfgFile)
		}
		s.exit(err)
	}
	cfg := &ServerConfig{}
	if err := json.Unmarshal(b, cfg); err != nil {
		s.exit(err)
	}
	return cfg
}

func (s *State) writeServerConfig(cfgPath string, cfg *ServerConfig) {
	cfgFile := filepath.Join(cfgPath, serverConfigFile)
	b, err := json.Marshal(cfg)
	if err != nil {
		s.exit(err)
	}
	err = ioutil.WriteFile(cfgFile, b, 0644)
	if err != nil {
		s.exit(err)
	}
}

// Keep the following declarations in sync with cmd/upspinserver/main.go.
// TODO(adg): move these to their own package if/when there are more users.

type ServerConfig struct {
	Addr   upspin.NetAddr
	User   upspin.UserName
	Bucket string
}

const serverConfigFile = "serverconfig.json"

var configureServerFiles = []string{
	"Writers",
	"public.upspinkey",
	"secret.upspinkey",
	"serverconfig.json",
	"serviceaccount.json",
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"regexp"

	"github.com/dokku/dokku/plugins/common"
)

//Get retreives a value from a config. If appName is empty the global config is used.
func Get(appName string, key string) (value string, ok bool) {
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return "", false
	}
	if err = validateKey(key); err != nil {
		return "", false
	}
	return env.Get(key)
}

//GetWithDefault gets a value from a config. If appName is empty the global config is used. If the appName or key do not exist defaultValue is returned.
func GetWithDefault(appName string, key string, defaultValue string) (value string) {
	value, ok := Get(appName, key)
	if !ok {
		return defaultValue
	}
	return value
}

//SetMany variables in the environment. If appName is empty the global config is used. If restart is true the app is restarted.
func SetMany(appName string, entries map[string]string, restart bool) (err error) {
	global := appName == ""
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return
	}
	keys := make([]string, 0, len(entries))
	for k := range entries {
		if err = validateKey(k); err != nil {
			return
		}
	}
	for k, v := range entries {
		env.Set(k, v)
		keys = append(keys, k)
	}
	if len(entries) != 0 {
		common.LogInfo1("Setting config vars")
		fmt.Println(prettyPrintEnvEntries("       ", entries))
		env.Write()
		triggerUpdate(appName, "set", keys)
	}
	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		triggerRestart(appName)
	}
	return
}

//UnsetMany a value in a config. If appName is empty the global config is used. If restart is true the app is restarted.
func UnsetMany(appName string, keys []string, restart bool) (err error) {
	global := appName == ""
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return
	}
	var changed = false
	for _, k := range keys {
		if err = validateKey(k); err != nil {
			return
		}
	}
	for _, k := range keys {
		if _, hasKey := env.Map()[k]; hasKey {
			common.LogInfo1(fmt.Sprintf("Unsetting %s", k))
			env.Unset(k)
			changed = true
		} else {
			common.LogInfo1(fmt.Sprintf("Skipping %s, it is not set in the environment", k))
		}
	}
	if changed {
		env.Write()
		triggerUpdate(appName, "unset", keys)
	}
	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		triggerRestart(appName)
	}
	return
}

func triggerRestart(appName string) {
	common.LogInfo1(fmt.Sprintf("Restarting app %s", appName))
	if err := common.PlugnTrigger("app-restart", appName); err != nil {
		common.LogWarn(fmt.Sprintf("Failure while restarting app: %s", err))
	}
}

func triggerUpdate(appName string, operation string, args []string) {
	args = append([]string{appName, operation}, args...)
	if err := common.PlugnTrigger("post-config-update", args...); err != nil {
		common.LogWarn(fmt.Sprintf("Failure while triggering post-config-update: %s", err))
	}
}

func loadAppOrGlobalEnv(appName string) (env *Env, err error) {
	if appName == "" || appName == "--global" {
		return LoadGlobalEnv()
	}
	return LoadAppEnv(appName)
}

func validateKey(key string) error {
	r, _ := regexp.Compile("^[a-zA-Z_][a-zA-Z0-9_]*$")
	if !r.MatchString(key) {
		return fmt.Errorf("Invalid key name: '%s'", key)
	}
	return nil
}
<commit_msg>Fix a typo [ci skip]<commit_after>package config

import (
	"fmt"
	"regexp"

	"github.com/dokku/dokku/plugins/common"
)

//Get retrieves a value from a config. If appName is empty the global config is used.
func Get(appName string, key string) (value string, ok bool) {
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return "", false
	}
	if err = validateKey(key); err != nil {
		return "", false
	}
	return env.Get(key)
}

//GetWithDefault gets a value from a config. If appName is empty the global config is used. If the appName or key do not exist defaultValue is returned.
func GetWithDefault(appName string, key string, defaultValue string) (value string) {
	value, ok := Get(appName, key)
	if !ok {
		return defaultValue
	}
	return value
}

//SetMany variables in the environment. If appName is empty the global config is used. If restart is true the app is restarted.
func SetMany(appName string, entries map[string]string, restart bool) (err error) {
	global := appName == ""
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return
	}
	keys := make([]string, 0, len(entries))
	for k := range entries {
		if err = validateKey(k); err != nil {
			return
		}
	}
	for k, v := range entries {
		env.Set(k, v)
		keys = append(keys, k)
	}
	if len(entries) != 0 {
		common.LogInfo1("Setting config vars")
		fmt.Println(prettyPrintEnvEntries("       ", entries))
		env.Write()
		triggerUpdate(appName, "set", keys)
	}
	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		triggerRestart(appName)
	}
	return
}

//UnsetMany a value in a config. If appName is empty the global config is used. If restart is true the app is restarted.
func UnsetMany(appName string, keys []string, restart bool) (err error) {
	global := appName == ""
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return
	}
	var changed = false
	for _, k := range keys {
		if err = validateKey(k); err != nil {
			return
		}
	}
	for _, k := range keys {
		if _, hasKey := env.Map()[k]; hasKey {
			common.LogInfo1(fmt.Sprintf("Unsetting %s", k))
			env.Unset(k)
			changed = true
		} else {
			common.LogInfo1(fmt.Sprintf("Skipping %s, it is not set in the environment", k))
		}
	}
	if changed {
		env.Write()
		triggerUpdate(appName, "unset", keys)
	}
	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		triggerRestart(appName)
	}
	return
}

func triggerRestart(appName string) {
	common.LogInfo1(fmt.Sprintf("Restarting app %s", appName))
	if err := common.PlugnTrigger("app-restart", appName); err != nil {
		common.LogWarn(fmt.Sprintf("Failure while restarting app: %s", err))
	}
}

func triggerUpdate(appName string, operation string, args []string) {
	args = append([]string{appName, operation}, args...)
	if err := common.PlugnTrigger("post-config-update", args...); err != nil {
		common.LogWarn(fmt.Sprintf("Failure while triggering post-config-update: %s", err))
	}
}

func loadAppOrGlobalEnv(appName string) (env *Env, err error) {
	if appName == "" || appName == "--global" {
		return LoadGlobalEnv()
	}
	return LoadAppEnv(appName)
}

func validateKey(key string) error {
	r, _ := regexp.Compile("^[a-zA-Z_][a-zA-Z0-9_]*$")
	if !r.MatchString(key) {
		return fmt.Errorf("Invalid key name: '%s'", key)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
	rulehunter - A server to find rules in data based on user specified goals
	Copyright (C) 2016 vLife Systems Ltd <http://vlifesystems.com>

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

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

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

import (
	"fmt"
	"github.com/kardianos/service"
	"github.com/vlifesystems/rulehunter/config"
	"github.com/vlifesystems/rulehunter/experiment"
	"github.com/vlifesystems/rulehunter/html"
	"github.com/vlifesystems/rulehunter/html/cmd"
	"github.com/vlifesystems/rulehunter/logger"
	"github.com/vlifesystems/rulehunter/progress"
	"github.com/vlifesystems/rulehunter/quitter"
	"github.com/vlifesystems/rulehunter/watcher"
	"log"
	"os"
	"path/filepath"
)

func main() {
	flags := parseFlags()
	l := logger.NewSvcLogger()
	exitCode, err := subMain(flags, l)
	if err != nil {
		log.Fatal(err)
	}
	os.Exit(exitCode)
}

// subMain is split from main to improve testing. quitter is used to stop
// the routine.
func subMain(
	flags *cmdFlags,
	l logger.Logger,
) (int, error) {
	if err := handleFlags(flags); err != nil {
		return 1, err
	}

	quit := quitter.New()
	defer quit.Quit()

	configFilename := filepath.Join(flags.configDir, "config.yaml")
	config, err := config.Load(configFilename)
	if err != nil {
		return 1, errConfigLoad{filename: configFilename, err: err}
	}

	htmlCmds := make(chan cmd.Cmd, 100)
	pm, err := progress.NewMonitor(
		filepath.Join(config.BuildDir, "progress"),
		htmlCmds,
	)
	if err != nil {
		return 1, err
	}
	prg := newProgram(config, pm, l, quit)

	s, err := newService(prg, config, flags)
	if err != nil {
		return 1, err
	}
	svcLogger, err := s.Logger(nil)
	if err != nil {
		return 1, err
	}

	l.SetSvcLogger(svcLogger)
	go l.Run(quit)
	go html.Run(config, prg.progressMonitor, l, quit, htmlCmds)

	if flags.install {
		s.Uninstall()
		if err = s.Install(); err != nil {
			return 1, err
		}
	} else if flags.serve {
		quit.Add()
		defer quit.Done()
		if err = s.Run(); err != nil {
			return 1, err
		}
	} else {
		if err := processDir(config, pm, l); err != nil {
			return 1, fmt.Errorf("Errors while processing dir")
		}
	}
	return 0, nil
}

func processDir(
	config *config.Config,
	pm *progress.ProgressMonitor,
	l logger.Logger,
) error {
	files, err := watcher.GetExperimentFiles(config.ExperimentsDir)
	if err != nil {
		return err
	}
	for _, file := range files {
		if err := pm.AddExperiment(file.Name()); err != nil {
			l.Error(err.Error())
			return err
		}
		if err := experiment.Process(file, config, l, pm); err != nil {
			l.Error(err.Error())
			return err
		}
	}
	return nil
}

func newService(
	prg *program,
	cfg *config.Config,
	flags *cmdFlags,
) (service.Service, error) {
	svcConfig := &service.Config{
		Name:        "rulehunter",
		DisplayName: "Rulehunter server",
		Description: "Rulehunter finds rules in data based on user specified goals.",
	}

	if cfg.User != "" {
		svcConfig.UserName = cfg.User
	}

	if flags.configDir != "" {
		svcConfig.Arguments =
			[]string{fmt.Sprintf("-configdir=%s", flags.configDir), "-serve"}
	}
	s, err := service.New(prg, svcConfig)
	return s, err
}
<commit_msg>Remove redundant quit.Add/Done before s.Run()<commit_after>/*
	rulehunter - A server to find rules in data based on user specified goals
	Copyright (C) 2016 vLife Systems Ltd <http://vlifesystems.com>

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

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

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

import (
	"fmt"
	"github.com/kardianos/service"
	"github.com/vlifesystems/rulehunter/config"
	"github.com/vlifesystems/rulehunter/experiment"
	"github.com/vlifesystems/rulehunter/html"
	"github.com/vlifesystems/rulehunter/html/cmd"
	"github.com/vlifesystems/rulehunter/logger"
	"github.com/vlifesystems/rulehunter/progress"
	"github.com/vlifesystems/rulehunter/quitter"
	"github.com/vlifesystems/rulehunter/watcher"
	"log"
	"os"
	"path/filepath"
)

func main() {
	flags := parseFlags()
	l := logger.NewSvcLogger()
	exitCode, err := subMain(flags, l)
	if err != nil {
		log.Fatal(err)
	}
	os.Exit(exitCode)
}

// subMain is split from main to improve testing. quitter is used to stop
// the routine.
func subMain(
	flags *cmdFlags,
	l logger.Logger,
) (int, error) {
	if err := handleFlags(flags); err != nil {
		return 1, err
	}

	quit := quitter.New()
	defer quit.Quit()

	configFilename := filepath.Join(flags.configDir, "config.yaml")
	config, err := config.Load(configFilename)
	if err != nil {
		return 1, errConfigLoad{filename: configFilename, err: err}
	}

	htmlCmds := make(chan cmd.Cmd, 100)
	pm, err := progress.NewMonitor(
		filepath.Join(config.BuildDir, "progress"),
		htmlCmds,
	)
	if err != nil {
		return 1, err
	}
	prg := newProgram(config, pm, l, quit)

	s, err := newService(prg, config, flags)
	if err != nil {
		return 1, err
	}
	svcLogger, err := s.Logger(nil)
	if err != nil {
		return 1, err
	}

	l.SetSvcLogger(svcLogger)
	go l.Run(quit)
	go html.Run(config, prg.progressMonitor, l, quit, htmlCmds)

	if flags.install {
		s.Uninstall()
		if err = s.Install(); err != nil {
			return 1, err
		}
	} else if flags.serve {
		if err = s.Run(); err != nil {
			return 1, err
		}
	} else {
		if err := processDir(config, pm, l); err != nil {
			return 1, fmt.Errorf("Errors while processing dir")
		}
	}
	return 0, nil
}

func processDir(
	config *config.Config,
	pm *progress.ProgressMonitor,
	l logger.Logger,
) error {
	files, err := watcher.GetExperimentFiles(config.ExperimentsDir)
	if err != nil {
		return err
	}
	for _, file := range files {
		if err := pm.AddExperiment(file.Name()); err != nil {
			l.Error(err.Error())
			return err
		}
		if err := experiment.Process(file, config, l, pm); err != nil {
			l.Error(err.Error())
			return err
		}
	}
	return nil
}

func newService(
	prg *program,
	cfg *config.Config,
	flags *cmdFlags,
) (service.Service, error) {
	svcConfig := &service.Config{
		Name:        "rulehunter",
		DisplayName: "Rulehunter server",
		Description: "Rulehunter finds rules in data based on user specified goals.",
	}

	if cfg.User != "" {
		svcConfig.UserName = cfg.User
	}

	if flags.configDir != "" {
		svcConfig.Arguments =
			[]string{fmt.Sprintf("-configdir=%s", flags.configDir), "-serve"}
	}
	s, err := service.New(prg, svcConfig)
	return s, err
}
<|endoftext|>


<commit_before>package jobs

import (
	"context"
	"time"

	"github.com/getfider/fider/app"
	"github.com/getfider/fider/app/models/dto"
	"github.com/getfider/fider/app/pkg/dbx"
	"github.com/getfider/fider/app/pkg/env"
	"github.com/getfider/fider/app/pkg/errors"
	"github.com/getfider/fider/app/pkg/log"
	"github.com/getfider/fider/app/pkg/rand"
)

type Handler interface {
	Run(ctx context.Context) error
	Schedule() string
}

type fiderJob struct {
	Name    string
	Handler Handler
}

func NewJob(ctx context.Context, name string, handler Handler) (string, fiderJob) {
	schedule := handler.Schedule()
	if env.IsDevelopment() {
		// job runs every 1 minute during development mode
		schedule = "0 * * * * *"
	}
	log.Debugf(ctx, "Job '@{JobName}' scheduled to run '@{Schedule}'", dto.Props{
		"JobName":  name,
		"Schedule": schedule,
	})
	return schedule, fiderJob{Name: name, Handler: handler}
}

func (j fiderJob) Run() {
	ctx := context.Background()
	ctx = log.WithProperties(ctx, dto.Props{
		log.PropertyKeyContextID: rand.String(32),
		log.PropertyKeyTag:       "JOBS",
	})

	trx, err := dbx.BeginTx(ctx)
	if err != nil {
		log.Error(ctx, errors.Wrap(err, "failed to open transaction while acquiring advisory lock"))
		return
	}

	ctx = context.WithValue(ctx, app.TransactionCtxKey, trx)

	defer func() {
		if r := recover(); r != nil {
			log.Error(ctx, errors.Panicked(r))
		}
	}()

	start := time.Now()
	log.Debugf(ctx, "Job '@{JobName}' started", dto.Props{
		"JobName": j.Name,
	})

	locked, unlock := dbx.TryLock(ctx, trx, j.Name)
	if !locked {
		log.Debugf(ctx, "Job '@{JobName}' skipped, could not acquire lock", dto.Props{
			"JobName": j.Name,
		})
		return
	}

	defer func() {
		elapsedMs := time.Since(start).Nanoseconds() / int64(time.Millisecond)
		log.Debugf(ctx, "Job '@{JobName}' finished in @{ElapsedMs:magenta}ms", dto.Props{
			"ElapsedMs": elapsedMs,
			"JobName":   j.Name,
		})

		// Jobs should take at least 1sec before unlocking to avoid double execution
		if elapsedMs <= 1000 {
			waitMs := time.Duration(1000 - elapsedMs)
			time.Sleep(waitMs * time.Millisecond)
		}
		unlock()
	}()

	if err := j.Handler.Run(ctx); err != nil {
		log.Error(ctx, err)
	}
}
<commit_msg>fix: release db threads on jobs<commit_after>package jobs

import (
	"context"
	"time"

	"github.com/getfider/fider/app"
	"github.com/getfider/fider/app/models/dto"
	"github.com/getfider/fider/app/pkg/dbx"
	"github.com/getfider/fider/app/pkg/env"
	"github.com/getfider/fider/app/pkg/errors"
	"github.com/getfider/fider/app/pkg/log"
	"github.com/getfider/fider/app/pkg/rand"
)

type Handler interface {
	Run(ctx context.Context) error
	Schedule() string
}

type fiderJob struct {
	Name    string
	Handler Handler
}

func NewJob(ctx context.Context, name string, handler Handler) (string, fiderJob) {
	schedule := handler.Schedule()
	if env.IsDevelopment() {
		// job runs every 1 minute during development mode
		schedule = "0 * * * * *"
	}
	log.Debugf(ctx, "Job '@{JobName}' scheduled to run '@{Schedule}'", dto.Props{
		"JobName":  name,
		"Schedule": schedule,
	})
	return schedule, fiderJob{Name: name, Handler: handler}
}

func (j fiderJob) Run() {
	ctx := context.Background()
	ctx = log.WithProperties(ctx, dto.Props{
		log.PropertyKeyContextID: rand.String(32),
		log.PropertyKeyTag:       "JOBS",
	})

	trx, err := dbx.BeginTx(ctx)
	if err != nil {
		log.Error(ctx, errors.Wrap(err, "failed to open transaction"))
		return
	}

	ctx = context.WithValue(ctx, app.TransactionCtxKey, trx)

	defer func() {
		if r := recover(); r != nil {
			log.Error(ctx, errors.Panicked(r))
			trx.MustRollback()
		}
	}()

	start := time.Now()
	log.Debugf(ctx, "Job '@{JobName}' started", dto.Props{
		"JobName": j.Name,
	})

	locked, unlock := dbx.TryLock(ctx, trx, j.Name)
	if !locked {
		log.Debugf(ctx, "Job '@{JobName}' skipped, could not acquire lock", dto.Props{
			"JobName": j.Name,
		})
		trx.MustCommit()
		return
	}

	defer func() {
		elapsedMs := time.Since(start).Nanoseconds() / int64(time.Millisecond)
		log.Debugf(ctx, "Job '@{JobName}' finished in @{ElapsedMs:magenta}ms", dto.Props{
			"ElapsedMs": elapsedMs,
			"JobName":   j.Name,
		})

		// Jobs should take at least 1sec before unlocking to avoid double execution
		if elapsedMs <= 1000 {
			waitMs := time.Duration(1000 - elapsedMs)
			time.Sleep(waitMs * time.Millisecond)
		}
		unlock()
	}()

	if err := j.Handler.Run(ctx); err != nil {
		log.Error(ctx, err)
		trx.MustRollback()
	} else {
		trx.MustCommit()
	}
}
<|endoftext|>


<commit_before>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

// Package agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package dao

import (
	"fmt"
	"io/ioutil"
//	"os"
	"strings"
	"testing"
)

func getTestingService() ServiceDefinition {
	service := ServiceDefinition{
		Name:        "testsvc",
		Description: "Top level service. This directory is part of a unit test.",
		LogFilters: map[string]string{
			"Pepe": "My Test Filter",
		},
		Services: []ServiceDefinition{
			ServiceDefinition{
				Name:    "s1",
				Command: "/usr/bin/python -m SimpleHTTPServer",
				ImageId: "ubuntu",
				LogFilters: map[string]string{
					"Pepe2": "My Second Filter",
				},
				ConfigFiles: map[string]ConfigFile{
					"/etc/my.cnf": ConfigFile{Filename: "/etc/my.cnf", Content: "\n# SAMPLE config file for mysql\n\n[mysqld]\n\ninnodb_buffer_pool_size = 16G\n\n"},
				},
				Endpoints: []ServiceEndpoint{
					ServiceEndpoint{
						Protocol:    "tcp",
						PortNumber:  8080,
						Application: "www",
						Purpose:     "export",
					},
					ServiceEndpoint{
						Protocol:    "tcp",
						PortNumber:  8081,
						Application: "websvc",
						Purpose:     "import",
					},
				},
				LogConfigs: []LogConfig{
					LogConfig{
						Path: "/tmp/foo",
						Type: "foo",
						Filters: []string{
							"Pepe",
						},
					},
				},
			},
			ServiceDefinition{
				Name:    "s2",
				Command: "/usr/bin/python -m SimpleHTTPServer",
				ImageId: "ubuntu",
				Endpoints: []ServiceEndpoint{
					ServiceEndpoint{
						Protocol:    "tcp",
						PortNumber:  8080,
						Application: "websvc",
						Purpose:     "export",
					},
				},
				LogConfigs: []LogConfig{
					LogConfig{
						Path: "/tmp/foo",
						Type: "foo",
					},
				},
			},
		},
	}

	return service
}

func TestGettingFilterDefinitionsFromServiceDefinitions(t *testing.T) {
	services := make([]ServiceDefinition, 1)
	services[0] = getTestingService()
	filterDefs := getFilterDefinitions(services)

	// make sure we find the specific filter definition we are looking for
	if filterDefs["Pepe"] != "My Test Filter" {
		t.Error("Was unable to extract the filter definition")
	}

	// make sure the number matches the number we define
	if len(filterDefs) != 2 {
		t.Error("Found " + string(len(filterDefs)) + " instead of 2 filter definitions")
	}
}

func TestConstructingFilterString(t *testing.T) {
	services := make([]ServiceDefinition, 1)
	services[0] = getTestingService()
	filterDefs := getFilterDefinitions(services)
	filters := getFilters(services, filterDefs)
	testString := "My Test Filter"

	// make sure our test filter definition is in the constructed filters
	if !strings.Contains(filters, testString) {
		t.Error(fmt.Sprintf("Was unable to find %s in the filters", testString))
	}
}

func TestWritingConfigFile(t *testing.T) {
	filters := "This is my test filter"
	tmpfile, err := ioutil.TempFile("", "logstash_test.conf")
	t.Logf("Created tempfile: %s", tmpfile.Name())
	if err != nil {
		t.Logf("could not create tempfile: %s", err)
		t.FailNow()
	}
	defer tmpfile.Close()
        _, err = tmpfile.Write([]byte("${FILTER_SECTION}"))
	if err != nil {
		t.Logf("%s", err)
		t.FailNow()
	}
	err = tmpfile.Sync()
	if err != nil {
		t.Logf("%s", err)
		t.FailNow()
	}

	//defer os.Remove(tmpfile.Name())

	if err = writeLogStashConfigFile(filters, tmpfile.Name()); err != nil {
		t.Error("error calling writeLogStashConfigFile: %s", err)
		t.Fail()
	}

	// read the contents
	contents, err := ioutil.ReadFile(tmpfile.Name())
	if err != nil {
		t.Error(fmt.Sprintf("Unable to read output file %v", err))
	}

	// make sure our filter string is in it
	if !strings.Contains(string(contents), filters) {
		t.Log("Read in contents: %s", string(contents))
		t.Log(filters)
		t.Error("Was unable to write the logstash conf file")
	}
}
<commit_msg>update test<commit_after>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

// Package agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package dao

import (
	"fmt"
	"io/ioutil"
//	"os"
	"strings"
	"testing"
)

func getTestingService() ServiceDefinition {
	service := ServiceDefinition{
		Name:        "testsvc",
		Description: "Top level service. This directory is part of a unit test.",
		LogFilters: map[string]string{
			"Pepe": "My Test Filter",
		},
		Services: []ServiceDefinition{
			ServiceDefinition{
				Name:    "s1",
				Command: "/usr/bin/python -m SimpleHTTPServer",
				ImageId: "ubuntu",
				LogFilters: map[string]string{
					"Pepe2": "My Second Filter",
				},
				ConfigFiles: map[string]ConfigFile{
					"/etc/my.cnf": ConfigFile{Filename: "/etc/my.cnf", Content: "\n# SAMPLE config file for mysql\n\n[mysqld]\n\ninnodb_buffer_pool_size = 16G\n\n"},
				},
				Endpoints: []ServiceEndpoint{
					ServiceEndpoint{
						Protocol:    "tcp",
						PortNumber:  8080,
						Application: "www",
						Purpose:     "export",
					},
					ServiceEndpoint{
						Protocol:    "tcp",
						PortNumber:  8081,
						Application: "websvc",
						Purpose:     "import",
					},
				},
				LogConfigs: []LogConfig{
					LogConfig{
						Path: "/tmp/foo",
						Type: "foo",
						Filters: []string{
							"Pepe",
						},
					},
				},
			},
			ServiceDefinition{
				Name:    "s2",
				Command: "/usr/bin/python -m SimpleHTTPServer",
				ImageId: "ubuntu",
				Endpoints: []ServiceEndpoint{
					ServiceEndpoint{
						Protocol:    "tcp",
						PortNumber:  8080,
						Application: "websvc",
						Purpose:     "export",
					},
				},
				LogConfigs: []LogConfig{
					LogConfig{
						Path: "/tmp/foo",
						Type: "foo",
					},
				},
			},
		},
	}

	return service
}

func TestGettingFilterDefinitionsFromServiceDefinitions(t *testing.T) {
	services := make([]ServiceDefinition, 1)
	services[0] = getTestingService()
	filterDefs := getFilterDefinitions(services)

	// make sure we find the specific filter definition we are looking for
	if filterDefs["Pepe"] != "My Test Filter" {
		t.Error("Was unable to extract the filter definition")
	}

	// make sure the number matches the number we define
	if len(filterDefs) != 2 {
		t.Error("Found " + string(len(filterDefs)) + " instead of 2 filter definitions")
	}
}

func TestConstructingFilterString(t *testing.T) {
	services := make([]ServiceDefinition, 1)
	services[0] = getTestingService()
	filterDefs := getFilterDefinitions(services)
	filters := getFilters(services, filterDefs)
	testString := "My Test Filter"

	// make sure our test filter definition is in the constructed filters
	if !strings.Contains(filters, testString) {
		t.Error(fmt.Sprintf("Was unable to find %s in the filters", testString))
	}
}

func TestWritingConfigFile(t *testing.T) {
	filters := "This is my test filter"
	tmpfile, err := ioutil.TempFile("", "logstash_test.conf")
	t.Logf("Created tempfile: %s", tmpfile.Name())
	if err != nil {
		t.Logf("could not create tempfile: %s", err)
		t.FailNow()
	}
	defer tmpfile.Close()
	defer os.Remove(tmpfile.Name())
        _, err = tmpfile.Write([]byte("${FILTER_SECTION}"))
	if err != nil {
		t.Logf("%s", err)
		t.FailNow()
	}
	err = tmpfile.Sync()
	if err != nil {
		t.Logf("%s", err)
		t.FailNow()
	}

	if err = writeLogStashConfigFile(filters, tmpfile.Name()); err != nil {
		t.Error("error calling writeLogStashConfigFile: %s", err)
		t.Fail()
	}

	// read the contents
	contents, err := ioutil.ReadFile(tmpfile.Name())
	if err != nil {
		t.Error(fmt.Sprintf("Unable to read output file %v", err))
	}

	// make sure our filter string is in it
	if !strings.Contains(string(contents), filters) {
		t.Log("Read in contents: %s", string(contents))
		t.Log(filters)
		t.Error("Was unable to write the logstash conf file")
	}
}
<|endoftext|>


<commit_before>package beats1

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dghubble/oauth1"
	"github.com/jesselucas/slackcmd/slack"
	"io/ioutil"
	"os"
	"strings"
)

type Command struct {
}

func (cmd Command) Request(sc *slack.SlashCommand) (*slack.CommandPayload, error) {
	// read credentials from environment variables
	slackAPIKey := os.Getenv("SLACK_KEY_BEATS1")
	consumerKey := os.Getenv("TWITTER_CONSUMER_KEY")
	consumerSecret := os.Getenv("TWITTER_CONSUMER_SECRET")
	accessToken := os.Getenv("TWITTER_ACCESS_TOKEN")
	accessTokenSecret := os.Getenv("TWITTER_ACCESS_TOKEN_SECRET")
	if slackAPIKey == "" || consumerKey == "" || consumerSecret == "" || accessToken == "" || accessTokenSecret == "" {
		panic("Missing required environment variable")
	}

	// Verify the request is coming from Slack
	if sc.Token != slackAPIKey {
		err := errors.New("Unauthorized Slack")
		return nil, err
	}

	// create payload
	cp := &slack.CommandPayload{
		Channel:       fmt.Sprintf("@%v", sc.UserName),
		Username:      "Beats1",
		Emoji:         ":metal:",
		SlashResponse: true,
		SendPayload:   false,
	}

	cp.Text = "Beats1"

	config := oauth1.NewConfig(consumerKey, consumerSecret)
	token := oauth1.NewToken(accessToken, accessTokenSecret)

	// httpClient will automatically authorize http.Request's
	httpClient := config.Client(token)

	url := fmt.Sprintf(
		"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=%v&count=%v&trim_user=%v",
		"beats1plays",
		"1",
		"true",
	)
	res, err := httpClient.Get(url)
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	var tweets []tweet
	json.Unmarshal(body, &tweets)

	var responseString string
	for _, t := range tweets {
		fmt.Println(t)

		responseString += fmt.Sprint(t)
	}

	cp.Text = string(responseString)

	return cp, nil
}

type tweet struct {
	Text     string `json:"text"`
	Entities entity `json:"entities"`
}

func (t tweet) String() string {

	// remove hashtag
	text := strings.Replace(t.Text, "#beats1", "", -1)

	// remove Urls
	urls := t.Entities.Urls
	var expandedURL string
	for _, u := range urls {
		urlString := u.Url
		text = strings.Replace(text, urlString, "", -1)
		expandedURL += u.ExpandedURL
	}

	// remove media
	media := t.Entities.Media
	for _, m := range media {
		mediaURL := m.Url
		text = strings.Replace(text, mediaURL, "", -1)
	}

	// trime white space
	text = strings.TrimSpace(text)

	return fmt.Sprintf(
		"<%v|%v>",
		slack.SanitizeString(expandedURL),
		slack.SanitizeString(text),
	)
}

type entity struct {
	Urls  []url   `json:"urls"`
	Media []media `json:"media"`
}

type url struct {
	Url         string `json:"url"`
	ExpandedURL string `json:"expanded_url"`
}

type media struct {
	Url string `json:"url"`
}
<commit_msg>Checking if there an expanded url before formatting beats1 string to return to slack.<commit_after>package beats1

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dghubble/oauth1"
	"github.com/jesselucas/slackcmd/slack"
	"io/ioutil"
	"os"
	"strings"
)

type Command struct {
}

func (cmd Command) Request(sc *slack.SlashCommand) (*slack.CommandPayload, error) {
	// read credentials from environment variables
	slackAPIKey := os.Getenv("SLACK_KEY_BEATS1")
	consumerKey := os.Getenv("TWITTER_CONSUMER_KEY")
	consumerSecret := os.Getenv("TWITTER_CONSUMER_SECRET")
	accessToken := os.Getenv("TWITTER_ACCESS_TOKEN")
	accessTokenSecret := os.Getenv("TWITTER_ACCESS_TOKEN_SECRET")
	if slackAPIKey == "" || consumerKey == "" || consumerSecret == "" || accessToken == "" || accessTokenSecret == "" {
		panic("Missing required environment variable")
	}

	// Verify the request is coming from Slack
	if sc.Token != slackAPIKey {
		err := errors.New("Unauthorized Slack")
		return nil, err
	}

	// create payload
	cp := &slack.CommandPayload{
		Channel:       fmt.Sprintf("@%v", sc.UserName),
		Username:      "Beats1",
		Emoji:         ":metal:",
		SlashResponse: true,
		SendPayload:   false,
	}

	cp.Text = "Beats1"

	config := oauth1.NewConfig(consumerKey, consumerSecret)
	token := oauth1.NewToken(accessToken, accessTokenSecret)

	// httpClient will automatically authorize http.Request's
	httpClient := config.Client(token)

	url := fmt.Sprintf(
		"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=%v&count=%v&trim_user=%v",
		"beats1plays",
		"1",
		"true",
	)
	res, err := httpClient.Get(url)
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	var tweets []tweet
	json.Unmarshal(body, &tweets)

	var responseString string
	for _, t := range tweets {
		fmt.Println(t)

		responseString += fmt.Sprint(t)
	}

	cp.Text = string(responseString)

	return cp, nil
}

type tweet struct {
	Text     string `json:"text"`
	Entities entity `json:"entities"`
}

func (t tweet) String() string {

	// remove hashtag
	text := strings.Replace(t.Text, "#beats1", "", -1)

	// remove Urls
	urls := t.Entities.Urls
	var expandedURL string
	for _, u := range urls {
		urlString := u.Url
		text = strings.Replace(text, urlString, "", -1)
		expandedURL += u.ExpandedURL
	}

	// remove media
	media := t.Entities.Media
	for _, m := range media {
		mediaURL := m.Url
		text = strings.Replace(text, mediaURL, "", -1)
	}

	// trime white space
	text = strings.TrimSpace(text)

	// if there is an expandedURL
	if expandedURL != "" {
		return fmt.Sprintf(
			"<%v|%v>",
			expandedURL,
			text,
		)
	}

	return fmt.Sprintf(
		"%v",
		text,
	)

}

type entity struct {
	Urls  []url   `json:"urls"`
	Media []media `json:"media"`
}

type url struct {
	Url         string `json:"url"`
	ExpandedURL string `json:"expanded_url"`
}

type media struct {
	Url string `json:"url"`
}
<|endoftext|>


<commit_before>package commands

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/zetamatta/go-windows-netresource"
	"github.com/zetamatta/go-windows-su"
)

func _getwd() string {
	wd, err := os.Getwd()
	if err != nil {
		return ""
	}
	return wd
}

func _clone(action string, out io.Writer) (int, error) {
	wd := _getwd()
	var err error
	var me string
	me, err = os.Executable()
	if err != nil {
		return 1, err
	}
	var pid int
	pid, err = su.ShellExecute(action, me, "", wd)
	if err != nil {
		pid, err = su.ShellExecute(action, "CMD.EXE", "/c \""+me+"\"", wd)
		if err != nil {
			return 1, err // return original error
		}
	}
	if pid > 0 {
		fmt.Fprintf(out, "[%d]\n", pid)
		if process, err := os.FindProcess(pid); err == nil {
			go func() {
				process.Wait()
				fmt.Fprintf(os.Stderr, "[%d]+ Done\n", pid)
			}()
		}
	}
	return 0, nil
}

func cmdClone(ctx context.Context, cmd Param) (int, error) {
	return _clone("open", cmd.Err())
}

func cmdSu(ctx context.Context, cmd Param) (int, error) {
	me, err := os.Executable()
	if err != nil {
		return 1, err
	}
	if me2, err2 := filepath.EvalSymlinks(me); err2 == nil {
		me = me2
	}
	wd, err := os.Getwd()
	if err != nil {
		return 2, err
	}
	if strings.HasSuffix(wd, `\`) {
		wd += "."
	}

	var buffer strings.Builder

	if netdrives, err := netresource.GetNetDrives(); err == nil {
		for _, n := range netdrives {
			fmt.Fprintf(&buffer, ` --netuse "%c:=%s"`, n.Letter, n.Remote)
		}
	}
	fmt.Fprintf(&buffer, ` --chdir "%s"`, wd)

	pid, err := su.ShellExecute("runas", me, buffer.String(), "")
	if err != nil {
		return 3, err
	}
	if pid > 0 {
		fmt.Fprintf(cmd.Err(), "[%d]\n", pid)
	}
	return 0, nil
}
<commit_msg>Fix: `clone` command showed security error dialog<commit_after>package commands

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/zetamatta/go-windows-netresource"
	"github.com/zetamatta/go-windows-su"
)

func _getwd() string {
	wd, err := os.Getwd()
	if err != nil {
		return ""
	}
	return wd
}

func _clone(action string, out io.Writer) (int, error) {
	wd := _getwd()
	var err error
	var me string
	me, err = os.Executable()
	if err != nil {
		return 1, err
	}
	var pid int
	if _me, err := filepath.EvalSymlinks(me); err == nil {
		me = _me
	}
	pid, err = su.ShellExecute(action, me, "", wd)
	if err != nil {
		pid, err = su.ShellExecute(action, "CMD.EXE", "/c \""+me+"\"", wd)
		if err != nil {
			return 1, err // return original error
		}
	}
	if pid > 0 {
		fmt.Fprintf(out, "[%d]\n", pid)
		if process, err := os.FindProcess(pid); err == nil {
			go func() {
				process.Wait()
				fmt.Fprintf(os.Stderr, "[%d]+ Done\n", pid)
			}()
		}
	}
	return 0, nil
}

func cmdClone(ctx context.Context, cmd Param) (int, error) {
	return _clone("open", cmd.Err())
}

func cmdSu(ctx context.Context, cmd Param) (int, error) {
	me, err := os.Executable()
	if err != nil {
		return 1, err
	}
	if me2, err2 := filepath.EvalSymlinks(me); err2 == nil {
		me = me2
	}
	wd, err := os.Getwd()
	if err != nil {
		return 2, err
	}
	if strings.HasSuffix(wd, `\`) {
		wd += "."
	}

	var buffer strings.Builder

	if netdrives, err := netresource.GetNetDrives(); err == nil {
		for _, n := range netdrives {
			fmt.Fprintf(&buffer, ` --netuse "%c:=%s"`, n.Letter, n.Remote)
		}
	}
	fmt.Fprintf(&buffer, ` --chdir "%s"`, wd)

	pid, err := su.ShellExecute("runas", me, buffer.String(), "")
	if err != nil {
		return 3, err
	}
	if pid > 0 {
		fmt.Fprintf(cmd.Err(), "[%d]\n", pid)
	}
	return 0, nil
}
<|endoftext|>