<commit_before>package main

import (
	"fmt"
	"io/ioutil"

	notaryclient "github.com/docker/notary/client"
	"github.com/docker/notary/passphrase"
	"github.com/docker/notary/trustmanager"
	"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 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    passphrase.Retriever

	paths                         []string
	allPaths, removeAll, forceYes bool
}

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

	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")
	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")
	cmd.AddCommand(cmdAddDelg)
	return cmd
}

// 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, true)
	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)
	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
	}

	// 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)
	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()
			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], ", prettyPrintPaths(d.paths))
		}
		cmd.Printf("Removal of delegation role %s %sto repository \"%s\" staged for next publish.\n", role, removingItems, gun)
	}
	cmd.Println("")

	return nil
}

// 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 {
	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 := trustmanager.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{""}
	}

	// 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)
	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, ", pubKeys)
	}
	if d.paths != nil || d.allPaths {
		addingItems = addingItems + fmt.Sprintf("with paths [%s], ", prettyPrintPaths(d.paths))
	}
	cmd.Printf(
		"Addition of delegation role %s %s to repository \"%s\" staged for next publish.\n",
		role, addingItems, gun)
	cmd.Println("")
	return nil
}
<commit_msg>Rename to ClearAllPaths, add comment for delegationAdd<commit_after>package main

import (
	"fmt"
	"io/ioutil"

	notaryclient "github.com/docker/notary/client"
	"github.com/docker/notary/passphrase"
	"github.com/docker/notary/trustmanager"
	"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 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    passphrase.Retriever

	paths                         []string
	allPaths, removeAll, forceYes bool
}

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

	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")
	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")
	cmd.AddCommand(cmdAddDelg)
	return cmd
}

// 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, true)
	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)
	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
	}

	// 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)
	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()
			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], ", prettyPrintPaths(d.paths))
		}
		cmd.Printf("Removal of delegation role %s %sto repository \"%s\" staged for next publish.\n", role, removingItems, gun)
	}
	cmd.Println("")

	return nil
}

// 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 := trustmanager.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{""}
	}

	// 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)
	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, ", pubKeys)
	}
	if d.paths != nil || d.allPaths {
		addingItems = addingItems + fmt.Sprintf("with paths [%s], ", prettyPrintPaths(d.paths))
	}
	cmd.Printf(
		"Addition of delegation role %s %s to repository \"%s\" staged for next publish.\n",
		role, addingItems, gun)
	cmd.Println("")
	return nil
}
<|endoftext|>


<commit_before>/*
 * MinIO Cloud Storage, (C) 2016, 2017, 2018 MinIO, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"context"
	"crypto/x509"
	"fmt"
	"net"
	"runtime"
	"strings"

	humanize "github.com/dustin/go-humanize"
	color "github.com/minio/minio/pkg/color"
	xnet "github.com/minio/minio/pkg/net"
)

// Documentation links, these are part of message printing code.
const (
	mcQuickStartGuide      = "https://docs.min.io/docs/minio-client-quickstart-guide"
	mcAdminQuickStartGuide = "https://docs.min.io/docs/minio-admin-complete-guide.html"
	goQuickStartGuide      = "https://docs.min.io/docs/golang-client-quickstart-guide"
	jsQuickStartGuide      = "https://docs.min.io/docs/javascript-client-quickstart-guide"
	javaQuickStartGuide    = "https://docs.min.io/docs/java-client-quickstart-guide"
	pyQuickStartGuide      = "https://docs.min.io/docs/python-client-quickstart-guide"
	dotnetQuickStartGuide  = "https://docs.min.io/docs/dotnet-client-quickstart-guide"
)

// generates format string depending on the string length and padding.
func getFormatStr(strLen int, padding int) string {
	formatStr := fmt.Sprintf("%ds", strLen+padding)
	return "%" + formatStr
}

func printStartupSafeModeMessage(apiEndpoints []string, err error) {
	logStartupMessage(color.RedBold("Server startup failed with '%v'", err))
	logStartupMessage(color.RedBold("Server switching to safe mode"))
	logStartupMessage(color.RedBold("Please use 'mc admin config' commands fix this issue"))

	// Object layer is initialized then print StorageInfo in safe mode.
	objAPI := newObjectLayerWithoutSafeModeFn()
	if objAPI != nil {
		if msg := getStorageInfoMsgSafeMode(objAPI.StorageInfo(context.Background(), false)); msg != "" {
			logStartupMessage(msg)
		}
	}

	// Get saved credentials.
	cred := globalActiveCred

	// Get saved region.
	region := globalServerRegion

	strippedAPIEndpoints := stripStandardPorts(apiEndpoints)

	apiEndpointStr := strings.Join(strippedAPIEndpoints, "  ")

	// Colorize the message and print.
	logStartupMessage(color.Red("Endpoint: ") + color.Bold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr)))
	if color.IsTerminal() && !globalCLIContext.Anonymous {
		logStartupMessage(color.Red("AccessKey: ") + color.Bold(fmt.Sprintf("%s ", cred.AccessKey)))
		logStartupMessage(color.Red("SecretKey: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
		if region != "" {
			logStartupMessage(color.Red("Region: ") + color.Bold(fmt.Sprintf(getFormatStr(len(region), 3), region)))
		}
	}

	// Prints `mc` cli configuration message chooses
	// first endpoint as default.
	alias := "myminio"
	endPoint := strippedAPIEndpoints[0]

	// Configure 'mc', following block prints platform specific information for minio client admin commands.
	if color.IsTerminal() && !globalCLIContext.Anonymous {
		logStartupMessage(color.RedBold("\nCommand-line Access: ") + mcAdminQuickStartGuide)
		if runtime.GOOS == globalWindowsOSName {
			mcMessage := fmt.Sprintf("> mc.exe config host add %s %s %s %s --api s3v4", alias,
				endPoint, cred.AccessKey, cred.SecretKey)
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
			mcMessage = fmt.Sprintf("> mc.exe admin config --help")
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
		} else {
			mcMessage := fmt.Sprintf("$ mc config host add %s %s %s %s --api s3v4", alias,
				endPoint, cred.AccessKey, cred.SecretKey)
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
			mcMessage = fmt.Sprintf("$ mc admin config --help")
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
		}
	}
}

// Prints the formatted startup message.
func printStartupMessage(apiEndpoints []string) {

	strippedAPIEndpoints := stripStandardPorts(apiEndpoints)
	// If cache layer is enabled, print cache capacity.
	cachedObjAPI := newCachedObjectLayerFn()
	if cachedObjAPI != nil {
		printCacheStorageInfo(cachedObjAPI.StorageInfo(context.Background()))
	}

	// Object layer is initialized then print StorageInfo.
	objAPI := newObjectLayerFn()
	if objAPI != nil {
		printStorageInfo(objAPI.StorageInfo(context.Background(), false))
	}

	// Prints credential, region and browser access.
	printServerCommonMsg(strippedAPIEndpoints)

	// Prints `mc` cli configuration message chooses
	// first endpoint as default.
	printCLIAccessMsg(strippedAPIEndpoints[0], "myminio")

	// Prints documentation message.
	printObjectAPIMsg()

	// SSL is configured reads certification chain, prints
	// authority and expiry.
	if globalIsSSL {
		printCertificateMsg(globalPublicCerts)
	}
}

// Returns true if input is not IPv4, false if it is.
func isNotIPv4(host string) bool {
	h, _, err := net.SplitHostPort(host)
	if err != nil {
		h = host
	}
	ip := net.ParseIP(h)
	ok := ip.To4() != nil // This is always true of IP is IPv4

	// Returns true if input is not IPv4.
	return !ok
}

// strip api endpoints list with standard ports such as
// port "80" and "443" before displaying on the startup
// banner.  Returns a new list of API endpoints.
func stripStandardPorts(apiEndpoints []string) (newAPIEndpoints []string) {
	newAPIEndpoints = make([]string, len(apiEndpoints))
	// Check all API endpoints for standard ports and strip them.
	for i, apiEndpoint := range apiEndpoints {
		u, err := xnet.ParseHTTPURL(apiEndpoint)
		if err != nil {
			continue
		}
		if globalMinioHost == "" && isNotIPv4(u.Host) {
			// Skip all non-IPv4 endpoints when we bind to all interfaces.
			continue
		}
		newAPIEndpoints[i] = u.String()
	}
	return newAPIEndpoints
}

// Prints common server startup message. Prints credential, region and browser access.
func printServerCommonMsg(apiEndpoints []string) {
	// Get saved credentials.
	cred := globalActiveCred

	// Get saved region.
	region := globalServerRegion

	apiEndpointStr := strings.Join(apiEndpoints, "  ")

	// Colorize the message and print.
	logStartupMessage(color.Blue("Endpoint: ") + color.Bold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr)))
	if color.IsTerminal() && !globalCLIContext.Anonymous {
		logStartupMessage(color.Blue("AccessKey: ") + color.Bold(fmt.Sprintf("%s ", cred.AccessKey)))
		logStartupMessage(color.Blue("SecretKey: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
		if region != "" {
			logStartupMessage(color.Blue("Region: ") + color.Bold(fmt.Sprintf(getFormatStr(len(region), 3), region)))
		}
	}
	printEventNotifiers()

	if globalBrowserEnabled {
		logStartupMessage(color.Blue("\nBrowser Access:"))
		logStartupMessage(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 3), apiEndpointStr))
	}
}

// Prints bucket notification configurations.
func printEventNotifiers() {
	if globalNotificationSys == nil {
		return
	}

	arns := globalNotificationSys.GetARNList()
	if len(arns) == 0 {
		return
	}

	arnMsg := color.Blue("SQS ARNs: ")
	for _, arn := range arns {
		arnMsg += color.Bold(fmt.Sprintf(getFormatStr(len(arn), 1), arn))
	}

	logStartupMessage(arnMsg)
}

// Prints startup message for command line access. Prints link to our documentation
// and custom platform specific message.
func printCLIAccessMsg(endPoint string, alias string) {
	// Get saved credentials.
	cred := globalActiveCred

	// Configure 'mc', following block prints platform specific information for minio client.
	if color.IsTerminal() && !globalCLIContext.Anonymous {
		logStartupMessage(color.Blue("\nCommand-line Access: ") + mcQuickStartGuide)
		if runtime.GOOS == globalWindowsOSName {
			mcMessage := fmt.Sprintf("$ mc.exe config host add %s %s %s %s", alias,
				endPoint, cred.AccessKey, cred.SecretKey)
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
		} else {
			mcMessage := fmt.Sprintf("$ mc config host add %s %s %s %s", alias,
				endPoint, cred.AccessKey, cred.SecretKey)
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
		}
	}
}

// Prints startup message for Object API acces, prints link to our SDK documentation.
func printObjectAPIMsg() {
	logStartupMessage(color.Blue("\nObject API (Amazon S3 compatible):"))
	logStartupMessage(color.Blue("   Go: ") + fmt.Sprintf(getFormatStr(len(goQuickStartGuide), 8), goQuickStartGuide))
	logStartupMessage(color.Blue("   Java: ") + fmt.Sprintf(getFormatStr(len(javaQuickStartGuide), 6), javaQuickStartGuide))
	logStartupMessage(color.Blue("   Python: ") + fmt.Sprintf(getFormatStr(len(pyQuickStartGuide), 4), pyQuickStartGuide))
	logStartupMessage(color.Blue("   JavaScript: ") + jsQuickStartGuide)
	logStartupMessage(color.Blue("   .NET: ") + fmt.Sprintf(getFormatStr(len(dotnetQuickStartGuide), 6), dotnetQuickStartGuide))
}

// Get formatted disk/storage info message.
func getStorageInfoMsgSafeMode(storageInfo StorageInfo) string {
	var msg string
	if storageInfo.Backend.Type == BackendErasure {
		diskInfo := fmt.Sprintf(" %d Online, %d Offline. ", storageInfo.Backend.OnlineDisks.Sum(), storageInfo.Backend.OfflineDisks.Sum())
		msg += color.Red("Status:") + fmt.Sprintf(getFormatStr(len(diskInfo), 8), diskInfo)
	}
	return msg
}

// Get formatted disk/storage info message.
func getStorageInfoMsg(storageInfo StorageInfo) string {
	var msg string
	if storageInfo.Backend.Type == BackendErasure {
		diskInfo := fmt.Sprintf(" %d Online, %d Offline. ", storageInfo.Backend.OnlineDisks.Sum(), storageInfo.Backend.OfflineDisks.Sum())
		msg += color.Blue("Status:") + fmt.Sprintf(getFormatStr(len(diskInfo), 8), diskInfo)
	}
	return msg
}

// Prints startup message of storage capacity and erasure information.
func printStorageInfo(storageInfo StorageInfo) {
	if msg := getStorageInfoMsg(storageInfo); msg != "" {
		logStartupMessage(msg)
	}
}

func printCacheStorageInfo(storageInfo CacheStorageInfo) {
	msg := fmt.Sprintf("%s %s Free, %s Total", color.Blue("Cache Capacity:"),
		humanize.IBytes(uint64(storageInfo.Free)),
		humanize.IBytes(uint64(storageInfo.Total)))
	logStartupMessage(msg)
}

// Prints certificate expiry date warning
func getCertificateChainMsg(certs []*x509.Certificate) string {
	msg := color.Blue("\nCertificate expiry info:\n")
	totalCerts := len(certs)
	var expiringCerts int
	for i := totalCerts - 1; i >= 0; i-- {
		cert := certs[i]
		if cert.NotAfter.Before(UTCNow().Add(globalMinioCertExpireWarnDays)) {
			expiringCerts++
			msg += fmt.Sprintf(color.Bold("#%d %s will expire on %s\n"), expiringCerts, cert.Subject.CommonName, cert.NotAfter)
		}
	}
	if expiringCerts > 0 {
		return msg
	}
	return ""
}

// Prints the certificate expiry message.
func printCertificateMsg(certs []*x509.Certificate) {
	logStartupMessage(getCertificateChainMsg(certs))
}
<commit_msg>Print node status even in --quiet mode (#9149)<commit_after>/*
 * MinIO Cloud Storage, (C) 2016, 2017, 2018 MinIO, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"context"
	"crypto/x509"
	"fmt"
	"net"
	"runtime"
	"strings"

	humanize "github.com/dustin/go-humanize"
	"github.com/minio/minio/cmd/logger"
	color "github.com/minio/minio/pkg/color"
	xnet "github.com/minio/minio/pkg/net"
)

// Documentation links, these are part of message printing code.
const (
	mcQuickStartGuide      = "https://docs.min.io/docs/minio-client-quickstart-guide"
	mcAdminQuickStartGuide = "https://docs.min.io/docs/minio-admin-complete-guide.html"
	goQuickStartGuide      = "https://docs.min.io/docs/golang-client-quickstart-guide"
	jsQuickStartGuide      = "https://docs.min.io/docs/javascript-client-quickstart-guide"
	javaQuickStartGuide    = "https://docs.min.io/docs/java-client-quickstart-guide"
	pyQuickStartGuide      = "https://docs.min.io/docs/python-client-quickstart-guide"
	dotnetQuickStartGuide  = "https://docs.min.io/docs/dotnet-client-quickstart-guide"
)

// generates format string depending on the string length and padding.
func getFormatStr(strLen int, padding int) string {
	formatStr := fmt.Sprintf("%ds", strLen+padding)
	return "%" + formatStr
}

func printStartupSafeModeMessage(apiEndpoints []string, err error) {
	logStartupMessage(color.RedBold("Server startup failed with '%v'", err))
	logStartupMessage(color.RedBold("Server switching to safe mode"))
	logStartupMessage(color.RedBold("Please use 'mc admin config' commands fix this issue"))

	// Object layer is initialized then print StorageInfo in safe mode.
	objAPI := newObjectLayerWithoutSafeModeFn()
	if objAPI != nil {
		if msg := getStorageInfoMsgSafeMode(objAPI.StorageInfo(context.Background(), false)); msg != "" {
			logStartupMessage(msg)
		}
	}

	// Get saved credentials.
	cred := globalActiveCred

	// Get saved region.
	region := globalServerRegion

	strippedAPIEndpoints := stripStandardPorts(apiEndpoints)

	apiEndpointStr := strings.Join(strippedAPIEndpoints, "  ")

	// Colorize the message and print.
	logStartupMessage(color.Red("Endpoint: ") + color.Bold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr)))
	if color.IsTerminal() && !globalCLIContext.Anonymous {
		logStartupMessage(color.Red("AccessKey: ") + color.Bold(fmt.Sprintf("%s ", cred.AccessKey)))
		logStartupMessage(color.Red("SecretKey: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
		if region != "" {
			logStartupMessage(color.Red("Region: ") + color.Bold(fmt.Sprintf(getFormatStr(len(region), 3), region)))
		}
	}

	// Prints `mc` cli configuration message chooses
	// first endpoint as default.
	alias := "myminio"
	endPoint := strippedAPIEndpoints[0]

	// Configure 'mc', following block prints platform specific information for minio client admin commands.
	if color.IsTerminal() && !globalCLIContext.Anonymous {
		logStartupMessage(color.RedBold("\nCommand-line Access: ") + mcAdminQuickStartGuide)
		if runtime.GOOS == globalWindowsOSName {
			mcMessage := fmt.Sprintf("> mc.exe config host add %s %s %s %s --api s3v4", alias,
				endPoint, cred.AccessKey, cred.SecretKey)
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
			mcMessage = fmt.Sprintf("> mc.exe admin config --help")
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
		} else {
			mcMessage := fmt.Sprintf("$ mc config host add %s %s %s %s --api s3v4", alias,
				endPoint, cred.AccessKey, cred.SecretKey)
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
			mcMessage = fmt.Sprintf("$ mc admin config --help")
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
		}
	}
}

// Prints the formatted startup message.
func printStartupMessage(apiEndpoints []string) {

	strippedAPIEndpoints := stripStandardPorts(apiEndpoints)
	// If cache layer is enabled, print cache capacity.
	cachedObjAPI := newCachedObjectLayerFn()
	if cachedObjAPI != nil {
		printCacheStorageInfo(cachedObjAPI.StorageInfo(context.Background()))
	}

	// Object layer is initialized then print StorageInfo.
	objAPI := newObjectLayerFn()
	if objAPI != nil {
		printStorageInfo(objAPI.StorageInfo(context.Background(), false))
	}

	// Prints credential, region and browser access.
	printServerCommonMsg(strippedAPIEndpoints)

	// Prints `mc` cli configuration message chooses
	// first endpoint as default.
	printCLIAccessMsg(strippedAPIEndpoints[0], "myminio")

	// Prints documentation message.
	printObjectAPIMsg()

	// SSL is configured reads certification chain, prints
	// authority and expiry.
	if globalIsSSL {
		printCertificateMsg(globalPublicCerts)
	}
}

// Returns true if input is not IPv4, false if it is.
func isNotIPv4(host string) bool {
	h, _, err := net.SplitHostPort(host)
	if err != nil {
		h = host
	}
	ip := net.ParseIP(h)
	ok := ip.To4() != nil // This is always true of IP is IPv4

	// Returns true if input is not IPv4.
	return !ok
}

// strip api endpoints list with standard ports such as
// port "80" and "443" before displaying on the startup
// banner.  Returns a new list of API endpoints.
func stripStandardPorts(apiEndpoints []string) (newAPIEndpoints []string) {
	newAPIEndpoints = make([]string, len(apiEndpoints))
	// Check all API endpoints for standard ports and strip them.
	for i, apiEndpoint := range apiEndpoints {
		u, err := xnet.ParseHTTPURL(apiEndpoint)
		if err != nil {
			continue
		}
		if globalMinioHost == "" && isNotIPv4(u.Host) {
			// Skip all non-IPv4 endpoints when we bind to all interfaces.
			continue
		}
		newAPIEndpoints[i] = u.String()
	}
	return newAPIEndpoints
}

// Prints common server startup message. Prints credential, region and browser access.
func printServerCommonMsg(apiEndpoints []string) {
	// Get saved credentials.
	cred := globalActiveCred

	// Get saved region.
	region := globalServerRegion

	apiEndpointStr := strings.Join(apiEndpoints, "  ")

	// Colorize the message and print.
	logStartupMessage(color.Blue("Endpoint: ") + color.Bold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr)))
	if color.IsTerminal() && !globalCLIContext.Anonymous {
		logStartupMessage(color.Blue("AccessKey: ") + color.Bold(fmt.Sprintf("%s ", cred.AccessKey)))
		logStartupMessage(color.Blue("SecretKey: ") + color.Bold(fmt.Sprintf("%s ", cred.SecretKey)))
		if region != "" {
			logStartupMessage(color.Blue("Region: ") + color.Bold(fmt.Sprintf(getFormatStr(len(region), 3), region)))
		}
	}
	printEventNotifiers()

	if globalBrowserEnabled {
		logStartupMessage(color.Blue("\nBrowser Access:"))
		logStartupMessage(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 3), apiEndpointStr))
	}
}

// Prints bucket notification configurations.
func printEventNotifiers() {
	if globalNotificationSys == nil {
		return
	}

	arns := globalNotificationSys.GetARNList()
	if len(arns) == 0 {
		return
	}

	arnMsg := color.Blue("SQS ARNs: ")
	for _, arn := range arns {
		arnMsg += color.Bold(fmt.Sprintf(getFormatStr(len(arn), 1), arn))
	}

	logStartupMessage(arnMsg)
}

// Prints startup message for command line access. Prints link to our documentation
// and custom platform specific message.
func printCLIAccessMsg(endPoint string, alias string) {
	// Get saved credentials.
	cred := globalActiveCred

	// Configure 'mc', following block prints platform specific information for minio client.
	if color.IsTerminal() && !globalCLIContext.Anonymous {
		logStartupMessage(color.Blue("\nCommand-line Access: ") + mcQuickStartGuide)
		if runtime.GOOS == globalWindowsOSName {
			mcMessage := fmt.Sprintf("$ mc.exe config host add %s %s %s %s", alias,
				endPoint, cred.AccessKey, cred.SecretKey)
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
		} else {
			mcMessage := fmt.Sprintf("$ mc config host add %s %s %s %s", alias,
				endPoint, cred.AccessKey, cred.SecretKey)
			logStartupMessage(fmt.Sprintf(getFormatStr(len(mcMessage), 3), mcMessage))
		}
	}
}

// Prints startup message for Object API acces, prints link to our SDK documentation.
func printObjectAPIMsg() {
	logStartupMessage(color.Blue("\nObject API (Amazon S3 compatible):"))
	logStartupMessage(color.Blue("   Go: ") + fmt.Sprintf(getFormatStr(len(goQuickStartGuide), 8), goQuickStartGuide))
	logStartupMessage(color.Blue("   Java: ") + fmt.Sprintf(getFormatStr(len(javaQuickStartGuide), 6), javaQuickStartGuide))
	logStartupMessage(color.Blue("   Python: ") + fmt.Sprintf(getFormatStr(len(pyQuickStartGuide), 4), pyQuickStartGuide))
	logStartupMessage(color.Blue("   JavaScript: ") + jsQuickStartGuide)
	logStartupMessage(color.Blue("   .NET: ") + fmt.Sprintf(getFormatStr(len(dotnetQuickStartGuide), 6), dotnetQuickStartGuide))
}

// Get formatted disk/storage info message.
func getStorageInfoMsgSafeMode(storageInfo StorageInfo) string {
	var msg string
	var mcMessage string
	if storageInfo.Backend.Type == BackendErasure {
		if storageInfo.Backend.OfflineDisks.Sum() > 0 {
			mcMessage = "Run `mc admin info` to look for server/disk info`"
		}
		diskInfo := fmt.Sprintf(" %d Online, %d Offline.%s ", storageInfo.Backend.OnlineDisks.Sum(), storageInfo.Backend.OfflineDisks.Sum(), mcMessage)
		msg += color.Red("Status:") + fmt.Sprintf(getFormatStr(len(diskInfo), 8), diskInfo)
	}
	return msg
}

// Get formatted disk/storage info message.
func getStorageInfoMsg(storageInfo StorageInfo) string {
	var msg string
	var mcMessage string
	if storageInfo.Backend.Type == BackendErasure {
		if storageInfo.Backend.OfflineDisks.Sum() > 0 {
			mcMessage = "Use `mc admin info` to look for server/disk info"
		}
		diskInfo := fmt.Sprintf(" %d Online, %d Offline.%s ", storageInfo.Backend.OnlineDisks.Sum(), storageInfo.Backend.OfflineDisks.Sum(), mcMessage)
		msg += color.Blue("Status:") + fmt.Sprintf(getFormatStr(len(diskInfo), 8), diskInfo)
	}
	return msg
}

// Prints startup message of storage capacity and erasure information.
func printStorageInfo(storageInfo StorageInfo) {
	if msg := getStorageInfoMsg(storageInfo); msg != "" {
		if globalCLIContext.Quiet {
			logger.Info(msg)
		}
		logStartupMessage(msg)
	}
}

func printCacheStorageInfo(storageInfo CacheStorageInfo) {
	msg := fmt.Sprintf("%s %s Free, %s Total", color.Blue("Cache Capacity:"),
		humanize.IBytes(uint64(storageInfo.Free)),
		humanize.IBytes(uint64(storageInfo.Total)))
	logStartupMessage(msg)
}

// Prints certificate expiry date warning
func getCertificateChainMsg(certs []*x509.Certificate) string {
	msg := color.Blue("\nCertificate expiry info:\n")
	totalCerts := len(certs)
	var expiringCerts int
	for i := totalCerts - 1; i >= 0; i-- {
		cert := certs[i]
		if cert.NotAfter.Before(UTCNow().Add(globalMinioCertExpireWarnDays)) {
			expiringCerts++
			msg += fmt.Sprintf(color.Bold("#%d %s will expire on %s\n"), expiringCerts, cert.Subject.CommonName, cert.NotAfter)
		}
	}
	if expiringCerts > 0 {
		return msg
	}
	return ""
}

// Prints the certificate expiry message.
func printCertificateMsg(certs []*x509.Certificate) {
	logStartupMessage(getCertificateChainMsg(certs))
}
<|endoftext|>


<commit_before>package rollbar

import (
	"fmt"
	"net/http"
	"strconv"

	"golang.org/x/net/context"

	"github.com/pkg/errors"
	"github.com/remind101/pkg/reporter"
	"github.com/stvp/rollbar"
)

const ErrorLevel = "error"

type rollbarReporter struct{}

// The stvp/rollbar package is implemented as a global, so let's not fool our
// callers by generating an instance of a reporter. Rollbar config is actually
// global, so we'll have the Rollbar reporter be a global too.
var Reporter = &rollbarReporter{}

func ConfigureReporter(token, environment string) {
	rollbar.Token = token
	rollbar.Environment = environment
}

func (r *rollbarReporter) Report(ctx context.Context, err error) error {
	var request *http.Request
	extraFields := []*rollbar.Field{}
	var stackTrace rollbar.Stack = nil

	if e, ok := err.(*reporter.Error); ok {
		extraFields = getContextData(e)

		if r := e.Request; r != nil {
			request = e.Request
		}

		stackTrace = makeRollbarStack(e.StackTrace())
	}

	reportToRollbar(request, err, stackTrace, extraFields)
	return nil
}

func reportToRollbar(request *http.Request, err error, stack rollbar.Stack, extraFields []*rollbar.Field) {
	if request != nil {
		if stack != nil {
			rollbar.RequestErrorWithStack(ErrorLevel, request, err, stack, extraFields...)
		} else {
			rollbar.RequestError(ErrorLevel, request, err, extraFields...)
		}
	} else {
		if stack != nil {
			rollbar.ErrorWithStack(ErrorLevel, err, stack, extraFields...)
		} else {
			rollbar.Error(ErrorLevel, err, extraFields...)
		}
	}
}

func makeRollbarStack(stack errors.StackTrace) rollbar.Stack {
	length := len(stack)
	rollbarStack := make(rollbar.Stack, length)
	for index, frame := range stack[:length] {
		// Rollbar's website has a "most recent call last" header. We need to
		// reverse the order of the stack frames we send it, so our stack traces
		// are shown in that order.
		rollbarStack[length-index-1] = rollbar.Frame{
			Line:     parseInt(fmt.Sprintf("%d", frame)),
			Filename: fmt.Sprintf("%s", frame),
			Method:   fmt.Sprintf("%n", frame)}
	}
	return rollbarStack
}

func parseInt(s string) int {
	i, _ := strconv.Atoi(s)
	return i
}

func getContextData(err *reporter.Error) []*rollbar.Field {
	fields := []*rollbar.Field{}
	for key, value := range err.Context {
		fields = append(fields, &rollbar.Field{key, value})
	}
	return fields
}
<commit_msg>Report error cause to rollbar (#58)<commit_after>package rollbar

import (
	"fmt"
	"net/http"
	"strconv"

	"golang.org/x/net/context"

	"github.com/pkg/errors"
	"github.com/remind101/pkg/reporter"
	"github.com/stvp/rollbar"
)

const ErrorLevel = "error"

type rollbarReporter struct{}

// The stvp/rollbar package is implemented as a global, so let's not fool our
// callers by generating an instance of a reporter. Rollbar config is actually
// global, so we'll have the Rollbar reporter be a global too.
var Reporter = &rollbarReporter{}

func ConfigureReporter(token, environment string) {
	rollbar.Token = token
	rollbar.Environment = environment
}

func (r *rollbarReporter) Report(ctx context.Context, err error) error {
	var request *http.Request
	extraFields := []*rollbar.Field{}
	var stackTrace rollbar.Stack = nil

	if e, ok := err.(*reporter.Error); ok {
		extraFields = getContextData(e)

		if r := e.Request; r != nil {
			request = e.Request
		}

		stackTrace = makeRollbarStack(e.StackTrace())
		err = e.Cause() // Report the actual cause of the error.
	}

	reportToRollbar(request, err, stackTrace, extraFields)
	return nil
}

func reportToRollbar(request *http.Request, err error, stack rollbar.Stack, extraFields []*rollbar.Field) {
	if request != nil {
		if stack != nil {
			rollbar.RequestErrorWithStack(ErrorLevel, request, err, stack, extraFields...)
		} else {
			rollbar.RequestError(ErrorLevel, request, err, extraFields...)
		}
	} else {
		if stack != nil {
			rollbar.ErrorWithStack(ErrorLevel, err, stack, extraFields...)
		} else {
			rollbar.Error(ErrorLevel, err, extraFields...)
		}
	}
}

func makeRollbarStack(stack errors.StackTrace) rollbar.Stack {
	length := len(stack)
	rollbarStack := make(rollbar.Stack, length)
	for index, frame := range stack[:length] {
		// Rollbar's website has a "most recent call last" header. We need to
		// reverse the order of the stack frames we send it, so our stack traces
		// are shown in that order.
		rollbarStack[length-index-1] = rollbar.Frame{
			Line:     parseInt(fmt.Sprintf("%d", frame)),
			Filename: fmt.Sprintf("%s", frame),
			Method:   fmt.Sprintf("%n", frame)}
	}
	return rollbarStack
}

func parseInt(s string) int {
	i, _ := strconv.Atoi(s)
	return i
}

func getContextData(err *reporter.Error) []*rollbar.Field {
	fields := []*rollbar.Field{}
	for key, value := range err.Context {
		fields = append(fields, &rollbar.Field{key, value})
	}
	return fields
}
<|endoftext|>


<commit_before>/*

JUnit XML Reporter for Ginkgo

For usage instructions: http://onsi.github.io/ginkgo/#generating_junit_xml_output

*/

package reporters

import (
	"encoding/xml"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"strings"

	"github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/types"
)

type JUnitTestSuite struct {
	XMLName   xml.Name        `xml:"testsuite"`
	TestCases []JUnitTestCase `xml:"testcase"`
	Name      string          `xml:"name,attr"`
	Tests     int             `xml:"tests,attr"`
	Failures  int             `xml:"failures,attr"`
	Errors    int             `xml:"errors,attr"`
	Time      float64         `xml:"time,attr"`
}

type JUnitTestCase struct {
	Name           string               `xml:"name,attr"`
	ClassName      string               `xml:"classname,attr"`
	PassedMessage  *JUnitPassedMessage  `xml:"passed,omitempty"`
	FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"`
	Skipped        *JUnitSkipped        `xml:"skipped,omitempty"`
	Time           float64              `xml:"time,attr"`
	SystemOut      string               `xml:"system-out,omitempty"`
}

type JUnitPassedMessage struct {
	Message string `xml:",chardata"`
}

type JUnitFailureMessage struct {
	Type    string `xml:"type,attr"`
	Message string `xml:",chardata"`
}

type JUnitSkipped struct {
	XMLName xml.Name `xml:"skipped"`
}

type JUnitReporter struct {
	suite          JUnitTestSuite
	filename       string
	testSuiteName  string
	ReporterConfig config.DefaultReporterConfigType
}

//NewJUnitReporter creates a new JUnit XML reporter.  The XML will be stored in the passed in filename.
func NewJUnitReporter(filename string) *JUnitReporter {
	return &JUnitReporter{
		filename: filename,
	}
}

func (reporter *JUnitReporter) SpecSuiteWillBegin(ginkgoConfig config.GinkgoConfigType, summary *types.SuiteSummary) {
	reporter.suite = JUnitTestSuite{
		Name:      summary.SuiteDescription,
		TestCases: []JUnitTestCase{},
	}
	reporter.testSuiteName = summary.SuiteDescription
	reporter.ReporterConfig = config.DefaultReporterConfig
}

func (reporter *JUnitReporter) SpecWillRun(specSummary *types.SpecSummary) {
}

func (reporter *JUnitReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {
	reporter.handleSetupSummary("BeforeSuite", setupSummary)
}

func (reporter *JUnitReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) {
	reporter.handleSetupSummary("AfterSuite", setupSummary)
}

func failureMessage(failure types.SpecFailure) string {
	return fmt.Sprintf("%s\n%s\n%s", failure.ComponentCodeLocation.String(), failure.Message, failure.Location.String())
}

func (reporter *JUnitReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) {
	if setupSummary.State != types.SpecStatePassed {
		testCase := JUnitTestCase{
			Name:      name,
			ClassName: reporter.testSuiteName,
		}

		testCase.FailureMessage = &JUnitFailureMessage{
			Type:    reporter.failureTypeForState(setupSummary.State),
			Message: failureMessage(setupSummary.Failure),
		}
		testCase.SystemOut = setupSummary.CapturedOutput
		testCase.Time = setupSummary.RunTime.Seconds()
		reporter.suite.TestCases = append(reporter.suite.TestCases, testCase)
	}
}

func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) {
	testCase := JUnitTestCase{
		Name:      strings.Join(specSummary.ComponentTexts[1:], " "),
		ClassName: reporter.testSuiteName,
	}
	if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed {
		testCase.PassedMessage = &JUnitPassedMessage{
			Message: specSummary.CapturedOutput,
		}
	}
	if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked {
		testCase.FailureMessage = &JUnitFailureMessage{
			Type:    reporter.failureTypeForState(specSummary.State),
			Message: failureMessage(specSummary.Failure),
		}
		if specSummary.State == types.SpecStatePanicked {
			testCase.FailureMessage.Message += fmt.Sprintf("\n\nPanic: %s\n\nFull stack:\n%s",
				specSummary.Failure.ForwardedPanic,
				specSummary.Failure.Location.FullStackTrace)
		}
		testCase.SystemOut = specSummary.CapturedOutput
	}
	if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending {
		testCase.Skipped = &JUnitSkipped{}
	}
	testCase.Time = specSummary.RunTime.Seconds()
	reporter.suite.TestCases = append(reporter.suite.TestCases, testCase)
}

func (reporter *JUnitReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) {
	reporter.suite.Tests = summary.NumberOfSpecsThatWillBeRun
	reporter.suite.Time = math.Trunc(summary.RunTime.Seconds()*1000) / 1000
	reporter.suite.Failures = summary.NumberOfFailedSpecs
	reporter.suite.Errors = 0
	if reporter.ReporterConfig.ReportFile != "" {
		reporter.filename = reporter.ReporterConfig.ReportFile
		fmt.Printf("\nJUnit path was configured: %s\n", reporter.filename)
	}
	filePath, _ := filepath.Abs(reporter.filename)
	dirPath := filepath.Dir(filePath)
	err := os.MkdirAll(dirPath, os.ModePerm)
	if err != nil {
		fmt.Printf("\nFailed to create JUnit directory: %s\n\t%s", filePath, err.Error())
	}
	file, err := os.Create(filePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create JUnit report file: %s\n\t%s", filePath, err.Error())
	}
	defer file.Close()
	file.WriteString(xml.Header)
	encoder := xml.NewEncoder(file)
	encoder.Indent("  ", "    ")
	err = encoder.Encode(reporter.suite)
	if err == nil {
		fmt.Fprintf(os.Stdout, "\nJUnit report was created: %s\n", filePath)
	} else {
		fmt.Fprintf(os.Stderr,"\nFailed to generate JUnit report data:\n\t%s", err.Error())
	}
}

func (reporter *JUnitReporter) failureTypeForState(state types.SpecState) string {
	switch state {
	case types.SpecStateFailed:
		return "Failure"
	case types.SpecStateTimedOut:
		return "Timeout"
	case types.SpecStatePanicked:
		return "Panic"
	default:
		return ""
	}
}
<commit_msg>Print Skip reason in JUnit reporter if one was provided<commit_after>/*

JUnit XML Reporter for Ginkgo

For usage instructions: http://onsi.github.io/ginkgo/#generating_junit_xml_output

*/

package reporters

import (
	"encoding/xml"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"strings"

	"github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/types"
)

type JUnitTestSuite struct {
	XMLName   xml.Name        `xml:"testsuite"`
	TestCases []JUnitTestCase `xml:"testcase"`
	Name      string          `xml:"name,attr"`
	Tests     int             `xml:"tests,attr"`
	Failures  int             `xml:"failures,attr"`
	Errors    int             `xml:"errors,attr"`
	Time      float64         `xml:"time,attr"`
}

type JUnitTestCase struct {
	Name           string               `xml:"name,attr"`
	ClassName      string               `xml:"classname,attr"`
	PassedMessage  *JUnitPassedMessage  `xml:"passed,omitempty"`
	FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"`
	Skipped        *JUnitSkipped        `xml:"skipped,omitempty"`
	Time           float64              `xml:"time,attr"`
	SystemOut      string               `xml:"system-out,omitempty"`
}

type JUnitPassedMessage struct {
	Message string `xml:",chardata"`
}

type JUnitFailureMessage struct {
	Type    string `xml:"type,attr"`
	Message string `xml:",chardata"`
}

type JUnitSkipped struct {
	Message string `xml:",chardata"`
}

type JUnitReporter struct {
	suite          JUnitTestSuite
	filename       string
	testSuiteName  string
	ReporterConfig config.DefaultReporterConfigType
}

//NewJUnitReporter creates a new JUnit XML reporter.  The XML will be stored in the passed in filename.
func NewJUnitReporter(filename string) *JUnitReporter {
	return &JUnitReporter{
		filename: filename,
	}
}

func (reporter *JUnitReporter) SpecSuiteWillBegin(ginkgoConfig config.GinkgoConfigType, summary *types.SuiteSummary) {
	reporter.suite = JUnitTestSuite{
		Name:      summary.SuiteDescription,
		TestCases: []JUnitTestCase{},
	}
	reporter.testSuiteName = summary.SuiteDescription
	reporter.ReporterConfig = config.DefaultReporterConfig
}

func (reporter *JUnitReporter) SpecWillRun(specSummary *types.SpecSummary) {
}

func (reporter *JUnitReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {
	reporter.handleSetupSummary("BeforeSuite", setupSummary)
}

func (reporter *JUnitReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) {
	reporter.handleSetupSummary("AfterSuite", setupSummary)
}

func failureMessage(failure types.SpecFailure) string {
	return fmt.Sprintf("%s\n%s\n%s", failure.ComponentCodeLocation.String(), failure.Message, failure.Location.String())
}

func (reporter *JUnitReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) {
	if setupSummary.State != types.SpecStatePassed {
		testCase := JUnitTestCase{
			Name:      name,
			ClassName: reporter.testSuiteName,
		}

		testCase.FailureMessage = &JUnitFailureMessage{
			Type:    reporter.failureTypeForState(setupSummary.State),
			Message: failureMessage(setupSummary.Failure),
		}
		testCase.SystemOut = setupSummary.CapturedOutput
		testCase.Time = setupSummary.RunTime.Seconds()
		reporter.suite.TestCases = append(reporter.suite.TestCases, testCase)
	}
}

func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) {
	testCase := JUnitTestCase{
		Name:      strings.Join(specSummary.ComponentTexts[1:], " "),
		ClassName: reporter.testSuiteName,
	}
	if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed {
		testCase.PassedMessage = &JUnitPassedMessage{
			Message: specSummary.CapturedOutput,
		}
	}
	if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked {
		testCase.FailureMessage = &JUnitFailureMessage{
			Type:    reporter.failureTypeForState(specSummary.State),
			Message: failureMessage(specSummary.Failure),
		}
		if specSummary.State == types.SpecStatePanicked {
			testCase.FailureMessage.Message += fmt.Sprintf("\n\nPanic: %s\n\nFull stack:\n%s",
				specSummary.Failure.ForwardedPanic,
				specSummary.Failure.Location.FullStackTrace)
		}
		testCase.SystemOut = specSummary.CapturedOutput
	}
	if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending {
		testCase.Skipped = &JUnitSkipped{}
		if specSummary.Failure.Message != "" {
			testCase.Skipped.Message = failureMessage(specSummary.Failure)
		}
	}
	testCase.Time = specSummary.RunTime.Seconds()
	reporter.suite.TestCases = append(reporter.suite.TestCases, testCase)
}

func (reporter *JUnitReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) {
	reporter.suite.Tests = summary.NumberOfSpecsThatWillBeRun
	reporter.suite.Time = math.Trunc(summary.RunTime.Seconds()*1000) / 1000
	reporter.suite.Failures = summary.NumberOfFailedSpecs
	reporter.suite.Errors = 0
	if reporter.ReporterConfig.ReportFile != "" {
		reporter.filename = reporter.ReporterConfig.ReportFile
		fmt.Printf("\nJUnit path was configured: %s\n", reporter.filename)
	}
	filePath, _ := filepath.Abs(reporter.filename)
	dirPath := filepath.Dir(filePath)
	err := os.MkdirAll(dirPath, os.ModePerm)
	if err != nil {
		fmt.Printf("\nFailed to create JUnit directory: %s\n\t%s", filePath, err.Error())
	}
	file, err := os.Create(filePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create JUnit report file: %s\n\t%s", filePath, err.Error())
	}
	defer file.Close()
	file.WriteString(xml.Header)
	encoder := xml.NewEncoder(file)
	encoder.Indent("  ", "    ")
	err = encoder.Encode(reporter.suite)
	if err == nil {
		fmt.Fprintf(os.Stdout, "\nJUnit report was created: %s\n", filePath)
	} else {
		fmt.Fprintf(os.Stderr,"\nFailed to generate JUnit report data:\n\t%s", err.Error())
	}
}

func (reporter *JUnitReporter) failureTypeForState(state types.SpecState) string {
	switch state {
	case types.SpecStateFailed:
		return "Failure"
	case types.SpecStateTimedOut:
		return "Timeout"
	case types.SpecStatePanicked:
		return "Panic"
	default:
		return ""
	}
}
<|endoftext|>


<commit_before>package cpp

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
)

type lexerState struct {
	pos  FilePos
	brdr *bufio.Reader
	//If this channel recieves a value, the lexing goroutines should close
	//its output channel and its error channel and terminate.
	cancel chan struct{}
	stream chan *Token
}

type breakout struct {
}

//Lex starts a goroutine which lexes the contents of the reader.
//fname is used for error messages when showing the source location.
//No preprocessing is done, this is just pure reading of the unprocessed
//source file.
//The goroutine will not stop until all tokens are read
func Lex(fname string, r io.Reader) chan *Token {
	ls := new(lexerState)
	ls.pos.File = fname
	ls.pos.Line = 1
	ls.pos.Col = 1
	ls.stream = make(chan *Token)
	ls.brdr = bufio.NewReader(r)
	go ls.lex()
	return ls.stream
}

func (ls *lexerState) lexError(e error) {
	eWithPos := fmt.Sprintf("Error while reading %s. %s", ls.pos, e.Error())
	ls.sendTok(ERROR, eWithPos)
	close(ls.stream)
	//recover exits the lexer cleanly
	panic(&breakout{})
}

func (ls *lexerState) lex() {

	//This recovery happens if lexError is called.
	defer func() {
		//XXX is this correct way to retrigger non breakout?
		if e := recover(); e != nil {
			_ = e.(breakout) // Will re-panic if not a parse error.
		}

	}()

	first, _, err := ls.brdr.ReadRune()

	for {
		if err == io.EOF {
			break
		}
		if err != nil {
			ls.lexError(err)
		}
		switch {
		case isAlpha(first) || first == '_':
			ls.brdr.UnreadRune()
			ls.readIdentOrKeyword()
		case isNumeric(first):
			ls.brdr.UnreadRune()
			ls.readConstantInt()
		case isWhiteSpace(first):
			ls.brdr.UnreadRune()
			ls.skipWhiteSpace()
		default:
			switch first {
			case '#':
				ls.sendTok('#', "#")
			case '!':
				ls.sendTok(NOT, "!")
			case '?':
				ls.sendTok('?', "?")
			case ':':
				ls.sendTok(COLON, ":")
			case '"':
				ls.brdr.UnreadByte()
				ls.readCString()
			case '(':
				ls.sendTok(LPAREN, "(")
			case ')':
				ls.sendTok(RPAREN, ")")
			case '{':
				ls.sendTok(LBRACE, "{")
			case '}':
				ls.sendTok(RBRACE, "}")
			case '[':
				ls.sendTok(LBRACK, "[")
			case ']':
				ls.sendTok(RBRACE, "]")
			case '<':
				ls.sendTok(LSS, "<")
			case '>':
				ls.sendTok(GTR, ">")
			case '+':
				second, _, _ := ls.brdr.ReadRune()
				if second == '+' {
					ls.sendTok(INC, "++")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(ADD, "+")
			case '.':
				ls.sendTok(PERIOD, ".")
			case '-':
				second, _, _ := ls.brdr.ReadRune()
				if second == '>' {
					ls.sendTok(ARROW, "->")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(SUB, "-")
			case ',':
				ls.sendTok(COMMA, ",")
			case '*':
				ls.sendTok(MUL, "*")
			case '/':
				second, _, _ := ls.brdr.ReadRune()
				if second == '*' { // C comment
					for {
						endChar, _, err := ls.brdr.ReadRune()
						if err == io.EOF {
							ls.lexError(fmt.Errorf("Unclosed comment."))
						}
						if err != nil {
							ls.lexError(err)
						}
						if endChar == '*' {
							closeBar, _, _ := ls.brdr.ReadRune()
							if closeBar == '/' {
								break
							}
						}
					}
				} else {
					ls.sendTok(QUO, "/")
				}
			case '|':
				second, _, _ := ls.brdr.ReadRune()
				if second == '|' {
					ls.sendTok(LOR, "||")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(OR, "|")
			case '&':
				second, _, _ := ls.brdr.ReadRune()
				if second == '&' {
					ls.sendTok(LAND, "&&")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(AND, "&")
			case '=':
				second, _, _ := ls.brdr.ReadRune()
				if second == '=' {
					ls.sendTok(EQL, "==")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(ASSIGN, "=")
			case ';':
				ls.sendTok(SEMICOLON, ";")
			default:
				//ls.lexError(fmt.Errorf("Internal Error - bad char code '%d'", first))
			}
		}
		first, _, err = ls.brdr.ReadRune()
	}
	close(ls.stream)
}

func (ls *lexerState) sendTok(kind TokenKind, val string) {
	var tok Token
	tok.Kind = kind
	tok.Val = val
	tok.Pos.Line = ls.pos.Line
	tok.Pos.Col = ls.pos.Col
	tok.Pos.File = ls.pos.File
	ls.stream <- &tok
}

func (ls *lexerState) readIdentOrKeyword() {
	var buff bytes.Buffer
	first, _, _ := ls.brdr.ReadRune()
	if !isValidIdentStart(first) {
		panic("internal error")
	}
	buff.WriteRune(first)
	for {
		b, _, err := ls.brdr.ReadRune()
		if isAlphaNumeric(b) || b == '_' {
			buff.WriteRune(b)
		} else {
			if err == nil {
				ls.brdr.UnreadByte()
			}
			str := buff.String()
			tokType, ok := keywordLUT[str]
			if !ok {
				tokType = IDENT
			}

			ls.sendTok(tokType, str)
			ls.pos.Col += len(str)
			break
		}
	}
}

func (ls *lexerState) skipWhiteSpace() {
	for {
		r, _, err := ls.brdr.ReadRune()
		if !isWhiteSpace(r) {
			if err == nil {
				ls.brdr.UnreadRune()
			}
			break
		}
		if r == '\n' {
			ls.pos.Line += 1
			ls.pos.Col = 1
		} else {
			ls.pos.Col += 1
		}
	}
}

func (ls *lexerState) readConstantInt() {
	var buff bytes.Buffer
	for {
		r, _, err := ls.brdr.ReadRune()
		if isNumeric(r) {
			buff.WriteRune(r)
		} else {
			if err == nil {
				ls.brdr.UnreadRune()
			}
			str := buff.String()
			ls.sendTok(INT_CONSTANT, str)
			ls.pos.Col += len(str)
			break
		}
	}
}

func (ls *lexerState) readCString() {
	first, _, err := ls.brdr.ReadRune()

	if err != nil {
		ls.lexError(err)
	}

	if first != '"' {
		ls.lexError(fmt.Errorf("internal error"))
	}

	escaped := false
	for {
		b, _, err := ls.brdr.ReadRune()
		if err == io.EOF {
			ls.lexError(fmt.Errorf("Unterminated string literal."))
		}
		if err != nil {
			ls.lexError(err)
		}
		if b == '"' && !escaped {
			break
		}
		if !escaped {
			if b == '\\' {
				escaped = true
			}
		} else {
			escaped = false
		}
	}
	ls.sendTok(STRING, "")
}

func isValidIdentStart(b rune) bool {
	return b == '_' || isAlpha(b)
}

func isAlpha(b rune) bool {
	if b >= 'a' && b <= 'z' {
		return true
	}
	if b >= 'A' && b <= 'Z' {
		return true
	}
	return false
}

func isWhiteSpace(b rune) bool {
	return b == ' ' || b == '\r' || b == '\n' || b == '\t'
}

func isNumeric(b rune) bool {
	if b >= '0' && b <= '9' {
		return true
	}
	return false
}

func isAlphaNumeric(b rune) bool {
	return isNumeric(b) || isAlpha(b)
}
<commit_msg>broken start of read directive<commit_after>package cpp

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
)

type lexerState struct {
	pos  FilePos
	brdr *bufio.Reader
	//At the beginning on line not including whitespace
	bol bool
	//If this channel recieves a value, the lexing goroutines should close
	//its output channel and its error channel and terminate.
	cancel chan struct{}
	stream chan *Token
}

type breakout struct {
}

//Lex starts a goroutine which lexes the contents of the reader.
//fname is used for error messages when showing the source location.
//No preprocessing is done, this is just pure reading of the unprocessed
//source file.
//The goroutine will not stop until all tokens are read
func Lex(fname string, r io.Reader) chan *Token {
	ls := new(lexerState)
	ls.pos.File = fname
	ls.pos.Line = 1
	ls.pos.Col = 1
	ls.stream = make(chan *Token)
	ls.brdr = bufio.NewReader(r)
	ls.bol = true
	go ls.lex()
	return ls.stream
}

func (ls *lexerState) lexError(e error) {
	eWithPos := fmt.Sprintf("Error while reading %s. %s", ls.pos, e.Error())
	ls.sendTok(ERROR, eWithPos)
	close(ls.stream)
	//recover exits the lexer cleanly
	panic(&breakout{})
}

func (ls *lexerState) lex() {

	//This recovery happens if lexError is called.
	defer func() {
		//XXX is this correct way to retrigger non breakout?
		if e := recover(); e != nil {
			_ = e.(breakout) // Will re-panic if not a parse error.
		}

	}()

	first, _, err := ls.brdr.ReadRune()

	for {
		if err == io.EOF {
			break
		}
		if err != nil {
			ls.lexError(err)
		}
		switch {
		case isAlpha(first) || first == '_':
			ls.brdr.UnreadRune()
			ls.readIdentOrKeyword()
		case isNumeric(first):
			ls.brdr.UnreadRune()
			ls.readConstantInt()
		case isWhiteSpace(first):
			ls.brdr.UnreadRune()
			ls.skipWhiteSpace()
		default:
			switch first {
			case '#':
				ls.readDirective()
			case '!':
				ls.sendTok(NOT, "!")
			case '?':
				ls.sendTok('?', "?")
			case ':':
				ls.sendTok(COLON, ":")
			case '"':
				ls.brdr.UnreadRune()
				ls.readCString()
			case '(':
				ls.sendTok(LPAREN, "(")
			case ')':
				ls.sendTok(RPAREN, ")")
			case '{':
				ls.sendTok(LBRACE, "{")
			case '}':
				ls.sendTok(RBRACE, "}")
			case '[':
				ls.sendTok(LBRACK, "[")
			case ']':
				ls.sendTok(RBRACE, "]")
			case '<':
				ls.sendTok(LSS, "<")
			case '>':
				ls.sendTok(GTR, ">")
			case '+':
				second, _, _ := ls.brdr.ReadRune()
				if second == '+' {
					ls.sendTok(INC, "++")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(ADD, "+")
			case '.':
				ls.sendTok(PERIOD, ".")
			case '-':
				second, _, _ := ls.brdr.ReadRune()
				if second == '>' {
					ls.sendTok(ARROW, "->")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(SUB, "-")
			case ',':
				ls.sendTok(COMMA, ",")
			case '*':
				ls.sendTok(MUL, "*")
			case '/':
				second, _, _ := ls.brdr.ReadRune()
				if second == '*' { // C comment
					for {
						endChar, _, err := ls.brdr.ReadRune()
						if err == io.EOF {
							ls.lexError(fmt.Errorf("Unclosed comment."))
						}
						if err != nil {
							ls.lexError(err)
						}
						if endChar == '*' {
							closeBar, _, _ := ls.brdr.ReadRune()
							if closeBar == '/' {
								break
							}
						}
					}
				} else {
					ls.sendTok(QUO, "/")
				}
			case '|':
				second, _, _ := ls.brdr.ReadRune()
				if second == '|' {
					ls.sendTok(LOR, "||")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(OR, "|")
			case '&':
				second, _, _ := ls.brdr.ReadRune()
				if second == '&' {
					ls.sendTok(LAND, "&&")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(AND, "&")
			case '=':
				second, _, _ := ls.brdr.ReadRune()
				if second == '=' {
					ls.sendTok(EQL, "==")
					break
				}
				ls.brdr.UnreadRune()
				ls.sendTok(ASSIGN, "=")
			case ';':
				ls.sendTok(SEMICOLON, ";")
			default:
				//ls.lexError(fmt.Errorf("Internal Error - bad char code '%d'", first))
			}
		}
		first, _, err = ls.brdr.ReadRune()
	}
	close(ls.stream)
}

func (ls *lexerState) sendTok(kind TokenKind, val string) {
	var tok Token
	tok.Kind = kind
	tok.Val = val
	tok.Pos.Line = ls.pos.Line
	tok.Pos.Col = ls.pos.Col
	tok.Pos.File = ls.pos.File
	ls.bol = false
	ls.stream <- &tok
}

func (ls *lexerState) readDirective() {

	if !ls.isAtLineStart() {
		ls.lexError(fmt.Errorf("Directive not at beginning of line."))
	}

	//Skip
	for {
		ws, _, err := ls.brdr.ReadRune()
		if err != nil {
			ls.lexError(err)
		}

		if ws == ' ' || ws == '\t' {
			continue
		}

		if ws == '\n' {
			ls.lexError(fmt.Errorf("Empty directive"))
		}

		break
	}

}

func (ls *lexerState) readIdentOrKeyword() {
	var buff bytes.Buffer
	first, _, _ := ls.brdr.ReadRune()
	if !isValidIdentStart(first) {
		panic("internal error")
	}
	buff.WriteRune(first)
	for {
		b, _, err := ls.brdr.ReadRune()
		if isAlphaNumeric(b) || b == '_' {
			buff.WriteRune(b)
		} else {
			if err == nil {
				ls.brdr.UnreadByte()
			}
			str := buff.String()
			tokType, ok := keywordLUT[str]
			if !ok {
				tokType = IDENT
			}

			ls.sendTok(tokType, str)
			ls.pos.Col += len(str)
			break
		}
	}
}

func (ls *lexerState) skipWhiteSpace() {
	for {
		r, _, err := ls.brdr.ReadRune()
		if !isWhiteSpace(r) {
			if err == nil {
				ls.brdr.UnreadRune()
			}
			break
		}
		if r == '\n' {
			ls.pos.Line += 1
			ls.pos.Col = 1
		} else {
			ls.pos.Col += 1
		}
	}
}

func (ls *lexerState) readConstantInt() {
	var buff bytes.Buffer
	for {
		r, _, err := ls.brdr.ReadRune()
		if isNumeric(r) {
			buff.WriteRune(r)
		} else {
			if err == nil {
				ls.brdr.UnreadRune()
			}
			str := buff.String()
			ls.sendTok(INT_CONSTANT, str)
			ls.pos.Col += len(str)
			break
		}
	}
}

func (ls *lexerState) readCString() {
	first, _, err := ls.brdr.ReadRune()

	if err != nil {
		ls.lexError(err)
	}

	if first != '"' {
		ls.lexError(fmt.Errorf("internal error"))
	}

	escaped := false
	for {
		b, _, err := ls.brdr.ReadRune()
		if err == io.EOF {
			ls.lexError(fmt.Errorf("Unterminated string literal."))
		}
		if err != nil {
			ls.lexError(err)
		}
		if b == '"' && !escaped {
			break
		}
		if !escaped {
			if b == '\\' {
				escaped = true
			}
		} else {
			escaped = false
		}
	}
	ls.sendTok(STRING, "")
}

func (ls *lexerState) isAtLineStart() bool {
	return ls.bol
}

func isValidIdentStart(b rune) bool {
	return b == '_' || isAlpha(b)
}

func isAlpha(b rune) bool {
	if b >= 'a' && b <= 'z' {
		return true
	}
	if b >= 'A' && b <= 'Z' {
		return true
	}
	return false
}

func isWhiteSpace(b rune) bool {
	return b == ' ' || b == '\r' || b == '\n' || b == '\t'
}

func isNumeric(b rune) bool {
	if b >= '0' && b <= '9' {
		return true
	}
	return false
}

func isAlphaNumeric(b rune) bool {
	return isNumeric(b) || isAlpha(b)
}
<|endoftext|>


<commit_before>package main

import (
	"config"
	"fmt"
	"os"
	"os/user"
	"path"

	"util"

	"config/iniconf"

	"logger"

	"strings"

	"model/yamlrepo"

	"github.com/astaxie/beego/logs"
	"github.com/urfave/cli"
)

var (
	version  = "v0.4.0"
	confPath = ".melanite.ini"
	conf     *config.Config
	log      *logs.BeeLogger
	repo     *yamlrepo.YAMLRepo
)

func init() {
	u, _ := user.Current()
	confPath = path.Join(u.HomeDir, confPath)
}

func main() {
	app := cli.NewApp()
	app.Version = version
	app.EnableBashCompletion = true
	app.Name = "Melanite (CLI tool)"

	if err := initConfig(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	initListSubCmd(app)
	initExecSubCmd(app)

	if err := app.Run(os.Args); err != nil {
		fmt.Println(err)
	}
}

func initConfig() error {
	var err error
	if !util.FileExist(confPath) {
		if !util.Confirm("Do you want to create your config file?(y or n)") {
			return fmt.Errorf("You should create your init config file")
		}
		createConfFile()
	}

	load := iniconf.New(confPath)
	conf, err = load.Load()
	if err != nil {
		return err
	}

	if conf.DataSource.Conn == "" || conf.DataSource.Type == "" {
		return fmt.Errorf("You should set DataSource in your config file: %s", confPath)
	}

	// fmt.Printf("conf: %v\n", conf)
	// init log
	if strings.ToLower(conf.Logger.LogType) == "console" {
		log = logger.NewConsoleLogger(conf.Logger.Level)
	} else {
		log = logger.NewFileLogger(conf.Logger.LogFile, conf.Logger.Level)
	}

	// init repo
	repo, _ = yamlrepo.New(conf.DataSource.Conn)
	return nil
}

func createConfFile() {
	f, err := os.Create(confPath)
	if err != nil {
		fmt.Println("create config file error: %s", err)
		return
	}

	defer f.Close()

	var defaultConfContent = `[Main]
sync=true
concurrentNum=5
timeout=30

[Logger]
level=error
logFile=
logType=console

[DataSource]
type=yaml
conn=
`
	f.Write([]byte(defaultConfContent))
}
<commit_msg>release v0.4.1<commit_after>package main

import (
	"config"
	"fmt"
	"os"
	"os/user"
	"path"

	"util"

	"config/iniconf"

	"logger"

	"strings"

	"model/yamlrepo"

	"github.com/astaxie/beego/logs"
	"github.com/urfave/cli"
)

var (
	version  = "v0.4.1"
	confPath = ".melanite.ini"
	conf     *config.Config
	log      *logs.BeeLogger
	repo     *yamlrepo.YAMLRepo
)

func init() {
	u, _ := user.Current()
	confPath = path.Join(u.HomeDir, confPath)
}

func main() {
	app := cli.NewApp()
	app.Version = version
	app.EnableBashCompletion = true
	app.Name = "Melanite (CLI tool)"

	if err := initConfig(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	initListSubCmd(app)
	initExecSubCmd(app)

	if err := app.Run(os.Args); err != nil {
		fmt.Println(err)
	}
}

func initConfig() error {
	var err error
	if !util.FileExist(confPath) {
		if !util.Confirm("Do you want to create your config file?(y or n)") {
			return fmt.Errorf("You should create your init config file")
		}
		createConfFile()
	}

	load := iniconf.New(confPath)
	conf, err = load.Load()
	if err != nil {
		return err
	}

	if conf.DataSource.Conn == "" || conf.DataSource.Type == "" {
		return fmt.Errorf("You should set DataSource in your config file: %s", confPath)
	}

	// fmt.Printf("conf: %v\n", conf)
	// init log
	if strings.ToLower(conf.Logger.LogType) == "console" {
		log = logger.NewConsoleLogger(conf.Logger.Level)
	} else {
		log = logger.NewFileLogger(conf.Logger.LogFile, conf.Logger.Level)
	}

	// init repo
	repo, _ = yamlrepo.New(conf.DataSource.Conn)
	return nil
}

func createConfFile() {
	f, err := os.Create(confPath)
	if err != nil {
		fmt.Println("create config file error: %s", err)
		return
	}

	defer f.Close()

	var defaultConfContent = `[Main]
sync=true
concurrentNum=5
timeout=30

[Logger]
level=error
logFile=
logType=console

[DataSource]
type=yaml
conn=
`
	f.Write([]byte(defaultConfContent))
}
<|endoftext|>


<commit_before>package api

import (
	"errors"
	"fmt"
	"net/http"
)

// StatusErrorf returns a new StatusError containing the specified status and message.
func StatusErrorf(status int, format string, a ...interface{}) StatusError {
	return StatusError{
		status: status,
		msg:    fmt.Sprintf(format, a...),
	}
}

// StatusError error type that contains an HTTP status code and message.
type StatusError struct {
	status int
	msg    string
}

// Error returns the error message or the http.StatusText() of the status code if message is empty.
func (e StatusError) Error() string {
	if e.msg != "" {
		return e.msg
	}

	return http.StatusText(e.status)
}

// Status returns the HTTP status code.
func (e StatusError) Status() int {
	return e.status
}

// StatusErrorMatch checks if err was caused by StatusError. Can optionally also check whether the StatusError's
// status code matches one of the supplied status codes in matchStatus.
// Returns the matched StatusError status code and true if match criteria are met, otherwise false.
func StatusErrorMatch(err error, matchStatusCodes ...int) (int, bool) {
	var statusErr StatusError

	if errors.As(err, &statusErr) {
		statusCode := statusErr.Status()

		if len(matchStatusCodes) <= 0 {
			return statusCode, true
		}

		for _, s := range matchStatusCodes {
			if statusCode == s {
				return statusCode, true
			}
		}
	}

	return -1, false
}

// StatusErrorCheck returns whether or not err was caused by a StatusError and if it matches one of the
// optional status codes.
func StatusErrorCheck(err error, matchStatusCodes ...int) bool {
	_, found := StatusErrorMatch(err, matchStatusCodes...)
	return found
}
<commit_msg>shared/api/error: Update StatusErrorf to not parse format to fmt.Sprintf if no replacement arguments<commit_after>package api

import (
	"errors"
	"fmt"
	"net/http"
)

// StatusErrorf returns a new StatusError containing the specified status and message.
func StatusErrorf(status int, format string, a ...interface{}) StatusError {
	var msg string
	if len(a) > 0 {
		msg = fmt.Sprintf(format, a...)
	} else {
		msg = format
	}

	return StatusError{
		status: status,
		msg:    msg,
	}
}

// StatusError error type that contains an HTTP status code and message.
type StatusError struct {
	status int
	msg    string
}

// Error returns the error message or the http.StatusText() of the status code if message is empty.
func (e StatusError) Error() string {
	if e.msg != "" {
		return e.msg
	}

	return http.StatusText(e.status)
}

// Status returns the HTTP status code.
func (e StatusError) Status() int {
	return e.status
}

// StatusErrorMatch checks if err was caused by StatusError. Can optionally also check whether the StatusError's
// status code matches one of the supplied status codes in matchStatus.
// Returns the matched StatusError status code and true if match criteria are met, otherwise false.
func StatusErrorMatch(err error, matchStatusCodes ...int) (int, bool) {
	var statusErr StatusError

	if errors.As(err, &statusErr) {
		statusCode := statusErr.Status()

		if len(matchStatusCodes) <= 0 {
			return statusCode, true
		}

		for _, s := range matchStatusCodes {
			if statusCode == s {
				return statusCode, true
			}
		}
	}

	return -1, false
}

// StatusErrorCheck returns whether or not err was caused by a StatusError and if it matches one of the
// optional status codes.
func StatusErrorCheck(err error, matchStatusCodes ...int) bool {
	_, found := StatusErrorMatch(err, matchStatusCodes...)
	return found
}
<|endoftext|>


<commit_before>package shared

import (
	"fmt"
	"strconv"
	"strings"
)

type ContainerAction string

const (
	Stop     ContainerAction = "stop"
	Start    ContainerAction = "start"
	Restart  ContainerAction = "restart"
	Freeze   ContainerAction = "freeze"
	Unfreeze ContainerAction = "unfreeze"
)

func IsInt64(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	return nil
}

func IsUint32(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseInt(value, 10, 32)
	if err != nil {
		return fmt.Errorf("Invalid value for uint32: %s: %v", value, err)
	}

	return nil
}

func IsPriority(value string) error {
	if value == "" {
		return nil
	}

	valueInt, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	if valueInt < 0 || valueInt > 10 {
		return fmt.Errorf("Invalid value for a limit '%s'. Must be between 0 and 10.", value)
	}

	return nil
}

func IsBool(value string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
		return fmt.Errorf("Invalid value for a boolean: %s", value)
	}

	return nil
}

func IsOneOf(value string, valid []string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(value, valid) {
		return fmt.Errorf("Invalid value: %s (not one of %s)", value, valid)
	}

	return nil
}

func IsAny(value string) error {
	return nil
}

// KnownContainerConfigKeys maps all fully defined, well-known config keys
// to an appropriate checker function, which validates whether or not a
// given value is syntactically legal.
var KnownContainerConfigKeys = map[string]func(value string) error{
	"boot.autostart":             IsBool,
	"boot.autostart.delay":       IsInt64,
	"boot.autostart.priority":    IsInt64,
	"boot.host_shutdown_timeout": IsInt64,

	"limits.cpu": IsAny,
	"limits.cpu.allowance": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			// Percentage based allocation
			_, err := strconv.Atoi(strings.TrimSuffix(value, "%"))
			if err != nil {
				return err
			}

			return nil
		}

		// Time based allocation
		fields := strings.SplitN(value, "/", 2)
		if len(fields) != 2 {
			return fmt.Errorf("Invalid allowance: %s", value)
		}

		_, err := strconv.Atoi(strings.TrimSuffix(fields[0], "ms"))
		if err != nil {
			return err
		}

		_, err = strconv.Atoi(strings.TrimSuffix(fields[1], "ms"))
		if err != nil {
			return err
		}

		return nil
	},
	"limits.cpu.priority": IsPriority,

	"limits.disk.priority": IsPriority,

	"limits.memory": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			_, err := strconv.ParseInt(strings.TrimSuffix(value, "%"), 10, 64)
			if err != nil {
				return err
			}

			return nil
		}

		_, err := ParseByteSizeString(value)
		if err != nil {
			return err
		}

		return nil
	},
	"limits.memory.enforce": func(value string) error {
		return IsOneOf(value, []string{"soft", "hard"})
	},
	"limits.memory.swap":          IsBool,
	"limits.memory.swap.priority": IsPriority,

	"limits.network.priority": IsPriority,

	"limits.processes": IsInt64,

	"linux.kernel_modules": IsAny,

	"security.nesting":    IsBool,
	"security.privileged": IsBool,

	"security.idmap.size":     IsUint32,
	"security.idmap.isolated": IsBool,

	"security.syscalls.blacklist_default": IsBool,
	"security.syscalls.blacklist_compat":  IsBool,
	"security.syscalls.blacklist":         IsAny,
	"security.syscalls.whitelist":         IsAny,

	// Caller is responsible for full validation of any raw.* value
	"raw.apparmor": IsAny,
	"raw.lxc":      IsAny,
	"raw.seccomp":  IsAny,
	"raw.idmap":    IsAny,

	"volatile.apply_template":   IsAny,
	"volatile.base_image":       IsAny,
	"volatile.last_state.idmap": IsAny,
	"volatile.last_state.power": IsAny,
	"volatile.idmap.next":       IsAny,
	"volatile.idmap.base":       IsAny,
}

// ConfigKeyChecker returns a function that will check whether or not
// a provide value is valid for the associate config key.  Returns an
// error if the key is not known.  The checker function only performs
// syntactic checking of the value, semantic and usage checking must
// be done by the caller.  User defined keys are always considered to
// be valid, e.g. user.* and environment.* keys.
func ConfigKeyChecker(key string) (func(value string) error, error) {
	if f, ok := KnownContainerConfigKeys[key]; ok {
		return f, nil
	}

	if strings.HasPrefix(key, "volatile.") {
		if strings.HasSuffix(key, ".hwaddr") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".name") {
			return IsAny, nil
		}
	}

	if strings.HasPrefix(key, "environment.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "user.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "image.") {
		return IsAny, nil
	}

	return nil, fmt.Errorf("Bad key: %s", key)
}
<commit_msg>Fix uint32 check<commit_after>package shared

import (
	"fmt"
	"strconv"
	"strings"
)

type ContainerAction string

const (
	Stop     ContainerAction = "stop"
	Start    ContainerAction = "start"
	Restart  ContainerAction = "restart"
	Freeze   ContainerAction = "freeze"
	Unfreeze ContainerAction = "unfreeze"
)

func IsInt64(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	return nil
}

func IsUint32(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseUint(value, 10, 32)
	if err != nil {
		return fmt.Errorf("Invalid value for uint32: %s: %v", value, err)
	}

	return nil
}

func IsPriority(value string) error {
	if value == "" {
		return nil
	}

	valueInt, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	if valueInt < 0 || valueInt > 10 {
		return fmt.Errorf("Invalid value for a limit '%s'. Must be between 0 and 10.", value)
	}

	return nil
}

func IsBool(value string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
		return fmt.Errorf("Invalid value for a boolean: %s", value)
	}

	return nil
}

func IsOneOf(value string, valid []string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(value, valid) {
		return fmt.Errorf("Invalid value: %s (not one of %s)", value, valid)
	}

	return nil
}

func IsAny(value string) error {
	return nil
}

// KnownContainerConfigKeys maps all fully defined, well-known config keys
// to an appropriate checker function, which validates whether or not a
// given value is syntactically legal.
var KnownContainerConfigKeys = map[string]func(value string) error{
	"boot.autostart":             IsBool,
	"boot.autostart.delay":       IsInt64,
	"boot.autostart.priority":    IsInt64,
	"boot.host_shutdown_timeout": IsInt64,

	"limits.cpu": IsAny,
	"limits.cpu.allowance": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			// Percentage based allocation
			_, err := strconv.Atoi(strings.TrimSuffix(value, "%"))
			if err != nil {
				return err
			}

			return nil
		}

		// Time based allocation
		fields := strings.SplitN(value, "/", 2)
		if len(fields) != 2 {
			return fmt.Errorf("Invalid allowance: %s", value)
		}

		_, err := strconv.Atoi(strings.TrimSuffix(fields[0], "ms"))
		if err != nil {
			return err
		}

		_, err = strconv.Atoi(strings.TrimSuffix(fields[1], "ms"))
		if err != nil {
			return err
		}

		return nil
	},
	"limits.cpu.priority": IsPriority,

	"limits.disk.priority": IsPriority,

	"limits.memory": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			_, err := strconv.ParseInt(strings.TrimSuffix(value, "%"), 10, 64)
			if err != nil {
				return err
			}

			return nil
		}

		_, err := ParseByteSizeString(value)
		if err != nil {
			return err
		}

		return nil
	},
	"limits.memory.enforce": func(value string) error {
		return IsOneOf(value, []string{"soft", "hard"})
	},
	"limits.memory.swap":          IsBool,
	"limits.memory.swap.priority": IsPriority,

	"limits.network.priority": IsPriority,

	"limits.processes": IsInt64,

	"linux.kernel_modules": IsAny,

	"security.nesting":    IsBool,
	"security.privileged": IsBool,

	"security.idmap.size":     IsUint32,
	"security.idmap.isolated": IsBool,

	"security.syscalls.blacklist_default": IsBool,
	"security.syscalls.blacklist_compat":  IsBool,
	"security.syscalls.blacklist":         IsAny,
	"security.syscalls.whitelist":         IsAny,

	// Caller is responsible for full validation of any raw.* value
	"raw.apparmor": IsAny,
	"raw.lxc":      IsAny,
	"raw.seccomp":  IsAny,
	"raw.idmap":    IsAny,

	"volatile.apply_template":   IsAny,
	"volatile.base_image":       IsAny,
	"volatile.last_state.idmap": IsAny,
	"volatile.last_state.power": IsAny,
	"volatile.idmap.next":       IsAny,
	"volatile.idmap.base":       IsAny,
}

// ConfigKeyChecker returns a function that will check whether or not
// a provide value is valid for the associate config key.  Returns an
// error if the key is not known.  The checker function only performs
// syntactic checking of the value, semantic and usage checking must
// be done by the caller.  User defined keys are always considered to
// be valid, e.g. user.* and environment.* keys.
func ConfigKeyChecker(key string) (func(value string) error, error) {
	if f, ok := KnownContainerConfigKeys[key]; ok {
		return f, nil
	}

	if strings.HasPrefix(key, "volatile.") {
		if strings.HasSuffix(key, ".hwaddr") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".name") {
			return IsAny, nil
		}
	}

	if strings.HasPrefix(key, "environment.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "user.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "image.") {
		return IsAny, nil
	}

	return nil, fmt.Errorf("Bad key: %s", key)
}
<|endoftext|>