<commit_before>package registry

import (
	"fmt"
	"io"
	"os"
	"strings"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kclientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/cmd/util/variable"
	configcmd "github.com/openshift/origin/pkg/config/cmd"
	dapi "github.com/openshift/origin/pkg/deploy/api"
	"github.com/openshift/origin/pkg/generate/app"
)

const longDesc = `
Install or configure a Docker registry for OpenShift

This command sets up a Docker registry integrated with OpenShift to provide notifications when
images are pushed. With no arguments, the command will check for the existing registry service
called 'docker-registry' and perform some diagnostics to ensure the registry is properly
configured and functioning.

If a registry service does not exist, the --create flag can be passed to
create a deployment configuration and service that will run the registry.

To run a highly available registry, you should be using a remote storage mechanism like an
object store (several are supported by the Docker registry). The default Docker registry image
is configured to accept configuration as environment variables - refer to the config file in
that image for more on setting up alternative storage. Once you've made those changes, you can
pass --replicas=2 or higher to ensure you have failover protection. The default registry setup
uses a local volume and the data will be lost if you delete the running pod.

Examples:
  Check the default Docker registry ("docker-registry"):

  $ %[1]s %[2]s

  See what the registry would look like if created:

  $ %[1]s %[2]s -o json

  Create a registry if it does not exist with two replicas:

  $ %[1]s %[2]s --create --replicas=2

  Use a different registry image and see the registry configuration:

  $ %[1]s %[2]s -o yaml --images=myrepo/docker-registry:mytag

ALPHA: This command is currently being actively developed. It is intended to simplify
  the tasks of setting up a Docker registry in a new installation. Some configuration
  beyond this command is still required to make your registry permanent.
`

type config struct {
	Type          string
	ImageTemplate variable.ImageTemplate
	Ports         string
	Replicas      int
	Labels        string
	Volume        string
	HostMount     string
	Create        bool
	Credentials   string

	// TODO: accept environment values.
}

const defaultLabel = "docker-registry=default"

func NewCmdRegistry(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command {
	cfg := &config{
		ImageTemplate: variable.NewDefaultImageTemplate(),

		Labels:   defaultLabel,
		Ports:    "5000",
		Volume:   "/registry",
		Replicas: 1,
	}

	cmd := &cobra.Command{
		Use:   name,
		Short: "Install and check OpenShift Docker registry",
		Long:  fmt.Sprintf(longDesc, parentName, name),

		Run: func(cmd *cobra.Command, args []string) {
			var name string
			switch len(args) {
			case 0:
				name = "docker-registry"
			default:
				glog.Fatalf("No arguments are allowed to this command")
			}

			ports, err := app.ContainerPortsFromString(cfg.Ports)
			if err != nil {
				glog.Fatal(err)
			}

			label := map[string]string{
				"docker-registry": "default",
			}
			if cfg.Labels != defaultLabel {
				valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ","))
				if err != nil {
					glog.Fatal(err)
				}
				if len(remove) > 0 {
					glog.Fatalf("You may not pass negative labels in %q", cfg.Labels)
				}
				label = valid
			}

			image := cfg.ImageTemplate.ExpandOrDie(cfg.Type)

			namespace, err := f.OpenShiftClientConfig.Namespace()
			if err != nil {
				glog.Fatalf("Error getting client: %v", err)
			}
			_, kClient, err := f.Clients()
			if err != nil {
				glog.Fatalf("Error getting client: %v", err)
			}

			p, output, err := cmdutil.PrinterForCommand(cmd)
			if err != nil {
				glog.Fatalf("Unable to configure printer: %v", err)
			}

			generate := output
			if !generate {
				_, err = kClient.Services(namespace).Get(name)
				if err != nil {
					if !errors.IsNotFound(err) {
						glog.Fatalf("Can't check for existing docker-registry %q: %v", name, err)
					}
					generate = true
				}
			}

			if generate {
				if !cfg.Create && !output {
					glog.Fatalf("Docker-registry %q does not exist (no service). Pass --create to install.", name)
				}

				// create new registry
				if len(cfg.Credentials) == 0 {
					glog.Fatalf("You must specify a .kubeconfig file path containing credentials for connecting the registry to the master with --credentials")
				}
				clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials}
				credentials, err := clientConfigLoadingRules.Load()
				if err != nil {
					glog.Fatalf("The provided credentials %q could not be loaded: %v", cfg.Credentials, err)
				}
				config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig()
				if err != nil {
					glog.Fatalf("The provided credentials %q could not be used: %v", cfg.Credentials, err)
				}
				if err := kclient.LoadTLSFiles(config); err != nil {
					glog.Fatalf("The provided credentials %q could not load certificate info: %v", cfg.Credentials, err)
				}
				insecure := "false"
				if config.Insecure {
					insecure = "true"
				}
				env := app.Environment{
					"OPENSHIFT_MASTER":    config.Host,
					"OPENSHIFT_CA_DATA":   string(config.CAData),
					"OPENSHIFT_KEY_DATA":  string(config.KeyData),
					"OPENSHIFT_CERT_DATA": string(config.CertData),
					"OPENSHIFT_INSECURE":  insecure,
				}

				mountHost := len(cfg.HostMount) > 0
				podTemplate := &kapi.PodTemplateSpec{
					ObjectMeta: kapi.ObjectMeta{Labels: label},
					Spec: kapi.PodSpec{
						Containers: []kapi.Container{
							{
								Name:  "registry",
								Image: image,
								Ports: ports,
								Env:   env.List(),
								VolumeMounts: []kapi.VolumeMount{
									{
										Name:      "registry-storage",
										MountPath: cfg.Volume,
									},
								},
								Privileged: mountHost,
								LivenessProbe: &kapi.Probe{
									InitialDelaySeconds: 3,
									TimeoutSeconds:      5,
									Handler: kapi.Handler{
										HTTPGet: &kapi.HTTPGetAction{
											Path: "/healthz",
											Port: util.NewIntOrStringFromInt(5000),
										},
									},
								},
							},
						},
						Volumes: []kapi.Volume{
							{
								Name:         "registry-storage",
								VolumeSource: kapi.VolumeSource{},
							},
						},
					},
				}
				if mountHost {
					podTemplate.Spec.Volumes[0].HostPath = &kapi.HostPathVolumeSource{Path: cfg.HostMount}
				} else {
					podTemplate.Spec.Volumes[0].EmptyDir = &kapi.EmptyDirVolumeSource{}
				}

				objects := []runtime.Object{
					&dapi.DeploymentConfig{
						ObjectMeta: kapi.ObjectMeta{
							Name:   name,
							Labels: label,
						},
						Triggers: []dapi.DeploymentTriggerPolicy{
							{Type: dapi.DeploymentTriggerOnConfigChange},
						},
						Template: dapi.DeploymentTemplate{
							Strategy: dapi.DeploymentStrategy{
								Type: dapi.DeploymentStrategyTypeRecreate,
							},
							ControllerTemplate: kapi.ReplicationControllerSpec{
								Replicas: cfg.Replicas,
								Selector: label,
								Template: podTemplate,
							},
						},
					},
				}
				objects = app.AddServices(objects)
				// TODO: label all created objects with the same label
				list := &kapi.List{Items: objects}

				if output {
					if err := p.PrintObj(list, out); err != nil {
						glog.Fatalf("Unable to print object: %v", err)
					}
					return
				}

				bulk := configcmd.Bulk{
					Factory: f.Factory,
					After:   configcmd.NewPrintNameOrErrorAfter(out, os.Stderr),
				}
				if errs := bulk.Create(list, namespace); len(errs) != 0 {
					os.Exit(1)
				}
				return
			}

			fmt.Fprintf(out, "Docker registry %q service exists\n", name)
		},
	}

	cmd.Flags().StringVar(&cfg.Type, "type", "docker-registry", "The registry image to use - if you specify --images this flag may be ignored.")
	cmd.Flags().StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "The image to base this registry on - ${component} will be replaced with --type")
	cmd.Flags().BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest image for the registry instead of the latest release.")
	cmd.Flags().StringVar(&cfg.Ports, "ports", cfg.Ports, "A comma delimited list of ports or port pairs to expose on the registry pod. The default is set for 5000.")
	cmd.Flags().IntVar(&cfg.Replicas, "replicas", cfg.Replicas, "The replication factor of the registry; commonly 2 when high availability is desired.")
	cmd.Flags().StringVar(&cfg.Labels, "labels", cfg.Labels, "A set of labels to uniquely identify the registry and its components.")
	cmd.Flags().StringVar(&cfg.Volume, "volume", cfg.Volume, "The volume path to use for registry storage; defaults to /registry which is the default for origin-docker-registry.")
	cmd.Flags().StringVar(&cfg.HostMount, "mount-host", cfg.HostMount, "If set, the registry volume will be created as a host-mount at this path.")
	cmd.Flags().BoolVar(&cfg.Create, "create", cfg.Create, "Create the registry if it does not exist.")
	cmd.Flags().StringVar(&cfg.Credentials, "credentials", "", "Path to a .kubeconfig file that will contain the credentials the registry should use to contact the master.")

	cmdutil.AddPrinterFlags(cmd)

	return cmd
}
<commit_msg>Disable registry liveness probe to support v1 & v2<commit_after>package registry

import (
	"fmt"
	"io"
	"os"
	"strings"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kclientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/cmd/util/variable"
	configcmd "github.com/openshift/origin/pkg/config/cmd"
	dapi "github.com/openshift/origin/pkg/deploy/api"
	"github.com/openshift/origin/pkg/generate/app"
)

const longDesc = `
Install or configure a Docker registry for OpenShift

This command sets up a Docker registry integrated with OpenShift to provide notifications when
images are pushed. With no arguments, the command will check for the existing registry service
called 'docker-registry' and perform some diagnostics to ensure the registry is properly
configured and functioning.

If a registry service does not exist, the --create flag can be passed to
create a deployment configuration and service that will run the registry.

To run a highly available registry, you should be using a remote storage mechanism like an
object store (several are supported by the Docker registry). The default Docker registry image
is configured to accept configuration as environment variables - refer to the config file in
that image for more on setting up alternative storage. Once you've made those changes, you can
pass --replicas=2 or higher to ensure you have failover protection. The default registry setup
uses a local volume and the data will be lost if you delete the running pod.

Examples:
  Check the default Docker registry ("docker-registry"):

  $ %[1]s %[2]s

  See what the registry would look like if created:

  $ %[1]s %[2]s -o json

  Create a registry if it does not exist with two replicas:

  $ %[1]s %[2]s --create --replicas=2

  Use a different registry image and see the registry configuration:

  $ %[1]s %[2]s -o yaml --images=myrepo/docker-registry:mytag

ALPHA: This command is currently being actively developed. It is intended to simplify
  the tasks of setting up a Docker registry in a new installation. Some configuration
  beyond this command is still required to make your registry permanent.
`

type config struct {
	Type          string
	ImageTemplate variable.ImageTemplate
	Ports         string
	Replicas      int
	Labels        string
	Volume        string
	HostMount     string
	Create        bool
	Credentials   string

	// TODO: accept environment values.
}

const defaultLabel = "docker-registry=default"

func NewCmdRegistry(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command {
	cfg := &config{
		ImageTemplate: variable.NewDefaultImageTemplate(),

		Labels:   defaultLabel,
		Ports:    "5000",
		Volume:   "/registry",
		Replicas: 1,
	}

	cmd := &cobra.Command{
		Use:   name,
		Short: "Install and check OpenShift Docker registry",
		Long:  fmt.Sprintf(longDesc, parentName, name),

		Run: func(cmd *cobra.Command, args []string) {
			var name string
			switch len(args) {
			case 0:
				name = "docker-registry"
			default:
				glog.Fatalf("No arguments are allowed to this command")
			}

			ports, err := app.ContainerPortsFromString(cfg.Ports)
			if err != nil {
				glog.Fatal(err)
			}

			label := map[string]string{
				"docker-registry": "default",
			}
			if cfg.Labels != defaultLabel {
				valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ","))
				if err != nil {
					glog.Fatal(err)
				}
				if len(remove) > 0 {
					glog.Fatalf("You may not pass negative labels in %q", cfg.Labels)
				}
				label = valid
			}

			image := cfg.ImageTemplate.ExpandOrDie(cfg.Type)

			namespace, err := f.OpenShiftClientConfig.Namespace()
			if err != nil {
				glog.Fatalf("Error getting client: %v", err)
			}
			_, kClient, err := f.Clients()
			if err != nil {
				glog.Fatalf("Error getting client: %v", err)
			}

			p, output, err := cmdutil.PrinterForCommand(cmd)
			if err != nil {
				glog.Fatalf("Unable to configure printer: %v", err)
			}

			generate := output
			if !generate {
				_, err = kClient.Services(namespace).Get(name)
				if err != nil {
					if !errors.IsNotFound(err) {
						glog.Fatalf("Can't check for existing docker-registry %q: %v", name, err)
					}
					generate = true
				}
			}

			if generate {
				if !cfg.Create && !output {
					glog.Fatalf("Docker-registry %q does not exist (no service). Pass --create to install.", name)
				}

				// create new registry
				if len(cfg.Credentials) == 0 {
					glog.Fatalf("You must specify a .kubeconfig file path containing credentials for connecting the registry to the master with --credentials")
				}
				clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials}
				credentials, err := clientConfigLoadingRules.Load()
				if err != nil {
					glog.Fatalf("The provided credentials %q could not be loaded: %v", cfg.Credentials, err)
				}
				config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig()
				if err != nil {
					glog.Fatalf("The provided credentials %q could not be used: %v", cfg.Credentials, err)
				}
				if err := kclient.LoadTLSFiles(config); err != nil {
					glog.Fatalf("The provided credentials %q could not load certificate info: %v", cfg.Credentials, err)
				}
				insecure := "false"
				if config.Insecure {
					insecure = "true"
				}
				env := app.Environment{
					"OPENSHIFT_MASTER":    config.Host,
					"OPENSHIFT_CA_DATA":   string(config.CAData),
					"OPENSHIFT_KEY_DATA":  string(config.KeyData),
					"OPENSHIFT_CERT_DATA": string(config.CertData),
					"OPENSHIFT_INSECURE":  insecure,
				}

				mountHost := len(cfg.HostMount) > 0
				podTemplate := &kapi.PodTemplateSpec{
					ObjectMeta: kapi.ObjectMeta{Labels: label},
					Spec: kapi.PodSpec{
						Containers: []kapi.Container{
							{
								Name:  "registry",
								Image: image,
								Ports: ports,
								Env:   env.List(),
								VolumeMounts: []kapi.VolumeMount{
									{
										Name:      "registry-storage",
										MountPath: cfg.Volume,
									},
								},
								Privileged: mountHost,
								// TODO reenable the liveness probe when we no longer support the v1 registry.
								/*
									LivenessProbe: &kapi.Probe{
										InitialDelaySeconds: 3,
										TimeoutSeconds:      5,
										Handler: kapi.Handler{
											HTTPGet: &kapi.HTTPGetAction{
												Path: "/healthz",
												Port: util.NewIntOrStringFromInt(5000),
											},
										},
									},
								*/
							},
						},
						Volumes: []kapi.Volume{
							{
								Name:         "registry-storage",
								VolumeSource: kapi.VolumeSource{},
							},
						},
					},
				}
				if mountHost {
					podTemplate.Spec.Volumes[0].HostPath = &kapi.HostPathVolumeSource{Path: cfg.HostMount}
				} else {
					podTemplate.Spec.Volumes[0].EmptyDir = &kapi.EmptyDirVolumeSource{}
				}

				objects := []runtime.Object{
					&dapi.DeploymentConfig{
						ObjectMeta: kapi.ObjectMeta{
							Name:   name,
							Labels: label,
						},
						Triggers: []dapi.DeploymentTriggerPolicy{
							{Type: dapi.DeploymentTriggerOnConfigChange},
						},
						Template: dapi.DeploymentTemplate{
							Strategy: dapi.DeploymentStrategy{
								Type: dapi.DeploymentStrategyTypeRecreate,
							},
							ControllerTemplate: kapi.ReplicationControllerSpec{
								Replicas: cfg.Replicas,
								Selector: label,
								Template: podTemplate,
							},
						},
					},
				}
				objects = app.AddServices(objects)
				// TODO: label all created objects with the same label
				list := &kapi.List{Items: objects}

				if output {
					if err := p.PrintObj(list, out); err != nil {
						glog.Fatalf("Unable to print object: %v", err)
					}
					return
				}

				bulk := configcmd.Bulk{
					Factory: f.Factory,
					After:   configcmd.NewPrintNameOrErrorAfter(out, os.Stderr),
				}
				if errs := bulk.Create(list, namespace); len(errs) != 0 {
					os.Exit(1)
				}
				return
			}

			fmt.Fprintf(out, "Docker registry %q service exists\n", name)
		},
	}

	cmd.Flags().StringVar(&cfg.Type, "type", "docker-registry", "The registry image to use - if you specify --images this flag may be ignored.")
	cmd.Flags().StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "The image to base this registry on - ${component} will be replaced with --type")
	cmd.Flags().BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest image for the registry instead of the latest release.")
	cmd.Flags().StringVar(&cfg.Ports, "ports", cfg.Ports, "A comma delimited list of ports or port pairs to expose on the registry pod. The default is set for 5000.")
	cmd.Flags().IntVar(&cfg.Replicas, "replicas", cfg.Replicas, "The replication factor of the registry; commonly 2 when high availability is desired.")
	cmd.Flags().StringVar(&cfg.Labels, "labels", cfg.Labels, "A set of labels to uniquely identify the registry and its components.")
	cmd.Flags().StringVar(&cfg.Volume, "volume", cfg.Volume, "The volume path to use for registry storage; defaults to /registry which is the default for origin-docker-registry.")
	cmd.Flags().StringVar(&cfg.HostMount, "mount-host", cfg.HostMount, "If set, the registry volume will be created as a host-mount at this path.")
	cmd.Flags().BoolVar(&cfg.Create, "create", cfg.Create, "Create the registry if it does not exist.")
	cmd.Flags().StringVar(&cfg.Credentials, "credentials", "", "Path to a .kubeconfig file that will contain the credentials the registry should use to contact the master.")

	cmdutil.AddPrinterFlags(cmd)

	return cmd
}
<|endoftext|>


<commit_before>package auth

import (
	"reflect"
	"testing"
)

// TestFactory verifies that the auth.Factory() function is working properly
func TestFactory(t *testing.T) {
	// Table of tests and the expected authenticators
	var tests = []struct {
		path string
		auth AuthMethod
	}{
		// Root - unauthenticated
		{"/", nil},
		// API root - unauthenticated
		{"/api", nil},
		// API login - bcrypt
		{"/api/v0/login", new(BcryptAuth)},
		// Other API calls - token
		{"/api/v0/status", new(TokenAuth)},
	}

	// Iterate and verify tests
	for _, test := range tests {
		// Verify proper authenticator chosen via factory
		if auth := Factory(test.path); reflect.TypeOf(auth) != reflect.TypeOf(test.auth) {
			t.Fatalf("mismatched authenticator type: %#v != %#v", auth, test.auth)
		}
	}
}

// TestbasicCredentials verifies that the basicCredentials function is working properly
func Test_basicCredentials(t *testing.T) {
	// Table of tests and expected output
	var tests = []struct {
		header   string
		username string
		password string
		err      error
	}{
		// Empty header
		{"", "", "", ErrEmptyBasic},
		// Missing second element
		{"Basic", "", "", ErrInvalidBasic},
		// Bad header prefix
		{"Digest XXX", "", "", ErrInvalidBasic},
		// Invalid base64
		{"Basic XXX", "", "", ErrInvalidBasic},
		// No colon delimiter
		{"Basic dGVzdHRlc3Q=", "", "", ErrInvalidBasic},
		// Valid credentials
		{"Basic dGVzdDp0ZXN0", "test", "test", nil},
	}

	// Iterate and verify tests
	for _, test := range tests {
		// Fetch credentials
		username, password, err := basicCredentials(test.header)

		// Verify proper username
		if username != test.username {
			t.Fatalf("mistmatched username: %v != %v", username, test.username)
		}

		// Verify proper password
		if password != test.password {
			t.Fatalf("mistmatched password: %v != %v", password, test.password)
		}

		// Verify proper error
		if err != test.err {
			t.Fatalf("mistmatched err: %v != %v", err, test.err)
		}
	}
}
<commit_msg>api/auth/auth_test: add regression test for Last.fm login<commit_after>package auth

import (
	"reflect"
	"testing"
)

// TestFactory verifies that the auth.Factory() function is working properly
func TestFactory(t *testing.T) {
	// Table of tests and the expected authenticators
	var tests = []struct {
		path string
		auth AuthMethod
	}{
		// Root - unauthenticated
		{"/", nil},
		// API root - unauthenticated
		{"/api", nil},
		// API login - bcrypt
		{"/api/v0/login", new(BcryptAuth)},
		// Other API calls - token
		{"/api/v0/status", new(TokenAuth)},
		// Bugfix: Last.fm login - token
		{"/api/v0/lastfm/login", new(TokenAuth)},
	}

	// Iterate and verify tests
	for _, test := range tests {
		// Verify proper authenticator chosen via factory
		if auth := Factory(test.path); reflect.TypeOf(auth) != reflect.TypeOf(test.auth) {
			t.Fatalf("mismatched authenticator type: %#v != %#v", auth, test.auth)
		}
	}
}

// TestbasicCredentials verifies that the basicCredentials function is working properly
func Test_basicCredentials(t *testing.T) {
	// Table of tests and expected output
	var tests = []struct {
		header   string
		username string
		password string
		err      error
	}{
		// Empty header
		{"", "", "", ErrEmptyBasic},
		// Missing second element
		{"Basic", "", "", ErrInvalidBasic},
		// Bad header prefix
		{"Digest XXX", "", "", ErrInvalidBasic},
		// Invalid base64
		{"Basic XXX", "", "", ErrInvalidBasic},
		// No colon delimiter
		{"Basic dGVzdHRlc3Q=", "", "", ErrInvalidBasic},
		// Valid credentials
		{"Basic dGVzdDp0ZXN0", "test", "test", nil},
	}

	// Iterate and verify tests
	for _, test := range tests {
		// Fetch credentials
		username, password, err := basicCredentials(test.header)

		// Verify proper username
		if username != test.username {
			t.Fatalf("mistmatched username: %v != %v", username, test.username)
		}

		// Verify proper password
		if password != test.password {
			t.Fatalf("mistmatched password: %v != %v", password, test.password)
		}

		// Verify proper error
		if err != test.err {
			t.Fatalf("mistmatched err: %v != %v", err, test.err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"github.com/pkg/errors"
	"html/template"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	g "github.com/gogits/git"
	"github.com/lemmi/ghfs"
	"github.com/lemmi/glubcms"
	"github.com/raymondbutcher/tidyhtml"
)

const (
	tmplPath = "templates"
)

func POE(err error, prefix ...interface{}) {
	if err != nil {
		log.Print(prefix...)
		log.Fatal(err)
	}
}

func parseTemplates(fs http.FileSystem) (*template.Template, error) {
	dir, err := fs.Open(tmplPath)
	if err != nil {
		return nil, errors.Wrapf(err, "Cannot open directory: %q", tmplPath)
	}
	defer dir.Close()
	tmain := template.New("main")
	fis, err := dir.Readdir(-1)
	if err != nil {
		return nil, errors.Wrapf(err, "Cannot read directory: %q", tmplPath)
	}
	for _, fi := range fis {
		if !strings.HasSuffix(fi.Name(), ".tmpl") {
			continue
		}
		fpath := filepath.Join(tmplPath, fi.Name())
		data, err := fs.Open(fpath)
		if err != nil {
			return nil, errors.Wrapf(err, "Cannot open file: %q", fpath)
		}
		databytes, err := ioutil.ReadAll(data)
		data.Close()
		if err != nil {
			return nil, errors.Wrapf(err, "Cannot read file: %q", fpath)
		}

		tname := strings.TrimSuffix(fi.Name(), ".tmpl")
		_, err = tmain.New(tname).Parse(string(databytes))
		if err != nil {
			return nil, errors.Wrapf(err, "Cannot parse template: %q", fpath)
		}
	}

	return tmain, nil
}

// Static file handling without showing directories
// TODO:
// - factor out

type StaticHandler struct {
	fs     http.FileSystem
	prefix string
}

func (sh StaticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := filepath.Clean(filepath.Join(sh.prefix, r.URL.Path))
	f, err := sh.fs.Open(path)
	if err != nil {
		http.Error(w, path, http.StatusNotFound)
		return
	}
	defer f.Close()
	stat, err := f.Stat()
	if err != nil {
		http.Error(w, path, http.StatusInternalServerError)
		return
	}
	if stat.IsDir() {
		http.Error(w, path, http.StatusNotFound)
		return
	}
	http.ServeContent(w, r, stat.Name(), stat.ModTime(), f)
}

func (sh StaticHandler) Cd(path string) StaticHandler {
	sh.prefix = filepath.Join(sh.prefix, path)
	return sh
}

func newStaticHandler(fs http.FileSystem) StaticHandler {
	return StaticHandler{fs: fs}
}

// Handling of an article or menu entry

type pageHandler struct {
	c *g.Commit
}

func newPageHandler(c *g.Commit) http.Handler {
	return pageHandler{c}
}
func (h pageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	tmpl := template.Must(parseTemplates(ghfs.FromCommit(h.c)))
	stree, err := h.c.Tree.SubTree("pages")
	POE(err, "Pages")

	p := glubcms.PageFromDir(ghfs.FromCommit(h.c, stree), r.URL.Path)
	buf := bytes.Buffer{}
	if err := tmpl.ExecuteTemplate(&buf, "main", p); err != nil {
		log.Println(errors.Wrapf(err, "template execution failed: %q", r.URL.Path))
		return
	}
	tbuf := bytes.Buffer{}
	if err := tidyhtml.Copy(&tbuf, &buf); err != nil {
		log.Println(errors.Wrapf(err, "tidyhtml failed: %q", r.URL.Path))
		return
	}
	w.Header().Set("Content-Type", "text/html")
	http.ServeContent(w, r, "", h.c.Author.When, bytes.NewReader(tbuf.Bytes()))
}

// Main handling of the site
// TODO:
// - parse meta entries first
// - choose handler based on that, not by path

type handler struct {
	prefix string
}

func newHandler(prefix string) handler {
	return handler{
		prefix: prefix,
	}
}

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path, err := filepath.Abs(h.prefix)
	// TODO errors package, proper http codes
	POE(err, "Filepath")

	repo, err := g.OpenRepository(path)
	POE(err, "OpenRepository")

	commit, err := repo.GetCommitOfBranch("master")
	POE(err, "LookupBranch")

	mux := http.NewServeMux()

	staticHandler := newStaticHandler(ghfs.FromCommit(commit))

	mux.Handle("/static/", staticHandler)
	mux.Handle("/robots.txt", staticHandler.Cd("/static"))
	mux.Handle("/favicon.ico", staticHandler.Cd("/static"))
	mux.Handle("/", newPageHandler(commit))
	w.Header().Set("ETag", strings.Trim(commit.Id.String(), "\""))
	w.Header().Set("Cache-Control", "max-age=32")
	mux.ServeHTTP(w, r)
}

func main() {
	prefix := flag.String("prefix", "../example_page", "path to the root dir")
	addr := flag.String("bind", "localhost:8080", "address or path to bind to")
	network := flag.String("net", "tcp", `"tcp", "tcp4", "tcp6", "unix" or "unixpacket"`)
	flag.Parse()
	ln, err := net.Listen(*network, *addr)
	if err != nil {
		panic(err)
	}
	defer ln.Close()
	if strings.HasPrefix(*network, "unix") {
		err = os.Chmod(*addr, 0666)
	}
	if err != nil {
		panic(err)
	}
	log.Fatal(http.Serve(ln, newHandler(*prefix)))
}
<commit_msg>gcserver: get rid of POE<commit_after>package main

import (
	"bytes"
	"flag"
	"html/template"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	g "github.com/gogits/git"
	"github.com/lemmi/ghfs"
	"github.com/lemmi/glubcms"
	"github.com/pkg/errors"
	"github.com/raymondbutcher/tidyhtml"
)

const (
	tmplPath = "templates"
)

func HttpError(w http.ResponseWriter, code int, logErr error) {
	log.Print(logErr)
	http.Error(w, http.StatusText(code), code)
}

func parseTemplates(fs http.FileSystem) (*template.Template, error) {
	dir, err := fs.Open(tmplPath)
	if err != nil {
		return nil, errors.Wrapf(err, "Cannot open directory: %q", tmplPath)
	}
	defer dir.Close()
	tmain := template.New("main")
	fis, err := dir.Readdir(-1)
	if err != nil {
		return nil, errors.Wrapf(err, "Cannot read directory: %q", tmplPath)
	}
	for _, fi := range fis {
		if !strings.HasSuffix(fi.Name(), ".tmpl") {
			continue
		}
		fpath := filepath.Join(tmplPath, fi.Name())
		data, err := fs.Open(fpath)
		if err != nil {
			return nil, errors.Wrapf(err, "Cannot open file: %q", fpath)
		}
		databytes, err := ioutil.ReadAll(data)
		data.Close()
		if err != nil {
			return nil, errors.Wrapf(err, "Cannot read file: %q", fpath)
		}

		tname := strings.TrimSuffix(fi.Name(), ".tmpl")
		_, err = tmain.New(tname).Parse(string(databytes))
		if err != nil {
			return nil, errors.Wrapf(err, "Cannot parse template: %q", fpath)
		}
	}

	return tmain, nil
}

// Static file handling without showing directories
// TODO:
// - factor out

type StaticHandler struct {
	fs     http.FileSystem
	prefix string
}

func (sh StaticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := filepath.Clean(filepath.Join(sh.prefix, r.URL.Path))
	f, err := sh.fs.Open(path)
	if err != nil {
		http.Error(w, path, http.StatusNotFound)
		return
	}
	defer f.Close()
	stat, err := f.Stat()
	if err != nil {
		http.Error(w, path, http.StatusInternalServerError)
		return
	}
	if stat.IsDir() {
		http.Error(w, path, http.StatusNotFound)
		return
	}
	http.ServeContent(w, r, stat.Name(), stat.ModTime(), f)
}

func (sh StaticHandler) Cd(path string) StaticHandler {
	sh.prefix = filepath.Join(sh.prefix, path)
	return sh
}

func newStaticHandler(fs http.FileSystem) StaticHandler {
	return StaticHandler{fs: fs}
}

// Handling of an article or menu entry
// TODO:
// - use http.FileSystem only

type pageHandler struct {
	c *g.Commit
}

func newPageHandler(c *g.Commit) http.Handler {
	return pageHandler{c}
}
func (h pageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	tmpl := template.Must(parseTemplates(ghfs.FromCommit(h.c)))
	stree, err := h.c.Tree.SubTree("pages")
	if err != nil {
		HttpError(w, http.StatusNotFound, errors.Wrap(err, "Pages"))
		return
	}

	p := glubcms.PageFromDir(ghfs.FromCommit(h.c, stree), r.URL.Path)
	buf := bytes.Buffer{}
	if err := tmpl.ExecuteTemplate(&buf, "main", p); err != nil {
		HttpError(w, http.StatusInternalServerError, errors.Wrapf(err, "template execution failed: %q", r.URL.Path))
		return
	}
	tbuf := bytes.Buffer{}
	if err := tidyhtml.Copy(&tbuf, &buf); err != nil {
		HttpError(w, http.StatusInternalServerError, errors.Wrapf(err, "tidyhtml failed: %q", r.URL.Path))
		return
	}
	w.Header().Set("Content-Type", "text/html")
	http.ServeContent(w, r, "", h.c.Author.When, bytes.NewReader(tbuf.Bytes()))
}

// Main handling of the site
// TODO:
// - parse meta entries first
// - choose handler based on that, not by path

type handler struct {
	prefix string
}

func newHandler(prefix string) handler {
	return handler{
		prefix: prefix,
	}
}

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path, err := filepath.Abs(h.prefix)
	if err != nil {
		HttpError(w, http.StatusInternalServerError, errors.Wrap(err, "filepath.Abs("+h.prefix+")"))
		return
	}

	repo, err := g.OpenRepository(path)
	if err != nil {
		HttpError(w, http.StatusInternalServerError, errors.Wrap(err, "g.OpenRepository("+path+")"))
		return
	}

	commit, err := repo.GetCommitOfBranch("master")
	if err != nil {
		HttpError(w, http.StatusInternalServerError, errors.Wrap(err, "Can not open master branch"))
		return
	}

	mux := http.NewServeMux()

	staticHandler := newStaticHandler(ghfs.FromCommit(commit))

	mux.Handle("/static/", staticHandler)
	mux.Handle("/robots.txt", staticHandler.Cd("/static"))
	mux.Handle("/favicon.ico", staticHandler.Cd("/static"))
	mux.Handle("/", newPageHandler(commit))
	w.Header().Set("ETag", strings.Trim(commit.Id.String(), "\""))
	w.Header().Set("Cache-Control", "max-age=32")
	mux.ServeHTTP(w, r)
}

func main() {
	prefix := flag.String("prefix", "../example_page", "path to the root dir")
	addr := flag.String("bind", "localhost:8080", "address or path to bind to")
	network := flag.String("net", "tcp", `"tcp", "tcp4", "tcp6", "unix" or "unixpacket"`)
	flag.Parse()
	ln, err := net.Listen(*network, *addr)
	if err != nil {
		panic(err)
	}
	defer ln.Close()
	if strings.HasPrefix(*network, "unix") {
		err = os.Chmod(*addr, 0666)
	}
	if err != nil {
		panic(err)
	}
	log.Fatal(http.Serve(ln, newHandler(*prefix)))
}
<|endoftext|>


<commit_before>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2015 Sippy Software, Inc. All rights reserved.
// Copyright (c) 2015 Andrii Pylypenko. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy

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

    "sippy/log"
    "sippy/utils"
)

type Timeout struct {
    callback        func()
    timeout         time.Duration
    logger          sippy_log.ErrorLogger
    shutdown_chan   chan struct{}
    shutdown        bool
    spread          float64
    nticks          int
    lock            sync.Mutex
    cb_lock         sync.Locker
    started         bool
}

func StartTimeoutWithSpread(callback func(), cb_lock sync.Locker, _timeout time.Duration, nticks int, logger sippy_log.ErrorLogger, spread float64) *Timeout {
    self := NewInactiveTimeout(callback, cb_lock, _timeout, nticks, logger)
    self.spread = spread
    self.Start()
    return self
}

func StartTimeout(callback func(), cb_lock sync.Locker, _timeout time.Duration, nticks int, logger sippy_log.ErrorLogger) *Timeout {
    return StartTimeoutWithSpread(callback, cb_lock, _timeout, nticks, logger, 0)
}

func NewInactiveTimeout(callback func(), cb_lock sync.Locker, _timeout time.Duration, nticks int, logger sippy_log.ErrorLogger) *Timeout {
    self := &Timeout{
        callback        : callback,
        timeout         : _timeout,
        nticks          : nticks,
        logger          : logger,
        shutdown_chan   : make(chan struct{}),
        shutdown        : false,
        spread          : 0,
        started         : false,
        cb_lock         : cb_lock,
    }
    return self
}

func (self *Timeout) Start() {
    self.lock.Lock()
    if ! self.started && self.callback != nil {
        self.started = true
        go self.run()
    }
    self.lock.Unlock()
}

func (self *Timeout) SpreadRuns(spread float64) {
    self.spread = spread
}

func (self *Timeout) Cancel() {
    self.shutdown = true
    close(self.shutdown_chan)
}

func (self *Timeout) run() {
    for !self.shutdown {
        self._run()
    }
    self.callback = nil
    self.cb_lock = nil
}

func (self *Timeout) _run() {
    for !self.shutdown {
        if self.nticks == 0 {
            self.shutdown = true
            break
        }
        if self.nticks > 0 {
            self.nticks--
        }
        t := self.timeout
        if self.spread > 0 {
            t = time.Duration(float64(t) * (1 + self.spread * (1 - 2 * rand.Float64())))
        }
        select {
        case <-self.shutdown_chan:
            self.shutdown = true
        case <-time.After(t):
            if ! self.shutdown {
                sippy_utils.SafeCall(self.callback, self.cb_lock, self.logger)
            }
        }
    }
}
<commit_msg>Use time.Timer instead of time.After in order to be able to cancel unneeded timers gracefully.<commit_after>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2015 Sippy Software, Inc. All rights reserved.
// Copyright (c) 2015 Andrii Pylypenko. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy

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

    "sippy/log"
    "sippy/utils"
)

type Timeout struct {
    callback        func()
    timeout         time.Duration
    logger          sippy_log.ErrorLogger
    shutdown_chan   chan struct{}
    shutdown        bool
    spread          float64
    nticks          int
    lock            sync.Mutex
    cb_lock         sync.Locker
    started         bool
}

func StartTimeoutWithSpread(callback func(), cb_lock sync.Locker, _timeout time.Duration, nticks int, logger sippy_log.ErrorLogger, spread float64) *Timeout {
    self := NewInactiveTimeout(callback, cb_lock, _timeout, nticks, logger)
    self.spread = spread
    self.Start()
    return self
}

func StartTimeout(callback func(), cb_lock sync.Locker, _timeout time.Duration, nticks int, logger sippy_log.ErrorLogger) *Timeout {
    return StartTimeoutWithSpread(callback, cb_lock, _timeout, nticks, logger, 0)
}

func NewInactiveTimeout(callback func(), cb_lock sync.Locker, _timeout time.Duration, nticks int, logger sippy_log.ErrorLogger) *Timeout {
    self := &Timeout{
        callback        : callback,
        timeout         : _timeout,
        nticks          : nticks,
        logger          : logger,
        shutdown_chan   : make(chan struct{}),
        shutdown        : false,
        spread          : 0,
        started         : false,
        cb_lock         : cb_lock,
    }
    return self
}

func (self *Timeout) Start() {
    self.lock.Lock()
    if ! self.started && self.callback != nil {
        self.started = true
        go self.run()
    }
    self.lock.Unlock()
}

func (self *Timeout) SpreadRuns(spread float64) {
    self.spread = spread
}

func (self *Timeout) Cancel() {
    self.shutdown = true
    close(self.shutdown_chan)
}

func (self *Timeout) run() {
    for !self.shutdown {
        self._run()
    }
    self.callback = nil
    self.cb_lock = nil
}

func (self *Timeout) _run() {
    for !self.shutdown {
        if self.nticks == 0 {
            self.shutdown = true
            break
        }
        if self.nticks > 0 {
            self.nticks--
        }
        t := self.timeout
        if self.spread > 0 {
            t = time.Duration(float64(t) * (1 + self.spread * (1 - 2 * rand.Float64())))
        }
        timer := time.NewTimer(t)
        select {
        case <-self.shutdown_chan:
            self.shutdown = true
        case <-timer.C:
            if ! self.shutdown {
                sippy_utils.SafeCall(self.callback, self.cb_lock, self.logger)
            }
        }
        timer.Stop()
    }
}
<|endoftext|>


<commit_before>// Package json generates methods for encoding/decoding types to/from JSON.
//
// When used correctly, these methods can easily give a ~200-300% performance
// increase when serializing objects to JSON while also reducing memory usage
// by ~95-99%. For taking advantage of these gains, you must use
// gnd.la/mux/serialize or Context.WriteJson to encode to JSON, since
// json.Marshal won't use these methods correctly and might even have worse
// performance when these methods are implemented.
//
// This is a small benchmark comparing the performance of these JSON encoding
// methods. JSONDirect uses WriteJSON(), JSONSerialize uses
// gnd.la/mux/serialize (which adds some overhead because it also sets the
// Content-Length and Content-Encoding headers and thus must encode into an
// intermediate buffer first), while JSON uses json.Marshal(). All three
// benchmarks write the result to ioutil.Discard.
//
//  BenchmarkJSONDirect	    1000000 1248 ns/op	117.73 MB/s 16 B/op	2 allocs/op
//  BenchmarkJSONSerialize  1000000 1587 ns/op	92.62 MB/s  16 B/op	2 allocs/op
//  BenchmarkJSON	    500000  4583 ns/op	32.07 MB/s  620 B/op	4 allocs/op
//
// Code generated by this package respects json related struct tags except
// omitempty and and encodes time.Time UTC as a Unix time (encoding/json uses
// time.Format).
//
// If you want to specify a different serialization when using encoding/json
// than when using this package, you can use the "genjson" field tag. Fields
// with a genjson tag will use it and ignore the "json" tag.
//
// The recommended way use to generate JSON methods for a given package is
// using the gondola command rather than using this package directly.
package json

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"gnd.la/gen/genutil"
	"gnd.la/log"
	"gnd.la/util/structs"
	"path/filepath"
	"regexp"
)

const (
	defaultBufSize = 8 * 1024
)

type Method struct {
	Key       string
	Name      string
	OmitEmpty bool
}

// Options specify the options used when generating JSON related
// methods.
type Options struct {
	// Wheter to generate a MarshalJSON method. This is false by default
	// because in most cases will result in lower performance when using
	// json.Marshal, since the encoder from encoding/json will revalidate
	// the returned JSON, resulting in a performance loss. Turn this on
	// only if you're using the Methods feature (otherwise you'll get
	// different results when serializing with json.Marshal).
	MarshalJSON bool
	// The size of the allocated buffers for serializing to JSON. If zero,
	// the default size of 8192 is used (8K).
	BufferSize int
	// The maximum buffer size. Buffers which grow past this size won't
	// be reused. If zero, it takes the same value os BufferSize.
	MaxBufferSize int
	// The number of buffers to be kept for reusing. If zero, it defaults
	// to GOMAXPROCS. Set it to a negative number to disable buffering.
	BufferCount int
	// If not zero, this takes precedence over BufferCount. The number of
	// maximum buffers will be GOMAXPROCS * BuffersPerProc.
	BuffersPerProc int
	// Methods indicates struct methods which should be included in the JSON
	// output. The key in the map is the type name in the package (e.g.
	// MyStruct not mypackage.MyStruct).
	Methods map[string][]*Method
	// If not nil, only types matching this regexp will be included.
	Include *regexp.Regexp
	// If not nil, types matching this regexp will be excluded.
	Exclude *regexp.Regexp
}

// Gen generates a WriteJSON method and, optionally, MarshalJSON for every
// exported type in the given package. The package might be either an
// absolute path or an import path.
func Gen(pkgName string, opts *Options) error {
	pkg, err := genutil.NewPackage(pkgName)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("package %s\n\n", pkg.Name()))
	buf.WriteString(genutil.AutogenString())
	buf.WriteString("\nimport (\n")
	imports := []string{"bytes", "io", "strconv", "unicode/utf8"}
	if opts == nil || opts.BufferCount == 0 || opts.BuffersPerProc != 0 {
		imports = append(imports, "runtime")
	}
	for _, v := range imports {
		buf.WriteString(fmt.Sprintf("%q\n", v))
	}
	buf.WriteString(")\n")
	buf.WriteString("var _ = strconv.FormatBool\n")
	var include *regexp.Regexp
	var exclude *regexp.Regexp
	if opts != nil {
		include = opts.Include
		exclude = opts.Exclude
	}
	var methods bytes.Buffer
	for _, v := range pkg.ExportedTypes(include, exclude) {
		methods.Reset()
		if err := jsonMarshal(v, opts, &methods); err != nil {
			log.Warningf("Skipping type %s: %s", v.Obj().Name(), err)
			continue
		}
		buf.WriteString(methods.String())
	}
	buf.WriteString(encode_go)
	bufSize := defaultBufSize
	maxBufSize := bufSize
	bufferCount := 0
	buffersPerProc := 0
	if opts != nil {
		if opts.BufferSize > 0 {
			bufSize = opts.BufferSize
			maxBufSize = bufSize
		}
		if opts.MaxBufferSize >= maxBufSize {
			maxBufSize = opts.MaxBufferSize
		}
		bufferCount = opts.BufferCount
		buffersPerProc = opts.BuffersPerProc
	}
	buf.WriteString(fmt.Sprintf("const jsonBufSize = %d\n", bufSize))
	buf.WriteString(fmt.Sprintf("const jsonMaxBufSize = %d\n", maxBufSize))
	if buffersPerProc > 0 {
		buf.WriteString(fmt.Sprintf("var jsonBufferCount = runtime.GOMAXPROCS(0) * %d\n", buffersPerProc))
	} else if bufferCount > 0 {
		buf.WriteString(fmt.Sprintf("const jsonBufferCount = %d\n", bufferCount))
	} else {
		buf.WriteString("var jsonBufferCount = runtime.GOMAXPROCS(0)\n")
	}
	buf.WriteString(buffer_go)
	out := filepath.Join(pkg.Dir(), "gen_json.go")
	log.Debugf("Writing autogenerated JSON methods to %s", out)
	return genutil.WriteAutogen(out, buf.Bytes())
}

func jsonMarshal(typ *types.Named, opts *Options, buf *bytes.Buffer) error {
	tname := typ.Obj().Name()
	if _, ok := typ.Underlying().(*types.Struct); ok {
		tname = "*" + tname
	}
	if opts != nil && opts.MarshalJSON {
		buf.WriteString(fmt.Sprintf("func(o %s) MarshalJSON() ([]byte, error) {\n", tname))
		buf.WriteString("var buf bytes.Buffer\n")
		buf.WriteString("_, err := o.WriteJSON(&buf)\n")
		buf.WriteString("return buf.Bytes(), err\n")
		buf.WriteString("}\n\n")
	}
	buf.WriteString(fmt.Sprintf("func(o %s) WriteJSON(w io.Writer) (int, error) {\n", tname))
	buf.WriteString("buf := jsonGetBuffer()\n")
	if err := jsonValue(typ, nil, "o", opts, buf); err != nil {
		return err
	}
	buf.WriteString("n, err := w.Write(buf.Bytes())\n")
	buf.WriteString("jsonPutBuffer(buf)\n")
	buf.WriteString("return n, err\n")
	buf.WriteString("}\n\n")
	return nil
}

func fieldTag(tag string) *structs.Tag {
	if gtag := structs.NewStringTagNamed(tag, "genjson"); gtag != nil && !gtag.IsEmpty() {
		return gtag
	}
	return structs.NewStringTagNamed(tag, "json")
}

func jsonStruct(st *types.Struct, p types.Type, name string, opts *Options, buf *bytes.Buffer) error {
	buf.WriteString("buf.WriteByte('{')\n")
	count := st.NumFields()
	hasFields := false
	for ii := 0; ii < count; ii++ {
		field := st.Field(ii)
		if field.IsExported() {
			key := field.Name()
			omitEmpty := false
			tag := st.Tag(ii)
			if ftag := fieldTag(tag); ftag != nil {
				if n := ftag.Name(); n != "" {
					key = n
				}
				omitEmpty = ftag.Has("omitempty")
			}
			if key != "-" {
				if hasFields {
					buf.WriteString("buf.WriteByte(',')\n")
				}
				hasFields = true
				if err := jsonField(field, key, name+"."+field.Name(), omitEmpty, opts, buf); err != nil {
					return err
				}
			}
		}
	}
	if opts != nil {
		if named, ok := p.(*types.Named); ok {
			methods := opts.Methods[named.Obj().Name()]
			count := named.NumMethods()
			for _, v := range methods {
				found := false
				for ii := 0; ii < count; ii++ {
					fn := named.Method(ii)
					if fn.Name() == v.Name {
						found = true
						signature := fn.Type().(*types.Signature)
						if p := signature.Params(); p != nil || p.Len() > 0 {
							return fmt.Errorf("method %s on type %s requires arguments", v.Name, named.Obj().Name())
						}
						res := signature.Results()
						if res == nil || res.Len() != 1 {
							return fmt.Errorf("method %s on type %s must return exactly one value", v.Name, named.Obj().Name())
						}
						if hasFields {
							buf.WriteString("buf.WriteByte(',')\n")
						}
						hasFields = true
						if err := jsonField(res.At(0), v.Key, name+"."+v.Name+"()", v.OmitEmpty, opts, buf); err != nil {
							return err
						}
						break
					}
				}
				if !found {
					return fmt.Errorf("type %s does not have method %s", named.Obj().Name(), v.Name)
				}
			}
		}
	}
	buf.WriteString("buf.WriteByte('}')\n")
	return nil
}

func jsonSlice(sl *types.Slice, p types.Type, name string, opts *Options, buf *bytes.Buffer) error {
	buf.WriteString("buf.WriteByte('[')\n")
	buf.WriteString(fmt.Sprintf("for ii, v := range %s {\n", name))
	buf.WriteString("if ii > 0 {\n")
	buf.WriteString("buf.WriteByte(',')\n")
	buf.WriteString("}\n")
	if err := jsonValue(sl.Elem(), nil, "v", opts, buf); err != nil {
		return err
	}
	buf.WriteString("}\n")
	buf.WriteString("buf.WriteByte(']')\n")
	return nil
}

func jsonField(field *types.Var, key string, name string, omitEmpty bool, opts *Options, buf *bytes.Buffer) error {
	// TODO: omitEmpty
	buf.WriteString(fmt.Sprintf("buf.WriteString(%q)\n", fmt.Sprintf("%q", key)))
	buf.WriteString("buf.WriteByte(':')\n")
	if err := jsonValue(field.Type(), nil, name, opts, buf); err != nil {
		return err
	}
	return nil
}

func jsonValue(vtype types.Type, ptype types.Type, name string, opts *Options, buf *bytes.Buffer) error {
	switch typ := vtype.(type) {
	case *types.Basic:
		k := typ.Kind()
		_, isPointer := ptype.(*types.Pointer)
		if isPointer {
			name = "*" + name
		}
		switch k {
		case types.Bool:
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatBool(%s))\n", name))
		case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatInt(int64(%s), 10))\n", name))
		case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64:
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatUint(uint64(%s), 10))\n", name))
		case types.Float32, types.Float64:
			bitSize := 64
			if k == types.Float32 {
				bitSize = 32
			}
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatFloat(float64(%s), 'g', -1, %d))\n", name, bitSize))
		case types.String:
			buf.WriteString(fmt.Sprintf("jsonEncodeString(buf, string(%s))\n", name))
		default:
			return fmt.Errorf("can't encode basic kind %v", typ.Kind())
		}
	case *types.Named:
		if typ.Obj().Pkg().Name() == "time" && typ.Obj().Name() == "Time" {
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatInt(%s.UTC().Unix(), 10))\n", name))
		} else {
			if err := jsonValue(typ.Underlying(), typ, name, opts, buf); err != nil {
				return err
			}
		}
	case *types.Slice:
		if err := jsonSlice(typ, ptype, name, opts, buf); err != nil {
			return err
		}
	case *types.Struct:
		if err := jsonStruct(typ, ptype, name, opts, buf); err != nil {
			return err
		}
	case *types.Pointer:
		buf.WriteString(fmt.Sprintf("if %s == nil {\n", name))
		buf.WriteString("buf.WriteString(\"null\")\n")
		buf.WriteString("} else {\n")
		if err := jsonValue(typ.Elem(), typ, name, opts, buf); err != nil {
			return err
		}
		buf.WriteString("}\n")
	default:
		return fmt.Errorf("can't encode type %T %v (%T)", typ, typ, typ.Underlying())
	}
	return nil
}
<commit_msg>Avoid getting an unused import from io<commit_after>// Package json generates methods for encoding/decoding types to/from JSON.
//
// When used correctly, these methods can easily give a ~200-300% performance
// increase when serializing objects to JSON while also reducing memory usage
// by ~95-99%. For taking advantage of these gains, you must use
// gnd.la/mux/serialize or Context.WriteJson to encode to JSON, since
// json.Marshal won't use these methods correctly and might even have worse
// performance when these methods are implemented.
//
// This is a small benchmark comparing the performance of these JSON encoding
// methods. JSONDirect uses WriteJSON(), JSONSerialize uses
// gnd.la/mux/serialize (which adds some overhead because it also sets the
// Content-Length and Content-Encoding headers and thus must encode into an
// intermediate buffer first), while JSON uses json.Marshal(). All three
// benchmarks write the result to ioutil.Discard.
//
//  BenchmarkJSONDirect	    1000000 1248 ns/op	117.73 MB/s 16 B/op	2 allocs/op
//  BenchmarkJSONSerialize  1000000 1587 ns/op	92.62 MB/s  16 B/op	2 allocs/op
//  BenchmarkJSON	    500000  4583 ns/op	32.07 MB/s  620 B/op	4 allocs/op
//
// Code generated by this package respects json related struct tags except
// omitempty and and encodes time.Time UTC as a Unix time (encoding/json uses
// time.Format).
//
// If you want to specify a different serialization when using encoding/json
// than when using this package, you can use the "genjson" field tag. Fields
// with a genjson tag will use it and ignore the "json" tag.
//
// The recommended way use to generate JSON methods for a given package is
// using the gondola command rather than using this package directly.
package json

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"gnd.la/gen/genutil"
	"gnd.la/log"
	"gnd.la/util/structs"
	"path/filepath"
	"regexp"
)

const (
	defaultBufSize = 8 * 1024
)

type Method struct {
	Key       string
	Name      string
	OmitEmpty bool
}

// Options specify the options used when generating JSON related
// methods.
type Options struct {
	// Wheter to generate a MarshalJSON method. This is false by default
	// because in most cases will result in lower performance when using
	// json.Marshal, since the encoder from encoding/json will revalidate
	// the returned JSON, resulting in a performance loss. Turn this on
	// only if you're using the Methods feature (otherwise you'll get
	// different results when serializing with json.Marshal).
	MarshalJSON bool
	// The size of the allocated buffers for serializing to JSON. If zero,
	// the default size of 8192 is used (8K).
	BufferSize int
	// The maximum buffer size. Buffers which grow past this size won't
	// be reused. If zero, it takes the same value os BufferSize.
	MaxBufferSize int
	// The number of buffers to be kept for reusing. If zero, it defaults
	// to GOMAXPROCS. Set it to a negative number to disable buffering.
	BufferCount int
	// If not zero, this takes precedence over BufferCount. The number of
	// maximum buffers will be GOMAXPROCS * BuffersPerProc.
	BuffersPerProc int
	// Methods indicates struct methods which should be included in the JSON
	// output. The key in the map is the type name in the package (e.g.
	// MyStruct not mypackage.MyStruct).
	Methods map[string][]*Method
	// If not nil, only types matching this regexp will be included.
	Include *regexp.Regexp
	// If not nil, types matching this regexp will be excluded.
	Exclude *regexp.Regexp
}

// Gen generates a WriteJSON method and, optionally, MarshalJSON for every
// exported type in the given package. The package might be either an
// absolute path or an import path.
func Gen(pkgName string, opts *Options) error {
	pkg, err := genutil.NewPackage(pkgName)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("package %s\n\n", pkg.Name()))
	buf.WriteString(genutil.AutogenString())
	buf.WriteString("\nimport (\n")
	imports := []string{"bytes", "io", "strconv", "unicode/utf8"}
	if opts == nil || opts.BufferCount == 0 || opts.BuffersPerProc != 0 {
		imports = append(imports, "runtime")
	}
	for _, v := range imports {
		buf.WriteString(fmt.Sprintf("%q\n", v))
	}
	buf.WriteString(")\n")
	buf.WriteString("var _ = strconv.FormatBool\n")
	buf.WriteString("var _ = io.ReadFull\n")
	var include *regexp.Regexp
	var exclude *regexp.Regexp
	if opts != nil {
		include = opts.Include
		exclude = opts.Exclude
	}
	var methods bytes.Buffer
	for _, v := range pkg.ExportedTypes(include, exclude) {
		methods.Reset()
		if err := jsonMarshal(v, opts, &methods); err != nil {
			log.Warningf("Skipping type %s: %s", v.Obj().Name(), err)
			continue
		}
		buf.WriteString(methods.String())
	}
	buf.WriteString(encode_go)
	bufSize := defaultBufSize
	maxBufSize := bufSize
	bufferCount := 0
	buffersPerProc := 0
	if opts != nil {
		if opts.BufferSize > 0 {
			bufSize = opts.BufferSize
			maxBufSize = bufSize
		}
		if opts.MaxBufferSize >= maxBufSize {
			maxBufSize = opts.MaxBufferSize
		}
		bufferCount = opts.BufferCount
		buffersPerProc = opts.BuffersPerProc
	}
	buf.WriteString(fmt.Sprintf("const jsonBufSize = %d\n", bufSize))
	buf.WriteString(fmt.Sprintf("const jsonMaxBufSize = %d\n", maxBufSize))
	if buffersPerProc > 0 {
		buf.WriteString(fmt.Sprintf("var jsonBufferCount = runtime.GOMAXPROCS(0) * %d\n", buffersPerProc))
	} else if bufferCount > 0 {
		buf.WriteString(fmt.Sprintf("const jsonBufferCount = %d\n", bufferCount))
	} else {
		buf.WriteString("var jsonBufferCount = runtime.GOMAXPROCS(0)\n")
	}
	buf.WriteString(buffer_go)
	out := filepath.Join(pkg.Dir(), "gen_json.go")
	log.Debugf("Writing autogenerated JSON methods to %s", out)
	return genutil.WriteAutogen(out, buf.Bytes())
}

func jsonMarshal(typ *types.Named, opts *Options, buf *bytes.Buffer) error {
	tname := typ.Obj().Name()
	if _, ok := typ.Underlying().(*types.Struct); ok {
		tname = "*" + tname
	}
	if opts != nil && opts.MarshalJSON {
		buf.WriteString(fmt.Sprintf("func(o %s) MarshalJSON() ([]byte, error) {\n", tname))
		buf.WriteString("var buf bytes.Buffer\n")
		buf.WriteString("_, err := o.WriteJSON(&buf)\n")
		buf.WriteString("return buf.Bytes(), err\n")
		buf.WriteString("}\n\n")
	}
	buf.WriteString(fmt.Sprintf("func(o %s) WriteJSON(w io.Writer) (int, error) {\n", tname))
	buf.WriteString("buf := jsonGetBuffer()\n")
	if err := jsonValue(typ, nil, "o", opts, buf); err != nil {
		return err
	}
	buf.WriteString("n, err := w.Write(buf.Bytes())\n")
	buf.WriteString("jsonPutBuffer(buf)\n")
	buf.WriteString("return n, err\n")
	buf.WriteString("}\n\n")
	return nil
}

func fieldTag(tag string) *structs.Tag {
	if gtag := structs.NewStringTagNamed(tag, "genjson"); gtag != nil && !gtag.IsEmpty() {
		return gtag
	}
	return structs.NewStringTagNamed(tag, "json")
}

func jsonStruct(st *types.Struct, p types.Type, name string, opts *Options, buf *bytes.Buffer) error {
	buf.WriteString("buf.WriteByte('{')\n")
	count := st.NumFields()
	hasFields := false
	for ii := 0; ii < count; ii++ {
		field := st.Field(ii)
		if field.IsExported() {
			key := field.Name()
			omitEmpty := false
			tag := st.Tag(ii)
			if ftag := fieldTag(tag); ftag != nil {
				if n := ftag.Name(); n != "" {
					key = n
				}
				omitEmpty = ftag.Has("omitempty")
			}
			if key != "-" {
				if hasFields {
					buf.WriteString("buf.WriteByte(',')\n")
				}
				hasFields = true
				if err := jsonField(field, key, name+"."+field.Name(), omitEmpty, opts, buf); err != nil {
					return err
				}
			}
		}
	}
	if opts != nil {
		if named, ok := p.(*types.Named); ok {
			methods := opts.Methods[named.Obj().Name()]
			count := named.NumMethods()
			for _, v := range methods {
				found := false
				for ii := 0; ii < count; ii++ {
					fn := named.Method(ii)
					if fn.Name() == v.Name {
						found = true
						signature := fn.Type().(*types.Signature)
						if p := signature.Params(); p != nil || p.Len() > 0 {
							return fmt.Errorf("method %s on type %s requires arguments", v.Name, named.Obj().Name())
						}
						res := signature.Results()
						if res == nil || res.Len() != 1 {
							return fmt.Errorf("method %s on type %s must return exactly one value", v.Name, named.Obj().Name())
						}
						if hasFields {
							buf.WriteString("buf.WriteByte(',')\n")
						}
						hasFields = true
						if err := jsonField(res.At(0), v.Key, name+"."+v.Name+"()", v.OmitEmpty, opts, buf); err != nil {
							return err
						}
						break
					}
				}
				if !found {
					return fmt.Errorf("type %s does not have method %s", named.Obj().Name(), v.Name)
				}
			}
		}
	}
	buf.WriteString("buf.WriteByte('}')\n")
	return nil
}

func jsonSlice(sl *types.Slice, p types.Type, name string, opts *Options, buf *bytes.Buffer) error {
	buf.WriteString("buf.WriteByte('[')\n")
	buf.WriteString(fmt.Sprintf("for ii, v := range %s {\n", name))
	buf.WriteString("if ii > 0 {\n")
	buf.WriteString("buf.WriteByte(',')\n")
	buf.WriteString("}\n")
	if err := jsonValue(sl.Elem(), nil, "v", opts, buf); err != nil {
		return err
	}
	buf.WriteString("}\n")
	buf.WriteString("buf.WriteByte(']')\n")
	return nil
}

func jsonField(field *types.Var, key string, name string, omitEmpty bool, opts *Options, buf *bytes.Buffer) error {
	// TODO: omitEmpty
	buf.WriteString(fmt.Sprintf("buf.WriteString(%q)\n", fmt.Sprintf("%q", key)))
	buf.WriteString("buf.WriteByte(':')\n")
	if err := jsonValue(field.Type(), nil, name, opts, buf); err != nil {
		return err
	}
	return nil
}

func jsonValue(vtype types.Type, ptype types.Type, name string, opts *Options, buf *bytes.Buffer) error {
	switch typ := vtype.(type) {
	case *types.Basic:
		k := typ.Kind()
		_, isPointer := ptype.(*types.Pointer)
		if isPointer {
			name = "*" + name
		}
		switch k {
		case types.Bool:
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatBool(%s))\n", name))
		case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatInt(int64(%s), 10))\n", name))
		case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64:
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatUint(uint64(%s), 10))\n", name))
		case types.Float32, types.Float64:
			bitSize := 64
			if k == types.Float32 {
				bitSize = 32
			}
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatFloat(float64(%s), 'g', -1, %d))\n", name, bitSize))
		case types.String:
			buf.WriteString(fmt.Sprintf("jsonEncodeString(buf, string(%s))\n", name))
		default:
			return fmt.Errorf("can't encode basic kind %v", typ.Kind())
		}
	case *types.Named:
		if typ.Obj().Pkg().Name() == "time" && typ.Obj().Name() == "Time" {
			buf.WriteString(fmt.Sprintf("buf.WriteString(strconv.FormatInt(%s.UTC().Unix(), 10))\n", name))
		} else {
			if err := jsonValue(typ.Underlying(), typ, name, opts, buf); err != nil {
				return err
			}
		}
	case *types.Slice:
		if err := jsonSlice(typ, ptype, name, opts, buf); err != nil {
			return err
		}
	case *types.Struct:
		if err := jsonStruct(typ, ptype, name, opts, buf); err != nil {
			return err
		}
	case *types.Pointer:
		buf.WriteString(fmt.Sprintf("if %s == nil {\n", name))
		buf.WriteString("buf.WriteString(\"null\")\n")
		buf.WriteString("} else {\n")
		if err := jsonValue(typ.Elem(), typ, name, opts, buf); err != nil {
			return err
		}
		buf.WriteString("}\n")
	default:
		return fmt.Errorf("can't encode type %T %v (%T)", typ, typ, typ.Underlying())
	}
	return nil
}
<|endoftext|>


<commit_before>package pixel_test

import (
	"testing"

	"github.com/faiface/pixel"
)

type rectTestInput struct {
	name string
	rect pixel.Rect
}

type rectTestTransform struct {
	name string
	f    func(pixel.Rect) pixel.Rect
}

func TestResizeRect(t *testing.T) {

	// rectangles
	squareAroundOrigin := rectTestInput{"square around origin", pixel.R(-10, -10, 10, 10)}
	squareAround2020 := rectTestInput{"square around 20, 20", pixel.R(10, 10, 30, 30)}
	rectangleAroundOrigin := rectTestInput{"rectangle around origin", pixel.R(-20, -10, 20, 10)}
	rectangleAround2020 := rectTestInput{"rectangle around 20, 20", pixel.R(0, 10, 40, 30)}

	// resize transformations
	resizeByHalfAroundCenter := rectTestTransform{"by half around center", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(rect.Center(), rect.Size().Scaled(0.5))
	}}
	resizeByHalfAroundMin := rectTestTransform{"by half around Min", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(rect.Min, rect.Size().Scaled(0.5))
	}}
	resizeByHalfAroundMax := rectTestTransform{"by half around Max", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(rect.Max, rect.Size().Scaled(0.5))
	}}
	resizeByHalfAroundMiddleOfLeftSide := rectTestTransform{"by half around middle of left side", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(pixel.V(rect.Min.X, rect.Center().Y), rect.Size().Scaled(0.5))
	}}
	resizeByHalfAroundOrigin := rectTestTransform{"by half around the origin", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(pixel.ZV, rect.Size().Scaled(0.5))
	}}

	testCases := []struct {
		input     rectTestInput
		transform rectTestTransform
		answer    pixel.Rect
	}{
		{squareAroundOrigin, resizeByHalfAroundCenter, pixel.R(-5, -5, 5, 5)},
		{squareAround2020, resizeByHalfAroundCenter, pixel.R(15, 15, 25, 25)},
		{rectangleAroundOrigin, resizeByHalfAroundCenter, pixel.R(-10, -5, 10, 5)},
		{rectangleAround2020, resizeByHalfAroundCenter, pixel.R(10, 15, 30, 25)},

		{squareAroundOrigin, resizeByHalfAroundMin, pixel.R(-10, -10, 0, 0)},
		{squareAround2020, resizeByHalfAroundMin, pixel.R(10, 10, 20, 20)},
		{rectangleAroundOrigin, resizeByHalfAroundMin, pixel.R(-20, -10, 0, 0)},
		{rectangleAround2020, resizeByHalfAroundMin, pixel.R(0, 10, 20, 20)},

		{squareAroundOrigin, resizeByHalfAroundMax, pixel.R(0, 0, 10, 10)},
		{squareAround2020, resizeByHalfAroundMax, pixel.R(20, 20, 30, 30)},
		{rectangleAroundOrigin, resizeByHalfAroundMax, pixel.R(0, 0, 20, 10)},
		{rectangleAround2020, resizeByHalfAroundMax, pixel.R(20, 20, 40, 30)},

		{squareAroundOrigin, resizeByHalfAroundMiddleOfLeftSide, pixel.R(-10, -5, 0, 5)},
		{squareAround2020, resizeByHalfAroundMiddleOfLeftSide, pixel.R(10, 15, 20, 25)},
		{rectangleAroundOrigin, resizeByHalfAroundMiddleOfLeftSide, pixel.R(-20, -5, 0, 5)},
		{rectangleAround2020, resizeByHalfAroundMiddleOfLeftSide, pixel.R(0, 15, 20, 25)},

		{squareAroundOrigin, resizeByHalfAroundOrigin, pixel.R(-5, -5, 5, 5)},
		{squareAround2020, resizeByHalfAroundOrigin, pixel.R(5, 5, 15, 15)},
		{rectangleAroundOrigin, resizeByHalfAroundOrigin, pixel.R(-10, -5, 10, 5)},
		{rectangleAround2020, resizeByHalfAroundOrigin, pixel.R(0, 5, 20, 15)},
	}

	for _, testCase := range testCases {
		testResult := testCase.transform.f(testCase.input.rect)
		if testResult != testCase.answer {
			t.Errorf("Resizing %s %s failed, got: %v, wanted: %v\n", testCase.input.name, testCase.transform.name, testResult, testCase.answer)
		}
	}
}
<commit_msg>minor adjustments with how tests are named and run<commit_after>package pixel_test

import (
	"fmt"
	"testing"

	"github.com/faiface/pixel"
)

type rectTestTransform struct {
	name string
	f    func(pixel.Rect) pixel.Rect
}

func TestResizeRect(t *testing.T) {

	// rectangles
	squareAroundOrigin := pixel.R(-10, -10, 10, 10)
	squareAround2020 := pixel.R(10, 10, 30, 30)
	rectangleAroundOrigin := pixel.R(-20, -10, 20, 10)
	rectangleAround2020 := pixel.R(0, 10, 40, 30)

	// resize transformations
	resizeByHalfAroundCenter := rectTestTransform{"by half around center", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(rect.Center(), rect.Size().Scaled(0.5))
	}}
	resizeByHalfAroundMin := rectTestTransform{"by half around Min", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(rect.Min, rect.Size().Scaled(0.5))
	}}
	resizeByHalfAroundMax := rectTestTransform{"by half around Max", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(rect.Max, rect.Size().Scaled(0.5))
	}}
	resizeByHalfAroundMiddleOfLeftSide := rectTestTransform{"by half around middle of left side", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(pixel.V(rect.Min.X, rect.Center().Y), rect.Size().Scaled(0.5))
	}}
	resizeByHalfAroundOrigin := rectTestTransform{"by half around the origin", func(rect pixel.Rect) pixel.Rect {
		return rect.Resized(pixel.ZV, rect.Size().Scaled(0.5))
	}}

	testCases := []struct {
		input     pixel.Rect
		transform rectTestTransform
		answer    pixel.Rect
	}{
		{squareAroundOrigin, resizeByHalfAroundCenter, pixel.R(-5, -5, 5, 5)},
		{squareAround2020, resizeByHalfAroundCenter, pixel.R(15, 15, 25, 25)},
		{rectangleAroundOrigin, resizeByHalfAroundCenter, pixel.R(-10, -5, 10, 5)},
		{rectangleAround2020, resizeByHalfAroundCenter, pixel.R(10, 15, 30, 25)},

		{squareAroundOrigin, resizeByHalfAroundMin, pixel.R(-10, -10, 0, 0)},
		{squareAround2020, resizeByHalfAroundMin, pixel.R(10, 10, 20, 20)},
		{rectangleAroundOrigin, resizeByHalfAroundMin, pixel.R(-20, -10, 0, 0)},
		{rectangleAround2020, resizeByHalfAroundMin, pixel.R(0, 10, 20, 20)},

		{squareAroundOrigin, resizeByHalfAroundMax, pixel.R(0, 0, 10, 10)},
		{squareAround2020, resizeByHalfAroundMax, pixel.R(20, 20, 30, 30)},
		{rectangleAroundOrigin, resizeByHalfAroundMax, pixel.R(0, 0, 20, 10)},
		{rectangleAround2020, resizeByHalfAroundMax, pixel.R(20, 20, 40, 30)},

		{squareAroundOrigin, resizeByHalfAroundMiddleOfLeftSide, pixel.R(-10, -5, 0, 5)},
		{squareAround2020, resizeByHalfAroundMiddleOfLeftSide, pixel.R(10, 15, 20, 25)},
		{rectangleAroundOrigin, resizeByHalfAroundMiddleOfLeftSide, pixel.R(-20, -5, 0, 5)},
		{rectangleAround2020, resizeByHalfAroundMiddleOfLeftSide, pixel.R(0, 15, 20, 25)},

		{squareAroundOrigin, resizeByHalfAroundOrigin, pixel.R(-5, -5, 5, 5)},
		{squareAround2020, resizeByHalfAroundOrigin, pixel.R(5, 5, 15, 15)},
		{rectangleAroundOrigin, resizeByHalfAroundOrigin, pixel.R(-10, -5, 10, 5)},
		{rectangleAround2020, resizeByHalfAroundOrigin, pixel.R(0, 5, 20, 15)},
	}

	for _, testCase := range testCases {
		t.Run(fmt.Sprintf("Resize %v %s", testCase.input, testCase.transform.name), func(t *testing.T) {
			testResult := testCase.transform.f(testCase.input)
			if testResult != testCase.answer {
				t.Errorf("Got: %v, wanted: %v\n", testResult, testCase.answer)
			}
		})
	}
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"github.com/danjac/podbaby/api/Godeps/_workspace/src/github.com/labstack/echo"
	"github.com/danjac/podbaby/models"
	"github.com/danjac/podbaby/store"
	"github.com/danjac/podbaby/store/Godeps/_workspace/src/github.com/DATA-DOG/go-sqlmock"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

func TestGetBookmarksIfOk(t *testing.T) {

	user := &models.User{
		ID: 10,
	}

	req, _ := http.NewRequest("GET", "/5/", nil)
	w := httptest.NewRecorder()

	e := echo.New()
	c := echo.NewContext(req, echo.NewResponse(w, e), e)

	/*
		r := e.Router()
		r.Add(echo.GET, "/:id/", nil, e)
		r.Find(echo.GET, "/5/", c)
	*/

	s, mock, err := store.NewMock()
	if err != nil {
		t.Fatal(err)
	}

	rows := sqlmock.NewRows([]string{""}).AddRow(1)
	mock.ExpectQuery(`^SELECT COUNT\(id\) FROM bookmarks*`).WillReturnRows(rows)

	rows = sqlmock.NewRows([]string{
		"id", "title", "enclosure_url", "description",
		"channel_id", "title", "image", "pub_date", "source",
	}).AddRow(1, "test", "test,mp3", "test", 2, "testing", "test.jpg", time.Now(), "")
	mock.ExpectQuery("^SELECT p.id, (.+) FROM podcasts p*").WillReturnRows(rows)

	c.Set(userContextKey, user)
	c.Set(storeContextKey, s)

	if err := getBookmarks(c); err != nil {
		t.Fatal(err)
	}

	body := w.Body.String()
	if !strings.Contains(body, `"title":"testing"`) {
		t.Fatal("Should contain title 'testing'")
	}

}

func TestGetBookmarksIfNotOk(t *testing.T) {

	user := &models.User{
		ID: 10,
	}

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()
	e := echo.New()

	c := echo.NewContext(req, echo.NewResponse(w, e), e)

	s, mock, err := store.NewMock()
	if err != nil {
		t.Fatal(err)
	}
	result := fmt.Errorf("some error")
	mock.ExpectQuery(`^SELECT COUNT\(id\) FROM bookmarks*`).WillReturnError(result)

	c.Set(userContextKey, user)
	c.Set(storeContextKey, s)

	if err := getBookmarks(c); err == nil {
		t.Fatal("This should not return an error")
	}

}
<commit_msg>Test fix<commit_after>package api

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/danjac/podbaby/api/Godeps/_workspace/src/github.com/labstack/echo"
	"github.com/danjac/podbaby/models"
	"github.com/danjac/podbaby/store"
	"github.com/danjac/podbaby/store/Godeps/_workspace/src/github.com/DATA-DOG/go-sqlmock"
)

func TestGetBookmarksIfOk(t *testing.T) {

	user := &models.User{
		ID: 10,
	}

	req, _ := http.NewRequest("GET", "/5/", nil)
	w := httptest.NewRecorder()

	e := echo.New()
	c := echo.NewContext(req, echo.NewResponse(w, e), e)

	/*
		r := e.Router()
		r.Add(echo.GET, "/:id/", nil, e)
		r.Find(echo.GET, "/5/", c)
	*/

	s, mock, err := store.NewMock()
	if err != nil {
		t.Fatal(err)
	}

	rows := sqlmock.NewRows([]string{""}).AddRow(1)
	mock.ExpectQuery(`^SELECT COUNT\(\*\) FROM \(SELECT DISTINCT podcast_id FROM bookmarks*`).WillReturnRows(rows)

	rows = sqlmock.NewRows([]string{
		"id", "title", "enclosure_url", "description",
		"channel_id", "title", "image", "pub_date", "source",
	}).AddRow(1, "test", "test,mp3", "test", 2, "testing", "test.jpg", time.Now(), "")
	mock.ExpectQuery("^SELECT p.id, (.+) FROM podcasts p*").WillReturnRows(rows)

	c.Set(userContextKey, user)
	c.Set(storeContextKey, s)

	if err := getBookmarks(c); err != nil {
		t.Fatal(err)
	}

	body := w.Body.String()
	if !strings.Contains(body, `"title":"testing"`) {
		t.Fatal("Should contain title 'testing'")
	}

}

func TestGetBookmarksIfNotOk(t *testing.T) {

	user := &models.User{
		ID: 10,
	}

	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()
	e := echo.New()

	c := echo.NewContext(req, echo.NewResponse(w, e), e)

	s, mock, err := store.NewMock()
	if err != nil {
		t.Fatal(err)
	}
	result := fmt.Errorf("some error")
	mock.ExpectQuery(`^SELECT COUNT\(id\) FROM bookmarks*`).WillReturnError(result)

	c.Set(userContextKey, user)
	c.Set(storeContextKey, s)

	if err := getBookmarks(c); err == nil {
		t.Fatal("This should not return an error")
	}

}
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"strings"
	"text/tabwriter"

	"github.com/docker/docker/api/types"
	Cli "github.com/docker/docker/cli"
	"github.com/docker/docker/daemon/network"
	"github.com/docker/docker/opts"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/pkg/stringid"
)

// CmdNetwork is the parent subcommand for all network commands
//
// Usage: docker network <COMMAND> [OPTIONS]
func (cli *DockerCli) CmdNetwork(args ...string) error {
	cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false)
	cmd.Require(flag.Min, 1)
	err := cmd.ParseFlags(args, true)
	cmd.Usage()
	return err
}

// CmdNetworkCreate creates a new network with a given name
//
// Usage: docker network create [OPTIONS] <NETWORK-NAME>
func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
	cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false)
	flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network")
	flOpts := opts.NewMapOpts(nil, nil)

	flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
	flIpamSubnet := opts.NewListOpts(nil)
	flIpamIPRange := opts.NewListOpts(nil)
	flIpamGateway := opts.NewListOpts(nil)
	flIpamAux := opts.NewMapOpts(nil, nil)

	cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment")
	cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
	cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet")
	cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver")
	cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options")

	cmd.Require(flag.Exact, 1)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
	if err != nil {
		return err
	}

	// Construct network create request body
	nc := types.NetworkCreate{
		Name:           cmd.Arg(0),
		Driver:         *flDriver,
		IPAM:           network.IPAM{Driver: *flIpamDriver, Config: ipamCfg},
		Options:        flOpts.GetAll(),
		CheckDuplicate: true,
	}
	obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil))
	if err != nil {
		return err
	}
	var resp types.NetworkCreateResponse
	err = json.Unmarshal(obj, &resp)
	if err != nil {
		return err
	}
	fmt.Fprintf(cli.out, "%s\n", resp.ID)
	return nil
}

// CmdNetworkRm deletes a network
//
// Usage: docker network rm <NETWORK-NAME | NETWORK-ID>
func (cli *DockerCli) CmdNetworkRm(args ...string) error {
	cmd := Cli.Subcmd("network rm", []string{"NETWORK"}, "Deletes a network", false)
	cmd.Require(flag.Exact, 1)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}
	_, _, err = readBody(cli.call("DELETE", "/networks/"+cmd.Arg(0), nil, nil))
	if err != nil {
		return err
	}
	return nil
}

// CmdNetworkConnect connects a container to a network
//
// Usage: docker network connect <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
	cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
	cmd.Require(flag.Exact, 2)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	nc := types.NetworkConnect{Container: cmd.Arg(1)}
	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil))
	return err
}

// CmdNetworkDisconnect disconnects a container from a network
//
// Usage: docker network disconnect <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
	cmd.Require(flag.Exact, 2)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	nc := types.NetworkConnect{Container: cmd.Arg(1)}
	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil))
	return err
}

// CmdNetworkLs lists all the netorks managed by docker daemon
//
// Usage: docker network ls [OPTIONS]
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
	cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")

	cmd.Require(flag.Exact, 0)
	err := cmd.ParseFlags(args, true)

	if err != nil {
		return err
	}
	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
	if err != nil {
		return err
	}

	var networkResources []types.NetworkResource
	err = json.Unmarshal(obj, &networkResources)
	if err != nil {
		return err
	}

	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)

	// unless quiet (-q) is specified, print field titles
	if !*quiet {
		fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
	}

	for _, networkResource := range networkResources {
		ID := networkResource.ID
		netName := networkResource.Name
		if !*noTrunc {
			ID = stringid.TruncateID(ID)
		}
		if *quiet {
			fmt.Fprintln(wr, ID)
			continue
		}
		driver := networkResource.Driver
		fmt.Fprintf(wr, "%s\t%s\t%s\t",
			ID,
			netName,
			driver)
		fmt.Fprint(wr, "\n")
	}
	wr.Flush()
	return nil
}

// CmdNetworkInspect inspects the network object for more details
//
// Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
	cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on a network", false)
	cmd.Require(flag.Min, 1)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	status := 0
	var networks []*types.NetworkResource
	for _, name := range cmd.Args() {
		obj, _, err := readBody(cli.call("GET", "/networks/"+name, nil, nil))
		if err != nil {
			if strings.Contains(err.Error(), "not found") {
				fmt.Fprintf(cli.err, "Error: No such network: %s\n", name)
			} else {
				fmt.Fprintf(cli.err, "%s", err)
			}
			status = 1
			continue
		}
		networkResource := types.NetworkResource{}
		if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&networkResource); err != nil {
			return err
		}

		networks = append(networks, &networkResource)
	}

	b, err := json.MarshalIndent(networks, "", "    ")
	if err != nil {
		return err
	}

	if _, err := io.Copy(cli.out, bytes.NewReader(b)); err != nil {
		return err
	}
	io.WriteString(cli.out, "\n")

	if status != 0 {
		return Cli.StatusError{StatusCode: status}
	}
	return nil
}

// Consolidates the ipam configuration as a group from differnt related configurations
// user can configure network with multiple non-overlapping subnets and hence it is
// possible to corelate the various related parameters and consolidate them.
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxilary addresses into
// structured ipam data.
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
	}
	iData := map[string]*network.IPAMConfig{}

	// Populate non-overlapping subnets into consolidation map
	for _, s := range subnets {
		for k := range iData {
			ok1, err := subnetMatches(s, k)
			if err != nil {
				return nil, err
			}
			ok2, err := subnetMatches(k, s)
			if err != nil {
				return nil, err
			}
			if ok1 || ok2 {
				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
			}
		}
		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
	}

	// Validate and add valid ip ranges
	for _, r := range ranges {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, r)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			if iData[s].IPRange != "" {
				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
			}
			d := iData[s]
			d.IPRange = r
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for range %s", r)
		}
	}

	// Validate and add valid gateways
	for _, g := range gateways {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, g)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			if iData[s].Gateway != "" {
				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
			}
			d := iData[s]
			d.Gateway = g
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
		}
	}

	// Validate and add aux-addresses
	for key, aa := range auxaddrs {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, aa)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			iData[s].AuxAddress[key] = aa
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
		}
	}

	idl := []network.IPAMConfig{}
	for _, v := range iData {
		idl = append(idl, *v)
	}
	return idl, nil
}

func subnetMatches(subnet, data string) (bool, error) {
	var (
		ip net.IP
	)

	_, s, err := net.ParseCIDR(subnet)
	if err != nil {
		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
	}

	if strings.Contains(data, "/") {
		ip, _, err = net.ParseCIDR(data)
		if err != nil {
			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
		}
	} else {
		ip = net.ParseIP(data)
	}

	return s.Contains(ip), nil
}

func networkUsage() string {
	networkCommands := map[string]string{
		"create":     "Create a network",
		"connect":    "Connect container to a network",
		"disconnect": "Disconnect container from a network",
		"inspect":    "Display detailed network information",
		"ls":         "List all networks",
		"rm":         "Remove a network",
	}

	help := "Commands:\n"

	for cmd, description := range networkCommands {
		help += fmt.Sprintf("  %-25.25s%s\n", cmd, description)
	}

	help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
	return help
}
<commit_msg>Let the api to choose the default network driver.<commit_after>package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"strings"
	"text/tabwriter"

	"github.com/docker/docker/api/types"
	Cli "github.com/docker/docker/cli"
	"github.com/docker/docker/daemon/network"
	"github.com/docker/docker/opts"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/pkg/stringid"
)

// CmdNetwork is the parent subcommand for all network commands
//
// Usage: docker network <COMMAND> [OPTIONS]
func (cli *DockerCli) CmdNetwork(args ...string) error {
	cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false)
	cmd.Require(flag.Min, 1)
	err := cmd.ParseFlags(args, true)
	cmd.Usage()
	return err
}

// CmdNetworkCreate creates a new network with a given name
//
// Usage: docker network create [OPTIONS] <NETWORK-NAME>
func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
	cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false)
	flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network")
	flOpts := opts.NewMapOpts(nil, nil)

	flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
	flIpamSubnet := opts.NewListOpts(nil)
	flIpamIPRange := opts.NewListOpts(nil)
	flIpamGateway := opts.NewListOpts(nil)
	flIpamAux := opts.NewMapOpts(nil, nil)

	cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment")
	cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
	cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet")
	cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver")
	cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options")

	cmd.Require(flag.Exact, 1)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	// Set the default driver to "" if the user didn't set the value.
	// That way we can know whether it was user input or not.
	driver := *flDriver
	if !cmd.IsSet("-driver") && !cmd.IsSet("d") {
		driver = ""
	}

	ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
	if err != nil {
		return err
	}

	// Construct network create request body
	nc := types.NetworkCreate{
		Name:           cmd.Arg(0),
		Driver:         driver,
		IPAM:           network.IPAM{Driver: *flIpamDriver, Config: ipamCfg},
		Options:        flOpts.GetAll(),
		CheckDuplicate: true,
	}
	obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil))
	if err != nil {
		return err
	}
	var resp types.NetworkCreateResponse
	err = json.Unmarshal(obj, &resp)
	if err != nil {
		return err
	}
	fmt.Fprintf(cli.out, "%s\n", resp.ID)
	return nil
}

// CmdNetworkRm deletes a network
//
// Usage: docker network rm <NETWORK-NAME | NETWORK-ID>
func (cli *DockerCli) CmdNetworkRm(args ...string) error {
	cmd := Cli.Subcmd("network rm", []string{"NETWORK"}, "Deletes a network", false)
	cmd.Require(flag.Exact, 1)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}
	_, _, err = readBody(cli.call("DELETE", "/networks/"+cmd.Arg(0), nil, nil))
	if err != nil {
		return err
	}
	return nil
}

// CmdNetworkConnect connects a container to a network
//
// Usage: docker network connect <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
	cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
	cmd.Require(flag.Exact, 2)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	nc := types.NetworkConnect{Container: cmd.Arg(1)}
	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil))
	return err
}

// CmdNetworkDisconnect disconnects a container from a network
//
// Usage: docker network disconnect <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
	cmd.Require(flag.Exact, 2)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	nc := types.NetworkConnect{Container: cmd.Arg(1)}
	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil))
	return err
}

// CmdNetworkLs lists all the netorks managed by docker daemon
//
// Usage: docker network ls [OPTIONS]
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
	cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")

	cmd.Require(flag.Exact, 0)
	err := cmd.ParseFlags(args, true)

	if err != nil {
		return err
	}
	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
	if err != nil {
		return err
	}

	var networkResources []types.NetworkResource
	err = json.Unmarshal(obj, &networkResources)
	if err != nil {
		return err
	}

	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)

	// unless quiet (-q) is specified, print field titles
	if !*quiet {
		fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
	}

	for _, networkResource := range networkResources {
		ID := networkResource.ID
		netName := networkResource.Name
		if !*noTrunc {
			ID = stringid.TruncateID(ID)
		}
		if *quiet {
			fmt.Fprintln(wr, ID)
			continue
		}
		driver := networkResource.Driver
		fmt.Fprintf(wr, "%s\t%s\t%s\t",
			ID,
			netName,
			driver)
		fmt.Fprint(wr, "\n")
	}
	wr.Flush()
	return nil
}

// CmdNetworkInspect inspects the network object for more details
//
// Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
	cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on a network", false)
	cmd.Require(flag.Min, 1)
	err := cmd.ParseFlags(args, true)
	if err != nil {
		return err
	}

	status := 0
	var networks []*types.NetworkResource
	for _, name := range cmd.Args() {
		obj, _, err := readBody(cli.call("GET", "/networks/"+name, nil, nil))
		if err != nil {
			if strings.Contains(err.Error(), "not found") {
				fmt.Fprintf(cli.err, "Error: No such network: %s\n", name)
			} else {
				fmt.Fprintf(cli.err, "%s", err)
			}
			status = 1
			continue
		}
		networkResource := types.NetworkResource{}
		if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&networkResource); err != nil {
			return err
		}

		networks = append(networks, &networkResource)
	}

	b, err := json.MarshalIndent(networks, "", "    ")
	if err != nil {
		return err
	}

	if _, err := io.Copy(cli.out, bytes.NewReader(b)); err != nil {
		return err
	}
	io.WriteString(cli.out, "\n")

	if status != 0 {
		return Cli.StatusError{StatusCode: status}
	}
	return nil
}

// Consolidates the ipam configuration as a group from differnt related configurations
// user can configure network with multiple non-overlapping subnets and hence it is
// possible to corelate the various related parameters and consolidate them.
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxilary addresses into
// structured ipam data.
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
	}
	iData := map[string]*network.IPAMConfig{}

	// Populate non-overlapping subnets into consolidation map
	for _, s := range subnets {
		for k := range iData {
			ok1, err := subnetMatches(s, k)
			if err != nil {
				return nil, err
			}
			ok2, err := subnetMatches(k, s)
			if err != nil {
				return nil, err
			}
			if ok1 || ok2 {
				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
			}
		}
		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
	}

	// Validate and add valid ip ranges
	for _, r := range ranges {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, r)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			if iData[s].IPRange != "" {
				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
			}
			d := iData[s]
			d.IPRange = r
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for range %s", r)
		}
	}

	// Validate and add valid gateways
	for _, g := range gateways {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, g)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			if iData[s].Gateway != "" {
				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
			}
			d := iData[s]
			d.Gateway = g
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
		}
	}

	// Validate and add aux-addresses
	for key, aa := range auxaddrs {
		match := false
		for _, s := range subnets {
			ok, err := subnetMatches(s, aa)
			if err != nil {
				return nil, err
			}
			if !ok {
				continue
			}
			iData[s].AuxAddress[key] = aa
			match = true
		}
		if !match {
			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
		}
	}

	idl := []network.IPAMConfig{}
	for _, v := range iData {
		idl = append(idl, *v)
	}
	return idl, nil
}

func subnetMatches(subnet, data string) (bool, error) {
	var (
		ip net.IP
	)

	_, s, err := net.ParseCIDR(subnet)
	if err != nil {
		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
	}

	if strings.Contains(data, "/") {
		ip, _, err = net.ParseCIDR(data)
		if err != nil {
			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
		}
	} else {
		ip = net.ParseIP(data)
	}

	return s.Contains(ip), nil
}

func networkUsage() string {
	networkCommands := map[string]string{
		"create":     "Create a network",
		"connect":    "Connect container to a network",
		"disconnect": "Disconnect container from a network",
		"inspect":    "Display detailed network information",
		"ls":         "List all networks",
		"rm":         "Remove a network",
	}

	help := "Commands:\n"

	for cmd, description := range networkCommands {
		help += fmt.Sprintf("  %-25.25s%s\n", cmd, description)
	}

	help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
	return help
}
<|endoftext|>