<commit_before>package command

import (
	"github.com/codegangsta/cli"
	"github.com/docker/libcompose/cli/app"
)

// CreateCommand defines the libcompose create subcommand.
func CreateCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "create",
		Usage:  "Create all services but do not start",
		Action: app.WithProject(factory, app.ProjectCreate),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "no-recreate",
				Usage: "If containers already exist, don't recreate them. Incompatible with --force-recreate.",
			},
			cli.BoolFlag{
				Name:  "force-recreate",
				Usage: "Recreate containers even if their configuration and image haven't changed. Incompatible with --no-recreate.",
			},
			cli.BoolFlag{
				Name:  "no-build",
				Usage: "Don't build an image, even if it's missing.",
			},
		},
	}
}

// BuildCommand defines the libcompose build subcommand.
func BuildCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "build",
		Usage:  "Build or rebuild services.",
		Action: app.WithProject(factory, app.ProjectBuild),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "no-cache",
				Usage: "Do not use cache when building the image",
			},
			cli.BoolFlag{
				Name:  "force-rm",
				Usage: "Always remove intermediate containers",
			},
			cli.BoolFlag{
				Name:  "pull",
				Usage: "Always attempt to pull a newer version of the image",
			},
		},
	}
}

// PsCommand defines the libcompose ps subcommand.
func PsCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "ps",
		Usage:  "List containers",
		Action: app.WithProject(factory, app.ProjectPs),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "q",
				Usage: "Only display IDs",
			},
		},
	}
}

// PortCommand defines the libcompose port subcommand.
func PortCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "port",
		Usage:  "Print the public port for a port binding",
		Action: app.WithProject(factory, app.ProjectPort),
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "protocol",
				Usage: "tcp or udp ",
				Value: "tcp",
			},
			cli.IntFlag{
				Name:  "index",
				Usage: "index of the container if there are multiple instances of a service",
				Value: 1,
			},
		},
	}
}

// UpCommand defines the libcompose up subcommand.
func UpCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "up",
		Usage:  "Bring all services up",
		Action: app.WithProject(factory, app.ProjectUp),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "d",
				Usage: "Do not block and log",
			},
			cli.BoolFlag{
				Name:  "no-build",
				Usage: "Don't build an image, even if it's missing.",
			},
			cli.BoolFlag{
				Name:  "no-recreate",
				Usage: "If containers already exist, don't recreate them. Incompatible with --force-recreate.",
			},
			cli.BoolFlag{
				Name:  "force-recreate",
				Usage: "Recreate containers even if their configuration and image haven't changed. Incompatible with --no-recreate.",
			},
		},
	}
}

// StartCommand defines the libcompose start subcommand.
func StartCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "start",
		Usage:  "Start services",
		Action: app.WithProject(factory, app.ProjectStart),
		Flags: []cli.Flag{
			cli.BoolTFlag{
				Name:  "d",
				Usage: "Do not block and log",
			},
		},
	}
}

// RunCommand defines the libcompose run subcommand.
func RunCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "run",
		Usage:  "Run a one-off command",
		Action: app.WithProject(factory, app.ProjectRun),
		Flags:  []cli.Flag{},
	}
}

// PullCommand defines the libcompose pull subcommand.
func PullCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "pull",
		Usage:  "Pulls images for services",
		Action: app.WithProject(factory, app.ProjectPull),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "ignore-pull-failures",
				Usage: "Pull what it can and ignores images with pull failures.",
			},
		},
	}
}

// LogsCommand defines the libcompose logs subcommand.
func LogsCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "logs",
		Usage:  "Get service logs",
		Action: app.WithProject(factory, app.ProjectLog),
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "lines",
				Usage: "number of lines to tail",
				Value: 100,
			},
			cli.BoolFlag{
				Name:  "follow",
				Usage: "Follow log output.",
			},
		},
	}
}

// RestartCommand defines the libcompose restart subcommand.
func RestartCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "restart",
		Usage:  "Restart services",
		Action: app.WithProject(factory, app.ProjectRestart),
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "timeout,t",
				Usage: "Specify a shutdown timeout in seconds.",
				Value: 10,
			},
		},
	}
}

// StopCommand defines the libcompose stop subcommand.
func StopCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "stop",
		Usage:  "Stop services",
		Action: app.WithProject(factory, app.ProjectStop),
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "timeout,t",
				Usage: "Specify a shutdown timeout in seconds.",
				Value: 10,
			},
		},
	}
}

// DownCommand defines the libcompose stop subcommand.
func DownCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "down",
		Usage:  "Stop and remove containers, networks, images, and volumes",
		Action: app.WithProject(factory, app.ProjectDown),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "volumes,v",
				Usage: "Remove data volumes",
			},
			cli.StringFlag{
				Name:  "rmi",
				Usage: "Remove images, type may be one of: 'all' to remove all images, or 'local' to remove only images that don't have an custom name set by the `image` field",
			},
			cli.BoolFlag{
				Name:  "remove-orphans",
				Usage: "Remove containers for services not defined in the Compose file",
			},
		},
	}
}

// ScaleCommand defines the libcompose scale subcommand.
func ScaleCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "scale",
		Usage:  "Scale services",
		Action: app.WithProject(factory, app.ProjectScale),
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "timeout,t",
				Usage: "Specify a shutdown timeout in seconds.",
				Value: 10,
			},
		},
	}
}

// RmCommand defines the libcompose rm subcommand.
func RmCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "rm",
		Usage:  "Delete services",
		Action: app.WithProject(factory, app.ProjectDelete),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "force,f",
				Usage: "Allow deletion of all services",
			},
			cli.BoolFlag{
				Name:  "v",
				Usage: "Remove volumes associated with containers",
			},
		},
	}
}

// KillCommand defines the libcompose kill subcommand.
func KillCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "kill",
		Usage:  "Force stop service containers",
		Action: app.WithProject(factory, app.ProjectKill),
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "signal,s",
				Usage: "SIGNAL to send to the container",
				Value: "SIGKILL",
			},
		},
	}
}

// PauseCommand defines the libcompose pause subcommand.
func PauseCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:  "pause",
		Usage: "Pause services.",
		// ArgsUsage: "[SERVICE...]",
		Action: app.WithProject(factory, app.ProjectPause),
	}
}

// UnpauseCommand defines the libcompose unpause subcommand.
func UnpauseCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:  "unpause",
		Usage: "Unpause services.",
		// ArgsUsage: "[SERVICE...]",
		Action: app.WithProject(factory, app.ProjectUnpause),
	}
}

// EventsCommand defines the libcompose events subcommand
func EventsCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "events",
		Usage:  "Receive real time events from containers.",
		Action: app.WithProject(factory, app.ProjectEvents),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "json",
				Usage: "Output events as a stream of json objects",
			},
		},
	}
}

// VersionCommand defines the libcompose version subcommand.
func VersionCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "version",
		Usage:  "Show version informations",
		Action: app.Version,
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "short",
				Usage: "Shows only Compose's version number.",
			},
		},
	}
}

// CommonFlags defines the flags that are in common for all subcommands.
func CommonFlags() []cli.Flag {
	return []cli.Flag{
		cli.BoolFlag{
			Name: "verbose,debug",
		},
		cli.StringSliceFlag{
			Name:   "file,f",
			Usage:  "Specify one or more alternate compose files (default: docker-compose.yml)",
			Value:  &cli.StringSlice{},
			EnvVar: "COMPOSE_FILE",
		},
		cli.StringFlag{
			Name:   "project-name,p",
			Usage:  "Specify an alternate project name (default: directory name)",
			EnvVar: "COMPOSE_PROJECT_NAME",
		},
	}
}
<commit_msg>Remove --lines flag for logs command<commit_after>package command

import (
	"github.com/codegangsta/cli"
	"github.com/docker/libcompose/cli/app"
)

// CreateCommand defines the libcompose create subcommand.
func CreateCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "create",
		Usage:  "Create all services but do not start",
		Action: app.WithProject(factory, app.ProjectCreate),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "no-recreate",
				Usage: "If containers already exist, don't recreate them. Incompatible with --force-recreate.",
			},
			cli.BoolFlag{
				Name:  "force-recreate",
				Usage: "Recreate containers even if their configuration and image haven't changed. Incompatible with --no-recreate.",
			},
			cli.BoolFlag{
				Name:  "no-build",
				Usage: "Don't build an image, even if it's missing.",
			},
		},
	}
}

// BuildCommand defines the libcompose build subcommand.
func BuildCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "build",
		Usage:  "Build or rebuild services.",
		Action: app.WithProject(factory, app.ProjectBuild),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "no-cache",
				Usage: "Do not use cache when building the image",
			},
			cli.BoolFlag{
				Name:  "force-rm",
				Usage: "Always remove intermediate containers",
			},
			cli.BoolFlag{
				Name:  "pull",
				Usage: "Always attempt to pull a newer version of the image",
			},
		},
	}
}

// PsCommand defines the libcompose ps subcommand.
func PsCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "ps",
		Usage:  "List containers",
		Action: app.WithProject(factory, app.ProjectPs),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "q",
				Usage: "Only display IDs",
			},
		},
	}
}

// PortCommand defines the libcompose port subcommand.
func PortCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "port",
		Usage:  "Print the public port for a port binding",
		Action: app.WithProject(factory, app.ProjectPort),
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "protocol",
				Usage: "tcp or udp ",
				Value: "tcp",
			},
			cli.IntFlag{
				Name:  "index",
				Usage: "index of the container if there are multiple instances of a service",
				Value: 1,
			},
		},
	}
}

// UpCommand defines the libcompose up subcommand.
func UpCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "up",
		Usage:  "Bring all services up",
		Action: app.WithProject(factory, app.ProjectUp),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "d",
				Usage: "Do not block and log",
			},
			cli.BoolFlag{
				Name:  "no-build",
				Usage: "Don't build an image, even if it's missing.",
			},
			cli.BoolFlag{
				Name:  "no-recreate",
				Usage: "If containers already exist, don't recreate them. Incompatible with --force-recreate.",
			},
			cli.BoolFlag{
				Name:  "force-recreate",
				Usage: "Recreate containers even if their configuration and image haven't changed. Incompatible with --no-recreate.",
			},
		},
	}
}

// StartCommand defines the libcompose start subcommand.
func StartCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "start",
		Usage:  "Start services",
		Action: app.WithProject(factory, app.ProjectStart),
		Flags: []cli.Flag{
			cli.BoolTFlag{
				Name:  "d",
				Usage: "Do not block and log",
			},
		},
	}
}

// RunCommand defines the libcompose run subcommand.
func RunCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "run",
		Usage:  "Run a one-off command",
		Action: app.WithProject(factory, app.ProjectRun),
		Flags:  []cli.Flag{},
	}
}

// PullCommand defines the libcompose pull subcommand.
func PullCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "pull",
		Usage:  "Pulls images for services",
		Action: app.WithProject(factory, app.ProjectPull),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "ignore-pull-failures",
				Usage: "Pull what it can and ignores images with pull failures.",
			},
		},
	}
}

// LogsCommand defines the libcompose logs subcommand.
func LogsCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "logs",
		Usage:  "Get service logs",
		Action: app.WithProject(factory, app.ProjectLog),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "follow",
				Usage: "Follow log output.",
			},
		},
	}
}

// RestartCommand defines the libcompose restart subcommand.
func RestartCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "restart",
		Usage:  "Restart services",
		Action: app.WithProject(factory, app.ProjectRestart),
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "timeout,t",
				Usage: "Specify a shutdown timeout in seconds.",
				Value: 10,
			},
		},
	}
}

// StopCommand defines the libcompose stop subcommand.
func StopCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "stop",
		Usage:  "Stop services",
		Action: app.WithProject(factory, app.ProjectStop),
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "timeout,t",
				Usage: "Specify a shutdown timeout in seconds.",
				Value: 10,
			},
		},
	}
}

// DownCommand defines the libcompose stop subcommand.
func DownCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "down",
		Usage:  "Stop and remove containers, networks, images, and volumes",
		Action: app.WithProject(factory, app.ProjectDown),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "volumes,v",
				Usage: "Remove data volumes",
			},
			cli.StringFlag{
				Name:  "rmi",
				Usage: "Remove images, type may be one of: 'all' to remove all images, or 'local' to remove only images that don't have an custom name set by the `image` field",
			},
			cli.BoolFlag{
				Name:  "remove-orphans",
				Usage: "Remove containers for services not defined in the Compose file",
			},
		},
	}
}

// ScaleCommand defines the libcompose scale subcommand.
func ScaleCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "scale",
		Usage:  "Scale services",
		Action: app.WithProject(factory, app.ProjectScale),
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "timeout,t",
				Usage: "Specify a shutdown timeout in seconds.",
				Value: 10,
			},
		},
	}
}

// RmCommand defines the libcompose rm subcommand.
func RmCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "rm",
		Usage:  "Delete services",
		Action: app.WithProject(factory, app.ProjectDelete),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "force,f",
				Usage: "Allow deletion of all services",
			},
			cli.BoolFlag{
				Name:  "v",
				Usage: "Remove volumes associated with containers",
			},
		},
	}
}

// KillCommand defines the libcompose kill subcommand.
func KillCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "kill",
		Usage:  "Force stop service containers",
		Action: app.WithProject(factory, app.ProjectKill),
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "signal,s",
				Usage: "SIGNAL to send to the container",
				Value: "SIGKILL",
			},
		},
	}
}

// PauseCommand defines the libcompose pause subcommand.
func PauseCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:  "pause",
		Usage: "Pause services.",
		// ArgsUsage: "[SERVICE...]",
		Action: app.WithProject(factory, app.ProjectPause),
	}
}

// UnpauseCommand defines the libcompose unpause subcommand.
func UnpauseCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:  "unpause",
		Usage: "Unpause services.",
		// ArgsUsage: "[SERVICE...]",
		Action: app.WithProject(factory, app.ProjectUnpause),
	}
}

// EventsCommand defines the libcompose events subcommand
func EventsCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "events",
		Usage:  "Receive real time events from containers.",
		Action: app.WithProject(factory, app.ProjectEvents),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "json",
				Usage: "Output events as a stream of json objects",
			},
		},
	}
}

// VersionCommand defines the libcompose version subcommand.
func VersionCommand(factory app.ProjectFactory) cli.Command {
	return cli.Command{
		Name:   "version",
		Usage:  "Show version informations",
		Action: app.Version,
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "short",
				Usage: "Shows only Compose's version number.",
			},
		},
	}
}

// CommonFlags defines the flags that are in common for all subcommands.
func CommonFlags() []cli.Flag {
	return []cli.Flag{
		cli.BoolFlag{
			Name: "verbose,debug",
		},
		cli.StringSliceFlag{
			Name:   "file,f",
			Usage:  "Specify one or more alternate compose files (default: docker-compose.yml)",
			Value:  &cli.StringSlice{},
			EnvVar: "COMPOSE_FILE",
		},
		cli.StringFlag{
			Name:   "project-name,p",
			Usage:  "Specify an alternate project name (default: directory name)",
			EnvVar: "COMPOSE_PROJECT_NAME",
		},
	}
}
<|endoftext|>


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

package commands

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"text/tabwriter"

	"github.com/romana/core/cli/util"
	"github.com/romana/core/common"
	"github.com/romana/core/common/api"

	"github.com/go-resty/resty"
	ms "github.com/mitchellh/mapstructure"
	log "github.com/romana/rlog"
	cli "github.com/spf13/cobra"
	config "github.com/spf13/viper"
)

// Policies structure is used to keep track of
// security policies and their status, as to if
// they were applied successfully or not.
type Policies struct {
	SecurityPolicies    []api.Policy
	AppliedSuccessfully []bool
}

// policyCmd represents the policy commands
var policyCmd = &cli.Command{
	Use:   "policy [add|show|list|remove]",
	Short: "Add, Remove or Show policies for romana services.",
	Long: `Add, Remove or Show policies for romana services.

For more information, please check http://romana.io
`,
}

func init() {
	policyCmd.AddCommand(policyAddCmd)
	policyCmd.AddCommand(policyRemoveCmd)
	policyCmd.AddCommand(policyListCmd)
	policyCmd.AddCommand(policyShowCmd)
}

var policyAddCmd = &cli.Command{
	Use:   "add [policyFile][STDIN]",
	Short: "Add a new policy.",
	Long: `Add a new policy.

Romana policies can be added for a specific network
using the policyFile provided or through input pipe.
The features supported are:
 * Policy addition through file with single policy in it
 * Policy addition through file with multiple policies
   in it
 * Both the above formats but taking input from standard
   input (STDIN) instead of a file
 * Tabular and json output for indication of policy
   addition
`,
	RunE:         policyAdd,
	SilenceUsage: true,
}

var policyRemoveCmd = &cli.Command{
	Use:          "remove [policyID]",
	Short:        "Remove a specific policy.",
	Long:         `Remove a specific policy.`,
	RunE:         policyRemove,
	SilenceUsage: true,
}

var policyListCmd = &cli.Command{
	Use:          "list",
	Short:        "List all policies.",
	Long:         `List all policies.`,
	RunE:         policyList,
	SilenceUsage: true,
}

var policyShowCmd = &cli.Command{
	Use:          "show [PolicyID]",
	Short:        "Show details about a specific policy using policyID.",
	Long:         `Show details about a specific policy using policyID.`,
	RunE:         policyShow,
	SilenceUsage: true,
}

// policyAdd adds romana policy for a specific tenant
// using the policyFile provided or through input pipe.
// The features supported are:
//  * Policy addition through file with single policy in it
//  * Policy addition through file with multiple policies
//    in it
//  * Both the above formats but taking input from standard
//    input (STDIN) instead of a file
//  * Tabular and json output for indication of policy
//    addition
func policyAdd(cmd *cli.Command, args []string) error {
	var buf []byte
	var policyFile string
	var err error
	isFile := true
	isJSON := config.GetString("Format") == "json"

	if len(args) == 0 {
		isFile = false
		buf, err = ioutil.ReadAll(os.Stdin)
		if err != nil {
			util.UsageError(cmd,
				"POLICY FILE name or piped input from 'STDIN' expected.")
			return fmt.Errorf("Cannot read 'STDIN': %s\n", err)
		}
	} else if len(args) != 1 {
		return util.UsageError(cmd,
			"POLICY FILE name or piped input from 'STDIN' expected.")
	}

	if isFile {
		policyFile = args[0]
	}

	rootURL := config.GetString("RootURL")

	reqPolicies := Policies{}
	if isFile {
		pBuf, err := ioutil.ReadFile(policyFile)
		if err != nil {
			return fmt.Errorf("File error: %s\n", err)
		}
		err = json.Unmarshal(pBuf, &reqPolicies.SecurityPolicies)
		if err != nil || len(reqPolicies.SecurityPolicies) == 0 {
			reqPolicies.SecurityPolicies = make([]api.Policy, 1)
			err = json.Unmarshal(pBuf, &reqPolicies.SecurityPolicies[0])
			if err != nil {
				return err
			}
		}
	} else {
		err = json.Unmarshal(buf, &reqPolicies.SecurityPolicies)
		if err != nil || len(reqPolicies.SecurityPolicies) == 0 {
			reqPolicies.SecurityPolicies = make([]api.Policy, 1)
			err = json.Unmarshal(buf, &reqPolicies.SecurityPolicies[0])
			if err != nil {
				return err
			}
		}
	}

	result := make([]map[string]interface{}, len(reqPolicies.SecurityPolicies))
	reqPolicies.AppliedSuccessfully = make([]bool, len(reqPolicies.SecurityPolicies))
	for i, pol := range reqPolicies.SecurityPolicies {
		reqPolicies.AppliedSuccessfully[i] = false
		r, err := resty.R().SetHeader("Content-Type", "application/json").
			SetBody(pol).Post(rootURL + "/policies")
		m := make(map[string]interface{})
		m["details"] = r.Status()
		m["status_code"] = r.StatusCode()
		result[i] = m
		if err != nil {
			log.Printf("Error applying policy (%s:%s): %v\n",
				pol.ID, pol.Description, err)
			continue
		}
		if r.StatusCode() != http.StatusOK {
			log.Printf("Error applying policy (%s:%s): %s\n",
				pol.ID, pol.Description, r.Status())
			continue
		}
		reqPolicies.AppliedSuccessfully[i] = true
	}

	if isJSON {
		for i := range reqPolicies.SecurityPolicies {
			var h common.HttpError
			dc := &ms.DecoderConfig{TagName: "json", Result: &h}
			decoder, err := ms.NewDecoder(dc)
			if err != nil {
				continue
			}
			err = decoder.Decode(result[i])
			if err != nil {
				continue
			}
			status, _ := json.MarshalIndent(h, "", "\t")
			fmt.Println(string(status))
		}
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 0, '\t', 0)
		fmt.Println("New Policies Processed:")
		fmt.Fprintln(w, "Id\t",
			"Direction\t",
			"Successful Applied?\t",
		)
		for i, p := range reqPolicies.SecurityPolicies {
			fmt.Fprintf(w, "%s \t %s \t %t \n", p.ID,
				p.Direction, reqPolicies.AppliedSuccessfully[i])
		}
		w.Flush()
	}

	return nil
}

// policyRemove removes policy using the policy name provided
// as argument through args. It returns error if policy is not
// found, or returns a list of policy ID's if multiple policies
// with same name are found.
func policyRemove(cmd *cli.Command, args []string) error {

	if len(args) != 1 {
		return fmt.Errorf("Policy remove takes exactly one argument i.e policy id.")
	}

	policyID := args[0]

	rootURL := config.GetString("RootURL")
	resp, err := resty.R().Delete(rootURL + "/policies/" + policyID)
	if err != nil {
		return err
	}

	if config.GetString("Format") == "json" {
		JSONFormat(resp.Body(), os.Stdout)
	} else {
		if resp.StatusCode() == http.StatusOK {
			fmt.Printf("Policy (ID: %s) deleted successfully.\n", policyID)
		} else {
			fmt.Printf("Error deleting policy (ID: %s): %s\n",
				policyID, resp.Status())
		}
	}

	return nil
}

// policyList lists policies in tabular or json format.
func policyList(cmd *cli.Command, args []string) error {
	if len(args) > 0 {
		return util.UsageError(cmd,
			"Policy listing takes no arguments.")
	}
	return policyListShow(true, nil)
}

// policyShow displays details about a specific policy
// in tabular or json format.
func policyShow(cmd *cli.Command, args []string) error {
	return policyListShow(false, args)
}

// policyListShow lists/shows policies in tabular or json format.
func policyListShow(listOnly bool, args []string) error {
	specificPolicies := false
	if len(args) > 0 {
		specificPolicies = true
	}

	if !listOnly && !specificPolicies {
		return fmt.Errorf("Policy show takes at-least one argument i.e policy id/s.")
	}

	rootURL := config.GetString("RootURL")
	resp, err := resty.R().Get(rootURL + "/policies")
	if err != nil {
		return err
	}

	var allPolicies []api.Policy
	err = json.Unmarshal(resp.Body(), &allPolicies)
	if err != nil {
		return err
	}

	var policies []api.Policy
	if listOnly {
		policies = allPolicies
	} else {
		if specificPolicies {
			for _, a := range args {
				for _, p := range allPolicies {
					if a == p.ID {
						policies = append(policies, p)
					}
				}
			}
		}
	}

	if config.GetString("Format") == "json" {
		body, _ := json.MarshalIndent(policies, "", "\t")
		fmt.Println(string(body))
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 0, '\t', 0)
		if listOnly {
			fmt.Println("Policy List")
			fmt.Fprintln(w, "Policy Id\t",
				"Direction\t",
				"Applied to\t",
				"No of Peers\t",
				"No of Rules\t",
				"Description\t",
			)
		} else {
			fmt.Println("Policy Details")
		}
		for _, p := range policies {
			if listOnly {
				noOfPeers := 0
				noOfRules := 0
				for i := range p.Ingress {
					noOfPeers += len(p.Ingress[i].Peers)
					noOfRules += len(p.Ingress[i].Rules)
				}

				fmt.Fprintln(w, p.ID, "\t",
					p.Direction, "\t",
					len(p.AppliedTo), "\t",
					noOfPeers, "\t",
					noOfRules, "\t",
					p.Description, "\t",
				)
			} else {
				fmt.Fprint(w,
					"Policy Id:\t", p.ID, "\n",
					"Direction:\t", p.Direction, "\n",
					"Description:\t", p.Description, "\n",
				)
				if len(p.AppliedTo) > 0 {
					fmt.Fprintln(w, "Applied To:")
					for _, ato := range p.AppliedTo {
						fmt.Fprintln(w,
							"\tPeer:\t", ato.Peer, "\n",
							"\tCidr:\t", ato.Cidr, "\n",
							"\tDestination:\t", ato.Dest, "\n",
							"\tTenantID:\t", ato.TenantID, "\n",
							"\tSegmentID:\t", ato.SegmentID,
						)
					}
				}
				if len(p.Ingress) > 0 {
					for _, ingress := range p.Ingress {
						if len(ingress.Peers) > 0 {
							fmt.Fprintln(w, "Peers:")
							for _, peer := range ingress.Peers {
								fmt.Fprintln(w,
									"\tPeer:\t", peer.Peer, "\n",
									"\tCidr:\t", peer.Cidr, "\n",
									"\tDestination:\t", peer.Dest, "\n",
									"\tTenantID:\t", peer.TenantID, "\n",
									"\tSegmentID:\t", peer.SegmentID,
								)
							}
						}
						if len(ingress.Rules) > 0 {
							fmt.Fprintln(w, "Rules:")
							for _, rule := range ingress.Rules {
								fmt.Fprintln(w,
									"\tProtocol:\t", rule.Protocol, "\n",
									"\tIsStateful:\t", rule.IsStateful, "\n",
									"\tPorts:\t", rule.Ports, "\n",
									"\tPortRanges:\t", rule.PortRanges, "\n",
									"\tIcmpType:\t", rule.IcmpType, "\n",
									"\tIcmpCode:\t", rule.IcmpCode,
								)
							}
						}
					}
				}
				fmt.Fprintln(w, "")
			}
		}
		w.Flush()
	}

	return nil
}
<commit_msg>cli: set content type and check http status while deleting romana policy.<commit_after>// Copyright (c) 2016 Pani Networks
// All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package commands

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"text/tabwriter"

	"github.com/romana/core/cli/util"
	"github.com/romana/core/common"
	"github.com/romana/core/common/api"

	"github.com/go-resty/resty"
	ms "github.com/mitchellh/mapstructure"
	log "github.com/romana/rlog"
	cli "github.com/spf13/cobra"
	config "github.com/spf13/viper"
)

// Policies structure is used to keep track of
// security policies and their status, as to if
// they were applied successfully or not.
type Policies struct {
	SecurityPolicies    []api.Policy
	AppliedSuccessfully []bool
}

// policyCmd represents the policy commands
var policyCmd = &cli.Command{
	Use:   "policy [add|show|list|remove]",
	Short: "Add, Remove or Show policies for romana services.",
	Long: `Add, Remove or Show policies for romana services.

For more information, please check http://romana.io
`,
}

func init() {
	policyCmd.AddCommand(policyAddCmd)
	policyCmd.AddCommand(policyRemoveCmd)
	policyCmd.AddCommand(policyListCmd)
	policyCmd.AddCommand(policyShowCmd)
}

var policyAddCmd = &cli.Command{
	Use:   "add [policyFile][STDIN]",
	Short: "Add a new policy.",
	Long: `Add a new policy.

Romana policies can be added for a specific network
using the policyFile provided or through input pipe.
The features supported are:
 * Policy addition through file with single policy in it
 * Policy addition through file with multiple policies
   in it
 * Both the above formats but taking input from standard
   input (STDIN) instead of a file
 * Tabular and json output for indication of policy
   addition
`,
	RunE:         policyAdd,
	SilenceUsage: true,
}

var policyRemoveCmd = &cli.Command{
	Use:          "remove [policyID]",
	Short:        "Remove a specific policy.",
	Long:         `Remove a specific policy.`,
	RunE:         policyRemove,
	SilenceUsage: true,
}

var policyListCmd = &cli.Command{
	Use:          "list",
	Short:        "List all policies.",
	Long:         `List all policies.`,
	RunE:         policyList,
	SilenceUsage: true,
}

var policyShowCmd = &cli.Command{
	Use:          "show [PolicyID]",
	Short:        "Show details about a specific policy using policyID.",
	Long:         `Show details about a specific policy using policyID.`,
	RunE:         policyShow,
	SilenceUsage: true,
}

// policyAdd adds romana policy for a specific tenant
// using the policyFile provided or through input pipe.
// The features supported are:
//  * Policy addition through file with single policy in it
//  * Policy addition through file with multiple policies
//    in it
//  * Both the above formats but taking input from standard
//    input (STDIN) instead of a file
//  * Tabular and json output for indication of policy
//    addition
func policyAdd(cmd *cli.Command, args []string) error {
	var buf []byte
	var policyFile string
	var err error
	isFile := true
	isJSON := config.GetString("Format") == "json"

	if len(args) == 0 {
		isFile = false
		buf, err = ioutil.ReadAll(os.Stdin)
		if err != nil {
			util.UsageError(cmd,
				"POLICY FILE name or piped input from 'STDIN' expected.")
			return fmt.Errorf("Cannot read 'STDIN': %s\n", err)
		}
	} else if len(args) != 1 {
		return util.UsageError(cmd,
			"POLICY FILE name or piped input from 'STDIN' expected.")
	}

	if isFile {
		policyFile = args[0]
	}

	rootURL := config.GetString("RootURL")

	reqPolicies := Policies{}
	if isFile {
		pBuf, err := ioutil.ReadFile(policyFile)
		if err != nil {
			return fmt.Errorf("File error: %s\n", err)
		}
		err = json.Unmarshal(pBuf, &reqPolicies.SecurityPolicies)
		if err != nil || len(reqPolicies.SecurityPolicies) == 0 {
			reqPolicies.SecurityPolicies = make([]api.Policy, 1)
			err = json.Unmarshal(pBuf, &reqPolicies.SecurityPolicies[0])
			if err != nil {
				return err
			}
		}
	} else {
		err = json.Unmarshal(buf, &reqPolicies.SecurityPolicies)
		if err != nil || len(reqPolicies.SecurityPolicies) == 0 {
			reqPolicies.SecurityPolicies = make([]api.Policy, 1)
			err = json.Unmarshal(buf, &reqPolicies.SecurityPolicies[0])
			if err != nil {
				return err
			}
		}
	}

	result := make([]map[string]interface{}, len(reqPolicies.SecurityPolicies))
	reqPolicies.AppliedSuccessfully = make([]bool, len(reqPolicies.SecurityPolicies))
	for i, pol := range reqPolicies.SecurityPolicies {
		reqPolicies.AppliedSuccessfully[i] = false
		r, err := resty.R().SetHeader("Content-Type", "application/json").
			SetBody(pol).Post(rootURL + "/policies")
		m := make(map[string]interface{})
		m["details"] = r.Status()
		m["status_code"] = r.StatusCode()
		result[i] = m
		if err != nil {
			log.Printf("Error applying policy (%s:%s): %v\n",
				pol.ID, pol.Description, err)
			continue
		}
		if r.StatusCode() != http.StatusOK {
			log.Printf("Error applying policy (%s:%s): %s\n",
				pol.ID, pol.Description, r.Status())
			continue
		}
		reqPolicies.AppliedSuccessfully[i] = true
	}

	if isJSON {
		for i := range reqPolicies.SecurityPolicies {
			var h common.HttpError
			dc := &ms.DecoderConfig{TagName: "json", Result: &h}
			decoder, err := ms.NewDecoder(dc)
			if err != nil {
				continue
			}
			err = decoder.Decode(result[i])
			if err != nil {
				continue
			}
			status, _ := json.MarshalIndent(h, "", "\t")
			fmt.Println(string(status))
		}
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 0, '\t', 0)
		fmt.Println("New Policies Processed:")
		fmt.Fprintln(w, "Id\t",
			"Direction\t",
			"Successful Applied?\t",
		)
		for i, p := range reqPolicies.SecurityPolicies {
			fmt.Fprintf(w, "%s \t %s \t %t \n", p.ID,
				p.Direction, reqPolicies.AppliedSuccessfully[i])
		}
		w.Flush()
	}

	return nil
}

// policyRemove removes policy using the policy name provided
// as argument through args. It returns error if policy is not
// found, or returns a list of policy ID's if multiple policies
// with same name are found.
func policyRemove(cmd *cli.Command, args []string) error {

	if len(args) != 1 {
		return fmt.Errorf("Policy remove takes exactly one argument i.e policy id.")
	}

	var policy api.Policy
	policy.ID = args[0]

	rootURL := config.GetString("RootURL")
	resp, err := resty.R().
		SetHeader("Content-Type", "application/json").
		SetBody(policy).Delete(rootURL + "/policies")
	if err != nil {
		return err
	}

	if config.GetString("Format") == "json" {
		JSONFormat(resp.Body(), os.Stdout)
	} else {
		if resp.StatusCode() == http.StatusOK {
			fmt.Printf("Policy (ID: %s) deleted successfully.\n", policy.ID)
		} else {
			fmt.Printf("Error deleting policy (ID: %s): %s\n",
				policy.ID, resp.Status())
		}
	}

	return nil
}

// policyList lists policies in tabular or json format.
func policyList(cmd *cli.Command, args []string) error {
	if len(args) > 0 {
		return util.UsageError(cmd,
			"Policy listing takes no arguments.")
	}
	return policyListShow(true, nil)
}

// policyShow displays details about a specific policy
// in tabular or json format.
func policyShow(cmd *cli.Command, args []string) error {
	return policyListShow(false, args)
}

// policyListShow lists/shows policies in tabular or json format.
func policyListShow(listOnly bool, args []string) error {
	specificPolicies := false
	if len(args) > 0 {
		specificPolicies = true
	}

	if !listOnly && !specificPolicies {
		return fmt.Errorf("Policy show takes at-least one argument i.e policy id/s.")
	}

	rootURL := config.GetString("RootURL")
	resp, err := resty.R().Get(rootURL + "/policies")
	if err != nil {
		return err
	}

	var allPolicies []api.Policy
	err = json.Unmarshal(resp.Body(), &allPolicies)
	if err != nil {
		return err
	}

	var policies []api.Policy
	if listOnly {
		policies = allPolicies
	} else {
		if specificPolicies {
			for _, a := range args {
				for _, p := range allPolicies {
					if a == p.ID {
						policies = append(policies, p)
					}
				}
			}
		}
	}

	if config.GetString("Format") == "json" {
		body, _ := json.MarshalIndent(policies, "", "\t")
		fmt.Println(string(body))
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 0, '\t', 0)
		if listOnly {
			fmt.Println("Policy List")
			fmt.Fprintln(w, "Policy Id\t",
				"Direction\t",
				"Applied to\t",
				"No of Peers\t",
				"No of Rules\t",
				"Description\t",
			)
		} else {
			fmt.Println("Policy Details")
		}
		for _, p := range policies {
			if listOnly {
				noOfPeers := 0
				noOfRules := 0
				for i := range p.Ingress {
					noOfPeers += len(p.Ingress[i].Peers)
					noOfRules += len(p.Ingress[i].Rules)
				}

				fmt.Fprintln(w, p.ID, "\t",
					p.Direction, "\t",
					len(p.AppliedTo), "\t",
					noOfPeers, "\t",
					noOfRules, "\t",
					p.Description, "\t",
				)
			} else {
				fmt.Fprint(w,
					"Policy Id:\t", p.ID, "\n",
					"Direction:\t", p.Direction, "\n",
					"Description:\t", p.Description, "\n",
				)
				if len(p.AppliedTo) > 0 {
					fmt.Fprintln(w, "Applied To:")
					for _, ato := range p.AppliedTo {
						fmt.Fprintln(w,
							"\tPeer:\t", ato.Peer, "\n",
							"\tCidr:\t", ato.Cidr, "\n",
							"\tDestination:\t", ato.Dest, "\n",
							"\tTenantID:\t", ato.TenantID, "\n",
							"\tSegmentID:\t", ato.SegmentID,
						)
					}
				}
				if len(p.Ingress) > 0 {
					for _, ingress := range p.Ingress {
						if len(ingress.Peers) > 0 {
							fmt.Fprintln(w, "Peers:")
							for _, peer := range ingress.Peers {
								fmt.Fprintln(w,
									"\tPeer:\t", peer.Peer, "\n",
									"\tCidr:\t", peer.Cidr, "\n",
									"\tDestination:\t", peer.Dest, "\n",
									"\tTenantID:\t", peer.TenantID, "\n",
									"\tSegmentID:\t", peer.SegmentID,
								)
							}
						}
						if len(ingress.Rules) > 0 {
							fmt.Fprintln(w, "Rules:")
							for _, rule := range ingress.Rules {
								fmt.Fprintln(w,
									"\tProtocol:\t", rule.Protocol, "\n",
									"\tIsStateful:\t", rule.IsStateful, "\n",
									"\tPorts:\t", rule.Ports, "\n",
									"\tPortRanges:\t", rule.PortRanges, "\n",
									"\tIcmpType:\t", rule.IcmpType, "\n",
									"\tIcmpCode:\t", rule.IcmpCode,
								)
							}
						}
					}
				}
				fmt.Fprintln(w, "")
			}
		}
		w.Flush()
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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 scorecard

import (
	"context"
	"fmt"

	"github.com/forseti-security/config-validator/pkg/api/validator"
	"github.com/forseti-security/config-validator/pkg/gcv"
	"github.com/pkg/errors"
)

// ScoringConfig holds settings for generating a score
type ScoringConfig struct {
	PolicyPath  string
	categories  map[string]*constraintCategory
	constraints map[string]*constraintViolations
	validator   *gcv.Validator
	ctx         context.Context
}

const otherCategoryKey = "other"

// constraintCategory holds constraints by category
type constraintCategory struct {
	Name        string
	constraints []*constraintViolations
}

func (c constraintCategory) Count() int {
	sum := 0
	for _, cv := range c.constraints {
		sum += cv.Count()
	}
	return sum
}

// constraintViolations holds violations for a particular constraint
type constraintViolations struct {
	constraint *validator.Constraint
	Violations []*validator.Violation `protobuf:"bytes,1,rep,name=violations,proto3" json:"violations,omitempty"`
}

func (cv constraintViolations) Count() int {
	return len(cv.Violations)
}

func (cv constraintViolations) GetName() string {
	return cv.constraint.GetMetadata().GetName()
}

var availableCategories = map[string]string{
	"operational-efficiency": "Operational Efficiency",
	"security":               "Security",
	"reliability":            "Reliability",
	otherCategoryKey:         "Other",
}

func getConstraintForViolation(config *ScoringConfig, violation *validator.Violation) (*constraintViolations, error) {
	key := violation.GetConstraint()
	cv, found := config.constraints[key]
	if !found {
		response, err := config.validator.GetConstraint(config.ctx, &validator.GetConstraintRequest{
			Name: key,
		})
		if err != nil {
			return nil, errors.Wrap(err, "Finding matching constraint")
		}

		constraint := response.GetConstraint()
		cv = &constraintViolations{
			constraint: response.GetConstraint(),
		}
		config.constraints[key] = cv

		annotations := constraint.GetMetadata().GetAnnotations()
		categoryKey, found := annotations["scorecard.cft.dev/category"]
		if !found {
			categoryKey = otherCategoryKey
		}

		category, found := config.categories[categoryKey]
		if !found {
			return nil, fmt.Errorf("Unknown constraint category %v for constraint %v", categoryKey, key)
		}
		category.constraints = append(category.constraints, cv)
	}
	return cv, nil
}

// attachViolations puts violations into their appropriate categories
func attachViolations(audit *validator.AuditResponse, config *ScoringConfig) error {
	// Build map of categories
	config.categories = make(map[string]*constraintCategory)
	for k, name := range availableCategories {
		config.categories[k] = &constraintCategory{
			Name: name,
		}
	}

	// Categorize violations
	config.constraints = make(map[string]*constraintViolations)
	for _, v := range audit.Violations {
		cv, err := getConstraintForViolation(config, v)
		if err != nil {
			return errors.Wrap(err, "Categorizing violation")
		}

		cv.Violations = append(cv.Violations, v)
	}

	return nil
}

// ScoreInventory creates a Scorecard for an inventory
func ScoreInventory(inventory *Inventory, config *ScoringConfig) error {
	config.ctx = context.Background()

	err := attachValidator(config)
	if err != nil {
		return errors.Wrap(err, "initializing gcv validator")
	}

	auditResult, err := getViolations(inventory, config)
	if err != nil {
		return err
	}

	err = attachViolations(auditResult, config)

	if len(auditResult.Violations) > 0 {
		fmt.Printf("\n\n%v total issues found\n", len(auditResult.Violations))
		for _, category := range config.categories {
			fmt.Printf("\n\n%v: %v issues found\n", category.Name, category.Count())
			fmt.Printf("----------\n")
			for _, cv := range category.constraints {
				fmt.Printf("%v: %v issues\n", cv.GetName(), cv.Count())
				for _, v := range cv.Violations {
					fmt.Printf("- %v\n\n",
						v.Message,
					)
					Log.Debug("Violation metadata", "metadata", v.GetMetadata())
				}
			}
		}
	} else {
		fmt.Println("No issues found found! You have a perfect score.")
	}

	return nil
}
<commit_msg>Update scorecard categories to use category bundles<commit_after>// Copyright 2019 Google LLC
//
// 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 scorecard

import (
	"context"
	"fmt"

	"github.com/forseti-security/config-validator/pkg/api/validator"
	"github.com/forseti-security/config-validator/pkg/gcv"
	"github.com/pkg/errors"
)

// ScoringConfig holds settings for generating a score
type ScoringConfig struct {
	PolicyPath  string
	categories  map[string]*constraintCategory
	constraints map[string]*constraintViolations
	validator   *gcv.Validator
	ctx         context.Context
}

const otherCategoryKey = "other"

// constraintCategory holds constraints by category
type constraintCategory struct {
	Name        string
	constraints []*constraintViolations
}

func (c constraintCategory) Count() int {
	sum := 0
	for _, cv := range c.constraints {
		sum += cv.Count()
	}
	return sum
}

// constraintViolations holds violations for a particular constraint
type constraintViolations struct {
	constraint *validator.Constraint
	Violations []*validator.Violation `protobuf:"bytes,1,rep,name=violations,proto3" json:"violations,omitempty"`
}

func (cv constraintViolations) Count() int {
	return len(cv.Violations)
}

func (cv constraintViolations) GetName() string {
	return cv.constraint.GetMetadata().GetName()
}

var availableCategories = map[string]string{
	"operational-efficiency": "Operational Efficiency",
	"security":               "Security",
	"reliability":            "Reliability",
	otherCategoryKey:         "Other",
}

func getConstraintForViolation(config *ScoringConfig, violation *validator.Violation) (*constraintViolations, error) {
	key := violation.GetConstraint()
	cv, found := config.constraints[key]
	if !found {
		response, err := config.validator.GetConstraint(config.ctx, &validator.GetConstraintRequest{
			Name: key,
		})
		if err != nil {
			return nil, errors.Wrap(err, "Finding matching constraint")
		}

		constraint := response.GetConstraint()
		cv = &constraintViolations{
			constraint: response.GetConstraint(),
		}
		config.constraints[key] = cv

		annotations := constraint.GetMetadata().GetAnnotations()
		categoryKey, found := annotations["bundles.validator.forsetisecurity.org/scorecard-v1"]
		if !found {
			categoryKey = otherCategoryKey
		}

		category, found := config.categories[categoryKey]
		if !found {
			return nil, fmt.Errorf("Unknown constraint category %v for constraint %v", categoryKey, key)
		}
		category.constraints = append(category.constraints, cv)
	}
	return cv, nil
}

// attachViolations puts violations into their appropriate categories
func attachViolations(audit *validator.AuditResponse, config *ScoringConfig) error {
	// Build map of categories
	config.categories = make(map[string]*constraintCategory)
	for k, name := range availableCategories {
		config.categories[k] = &constraintCategory{
			Name: name,
		}
	}

	// Categorize violations
	config.constraints = make(map[string]*constraintViolations)
	for _, v := range audit.Violations {
		cv, err := getConstraintForViolation(config, v)
		if err != nil {
			return errors.Wrap(err, "Categorizing violation")
		}

		cv.Violations = append(cv.Violations, v)
	}

	return nil
}

// ScoreInventory creates a Scorecard for an inventory
func ScoreInventory(inventory *Inventory, config *ScoringConfig) error {
	config.ctx = context.Background()

	err := attachValidator(config)
	if err != nil {
		return errors.Wrap(err, "initializing gcv validator")
	}

	auditResult, err := getViolations(inventory, config)
	if err != nil {
		return err
	}

	err = attachViolations(auditResult, config)

	if len(auditResult.Violations) > 0 {
		fmt.Printf("\n\n%v total issues found\n", len(auditResult.Violations))
		for _, category := range config.categories {
			fmt.Printf("\n\n%v: %v issues found\n", category.Name, category.Count())
			fmt.Printf("----------\n")
			for _, cv := range category.constraints {
				fmt.Printf("%v: %v issues\n", cv.GetName(), cv.Count())
				for _, v := range cv.Violations {
					fmt.Printf("- %v\n\n",
						v.Message,
					)
					Log.Debug("Violation metadata", "metadata", v.GetMetadata())
				}
			}
		}
	} else {
		fmt.Println("No issues found found! You have a perfect score.")
	}

	return nil
}
<|endoftext|>


<commit_before>package prettyprint

import "fmt"

// A petabyte is 2^50, so a float64, which holds 2^53 ints, is big
// enough for whatever purposes.

// Size is wrapper for humanizing byte sizes. Sizes are reported in
// base-2 equivalents, not base-10, i.e. 1 KB = 1024 bytes.
type Size float64

func (size Size) String() string {
	const (
		kilobyte = 1 << (10 * (iota + 1))
		megabyte
		gigabyte
		terabyte
		petabyte
	)

	format := "%.f"

	switch {
	case size >= petabyte:
		format = "%3.1f PB"
		size /= terabyte
	case size >= terabyte:
		format = "%3.1f TB"
		size /= terabyte
	case size >= gigabyte:
		format = "%3.1f GB"
		size /= gigabyte
	case size >= megabyte:
		format = "%3.1f MB"
		size /= megabyte
	case size >= kilobyte:
		format = "%3.1f KB"
		size /= kilobyte
	}
	return fmt.Sprintf(format, size)
}
<commit_msg>Prettyprint size: Use B for bytes<commit_after>package prettyprint

import "fmt"

// A petabyte is 2^50, so a float64, which holds 2^53 ints, is big
// enough for whatever purposes.

// Size is wrapper for humanizing byte sizes. Sizes are reported in
// base-2 equivalents, not base-10, i.e. 1 KB = 1024 bytes.
type Size float64

func (size Size) String() string {
	const (
		kilobyte = 1 << (10 * (iota + 1))
		megabyte
		gigabyte
		terabyte
		petabyte
	)

	format := "%.f B"

	switch {
	case size >= petabyte:
		format = "%3.1f PB"
		size /= terabyte
	case size >= terabyte:
		format = "%3.1f TB"
		size /= terabyte
	case size >= gigabyte:
		format = "%3.1f GB"
		size /= gigabyte
	case size >= megabyte:
		format = "%3.1f MB"
		size /= megabyte
	case size >= kilobyte:
		format = "%3.1f KB"
		size /= kilobyte
	}
	return fmt.Sprintf(format, size)
}
<|endoftext|>


<commit_before>package circuit

import (
	"fmt"
	"sync/atomic"
	"testing"
	"time"

	"github.com/cenk/backoff"
	"github.com/facebookgo/clock"
)

func init() {
	defaultInitialBackOffInterval = time.Millisecond
}

func TestBreakerTripping(t *testing.T) {
	cb := NewBreaker()

	if cb.Tripped() {
		t.Fatal("expected breaker to not be tripped")
	}

	cb.Trip()
	if !cb.Tripped() {
		t.Fatal("expected breaker to be tripped")
	}

	cb.Reset()
	if cb.Tripped() {
		t.Fatal("expected breaker to have been reset")
	}
}

func TestBreakerCounts(t *testing.T) {
	cb := NewBreaker()

	cb.Fail()
	if failures := cb.Failures(); failures != 1 {
		t.Fatalf("expected failure count to be 1, got %d", failures)
	}

	cb.Fail()
	if consecFailures := cb.ConsecFailures(); consecFailures != 2 {
		t.Fatalf("expected 2 consecutive failures, got %d", consecFailures)
	}

	cb.Success()
	if successes := cb.Successes(); successes != 1 {
		t.Fatalf("expected success count to be 1, got %d", successes)
	}
	if consecFailures := cb.ConsecFailures(); consecFailures != 0 {
		t.Fatalf("expected 0 consecutive failures, got %d", consecFailures)
	}

	cb.Reset()
	if failures := cb.Failures(); failures != 0 {
		t.Fatalf("expected failure count to be 0, got %d", failures)
	}
	if successes := cb.Successes(); successes != 0 {
		t.Fatalf("expected success count to be 0, got %d", successes)
	}
	if consecFailures := cb.ConsecFailures(); consecFailures != 0 {
		t.Fatalf("expected 0 consecutive failures, got %d", consecFailures)
	}
}

func TestErrorRate(t *testing.T) {
	cb := NewBreaker()
	if er := cb.ErrorRate(); er != 0.0 {
		t.Fatalf("expected breaker with no samples to have 0 error rate, got %f", er)
	}
}

func TestBreakerEvents(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	events := cb.Subscribe()

	cb.Trip()
	if e := <-events; e != BreakerTripped {
		t.Fatalf("expected to receive a trip event, got %d", e)
	}

	c.Add(cb.nextBackOff + 1)
	cb.Ready()
	if e := <-events; e != BreakerReady {
		t.Fatalf("expected to receive a breaker ready event, got %d", e)
	}

	cb.Reset()
	if e := <-events; e != BreakerReset {
		t.Fatalf("expected to receive a reset event, got %d", e)
	}

	cb.Fail()
	if e := <-events; e != BreakerFail {
		t.Fatalf("expected to receive a fail event, got %d", e)
	}
}

func TestAddRemoveListener(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	events := make(chan ListenerEvent, 100)
	cb.AddListener(events)

	cb.Trip()
	if e := <-events; e.Event != BreakerTripped {
		t.Fatalf("expected to receive a trip event, got %d", e)
	}

	c.Add(cb.nextBackOff + 1)
	cb.Ready()
	if e := <-events; e.Event != BreakerReady {
		t.Fatalf("expected to receive a breaker ready event, got %d", e)
	}

	cb.Reset()
	if e := <-events; e.Event != BreakerReset {
		t.Fatalf("expected to receive a reset event, got %d", e)
	}

	cb.Fail()
	if e := <-events; e.Event != BreakerFail {
		t.Fatalf("expected to receive a fail event, got %d", e)
	}

	cb.RemoveListener(events)
	cb.Reset()
	select {
	case e := <-events:
		t.Fatalf("after removing listener, should not receive reset event; got %s", e)
	default:
		// Expected.
	}
}

func TestTrippableBreakerState(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c

	if !cb.Ready() {
		t.Fatal("expected breaker to be ready")
	}

	cb.Trip()
	if cb.Ready() {
		t.Fatal("expected breaker to not be ready")
	}
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready after reset timeout")
	}

	cb.Fail()
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready after reset timeout, post failure")
	}
}

func TestTrippableBreakerManualBreak(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	cb.Break()
	c.Add(cb.nextBackOff + 1)

	if cb.Ready() {
		t.Fatal("expected breaker to still be tripped")
	}

	cb.Reset()
	cb.Trip()
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready")
	}
}

func TestThresholdBreaker(t *testing.T) {
	cb := NewThresholdBreaker(2)

	if cb.Tripped() {
		t.Fatal("expected threshold breaker to be open")
	}

	cb.Fail()
	if cb.Tripped() {
		t.Fatal("expected threshold breaker to still be open")
	}

	cb.Fail()
	if !cb.Tripped() {
		t.Fatal("expected threshold breaker to be tripped")
	}

	cb.Reset()
	if failures := cb.Failures(); failures != 0 {
		t.Fatalf("expected reset to set failures to 0, got %d", failures)
	}
	if cb.Tripped() {
		t.Fatal("expected threshold breaker to be open")
	}
}

func TestConsecutiveBreaker(t *testing.T) {
	cb := NewConsecutiveBreaker(3)

	if cb.Tripped() {
		t.Fatal("expected consecutive breaker to be open")
	}

	cb.Fail()
	cb.Success()
	cb.Fail()
	cb.Fail()
	if cb.Tripped() {
		t.Fatal("expected consecutive breaker to be open")
	}
	cb.Fail()
	if !cb.Tripped() {
		t.Fatal("expected consecutive breaker to be tripped")
	}
}

func TestThresholdBreakerCalling(t *testing.T) {
	circuit := func() error {
		return fmt.Errorf("error")
	}

	cb := NewThresholdBreaker(2)

	err := cb.Call(circuit, 0) // First failure
	if err == nil {
		t.Fatal("expected threshold breaker to error")
	}
	if cb.Tripped() {
		t.Fatal("expected threshold breaker to be open")
	}

	err = cb.Call(circuit, 0) // Second failure trips
	if err == nil {
		t.Fatal("expected threshold breaker to error")
	}
	if !cb.Tripped() {
		t.Fatal("expected threshold breaker to be tripped")
	}
}

func TestThresholdBreakerResets(t *testing.T) {
	called := 0
	success := false
	circuit := func() error {
		if called == 0 {
			called++
			return fmt.Errorf("error")
		}
		success = true
		return nil
	}

	c := clock.NewMock()
	cb := NewThresholdBreaker(1)
	cb.Clock = c
	err := cb.Call(circuit, 0)
	if err == nil {
		t.Fatal("Expected cb to return an error")
	}

	c.Add(cb.nextBackOff + 1)
	for i := 0; i < 4; i++ {
		err = cb.Call(circuit, 0)
		if err != nil {
			t.Fatal("Expected cb to be successful")
		}

		if !success {
			t.Fatal("Expected cb to have been reset")
		}
	}
}

func TestTimeoutBreaker(t *testing.T) {
	c := clock.NewMock()
	called := int32(0)
	circuit := func() error {
		atomic.AddInt32(&called, 1)
		c.Add(time.Millisecond)
		return nil
	}

	cb := NewThresholdBreaker(1)
	cb.Clock = c
	err := cb.Call(circuit, time.Millisecond)
	if err == nil {
		t.Fatal("expected timeout breaker to return an error")
	}
	cb.Call(circuit, time.Millisecond)

	if !cb.Tripped() {
		t.Fatal("expected timeout breaker to be open")
	}
}

func TestRateBreakerTripping(t *testing.T) {
	cb := NewRateBreaker(0.5, 4)
	cb.Success()
	cb.Success()
	cb.Fail()
	cb.Fail()

	if !cb.Tripped() {
		t.Fatal("expected rate breaker to be tripped")
	}

	if er := cb.ErrorRate(); er != 0.5 {
		t.Fatalf("expected error rate to be 0.5, got %f", er)
	}
}

func TestRateBreakerSampleSize(t *testing.T) {
	cb := NewRateBreaker(0.5, 100)
	cb.Fail()

	if cb.Tripped() {
		t.Fatal("expected rate breaker to not be tripped yet")
	}
}

func TestRateBreakerResets(t *testing.T) {
	serviceError := fmt.Errorf("service error")

	called := 0
	success := false
	circuit := func() error {
		if called < 4 {
			called++
			return serviceError
		}
		success = true
		return nil
	}

	c := clock.NewMock()
	cb := NewRateBreaker(0.5, 4)
	cb.Clock = c
	var err error
	for i := 0; i < 4; i++ {
		err = cb.Call(circuit, 0)
		if err == nil {
			t.Fatal("Expected cb to return an error (closed breaker, service failure)")
		} else if err != serviceError {
			t.Fatal("Expected cb to return error from service (closed breaker, service failure)")
		}
	}

	err = cb.Call(circuit, 0)
	if err == nil {
		t.Fatal("Expected cb to return an error (open breaker)")
	} else if err != ErrBreakerOpen {
		t.Fatal("Expected cb to return open open breaker error (open breaker)")
	}

	c.Add(cb.nextBackOff + 1)
	err = cb.Call(circuit, 0)
	if err != nil {
		t.Fatal("Expected cb to be successful")
	}

	if !success {
		t.Fatal("Expected cb to have been reset")
	}
}

func TestNeverRetryAfterBackoffStops(t *testing.T) {
	cb := NewBreakerWithOptions(&Options{
		BackOff: &backoff.StopBackOff{},
	})

	cb.Trip()

	// circuit should be open and never retry again
	// when nextBackoff is backoff.Stop
	called := 0
	cb.Call(func() error {
		called = 1
		return nil
	}, 0)

	if called == 1 {
		t.Fatal("Expected cb to never retry")
	}
}
<commit_msg>avoid data race on the mock clock<commit_after>package circuit

import (
	"fmt"
	"sync/atomic"
	"testing"
	"time"

	"github.com/cenk/backoff"
	"github.com/facebookgo/clock"
)

func init() {
	defaultInitialBackOffInterval = time.Millisecond
}

func TestBreakerTripping(t *testing.T) {
	cb := NewBreaker()

	if cb.Tripped() {
		t.Fatal("expected breaker to not be tripped")
	}

	cb.Trip()
	if !cb.Tripped() {
		t.Fatal("expected breaker to be tripped")
	}

	cb.Reset()
	if cb.Tripped() {
		t.Fatal("expected breaker to have been reset")
	}
}

func TestBreakerCounts(t *testing.T) {
	cb := NewBreaker()

	cb.Fail()
	if failures := cb.Failures(); failures != 1 {
		t.Fatalf("expected failure count to be 1, got %d", failures)
	}

	cb.Fail()
	if consecFailures := cb.ConsecFailures(); consecFailures != 2 {
		t.Fatalf("expected 2 consecutive failures, got %d", consecFailures)
	}

	cb.Success()
	if successes := cb.Successes(); successes != 1 {
		t.Fatalf("expected success count to be 1, got %d", successes)
	}
	if consecFailures := cb.ConsecFailures(); consecFailures != 0 {
		t.Fatalf("expected 0 consecutive failures, got %d", consecFailures)
	}

	cb.Reset()
	if failures := cb.Failures(); failures != 0 {
		t.Fatalf("expected failure count to be 0, got %d", failures)
	}
	if successes := cb.Successes(); successes != 0 {
		t.Fatalf("expected success count to be 0, got %d", successes)
	}
	if consecFailures := cb.ConsecFailures(); consecFailures != 0 {
		t.Fatalf("expected 0 consecutive failures, got %d", consecFailures)
	}
}

func TestErrorRate(t *testing.T) {
	cb := NewBreaker()
	if er := cb.ErrorRate(); er != 0.0 {
		t.Fatalf("expected breaker with no samples to have 0 error rate, got %f", er)
	}
}

func TestBreakerEvents(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	events := cb.Subscribe()

	cb.Trip()
	if e := <-events; e != BreakerTripped {
		t.Fatalf("expected to receive a trip event, got %d", e)
	}

	c.Add(cb.nextBackOff + 1)
	cb.Ready()
	if e := <-events; e != BreakerReady {
		t.Fatalf("expected to receive a breaker ready event, got %d", e)
	}

	cb.Reset()
	if e := <-events; e != BreakerReset {
		t.Fatalf("expected to receive a reset event, got %d", e)
	}

	cb.Fail()
	if e := <-events; e != BreakerFail {
		t.Fatalf("expected to receive a fail event, got %d", e)
	}
}

func TestAddRemoveListener(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	events := make(chan ListenerEvent, 100)
	cb.AddListener(events)

	cb.Trip()
	if e := <-events; e.Event != BreakerTripped {
		t.Fatalf("expected to receive a trip event, got %d", e)
	}

	c.Add(cb.nextBackOff + 1)
	cb.Ready()
	if e := <-events; e.Event != BreakerReady {
		t.Fatalf("expected to receive a breaker ready event, got %d", e)
	}

	cb.Reset()
	if e := <-events; e.Event != BreakerReset {
		t.Fatalf("expected to receive a reset event, got %d", e)
	}

	cb.Fail()
	if e := <-events; e.Event != BreakerFail {
		t.Fatalf("expected to receive a fail event, got %d", e)
	}

	cb.RemoveListener(events)
	cb.Reset()
	select {
	case e := <-events:
		t.Fatalf("after removing listener, should not receive reset event; got %s", e)
	default:
		// Expected.
	}
}

func TestTrippableBreakerState(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c

	if !cb.Ready() {
		t.Fatal("expected breaker to be ready")
	}

	cb.Trip()
	if cb.Ready() {
		t.Fatal("expected breaker to not be ready")
	}
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready after reset timeout")
	}

	cb.Fail()
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready after reset timeout, post failure")
	}
}

func TestTrippableBreakerManualBreak(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	cb.Break()
	c.Add(cb.nextBackOff + 1)

	if cb.Ready() {
		t.Fatal("expected breaker to still be tripped")
	}

	cb.Reset()
	cb.Trip()
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready")
	}
}

func TestThresholdBreaker(t *testing.T) {
	cb := NewThresholdBreaker(2)

	if cb.Tripped() {
		t.Fatal("expected threshold breaker to be open")
	}

	cb.Fail()
	if cb.Tripped() {
		t.Fatal("expected threshold breaker to still be open")
	}

	cb.Fail()
	if !cb.Tripped() {
		t.Fatal("expected threshold breaker to be tripped")
	}

	cb.Reset()
	if failures := cb.Failures(); failures != 0 {
		t.Fatalf("expected reset to set failures to 0, got %d", failures)
	}
	if cb.Tripped() {
		t.Fatal("expected threshold breaker to be open")
	}
}

func TestConsecutiveBreaker(t *testing.T) {
	cb := NewConsecutiveBreaker(3)

	if cb.Tripped() {
		t.Fatal("expected consecutive breaker to be open")
	}

	cb.Fail()
	cb.Success()
	cb.Fail()
	cb.Fail()
	if cb.Tripped() {
		t.Fatal("expected consecutive breaker to be open")
	}
	cb.Fail()
	if !cb.Tripped() {
		t.Fatal("expected consecutive breaker to be tripped")
	}
}

func TestThresholdBreakerCalling(t *testing.T) {
	circuit := func() error {
		return fmt.Errorf("error")
	}

	cb := NewThresholdBreaker(2)

	err := cb.Call(circuit, 0) // First failure
	if err == nil {
		t.Fatal("expected threshold breaker to error")
	}
	if cb.Tripped() {
		t.Fatal("expected threshold breaker to be open")
	}

	err = cb.Call(circuit, 0) // Second failure trips
	if err == nil {
		t.Fatal("expected threshold breaker to error")
	}
	if !cb.Tripped() {
		t.Fatal("expected threshold breaker to be tripped")
	}
}

func TestThresholdBreakerResets(t *testing.T) {
	called := 0
	success := false
	circuit := func() error {
		if called == 0 {
			called++
			return fmt.Errorf("error")
		}
		success = true
		return nil
	}

	c := clock.NewMock()
	cb := NewThresholdBreaker(1)
	cb.Clock = c
	err := cb.Call(circuit, 0)
	if err == nil {
		t.Fatal("Expected cb to return an error")
	}

	c.Add(cb.nextBackOff + 1)
	for i := 0; i < 4; i++ {
		err = cb.Call(circuit, 0)
		if err != nil {
			t.Fatal("Expected cb to be successful")
		}

		if !success {
			t.Fatal("Expected cb to have been reset")
		}
	}
}

func TestTimeoutBreaker(t *testing.T) {
	wait := make(chan struct{})

	c := clock.NewMock()
	called := int32(0)

	circuit := func() error {
		wait <- struct{}{}
		atomic.AddInt32(&called, 1)
		<-wait
		return nil
	}

	cb := NewThresholdBreaker(1)
	cb.Clock = c

	errc := make(chan error)
	go func() { errc <- cb.Call(circuit, time.Millisecond) }()

	<-wait
	c.Add(time.Millisecond * 3)
	wait <- struct{}{}

	err := <-errc
	if err == nil {
		t.Fatal("expected timeout breaker to return an error")
	}

	go cb.Call(circuit, time.Millisecond)
	<-wait
	c.Add(time.Millisecond * 3)
	wait <- struct{}{}

	if !cb.Tripped() {
		t.Fatal("expected timeout breaker to be open")
	}
}

func TestRateBreakerTripping(t *testing.T) {
	cb := NewRateBreaker(0.5, 4)
	cb.Success()
	cb.Success()
	cb.Fail()
	cb.Fail()

	if !cb.Tripped() {
		t.Fatal("expected rate breaker to be tripped")
	}

	if er := cb.ErrorRate(); er != 0.5 {
		t.Fatalf("expected error rate to be 0.5, got %f", er)
	}
}

func TestRateBreakerSampleSize(t *testing.T) {
	cb := NewRateBreaker(0.5, 100)
	cb.Fail()

	if cb.Tripped() {
		t.Fatal("expected rate breaker to not be tripped yet")
	}
}

func TestRateBreakerResets(t *testing.T) {
	serviceError := fmt.Errorf("service error")

	called := 0
	success := false
	circuit := func() error {
		if called < 4 {
			called++
			return serviceError
		}
		success = true
		return nil
	}

	c := clock.NewMock()
	cb := NewRateBreaker(0.5, 4)
	cb.Clock = c
	var err error
	for i := 0; i < 4; i++ {
		err = cb.Call(circuit, 0)
		if err == nil {
			t.Fatal("Expected cb to return an error (closed breaker, service failure)")
		} else if err != serviceError {
			t.Fatal("Expected cb to return error from service (closed breaker, service failure)")
		}
	}

	err = cb.Call(circuit, 0)
	if err == nil {
		t.Fatal("Expected cb to return an error (open breaker)")
	} else if err != ErrBreakerOpen {
		t.Fatal("Expected cb to return open open breaker error (open breaker)")
	}

	c.Add(cb.nextBackOff + 1)
	err = cb.Call(circuit, 0)
	if err != nil {
		t.Fatal("Expected cb to be successful")
	}

	if !success {
		t.Fatal("Expected cb to have been reset")
	}
}

func TestNeverRetryAfterBackoffStops(t *testing.T) {
	cb := NewBreakerWithOptions(&Options{
		BackOff: &backoff.StopBackOff{},
	})

	cb.Trip()

	// circuit should be open and never retry again
	// when nextBackoff is backoff.Stop
	called := 0
	cb.Call(func() error {
		called = 1
		return nil
	}, 0)

	if called == 1 {
		t.Fatal("Expected cb to never retry")
	}
}
<|endoftext|>


<commit_before>package config

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

	"github.com/docker/cli/cli/config/configfile"
	"github.com/docker/cli/cli/config/credentials"
	"github.com/docker/cli/cli/config/types"
	"github.com/docker/docker/pkg/homedir"
	"github.com/pkg/errors"
)

const (
	// ConfigFileName is the name of config file
	ConfigFileName = "config.json"
	configFileDir  = ".docker"
	oldConfigfile  = ".dockercfg"
	contextsDir    = "contexts"
)

var (
	initConfigDir = new(sync.Once)
	configDir     string
	homeDir       string
)

// resetHomeDir is used in testing to reset the "homeDir" package variable to
// force re-lookup of the home directory between tests.
func resetHomeDir() {
	homeDir = ""
}

func getHomeDir() string {
	if homeDir == "" {
		homeDir = homedir.Get()
	}
	return homeDir
}

// resetConfigDir is used in testing to reset the "configDir" package variable
// and its sync.Once to force re-lookup between tests.
func resetConfigDir() {
	configDir = ""
	initConfigDir = new(sync.Once)
}

func setConfigDir() {
	if configDir != "" {
		return
	}
	configDir = os.Getenv("DOCKER_CONFIG")
	if configDir == "" {
		configDir = filepath.Join(getHomeDir(), configFileDir)
	}
}

// Dir returns the directory the configuration file is stored in
func Dir() string {
	initConfigDir.Do(setConfigDir)
	return configDir
}

// ContextStoreDir returns the directory the docker contexts are stored in
func ContextStoreDir() string {
	return filepath.Join(Dir(), contextsDir)
}

// SetDir sets the directory the configuration file is stored in
func SetDir(dir string) {
	configDir = filepath.Clean(dir)
}

// Path returns the path to a file relative to the config dir
func Path(p ...string) (string, error) {
	path := filepath.Join(append([]string{Dir()}, p...)...)
	if !strings.HasPrefix(path, Dir()+string(filepath.Separator)) {
		return "", errors.Errorf("path %q is outside of root config directory %q", path, Dir())
	}
	return path, nil
}

// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
// a non-nested reader
func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
	configFile := configfile.ConfigFile{
		AuthConfigs: make(map[string]types.AuthConfig),
	}
	err := configFile.LegacyLoadFromReader(configData)
	return &configFile, err
}

// LoadFromReader is a convenience function that creates a ConfigFile object from
// a reader
func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
	configFile := configfile.ConfigFile{
		AuthConfigs: make(map[string]types.AuthConfig),
	}
	err := configFile.LoadFromReader(configData)
	return &configFile, err
}

// TODO remove this temporary hack, which is used to warn about the deprecated ~/.dockercfg file
var printLegacyFileWarning bool

// Load reads the configuration files in the given directory, and sets up
// the auth config information and returns values.
// FIXME: use the internal golang config parser
func Load(configDir string) (*configfile.ConfigFile, error) {
	printLegacyFileWarning = false

	if configDir == "" {
		configDir = Dir()
	}

	filename := filepath.Join(configDir, ConfigFileName)
	configFile := configfile.New(filename)

	// Try happy path first - latest config file
	if file, err := os.Open(filename); err == nil {
		defer file.Close()
		err = configFile.LoadFromReader(file)
		if err != nil {
			err = errors.Wrap(err, filename)
		}
		return configFile, err
	} else if !os.IsNotExist(err) {
		// if file is there but we can't stat it for any reason other
		// than it doesn't exist then stop
		return configFile, errors.Wrap(err, filename)
	}

	// Can't find latest config file so check for the old one
	filename = filepath.Join(getHomeDir(), oldConfigfile)
	if file, err := os.Open(filename); err == nil {
		printLegacyFileWarning = true
		defer file.Close()
		if err := configFile.LegacyLoadFromReader(file); err != nil {
			return configFile, errors.Wrap(err, filename)
		}
	}
	return configFile, nil
}

// LoadDefaultConfigFile attempts to load the default config file and returns
// an initialized ConfigFile struct if none is found.
func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile {
	configFile, err := Load(Dir())
	if err != nil {
		fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err)
	}
	if printLegacyFileWarning {
		_, _ = fmt.Fprintln(stderr, "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release")
	}
	if !configFile.ContainsAuth() {
		configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore)
	}
	return configFile
}
<commit_msg>fix innocuous data-race when config.Load called in parallel<commit_after>package config

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

	"github.com/docker/cli/cli/config/configfile"
	"github.com/docker/cli/cli/config/credentials"
	"github.com/docker/cli/cli/config/types"
	"github.com/docker/docker/pkg/homedir"
	"github.com/pkg/errors"
)

const (
	// ConfigFileName is the name of config file
	ConfigFileName = "config.json"
	configFileDir  = ".docker"
	oldConfigfile  = ".dockercfg"
	contextsDir    = "contexts"
)

var (
	initConfigDir = new(sync.Once)
	configDir     string
	homeDir       string
)

// resetHomeDir is used in testing to reset the "homeDir" package variable to
// force re-lookup of the home directory between tests.
func resetHomeDir() {
	homeDir = ""
}

func getHomeDir() string {
	if homeDir == "" {
		homeDir = homedir.Get()
	}
	return homeDir
}

// resetConfigDir is used in testing to reset the "configDir" package variable
// and its sync.Once to force re-lookup between tests.
func resetConfigDir() {
	configDir = ""
	initConfigDir = new(sync.Once)
}

func setConfigDir() {
	if configDir != "" {
		return
	}
	configDir = os.Getenv("DOCKER_CONFIG")
	if configDir == "" {
		configDir = filepath.Join(getHomeDir(), configFileDir)
	}
}

// Dir returns the directory the configuration file is stored in
func Dir() string {
	initConfigDir.Do(setConfigDir)
	return configDir
}

// ContextStoreDir returns the directory the docker contexts are stored in
func ContextStoreDir() string {
	return filepath.Join(Dir(), contextsDir)
}

// SetDir sets the directory the configuration file is stored in
func SetDir(dir string) {
	configDir = filepath.Clean(dir)
}

// Path returns the path to a file relative to the config dir
func Path(p ...string) (string, error) {
	path := filepath.Join(append([]string{Dir()}, p...)...)
	if !strings.HasPrefix(path, Dir()+string(filepath.Separator)) {
		return "", errors.Errorf("path %q is outside of root config directory %q", path, Dir())
	}
	return path, nil
}

// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
// a non-nested reader
func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
	configFile := configfile.ConfigFile{
		AuthConfigs: make(map[string]types.AuthConfig),
	}
	err := configFile.LegacyLoadFromReader(configData)
	return &configFile, err
}

// LoadFromReader is a convenience function that creates a ConfigFile object from
// a reader
func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
	configFile := configfile.ConfigFile{
		AuthConfigs: make(map[string]types.AuthConfig),
	}
	err := configFile.LoadFromReader(configData)
	return &configFile, err
}

// Load reads the configuration files in the given directory, and sets up
// the auth config information and returns values.
// FIXME: use the internal golang config parser
func Load(configDir string) (*configfile.ConfigFile, error) {
	cfg, _, err := load(configDir)
	return cfg, err
}

// TODO remove this temporary hack, which is used to warn about the deprecated ~/.dockercfg file
// so we can remove the bool return value and collapse this back into `Load`
func load(configDir string) (*configfile.ConfigFile, bool, error) {
	printLegacyFileWarning := false

	if configDir == "" {
		configDir = Dir()
	}

	filename := filepath.Join(configDir, ConfigFileName)
	configFile := configfile.New(filename)

	// Try happy path first - latest config file
	if file, err := os.Open(filename); err == nil {
		defer file.Close()
		err = configFile.LoadFromReader(file)
		if err != nil {
			err = errors.Wrap(err, filename)
		}
		return configFile, printLegacyFileWarning, err
	} else if !os.IsNotExist(err) {
		// if file is there but we can't stat it for any reason other
		// than it doesn't exist then stop
		return configFile, printLegacyFileWarning, errors.Wrap(err, filename)
	}

	// Can't find latest config file so check for the old one
	filename = filepath.Join(getHomeDir(), oldConfigfile)
	if file, err := os.Open(filename); err == nil {
		printLegacyFileWarning = true
		defer file.Close()
		if err := configFile.LegacyLoadFromReader(file); err != nil {
			return configFile, printLegacyFileWarning, errors.Wrap(err, filename)
		}
	}
	return configFile, printLegacyFileWarning, nil
}

// LoadDefaultConfigFile attempts to load the default config file and returns
// an initialized ConfigFile struct if none is found.
func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile {
	configFile, printLegacyFileWarning, err := load(Dir())
	if err != nil {
		fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err)
	}
	if printLegacyFileWarning {
		_, _ = fmt.Fprintln(stderr, "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release")
	}
	if !configFile.ContainsAuth() {
		configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore)
	}
	return configFile
}
<|endoftext|>


<commit_before>// Package genkey implements the genkey command.
package genkey

import (
	"encoding/json"
	"errors"

	"github.com/cloudflare/cfssl/cli"
	"github.com/cloudflare/cfssl/csr"
	cferr "github.com/cloudflare/cfssl/errors"
	"github.com/cloudflare/cfssl/initca"
)

var genkeyUsageText = `cfssl genkey -- generate a new key and CSR

Usage of genkey:
        cfssl genkey CSRJSON

Arguments:
        CSRJSON:    JSON file containing the request, use '-' for reading JSON from stdin

Flags:
`

var genkeyFlags = []string{"initca", "config"}

func genkeyMain(args []string, c cli.Config) (err error) {
	csrFile, args, err := cli.PopFirstArgument(args)
	if err != nil {
		return
	}

	csrFileBytes, err := cli.ReadStdin(csrFile)
	if err != nil {
		return
	}

	req := csr.CertificateRequest{
		KeyRequest: csr.NewBasicKeyRequest(),
	}
	err = json.Unmarshal(csrFileBytes, &req)
	if err != nil {
		return
	}

	if c.IsCA {
		var key, csrPEM, cert []byte
		cert, csrPEM, key, err = initca.New(&req)
		if err != nil {
			return
		}

		cli.PrintCert(key, csrPEM, cert)
	} else {
		if req.CA != nil {
			err = errors.New("ca section only permitted in initca")
			return
		}

		var key, csrPEM []byte
		g := &csr.Generator{Validator: Validator}
		csrPEM, key, err = g.ProcessRequest(&req)
		if err != nil {
			key = nil
			return
		}

		cli.PrintCert(key, csrPEM, nil)
	}
	return nil
}

// Validator returns true if the csr has at least one host
func Validator(req *csr.CertificateRequest) error {
	if len(req.Hosts) == 0 {
		return cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest, errors.New("missing hosts field"))
	}
	return nil
}

// CLIGenKey is a subcommand for generating a new key and CSR from a
// JSON CSR request file.
var Command = &cli.Command{UsageText: genkeyUsageText, Flags: genkeyFlags, Main: genkeyMain}
<commit_msg>Remove host count checking<commit_after>// Package genkey implements the genkey command.
package genkey

import (
	"encoding/json"
	"errors"

	"github.com/cloudflare/cfssl/cli"
	"github.com/cloudflare/cfssl/csr"
	cferr "github.com/cloudflare/cfssl/errors"
	"github.com/cloudflare/cfssl/initca"
)

var genkeyUsageText = `cfssl genkey -- generate a new key and CSR

Usage of genkey:
        cfssl genkey CSRJSON

Arguments:
        CSRJSON:    JSON file containing the request, use '-' for reading JSON from stdin

Flags:
`

var genkeyFlags = []string{"initca", "config"}

func genkeyMain(args []string, c cli.Config) (err error) {
	csrFile, args, err := cli.PopFirstArgument(args)
	if err != nil {
		return
	}

	csrFileBytes, err := cli.ReadStdin(csrFile)
	if err != nil {
		return
	}

	req := csr.CertificateRequest{
		KeyRequest: csr.NewBasicKeyRequest(),
	}
	err = json.Unmarshal(csrFileBytes, &req)
	if err != nil {
		return
	}

	if c.IsCA {
		var key, csrPEM, cert []byte
		cert, csrPEM, key, err = initca.New(&req)
		if err != nil {
			return
		}

		cli.PrintCert(key, csrPEM, cert)
	} else {
		if req.CA != nil {
			err = errors.New("ca section only permitted in initca")
			return
		}

		var key, csrPEM []byte
		g := &csr.Generator{Validator: Validator}
		csrPEM, key, err = g.ProcessRequest(&req)
		if err != nil {
			key = nil
			return
		}

		cli.PrintCert(key, csrPEM, nil)
	}
	return nil
}

// Validator returns true if the csr has at least one host
func Validator(req *csr.CertificateRequest) error {
	return nil
}

// CLIGenKey is a subcommand for generating a new key and CSR from a
// JSON CSR request file.
var Command = &cli.Command{UsageText: genkeyUsageText, Flags: genkeyFlags, Main: genkeyMain}
<|endoftext|>