<commit_before>package main

import (
	"crypto/rsa"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/url"
	"strings"

	"github.com/spf13/cobra"

	"github.com/kubernetes-sigs/bootkube/pkg/asset"
	"github.com/kubernetes-sigs/bootkube/pkg/tlsutil"
)

const (
	apiOffset            = 1
	dnsOffset            = 10
	defaultServiceBaseIP = "10.3.0.0"
	defaultEtcdServers   = "https://127.0.0.1:2379"
)

var (
	cmdRender = &cobra.Command{
		Use:          "render",
		Short:        "Render default cluster manifests",
		Long:         "",
		PreRunE:      validateRenderOpts,
		RunE:         runCmdRender,
		SilenceUsage: true,
	}

	renderOpts struct {
		assetDir            string
		caCertificatePath   string
		caPrivateKeyPath    string
		etcdCAPath          string
		etcdCertificatePath string
		etcdPrivateKeyPath  string
		etcdServers         string
		apiServers          string
		altNames            string
		podCIDR             string
		serviceCIDR         string
		cloudProvider       string
		networkProvider     string
	}

	imageVersions = asset.DefaultImages
)

func init() {
	cmdRoot.AddCommand(cmdRender)
	cmdRender.Flags().StringVar(&renderOpts.assetDir, "asset-dir", "", "Output path for rendered assets")
	cmdRender.Flags().StringVar(&renderOpts.caCertificatePath, "ca-certificate-path", "", "Path to an existing PEM encoded CA. If provided, TLS assets will be generated using this certificate authority.")
	cmdRender.Flags().StringVar(&renderOpts.caPrivateKeyPath, "ca-private-key-path", "", "Path to an existing Certificate Authority RSA private key. Required if --ca-certificate is set.")
	cmdRender.Flags().StringVar(&renderOpts.etcdCAPath, "etcd-ca-path", "", "Path to an existing PEM encoded CA that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-certificate-path and --etcd-private-key-path, and must have etcd configured to use TLS with matching secrets.")
	cmdRender.Flags().StringVar(&renderOpts.etcdCertificatePath, "etcd-certificate-path", "", "Path to an existing certificate that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-ca-path and --etcd-private-key-path, and must have etcd configured to use TLS with matching secrets.")
	cmdRender.Flags().StringVar(&renderOpts.etcdPrivateKeyPath, "etcd-private-key-path", "", "Path to an existing private key that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-ca-path and --etcd-certificate-path, and must have etcd configured to use TLS with matching secrets.")
	cmdRender.Flags().StringVar(&renderOpts.etcdServers, "etcd-servers", defaultEtcdServers, "List of etcd servers URLs including host:port, comma separated")
	cmdRender.Flags().StringVar(&renderOpts.apiServers, "api-servers", "https://127.0.0.1:6443", "List of API server URLs including host:port, comma seprated")
	cmdRender.Flags().StringVar(&renderOpts.altNames, "api-server-alt-names", "", "List of SANs to use in api-server certificate. Example: 'IP=127.0.0.1,IP=127.0.0.2,DNS=localhost'. If empty, SANs will be extracted from the --api-servers flag.")
	cmdRender.Flags().StringVar(&renderOpts.podCIDR, "pod-cidr", "10.2.0.0/16", "The CIDR range of cluster pods.")
	cmdRender.Flags().StringVar(&renderOpts.serviceCIDR, "service-cidr", "10.3.0.0/24", "The CIDR range of cluster services.")
	cmdRender.Flags().StringVar(&renderOpts.cloudProvider, "cloud-provider", "", "The provider for cloud services.  Empty string for no provider")
	cmdRender.Flags().StringVar(&renderOpts.networkProvider, "network-provider", "flannel", "CNI network provider (flannel or experimental-canal or experimental-calico).")
}

func runCmdRender(cmd *cobra.Command, args []string) error {
	config, err := flagsToAssetConfig()
	if err != nil {
		return err
	}

	as, err := asset.NewDefaultAssets(*config)
	if err != nil {
		return err
	}

	return as.WriteFiles(renderOpts.assetDir)
}

func validateRenderOpts(cmd *cobra.Command, args []string) error {
	if renderOpts.caCertificatePath != "" && renderOpts.caPrivateKeyPath == "" {
		return errors.New("You must provide the --ca-private-key-path flag when --ca-certificate-path is provided.")
	}
	if renderOpts.caPrivateKeyPath != "" && renderOpts.caCertificatePath == "" {
		return errors.New("You must provide the --ca-certificate-path flag when --ca-private-key-path is provided.")
	}
	if (renderOpts.etcdCAPath != "" || renderOpts.etcdCertificatePath != "" || renderOpts.etcdPrivateKeyPath != "") && (renderOpts.etcdCAPath == "" || renderOpts.etcdCertificatePath == "" || renderOpts.etcdPrivateKeyPath == "") {
		return errors.New("You must specify either all or none of --etcd-ca-path, --etcd-certificate-path, and --etcd-private-key-path")
	}
	if renderOpts.assetDir == "" {
		return errors.New("Missing required flag: --asset-dir")
	}
	if renderOpts.etcdServers == "" {
		return errors.New("Missing required flag: --etcd-servers")
	}
	if renderOpts.apiServers == "" {
		return errors.New("Missing required flag: --api-servers")
	}
	if renderOpts.networkProvider != asset.NetworkFlannel && renderOpts.networkProvider != asset.NetworkCalico && renderOpts.networkProvider != asset.NetworkCanal {
		return errors.New("Must specify --network-provider flannel or experimental-calico or experimental-canal")
	}
	return nil
}

func flagsToAssetConfig() (c *asset.Config, err error) {
	apiServers, err := parseURLs(renderOpts.apiServers)
	if err != nil {
		return nil, err
	}
	altNames, err := parseAltNames(renderOpts.altNames)
	if err != nil {
		return nil, err
	}
	if altNames == nil {
		// Fall back to parsing from api-server list
		altNames = altNamesFromURLs(apiServers)
	}

	var caCert *x509.Certificate
	var caPrivKey *rsa.PrivateKey
	if renderOpts.caCertificatePath != "" {
		caPrivKey, caCert, err = parseCertAndPrivateKeyFromDisk(renderOpts.caCertificatePath, renderOpts.caPrivateKeyPath)
		if err != nil {
			return nil, err
		}
	}

	_, podNet, err := net.ParseCIDR(renderOpts.podCIDR)
	if err != nil {
		return nil, err
	}

	_, serviceNet, err := net.ParseCIDR(renderOpts.serviceCIDR)
	if err != nil {
		return nil, err
	}

	if podNet.Contains(serviceNet.IP) || serviceNet.Contains(podNet.IP) {
		return nil, fmt.Errorf("Pod CIDR %s and service CIDR %s must not overlap", podNet.String(), serviceNet.String())
	}

	apiServiceIP, err := offsetServiceIP(serviceNet, apiOffset)
	if err != nil {
		return nil, err
	}

	dnsServiceIP, err := offsetServiceIP(serviceNet, dnsOffset)
	if err != nil {
		return nil, err
	}

	etcdServers, err := parseURLs(renderOpts.etcdServers)
	if err != nil {
		return nil, err
	}

	etcdUseTLS := false
	for _, url := range etcdServers {
		if url.Scheme == "https" {
			etcdUseTLS = true
		}
	}

	var etcdCACert *x509.Certificate
	if renderOpts.etcdCAPath != "" {
		etcdCACert, err = parseCertFromDisk(renderOpts.etcdCAPath)
		if err != nil {
			return nil, err
		}
	}
	var etcdClientCert *x509.Certificate
	var etcdClientKey *rsa.PrivateKey
	if renderOpts.etcdCertificatePath != "" {
		etcdClientKey, etcdClientCert, err = parseCertAndPrivateKeyFromDisk(renderOpts.etcdCertificatePath, renderOpts.etcdPrivateKeyPath)
		if err != nil {
			return nil, err
		}
	}

	// TODO: Find better option than asking users to make manual changes
	if serviceNet.IP.String() != defaultServiceBaseIP {
		fmt.Printf("You have selected a non-default service CIDR %s - be sure your kubelet service file uses --cluster-dns=%s\n", serviceNet.String(), dnsServiceIP.String())
	}

	return &asset.Config{
		EtcdCACert:      etcdCACert,
		EtcdClientCert:  etcdClientCert,
		EtcdClientKey:   etcdClientKey,
		EtcdServers:     etcdServers,
		EtcdUseTLS:      etcdUseTLS,
		CACert:          caCert,
		CAPrivKey:       caPrivKey,
		APIServers:      apiServers,
		AltNames:        altNames,
		PodCIDR:         podNet,
		ServiceCIDR:     serviceNet,
		APIServiceIP:    apiServiceIP,
		DNSServiceIP:    dnsServiceIP,
		CloudProvider:   renderOpts.cloudProvider,
		NetworkProvider: renderOpts.networkProvider,
		Images:          imageVersions,
	}, nil
}

func parseCertAndPrivateKeyFromDisk(caCertPath, privKeyPath string) (*rsa.PrivateKey, *x509.Certificate, error) {
	// Parse CA Private key.
	keypem, err := ioutil.ReadFile(privKeyPath)
	if err != nil {
		return nil, nil, fmt.Errorf("error reading ca private key file at %s: %v", privKeyPath, err)
	}
	key, err := tlsutil.ParsePEMEncodedPrivateKey(keypem)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to parse CA private key: %v", err)
	}
	// Parse CA Cert.
	cert, err := parseCertFromDisk(caCertPath)
	if err != nil {
		return nil, nil, err
	}
	return key, cert, nil
}

func parseCertFromDisk(caCertPath string) (*x509.Certificate, error) {
	capem, err := ioutil.ReadFile(caCertPath)
	if err != nil {
		return nil, fmt.Errorf("error reading ca cert file at %s: %v", caCertPath, err)
	}
	cert, err := tlsutil.ParsePEMEncodedCACert(capem)
	if err != nil {
		return nil, fmt.Errorf("unable to parse CA Cert: %v", err)
	}
	return cert, nil
}

func parseURLs(s string) ([]*url.URL, error) {
	var out []*url.URL
	for _, u := range strings.Split(s, ",") {
		parsed, err := url.Parse(u)
		if err != nil {
			return nil, err
		}
		out = append(out, parsed)
	}
	return out, nil
}

func parseAltNames(s string) (*tlsutil.AltNames, error) {
	if s == "" {
		return nil, nil
	}
	var alt tlsutil.AltNames
	for _, an := range strings.Split(s, ",") {
		switch {
		case strings.HasPrefix(an, "DNS="):
			alt.DNSNames = append(alt.DNSNames, strings.TrimPrefix(an, "DNS="))
		case strings.HasPrefix(an, "IP="):
			ip := net.ParseIP(strings.TrimPrefix(an, "IP="))
			if ip == nil {
				return nil, fmt.Errorf("Invalid IP alt name: %s", an)
			}
			alt.IPs = append(alt.IPs, ip)
		default:
			return nil, fmt.Errorf("Invalid alt name: %s", an)
		}
	}
	return &alt, nil
}

func altNamesFromURLs(urls []*url.URL) *tlsutil.AltNames {
	var an tlsutil.AltNames
	for _, u := range urls {
		host, _, err := net.SplitHostPort(u.Host)
		if err != nil {
			host = u.Host
		}
		ip := net.ParseIP(host)
		if ip == nil {
			an.DNSNames = append(an.DNSNames, host)
		} else {
			an.IPs = append(an.IPs, ip)
		}
	}
	return &an
}

// offsetServiceIP returns an IP offset by up to 255.
// TODO: do numeric conversion to generalize this utility.
func offsetServiceIP(ipnet *net.IPNet, offset int) (net.IP, error) {
	ip := make(net.IP, len(ipnet.IP))
	copy(ip, ipnet.IP)
	for i := 0; i < offset; i++ {
		incIPv4(ip)
	}
	if ipnet.Contains(ip) {
		return ip, nil
	}
	return net.IP([]byte("")), fmt.Errorf("Service IP %v is not in %s", ip, ipnet)
}

func incIPv4(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}
<commit_msg>grammar changes<commit_after>package main

import (
	"crypto/rsa"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/url"
	"strings"

	"github.com/spf13/cobra"

	"github.com/kubernetes-sigs/bootkube/pkg/asset"
	"github.com/kubernetes-sigs/bootkube/pkg/tlsutil"
)

const (
	apiOffset            = 1
	dnsOffset            = 10
	defaultServiceBaseIP = "10.3.0.0"
	defaultEtcdServers   = "https://127.0.0.1:2379"
)

var (
	cmdRender = &cobra.Command{
		Use:          "render",
		Short:        "Render default cluster manifests",
		Long:         "",
		PreRunE:      validateRenderOpts,
		RunE:         runCmdRender,
		SilenceUsage: true,
	}

	renderOpts struct {
		assetDir            string
		caCertificatePath   string
		caPrivateKeyPath    string
		etcdCAPath          string
		etcdCertificatePath string
		etcdPrivateKeyPath  string
		etcdServers         string
		apiServers          string
		altNames            string
		podCIDR             string
		serviceCIDR         string
		cloudProvider       string
		networkProvider     string
	}

	imageVersions = asset.DefaultImages
)

func init() {
	cmdRoot.AddCommand(cmdRender)
	cmdRender.Flags().StringVar(&renderOpts.assetDir, "asset-dir", "", "Output path for rendered assets")
	cmdRender.Flags().StringVar(&renderOpts.caCertificatePath, "ca-certificate-path", "", "Path to an existing PEM encoded CA. If provided, TLS assets will be generated using this certificate authority.")
	cmdRender.Flags().StringVar(&renderOpts.caPrivateKeyPath, "ca-private-key-path", "", "Path to an existing Certificate Authority RSA private key. Required if --ca-certificate is set.")
	cmdRender.Flags().StringVar(&renderOpts.etcdCAPath, "etcd-ca-path", "", "Path to an existing PEM encoded CA that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-certificate-path and --etcd-private-key-path, and must have etcd configured to use TLS with matching secrets.")
	cmdRender.Flags().StringVar(&renderOpts.etcdCertificatePath, "etcd-certificate-path", "", "Path to an existing certificate that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-ca-path and --etcd-private-key-path, and must have etcd configured to use TLS with matching secrets.")
	cmdRender.Flags().StringVar(&renderOpts.etcdPrivateKeyPath, "etcd-private-key-path", "", "Path to an existing private key that will be used for TLS-enabled communication between the apiserver and etcd. Must be used in conjunction with --etcd-ca-path and --etcd-certificate-path, and must have etcd configured to use TLS with matching secrets.")
	cmdRender.Flags().StringVar(&renderOpts.etcdServers, "etcd-servers", defaultEtcdServers, "List of etcd servers URLs including host:port, comma separated")
	cmdRender.Flags().StringVar(&renderOpts.apiServers, "api-servers", "https://127.0.0.1:6443", "List of API server URLs including host:port, comma seprated")
	cmdRender.Flags().StringVar(&renderOpts.altNames, "api-server-alt-names", "", "List of SANs to use in api-server certificate. Example: 'IP=127.0.0.1,IP=127.0.0.2,DNS=localhost'. If empty, SANs will be extracted from the --api-servers flag.")
	cmdRender.Flags().StringVar(&renderOpts.podCIDR, "pod-cidr", "10.2.0.0/16", "The CIDR range of cluster pods.")
	cmdRender.Flags().StringVar(&renderOpts.serviceCIDR, "service-cidr", "10.3.0.0/24", "The CIDR range of cluster services.")
	cmdRender.Flags().StringVar(&renderOpts.cloudProvider, "cloud-provider", "", "The provider for cloud services.  Empty string for no provider")
	cmdRender.Flags().StringVar(&renderOpts.networkProvider, "network-provider", "flannel", "CNI network provider (flannel, experimental-canal or experimental-calico).")
}

func runCmdRender(cmd *cobra.Command, args []string) error {
	config, err := flagsToAssetConfig()
	if err != nil {
		return err
	}

	as, err := asset.NewDefaultAssets(*config)
	if err != nil {
		return err
	}

	return as.WriteFiles(renderOpts.assetDir)
}

func validateRenderOpts(cmd *cobra.Command, args []string) error {
	if renderOpts.caCertificatePath != "" && renderOpts.caPrivateKeyPath == "" {
		return errors.New("You must provide the --ca-private-key-path flag when --ca-certificate-path is provided.")
	}
	if renderOpts.caPrivateKeyPath != "" && renderOpts.caCertificatePath == "" {
		return errors.New("You must provide the --ca-certificate-path flag when --ca-private-key-path is provided.")
	}
	if (renderOpts.etcdCAPath != "" || renderOpts.etcdCertificatePath != "" || renderOpts.etcdPrivateKeyPath != "") && (renderOpts.etcdCAPath == "" || renderOpts.etcdCertificatePath == "" || renderOpts.etcdPrivateKeyPath == "") {
		return errors.New("You must specify either all or none of --etcd-ca-path, --etcd-certificate-path, and --etcd-private-key-path")
	}
	if renderOpts.assetDir == "" {
		return errors.New("Missing required flag: --asset-dir")
	}
	if renderOpts.etcdServers == "" {
		return errors.New("Missing required flag: --etcd-servers")
	}
	if renderOpts.apiServers == "" {
		return errors.New("Missing required flag: --api-servers")
	}
	if renderOpts.networkProvider != asset.NetworkFlannel && renderOpts.networkProvider != asset.NetworkCalico && renderOpts.networkProvider != asset.NetworkCanal {
		return errors.New("Must specify --network-provider flannel or experimental-calico or experimental-canal")
	}
	return nil
}

func flagsToAssetConfig() (c *asset.Config, err error) {
	apiServers, err := parseURLs(renderOpts.apiServers)
	if err != nil {
		return nil, err
	}
	altNames, err := parseAltNames(renderOpts.altNames)
	if err != nil {
		return nil, err
	}
	if altNames == nil {
		// Fall back to parsing from api-server list
		altNames = altNamesFromURLs(apiServers)
	}

	var caCert *x509.Certificate
	var caPrivKey *rsa.PrivateKey
	if renderOpts.caCertificatePath != "" {
		caPrivKey, caCert, err = parseCertAndPrivateKeyFromDisk(renderOpts.caCertificatePath, renderOpts.caPrivateKeyPath)
		if err != nil {
			return nil, err
		}
	}

	_, podNet, err := net.ParseCIDR(renderOpts.podCIDR)
	if err != nil {
		return nil, err
	}

	_, serviceNet, err := net.ParseCIDR(renderOpts.serviceCIDR)
	if err != nil {
		return nil, err
	}

	if podNet.Contains(serviceNet.IP) || serviceNet.Contains(podNet.IP) {
		return nil, fmt.Errorf("Pod CIDR %s and service CIDR %s must not overlap", podNet.String(), serviceNet.String())
	}

	apiServiceIP, err := offsetServiceIP(serviceNet, apiOffset)
	if err != nil {
		return nil, err
	}

	dnsServiceIP, err := offsetServiceIP(serviceNet, dnsOffset)
	if err != nil {
		return nil, err
	}

	etcdServers, err := parseURLs(renderOpts.etcdServers)
	if err != nil {
		return nil, err
	}

	etcdUseTLS := false
	for _, url := range etcdServers {
		if url.Scheme == "https" {
			etcdUseTLS = true
		}
	}

	var etcdCACert *x509.Certificate
	if renderOpts.etcdCAPath != "" {
		etcdCACert, err = parseCertFromDisk(renderOpts.etcdCAPath)
		if err != nil {
			return nil, err
		}
	}
	var etcdClientCert *x509.Certificate
	var etcdClientKey *rsa.PrivateKey
	if renderOpts.etcdCertificatePath != "" {
		etcdClientKey, etcdClientCert, err = parseCertAndPrivateKeyFromDisk(renderOpts.etcdCertificatePath, renderOpts.etcdPrivateKeyPath)
		if err != nil {
			return nil, err
		}
	}

	// TODO: Find better option than asking users to make manual changes
	if serviceNet.IP.String() != defaultServiceBaseIP {
		fmt.Printf("You have selected a non-default service CIDR %s - be sure your kubelet service file uses --cluster-dns=%s\n", serviceNet.String(), dnsServiceIP.String())
	}

	return &asset.Config{
		EtcdCACert:      etcdCACert,
		EtcdClientCert:  etcdClientCert,
		EtcdClientKey:   etcdClientKey,
		EtcdServers:     etcdServers,
		EtcdUseTLS:      etcdUseTLS,
		CACert:          caCert,
		CAPrivKey:       caPrivKey,
		APIServers:      apiServers,
		AltNames:        altNames,
		PodCIDR:         podNet,
		ServiceCIDR:     serviceNet,
		APIServiceIP:    apiServiceIP,
		DNSServiceIP:    dnsServiceIP,
		CloudProvider:   renderOpts.cloudProvider,
		NetworkProvider: renderOpts.networkProvider,
		Images:          imageVersions,
	}, nil
}

func parseCertAndPrivateKeyFromDisk(caCertPath, privKeyPath string) (*rsa.PrivateKey, *x509.Certificate, error) {
	// Parse CA Private key.
	keypem, err := ioutil.ReadFile(privKeyPath)
	if err != nil {
		return nil, nil, fmt.Errorf("error reading ca private key file at %s: %v", privKeyPath, err)
	}
	key, err := tlsutil.ParsePEMEncodedPrivateKey(keypem)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to parse CA private key: %v", err)
	}
	// Parse CA Cert.
	cert, err := parseCertFromDisk(caCertPath)
	if err != nil {
		return nil, nil, err
	}
	return key, cert, nil
}

func parseCertFromDisk(caCertPath string) (*x509.Certificate, error) {
	capem, err := ioutil.ReadFile(caCertPath)
	if err != nil {
		return nil, fmt.Errorf("error reading ca cert file at %s: %v", caCertPath, err)
	}
	cert, err := tlsutil.ParsePEMEncodedCACert(capem)
	if err != nil {
		return nil, fmt.Errorf("unable to parse CA Cert: %v", err)
	}
	return cert, nil
}

func parseURLs(s string) ([]*url.URL, error) {
	var out []*url.URL
	for _, u := range strings.Split(s, ",") {
		parsed, err := url.Parse(u)
		if err != nil {
			return nil, err
		}
		out = append(out, parsed)
	}
	return out, nil
}

func parseAltNames(s string) (*tlsutil.AltNames, error) {
	if s == "" {
		return nil, nil
	}
	var alt tlsutil.AltNames
	for _, an := range strings.Split(s, ",") {
		switch {
		case strings.HasPrefix(an, "DNS="):
			alt.DNSNames = append(alt.DNSNames, strings.TrimPrefix(an, "DNS="))
		case strings.HasPrefix(an, "IP="):
			ip := net.ParseIP(strings.TrimPrefix(an, "IP="))
			if ip == nil {
				return nil, fmt.Errorf("Invalid IP alt name: %s", an)
			}
			alt.IPs = append(alt.IPs, ip)
		default:
			return nil, fmt.Errorf("Invalid alt name: %s", an)
		}
	}
	return &alt, nil
}

func altNamesFromURLs(urls []*url.URL) *tlsutil.AltNames {
	var an tlsutil.AltNames
	for _, u := range urls {
		host, _, err := net.SplitHostPort(u.Host)
		if err != nil {
			host = u.Host
		}
		ip := net.ParseIP(host)
		if ip == nil {
			an.DNSNames = append(an.DNSNames, host)
		} else {
			an.IPs = append(an.IPs, ip)
		}
	}
	return &an
}

// offsetServiceIP returns an IP offset by up to 255.
// TODO: do numeric conversion to generalize this utility.
func offsetServiceIP(ipnet *net.IPNet, offset int) (net.IP, error) {
	ip := make(net.IP, len(ipnet.IP))
	copy(ip, ipnet.IP)
	for i := 0; i < offset; i++ {
		incIPv4(ip)
	}
	if ipnet.Contains(ip) {
		return ip, nil
	}
	return net.IP([]byte("")), fmt.Errorf("Service IP %v is not in %s", ip, ipnet)
}

func incIPv4(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}
<|endoftext|>


<commit_before>package nbt

import (
	"encoding/binary"
	"fmt"
	"io"
)

const (
	TAG_End = iota
	TAG_Byte
	TAG_Short
	TAG_Int
	TAG_Long
	TAG_Float
	TAG_Double
	TAG_Byte_Array
	TAG_String
	TAG_List
	TAG_Compound
	TAG_Int_Array
)

func tagToString(t byte) string {
	switch t {
	case TAG_End:
		return "TAG_End"
	case TAG_Byte:
		return "TAG_Byte"
	case TAG_Short:
		return "TAG_Short"
	case TAG_Int:
		return "TAG_Int"
	case TAG_Long:
		return "TAG_Long"
	case TAG_Float:
		return "TAG_Float"
	case TAG_Double:
		return "TAG_Double"
	case TAG_Byte_Array:
		return "TAG_Byte_Array"
	case TAG_String:
		return "TAG_String"
	case TAG_List:
		return "TAG_List"
	case TAG_Compound:
		return "TAG_Compound"
	case TAG_Int_Array:
		return "TAG_Int_Array"
	}
	return "Unknown tag!"
}

type Tag struct {
	Type    byte
	Name    string
	Payload interface{}
}

func ReadTag(r io.Reader) (t Tag, err error) {
	// read a byte
	ttype := make([]byte, 1)
	_, err = io.ReadFull(r, ttype)
	if err != nil {
		return
	}

	// TAG_End isn't really a tag
	if ttype[0] == byte(TAG_End) {
		return
	}

	// Real tags need types
	t.Type = ttype[0]

	// Now about that name
	var strlen int16
	err = binary.Read(r, binary.BigEndian, &strlen)
	if err != nil {
		return
	}

	strbytes := make([]byte, strlen)

	_, err = io.ReadFull(r, strbytes)
	if err != nil {
		return
	}

	t.Name = string(strbytes)

	// Payload-specific code goes here
	switch t.Type {
	case TAG_Byte:
		payload := make([]byte, 1)
		_, err = io.ReadFull(r, payload)
		if err != nil {
			return
		}
		t.Payload = payload
	case TAG_Short:
		var payload int16
		err = binary.Read(r, binary.BigEndian, &payload)
		if err != nil {
			return
		}
		t.Payload = payload
	case TAG_Int:
		var payload int32
		err = binary.Read(r, binary.BigEndian, &payload)
		if err != nil {
			return
		}
		t.Payload = payload
	case TAG_Long:
		var payload int64
		err = binary.Read(r, binary.BigEndian, &payload)
		if err != nil {
			return
		}
		t.Payload = payload
	case TAG_Float:
		var payload float32
		err = binary.Read(r, binary.BigEndian, &payload)
		if err != nil {
			return
		}
		t.Payload = payload
	case TAG_Double:
		var payload float64
		err = binary.Read(r, binary.BigEndian, &payload)
		if err != nil {
			return
		}
		t.Payload = payload
	case TAG_Byte_Array:
		var strlen int32
		err = binary.Read(r, binary.BigEndian, &strlen)
		if err != nil {
			return
		}

		strbytes := make([]byte, strlen)

		_, err = io.ReadFull(r, strbytes)
		if err != nil {
			return
		}

		t.Payload = strbytes
	case TAG_String:
		var strlen int16
		err = binary.Read(r, binary.BigEndian, &strlen)
		if err != nil {
			return
		}

		strbytes := make([]byte, strlen)

		_, err = io.ReadFull(r, strbytes)
		if err != nil {
			return
		}

		t.Payload = string(strbytes)
	case TAG_List:
		// JMT: this is a little annoying.
		var payload int
		t.Payload = payload
	case TAG_Compound:
		payload := []Tag{}
		var newtag, emptytag Tag
		for newtag, err = ReadTag(r); newtag != emptytag; newtag, err = ReadTag(r) {
			payload = append(payload, newtag)
		}
		t.Payload = payload
	case TAG_Int_Array:
		var strlen int32
		err = binary.Read(r, binary.BigEndian, &strlen)
		if err != nil {
			return
		}

		ints := make([]int32, strlen)
		for key := range ints {
			err = binary.Read(r, binary.BigEndian, &ints[key])
			if err != nil {
				return
			}
		}

		t.Payload = ints
	default:
		err = fmt.Errorf("unknown tag")
	}
	return
}

// other way

func WriteTag(w io.Writer, t Tag) (err error) {
	w.Write([]byte{t.Type})

	if t.Type == TAG_End {
		return
	}

	binary.Write(w, binary.BigEndian, int16(len(t.Name)))
	w.Write([]byte(t.Name))

	switch t.Type {
	case TAG_Byte:
		w.Write(t.Payload.([]byte))
	case TAG_Short:
		binary.Write(w, binary.BigEndian, t.Payload.(int16))
	case TAG_Int:
		binary.Write(w, binary.BigEndian, t.Payload.(int32))
	case TAG_Long:
		binary.Write(w, binary.BigEndian, t.Payload.(int64))
	case TAG_Float:
		binary.Write(w, binary.BigEndian, t.Payload.(float32))
	case TAG_Double:
		binary.Write(w, binary.BigEndian, t.Payload.(float64))
	case TAG_Byte_Array:
		binary.Write(w, binary.BigEndian, int32(len(t.Payload.([]byte))))
		for _, value := range t.Payload.([]byte) {
			binary.Write(w, binary.BigEndian, value)
		}
	case TAG_String:
		binary.Write(w, binary.BigEndian, int16(len(t.Payload.(string))))
		w.Write([]byte(t.Payload.(string)))
	case TAG_List:
	case TAG_Compound:
		tags := append(t.Payload.([]Tag), Tag{Type: TAG_End})
		for _, tag := range tags {
			WriteTag(w, tag)
		}
	case TAG_Int_Array:
		binary.Write(w, binary.BigEndian, int32(len(t.Payload.([]int32))))
		for _, value := range t.Payload.([]int32) {
			binary.Write(w, binary.BigEndian, value)
		}

	default:
		err = fmt.Errorf("unknown tag")
	}
	return
}
<commit_msg>Refactored NBT code in preparation for TAG_List.<commit_after>package nbt

import (
	"encoding/binary"
	"fmt"
	"io"
)

const (
	TAG_End        byte = 0
	TAG_Byte       byte = 1
	TAG_Short      byte = 2
	TAG_Int        byte = 3
	TAG_Long       byte = 4
	TAG_Float      byte = 5
	TAG_Double     byte = 6
	TAG_Byte_Array byte = 7
	TAG_String     byte = 8
	TAG_List       byte = 9
	TAG_Compound   byte = 10
	TAG_Int_Array  byte = 11
)

type Tag struct {
	Type    byte
	Name    string
	Payload interface{}
}

type PayloadReader func(io.Reader) interface{}
type PayloadWriter func(io.Writer, interface{})

var Tags = map[byte]struct {
	String  string
	PReader PayloadReader
	PWriter PayloadWriter
}{
	TAG_End: {"TAG_End",
		nil,
		nil,
	},
	TAG_Byte: {"TAG_Byte",
		func(r io.Reader) interface{} {
			payload := make([]byte, 1)
			if _, err := io.ReadFull(r, payload); err != nil {
				panic(err)
			}
			return payload
		},
		func(w io.Writer, i interface{}) {
			w.Write(i.([]byte))
		},
	},
	TAG_Short: {"TAG_Short",
		func(r io.Reader) interface{} {
			var payload int16
			if err := binary.Read(r, binary.BigEndian, &payload); err != nil {
				panic(err)
			}
			return payload
		},
		func(w io.Writer, i interface{}) {
			binary.Write(w, binary.BigEndian, i.(int16))
		},
	},
	TAG_Int: {"TAG_Int",
		func(r io.Reader) interface{} {
			var payload int32
			if err := binary.Read(r, binary.BigEndian, &payload); err != nil {
				panic(err)
			}
			return payload
		},
		func(w io.Writer, i interface{}) {
			binary.Write(w, binary.BigEndian, i.(int32))
		},
	},
	TAG_Long: {"TAG_Long",
		func(r io.Reader) interface{} {
			var payload int64
			if err := binary.Read(r, binary.BigEndian, &payload); err != nil {
				panic(err)
			}
			return payload
		},
		func(w io.Writer, i interface{}) {
			binary.Write(w, binary.BigEndian, i.(int64))
		},
	},
	TAG_Float: {"TAG_Float",
		func(r io.Reader) interface{} {
			var payload float32
			if err := binary.Read(r, binary.BigEndian, &payload); err != nil {
				panic(err)
			}
			return payload
		},
		func(w io.Writer, i interface{}) {
			binary.Write(w, binary.BigEndian, i.(float32))
		},
	},
	TAG_Double: {"TAG_Double",
		func(r io.Reader) interface{} {
			var payload float64
			if err := binary.Read(r, binary.BigEndian, &payload); err != nil {
				panic(err)
			}
			return payload
		},
		func(w io.Writer, i interface{}) {
			binary.Write(w, binary.BigEndian, i.(float64))
		},
	},
	TAG_Byte_Array: {"TAG_Byte_Array",
		func(r io.Reader) interface{} {
			var strlen int32
			if err := binary.Read(r, binary.BigEndian, &strlen); err != nil {
				panic(err)
			}

			strbytes := make([]byte, strlen)

			if _, err := io.ReadFull(r, strbytes); err != nil {
				panic(err)
			}

			return strbytes
		},
		func(w io.Writer, i interface{}) {
			binary.Write(w, binary.BigEndian, int32(len(i.([]byte))))
			for _, value := range i.([]byte) {
				binary.Write(w, binary.BigEndian, value)
			}
		},
	},
	TAG_String: {"TAG_String",
		func(r io.Reader) interface{} {
			var strlen int16
			if err := binary.Read(r, binary.BigEndian, &strlen); err != nil {
				panic(err)
			}

			strbytes := make([]byte, strlen)

			if _, err := io.ReadFull(r, strbytes); err != nil {
				panic(err)
			}

			return string(strbytes)
		},
		func(w io.Writer, i interface{}) {
			binary.Write(w, binary.BigEndian, int16(len(i.(string))))
			w.Write([]byte(i.(string)))
		},
	},
	TAG_List: {"TAG_List",
		func(r io.Reader) interface{} {
			var i interface{}
			return i
		},
		func(w io.Writer, i interface{}) {
			return
		},
	},
	TAG_Compound: {"TAG_Compound",
		// JMT: figure out how to break loop
		nil,
		nil,
	},
	TAG_Int_Array: {"TAG_Int_Array",
		func(r io.Reader) interface{} {
			var strlen int32
			if err := binary.Read(r, binary.BigEndian, &strlen); err != nil {
				panic(err)
			}

			ints := make([]int32, strlen)
			for key := range ints {
				if err := binary.Read(r, binary.BigEndian, &ints[key]); err != nil {
					panic(err)
				}
			}

			return ints
		},
		func(w io.Writer, i interface{}) {
			binary.Write(w, binary.BigEndian, int32(len(i.([]int32))))
			for _, value := range i.([]int32) {
				binary.Write(w, binary.BigEndian, value)
			}
		},
	},
}

// JMT: The compound reader and writer cause an initialization loop
// when added to the Tags variable.

func readCompound(r io.Reader) interface{} {
	payload := []Tag{}
	var emptytag Tag
	for newtag, err := ReadTag(r); newtag != emptytag; newtag, err = ReadTag(r) {
		if err != nil {
			panic(err)
		}

		payload = append(payload, newtag)
	}
	return payload
}

func writeCompound(w io.Writer, i interface{}) {
	tags := append(i.([]Tag), Tag{Type: TAG_End})
	for _, tag := range tags {
		WriteTag(w, tag)
	}
}

func ReadTag(r io.Reader) (t Tag, err error) {
	// read a byte
	ttype := Tags[TAG_Byte].PReader(r).([]byte)[0]

	// TAG_End isn't really a tag
	if ttype == TAG_End {
		return
	}

	// Real tags need types
	t.Type = ttype

	// Now about that name
	t.Name = Tags[TAG_String].PReader(r).(string)

	// Putting this in the widget causes an initialization loop issue
	// (Tags refers to readCompound refers to ReadTag refers to Tags)
	if t.Type == TAG_Compound {
		t.Payload = readCompound(r)
	} else if val, ok := Tags[t.Type]; ok {
		t.Payload = val.PReader(r)
	} else {
		err = fmt.Errorf("unknown tag")
	}
	return
}

func WriteTag(w io.Writer, t Tag) (err error) {
	// JMT: this []byte{} bit feels wrong
	Tags[TAG_Byte].PWriter(w, []byte{t.Type})

	if t.Type == TAG_End {
		return
	}

	Tags[TAG_String].PWriter(w, t.Name)

	// JMT: initialization loop issue here too
	if t.Type == TAG_Compound {
		writeCompound(w, t.Payload)
	} else if val, ok := Tags[t.Type]; ok {
		val.PWriter(w, t.Payload)
	} else {
		err = fmt.Errorf("unknown tag")
	}
	return
}
<|endoftext|>


<commit_before>package cmd

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

	"github.com/ryanuber/go-glob"
	"github.com/spf13/cobra"
	"github.com/vbauerster/mpb"
	"golang.org/x/sync/errgroup"

	"github.com/Shopify/themekit/kit"
)

type (
	commandArbiter struct {
		progress           *mpb.Progress
		verbose            bool
		force              bool
		master             string
		configPath         string
		allenvs            bool
		environments       stringArgArray
		notifyFile         string
		flagConfig         kit.Configuration
		disableIgnore      bool
		ignoredFiles       stringArgArray
		ignores            stringArgArray
		activeThemeClients []kit.ThemeClient
		allThemeClients    []kit.ThemeClient
		manifest           *fileManifest
	}
	cobraCmdE     func(*cobra.Command, []string) error
	arbitratedCmd func(kit.ThemeClient, []string) error
	assetAction   struct {
		asset kit.Asset
		event kit.EventType
	}
)

func newCommandArbiter() *commandArbiter {
	pwd, _ := os.Getwd()
	return &commandArbiter{
		progress:   mpb.New(nil),
		configPath: filepath.Join(pwd, "config.yml"),
		flagConfig: kit.Configuration{},
	}
}

func (arbiter *commandArbiter) generateManifest() error {
	var err error
	arbiter.manifest, err = newFileManifest(filepath.Dir(arbiter.configPath), arbiter.allThemeClients)
	return err
}

func (arbiter *commandArbiter) generateThemeClients(cmd *cobra.Command, args []string) error {
	arbiter.activeThemeClients = []kit.ThemeClient{}
	arbiter.allThemeClients = []kit.ThemeClient{}
	configEnvs, err := kit.LoadEnvironments(arbiter.configPath)

	if err != nil && os.IsNotExist(err) {
		return fmt.Errorf("Could not find config file at %v", arbiter.configPath)
	} else if err != nil {
		return err
	}

	for env := range configEnvs {
		config, err := configEnvs.GetConfiguration(env)
		if err != nil {
			return err
		}
		if arbiter.disableIgnore {
			config.IgnoredFiles = []string{}
			config.Ignores = []string{}
		}
		if config.Proxy != "" {
			stdOut.Printf(
				"[%s] Proxy URL detected from Configuration: %s SSL Certificate Validation will be disabled!",
				green(config.Environment),
				yellow(config.Proxy),
			)
		}

		client, err := kit.NewThemeClient(config)
		if err != nil {
			return err
		}

		if arbiter.shouldUseEnvironment(env) {
			arbiter.activeThemeClients = append(arbiter.activeThemeClients, client)
		}
		arbiter.allThemeClients = append(arbiter.allThemeClients, client)
	}

	if len(arbiter.activeThemeClients) == 0 {
		return fmt.Errorf("Could not load any valid environments")
	}

	return arbiter.generateManifest()
}

func (arbiter *commandArbiter) shouldUseEnvironment(envName string) bool {
	flagEnvs := arbiter.environments.Value()
	if arbiter.allenvs || (len(flagEnvs) == 0 && envName == kit.DefaultEnvironment) {
		return true
	}
	for _, env := range flagEnvs {
		if env == envName || glob.Glob(env, envName) {
			return true
		}
	}
	return false
}

func (arbiter *commandArbiter) forEachClient(handler arbitratedCmd) cobraCmdE {
	return func(cmd *cobra.Command, args []string) error {
		var handlerGroup errgroup.Group
		for _, client := range arbiter.activeThemeClients {
			client := client
			handlerGroup.Go(func() error {
				return handler(client, args)
			})
		}
		return handlerGroup.Wait()
	}
}

func (arbiter *commandArbiter) forSingleClient(handler arbitratedCmd) cobraCmdE {
	return func(cmd *cobra.Command, args []string) error {
		if len(arbiter.activeThemeClients) > 1 {
			return fmt.Errorf("more than one environment specified for a single environment command")
		}

		return handler(arbiter.activeThemeClients[0], args)
	}
}

func (arbiter *commandArbiter) setFlagConfig() {
	if !arbiter.disableIgnore {
		arbiter.flagConfig.IgnoredFiles = arbiter.ignoredFiles.Value()
		arbiter.flagConfig.Ignores = arbiter.ignores.Value()
	}
	kit.SetFlagConfig(arbiter.flagConfig)
}

func (arbiter *commandArbiter) newProgressBar(count int, name string) *mpb.Bar {
	var bar *mpb.Bar
	if !arbiter.verbose {
		bar = arbiter.progress.AddBar(int64(count)).
			PrependName(fmt.Sprintf("[%s]: ", name), 0).
			AppendPercentage().
			PrependCounters(0, 0)
	}
	return bar
}

func (arbiter *commandArbiter) generateAssetActions(client kit.ThemeClient, filenames []string, destructive bool) (map[string]assetAction, error) {
	assetsActions := map[string]assetAction{}
	var err error
	var assets []kit.Asset
	if len(filenames) == 0 && destructive {
		if assets, err = client.AssetList(); err != nil {
			return nil, err
		}
		for _, asset := range assets {
			assetsActions[asset.Key] = assetAction{asset: asset, event: kit.Remove}
		}
	}

	if assets, err = client.LocalAssets(filenames...); err != nil {
		return nil, err
	}
	for _, asset := range assets {
		assetsActions[asset.Key] = assetAction{asset: asset, event: kit.Update}
	}

	return assetsActions, nil
}

func (arbiter *commandArbiter) preflightCheck(actions map[string]assetAction, destructive bool) error {
	if arbiter.force {
		return nil
	}

	for _, client := range arbiter.activeThemeClients {
		diff := arbiter.manifest.Diff(actions, client.Config.Environment, arbiter.master)
		if diff.Any(destructive) {
			return diff
		}
	}

	return nil
}
<commit_msg>Added a better error output when the configuration is invalid<commit_after>package cmd

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

	"github.com/ryanuber/go-glob"
	"github.com/spf13/cobra"
	"github.com/vbauerster/mpb"
	"golang.org/x/sync/errgroup"

	"github.com/Shopify/themekit/kit"
)

type (
	commandArbiter struct {
		progress           *mpb.Progress
		verbose            bool
		force              bool
		master             string
		configPath         string
		allenvs            bool
		environments       stringArgArray
		notifyFile         string
		flagConfig         kit.Configuration
		disableIgnore      bool
		ignoredFiles       stringArgArray
		ignores            stringArgArray
		activeThemeClients []kit.ThemeClient
		allThemeClients    []kit.ThemeClient
		manifest           *fileManifest
	}
	cobraCmdE     func(*cobra.Command, []string) error
	arbitratedCmd func(kit.ThemeClient, []string) error
	assetAction   struct {
		asset kit.Asset
		event kit.EventType
	}
)

func newCommandArbiter() *commandArbiter {
	pwd, _ := os.Getwd()
	return &commandArbiter{
		progress:   mpb.New(nil),
		configPath: filepath.Join(pwd, "config.yml"),
		flagConfig: kit.Configuration{},
	}
}

func (arbiter *commandArbiter) generateManifest() error {
	var err error
	arbiter.manifest, err = newFileManifest(filepath.Dir(arbiter.configPath), arbiter.allThemeClients)
	return err
}

func (arbiter *commandArbiter) generateThemeClients(cmd *cobra.Command, args []string) error {
	arbiter.activeThemeClients = []kit.ThemeClient{}
	arbiter.allThemeClients = []kit.ThemeClient{}
	configEnvs, err := kit.LoadEnvironments(arbiter.configPath)

	if err != nil && os.IsNotExist(err) {
		return fmt.Errorf("Could not find config file at %v", arbiter.configPath)
	} else if err != nil {
		return err
	}

	for env := range configEnvs {
		config, err := configEnvs.GetConfiguration(env)
		if err != nil {
			return fmt.Errorf(
				`[%s] Problem loading environment [%s] All environments are required to be valid so that asset versions can be validated`,
				green(config.Environment),
				yellow(err.Error()),
			)
		}
		if arbiter.disableIgnore {
			config.IgnoredFiles = []string{}
			config.Ignores = []string{}
		}
		if config.Proxy != "" {
			stdOut.Printf(
				"[%s] Proxy URL detected from Configuration: %s SSL Certificate Validation will be disabled!",
				green(config.Environment),
				yellow(config.Proxy),
			)
		}

		client, err := kit.NewThemeClient(config)
		if err != nil {
			return err
		}

		if arbiter.shouldUseEnvironment(env) {
			arbiter.activeThemeClients = append(arbiter.activeThemeClients, client)
		}
		arbiter.allThemeClients = append(arbiter.allThemeClients, client)
	}

	if len(arbiter.activeThemeClients) == 0 {
		return fmt.Errorf("Could not load any valid environments")
	}

	return arbiter.generateManifest()
}

func (arbiter *commandArbiter) shouldUseEnvironment(envName string) bool {
	flagEnvs := arbiter.environments.Value()
	if arbiter.allenvs || (len(flagEnvs) == 0 && envName == kit.DefaultEnvironment) {
		return true
	}
	for _, env := range flagEnvs {
		if env == envName || glob.Glob(env, envName) {
			return true
		}
	}
	return false
}

func (arbiter *commandArbiter) forEachClient(handler arbitratedCmd) cobraCmdE {
	return func(cmd *cobra.Command, args []string) error {
		var handlerGroup errgroup.Group
		for _, client := range arbiter.activeThemeClients {
			client := client
			handlerGroup.Go(func() error {
				return handler(client, args)
			})
		}
		return handlerGroup.Wait()
	}
}

func (arbiter *commandArbiter) forSingleClient(handler arbitratedCmd) cobraCmdE {
	return func(cmd *cobra.Command, args []string) error {
		if len(arbiter.activeThemeClients) > 1 {
			return fmt.Errorf("more than one environment specified for a single environment command")
		}

		return handler(arbiter.activeThemeClients[0], args)
	}
}

func (arbiter *commandArbiter) setFlagConfig() {
	if !arbiter.disableIgnore {
		arbiter.flagConfig.IgnoredFiles = arbiter.ignoredFiles.Value()
		arbiter.flagConfig.Ignores = arbiter.ignores.Value()
	}
	kit.SetFlagConfig(arbiter.flagConfig)
}

func (arbiter *commandArbiter) newProgressBar(count int, name string) *mpb.Bar {
	var bar *mpb.Bar
	if !arbiter.verbose {
		bar = arbiter.progress.AddBar(int64(count)).
			PrependName(fmt.Sprintf("[%s]: ", name), 0).
			AppendPercentage().
			PrependCounters(0, 0)
	}
	return bar
}

func (arbiter *commandArbiter) generateAssetActions(client kit.ThemeClient, filenames []string, destructive bool) (map[string]assetAction, error) {
	assetsActions := map[string]assetAction{}
	var err error
	var assets []kit.Asset
	if len(filenames) == 0 && destructive {
		if assets, err = client.AssetList(); err != nil {
			return nil, err
		}
		for _, asset := range assets {
			assetsActions[asset.Key] = assetAction{asset: asset, event: kit.Remove}
		}
	}

	if assets, err = client.LocalAssets(filenames...); err != nil {
		return nil, err
	}
	for _, asset := range assets {
		assetsActions[asset.Key] = assetAction{asset: asset, event: kit.Update}
	}

	return assetsActions, nil
}

func (arbiter *commandArbiter) preflightCheck(actions map[string]assetAction, destructive bool) error {
	if arbiter.force {
		return nil
	}

	for _, client := range arbiter.activeThemeClients {
		diff := arbiter.manifest.Diff(actions, client.Config.Environment, arbiter.master)
		if diff.Any(destructive) {
			return diff
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"os"
	"testing"

	"github.com/Sirupsen/logrus"
	"github.com/stretchr/testify/assert"

	"github.com/antham/doc-hunt/file"

	"github.com/antham/doc-hunt/ui"
	"github.com/antham/doc-hunt/util"
)

func TestAddConfigWithMissingFileDoc(t *testing.T) {
	ui.Error = func(err error) {
		assert.EqualError(t, err, "Missing doc identifier", "Must return a missing file doc error")
	}

	util.ErrorExit = func() {
		t.SkipNow()
	}

	os.Args = []string{"", "config", "add"}

	err := RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfigWithMissingFileSources(t *testing.T) {
	ui.Error = func(err error) {
		assert.EqualError(t, err, "Missing source identifiers", "Must return a missing source identifier error")
	}

	util.ErrorExit = func() {
		t.SkipNow()
	}

	os.Args = []string{"", "config", "add", "test"}

	err := RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfigWithMoreThanTwoArguments(t *testing.T) {
	ui.Error = func(err error) {
		assert.EqualError(t, err, "No more than 2 arguments expected", "Must return an overflow argument error")
	}

	util.ErrorExit = func() {
		t.SkipNow()
	}

	os.Args = []string{"", "config", "add", "test", "test", "test"}

	err := RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfig(t *testing.T) {
	createMocks()
	err := file.Initialize()

	if err != nil {
		logrus.Fatal(err)
	}

	ui.Success = func(msg string) {
		assert.Equal(t, "Config added", msg, "Must display a success message")
	}

	util.SuccessExit = func() {
		t.SkipNow()
	}

	os.Args = []string{"", "config", "add", "doc_file_to_track.txt", "source1.php,source2.php"}

	err = RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfigWithDryRun(t *testing.T) {
	createMocks()
	err := file.Initialize()
	output := []byte{}
	out = bytes.NewBuffer(output)

	if err != nil {
		logrus.Fatal(err)
	}

	util.SuccessExit = func() {
		assert.Regexp(t, `doc_file_to_track.txt`, out, "Must render document")
		assert.Regexp(t, `Files matching regexp "source1.php"`, out, "Must render original regexp")
		assert.Regexp(t, `=> source1.php`, out, "Must render source")
		assert.Regexp(t, `Files matching regexp "source2.php"`, out, "Must render original regexp")
		assert.Regexp(t, `=> source2.php`, out, "Must render source")
	}

	os.Args = []string{"", "config", "add", "-n", "doc_file_to_track.txt", "source1.php,source2.php"}

	err = RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestParseConfigAddArgsWithUnexistingFileDoc(t *testing.T) {
	createMocks()

	_, _, err := parseConfigAddArgs([]string{"whatever", "test"})

	assert.EqualError(t, err, "Doc whatever is not a valid existing file, nor a valid existing folder, nor a valid URL", "Must return an unexisting doc identifier error")
}

func TestParseConfigAddArgsWithADocFile(t *testing.T) {
	createMocks()

	doc, sources, err := parseConfigAddArgs([]string{"doc_file_to_track.txt", "source1.php,source2.php"})

	assert.NoError(t, err, "Must return no error")
	assert.Regexp(t, "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", doc.ID, "Must return an id")
	assert.Equal(t, "doc_file_to_track.txt", doc.Identifier, "Must return doc file path")
	assert.EqualValues(t, file.DFILE, doc.Category, "Must return a file doc category")
	assert.Equal(t, "source1.php", (*sources)[0].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[0].Category, "Must return regexp file type")
	assert.Equal(t, "source2.php", (*sources)[1].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[1].Category, "Must return regexp file type")
}

func TestParseConfigAddArgsWithADocURL(t *testing.T) {
	createMocks()

	doc, sources, err := parseConfigAddArgs([]string{"http://google.com", "source1.php,source2.php"})

	assert.Equal(t, "http://google.com", doc.Identifier, "Must return a doc url")
	assert.NoError(t, err, "Must return no error")
	assert.Equal(t, "source1.php", (*sources)[0].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[0].Category, "Must return regexp file type")
	assert.Equal(t, "source2.php", (*sources)[1].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[1].Category, "Must return regexp file type")
	assert.EqualValues(t, file.DURL, doc.Category, "Must return an URL doc category")
}

func TestParseConfigAddArgsWithADocFolder(t *testing.T) {
	createMocks()
	createSubTestDirectory("test2")

	doc, sources, err := parseConfigAddArgs([]string{"test2", "source1.php,source2.php"})

	assert.Equal(t, "test2", doc.Identifier, "Must return a doc folder")
	assert.NoError(t, err, "Must return no error")
	assert.Equal(t, "source1.php", (*sources)[0].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[0].Category, "Must return regexp file type")
	assert.Equal(t, "source2.php", (*sources)[1].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[1].Category, "Must return regexp file type")
	assert.EqualValues(t, file.DFOLDER, doc.Category, "Must return a folder doc category")
}

func TestParseConfigAddArgsWithAFileSourceRegexp(t *testing.T) {
	createMocks()
	createSubTestDirectory("test2")

	doc, sources, err := parseConfigAddArgs([]string{"doc_file_to_track.txt", "test2"})

	assert.Equal(t, "doc_file_to_track.txt", doc.Identifier, "Must return a doc file")
	assert.NoError(t, err, "Must return no error")
	assert.Equal(t, "test2", (*sources)[0].Identifier, "Must return sources path")
	assert.EqualValues(t, file.SFILEREG, (*sources)[0].Category, "Must return sources regexp type")
	assert.EqualValues(t, file.DFILE, doc.Category, "Must return a file doc category")
}
<commit_msg>test(cmd/config_add_test) : add dry-run tests<commit_after>package cmd

import (
	"bytes"
	"os"
	"testing"

	"github.com/Sirupsen/logrus"
	"github.com/stretchr/testify/assert"

	"github.com/antham/doc-hunt/file"

	"github.com/antham/doc-hunt/ui"
	"github.com/antham/doc-hunt/util"
)

func TestAddConfigWithMissingFileDoc(t *testing.T) {
	ui.Error = func(err error) {
		assert.EqualError(t, err, "Missing doc identifier", "Must return a missing file doc error")
	}

	util.ErrorExit = func() {
		t.SkipNow()
	}

	os.Args = []string{"", "config", "add"}

	err := RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfigWithMissingFileSources(t *testing.T) {
	ui.Error = func(err error) {
		assert.EqualError(t, err, "Missing source identifiers", "Must return a missing source identifier error")
	}

	util.ErrorExit = func() {
		t.SkipNow()
	}

	os.Args = []string{"", "config", "add", "test"}

	err := RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfigWithMoreThanTwoArguments(t *testing.T) {
	ui.Error = func(err error) {
		assert.EqualError(t, err, "No more than 2 arguments expected", "Must return an overflow argument error")
	}

	util.ErrorExit = func() {
		t.SkipNow()
	}

	os.Args = []string{"", "config", "add", "test", "test", "test"}

	err := RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfig(t *testing.T) {
	createMocks()
	err := file.Initialize()

	if err != nil {
		logrus.Fatal(err)
	}

	ui.Success = func(msg string) {
		assert.Equal(t, "Config added", msg, "Must display a success message")
	}

	util.SuccessExit = func() {
		t.SkipNow()
	}

	os.Args = []string{"", "config", "add", "doc_file_to_track.txt", "source1.php,source2.php"}

	err = RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfigWithDryRun(t *testing.T) {
	createMocks()
	err := file.Initialize()
	output := []byte{}
	out = bytes.NewBuffer(output)

	if err != nil {
		logrus.Fatal(err)
	}

	util.SuccessExit = func() {
		assert.Regexp(t, `doc_file_to_track.txt`, out, "Must render document")
		assert.Regexp(t, `Files matching regexp "source1.php"`, out, "Must render original regexp")
		assert.Regexp(t, `=> source1.php`, out, "Must render source")
		assert.Regexp(t, `Files matching regexp "source2.php"`, out, "Must render original regexp")
		assert.Regexp(t, `=> source2.php`, out, "Must render source")
	}

	os.Args = []string{"", "config", "add", "-n", "doc_file_to_track.txt", "source1.php,source2.php"}

	err = RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestAddConfigWithDryRunAndMissingSource(t *testing.T) {
	createMocks()
	err := file.Initialize()
	output := []byte{}
	out = bytes.NewBuffer(output)

	if err != nil {
		logrus.Fatal(err)
	}

	util.SuccessExit = func() {
		assert.Regexp(t, `doc_file_to_track.txt`, out, "Must render document")
		assert.Regexp(t, `Files matching regexp "source1.php"`, out, "Must render original regexp")
		assert.Regexp(t, `=> source1.php`, out, "Must render source")
		assert.Regexp(t, `Files matching regexp "s.php"`, out, "Must render original regexp")
		assert.Regexp(t, `=> No files found`, out, "Must render source")
	}

	os.Args = []string{"", "config", "add", "-n", "doc_file_to_track.txt", "source1.php,s.php"}

	err = RootCmd.Execute()

	if err != nil {
		logrus.Fatal(err)
	}
}

func TestParseConfigAddArgsWithUnexistingFileDoc(t *testing.T) {
	createMocks()

	_, _, err := parseConfigAddArgs([]string{"whatever", "test"})

	assert.EqualError(t, err, "Doc whatever is not a valid existing file, nor a valid existing folder, nor a valid URL", "Must return an unexisting doc identifier error")
}

func TestParseConfigAddArgsWithADocFile(t *testing.T) {
	createMocks()

	doc, sources, err := parseConfigAddArgs([]string{"doc_file_to_track.txt", "source1.php,source2.php"})

	assert.NoError(t, err, "Must return no error")
	assert.Regexp(t, "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", doc.ID, "Must return an id")
	assert.Equal(t, "doc_file_to_track.txt", doc.Identifier, "Must return doc file path")
	assert.EqualValues(t, file.DFILE, doc.Category, "Must return a file doc category")
	assert.Equal(t, "source1.php", (*sources)[0].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[0].Category, "Must return regexp file type")
	assert.Equal(t, "source2.php", (*sources)[1].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[1].Category, "Must return regexp file type")
}

func TestParseConfigAddArgsWithADocURL(t *testing.T) {
	createMocks()

	doc, sources, err := parseConfigAddArgs([]string{"http://google.com", "source1.php,source2.php"})

	assert.Equal(t, "http://google.com", doc.Identifier, "Must return a doc url")
	assert.NoError(t, err, "Must return no error")
	assert.Equal(t, "source1.php", (*sources)[0].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[0].Category, "Must return regexp file type")
	assert.Equal(t, "source2.php", (*sources)[1].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[1].Category, "Must return regexp file type")
	assert.EqualValues(t, file.DURL, doc.Category, "Must return an URL doc category")
}

func TestParseConfigAddArgsWithADocFolder(t *testing.T) {
	createMocks()
	createSubTestDirectory("test2")

	doc, sources, err := parseConfigAddArgs([]string{"test2", "source1.php,source2.php"})

	assert.Equal(t, "test2", doc.Identifier, "Must return a doc folder")
	assert.NoError(t, err, "Must return no error")
	assert.Equal(t, "source1.php", (*sources)[0].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[0].Category, "Must return regexp file type")
	assert.Equal(t, "source2.php", (*sources)[1].Identifier, "Must return source file regexp")
	assert.EqualValues(t, file.SFILEREG, (*sources)[1].Category, "Must return regexp file type")
	assert.EqualValues(t, file.DFOLDER, doc.Category, "Must return a folder doc category")
}

func TestParseConfigAddArgsWithAFileSourceRegexp(t *testing.T) {
	createMocks()
	createSubTestDirectory("test2")

	doc, sources, err := parseConfigAddArgs([]string{"doc_file_to_track.txt", "test2"})

	assert.Equal(t, "doc_file_to_track.txt", doc.Identifier, "Must return a doc file")
	assert.NoError(t, err, "Must return no error")
	assert.Equal(t, "test2", (*sources)[0].Identifier, "Must return sources path")
	assert.EqualValues(t, file.SFILEREG, (*sources)[0].Category, "Must return sources regexp type")
	assert.EqualValues(t, file.DFILE, doc.Category, "Must return a file doc category")
}
<|endoftext|>


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

// Author: jsing@google.com (Joel Sing)

package ncc

// This file contains ARP related functions for the Seesaw Network Control
// component.

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"net"
	"syscall"
	"unsafe"

	ncctypes "github.com/google/seesaw/ncc/types"

	log "github.com/golang/glog"
)

const (
	opARPRequest = 1
	opARPReply   = 2
)

var (
	ethernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
)

func htons(p uint16) uint16 {
	var b [2]byte
	binary.BigEndian.PutUint16(b[:], p)
	return *(*uint16)(unsafe.Pointer(&b))
}

// arpHeader specifies the header for an ARP message.
type arpHeader struct {
	hardwareType          uint16
	protocolType          uint16
	hardwareAddressLength uint8
	protocolAddressLength uint8
	opcode                uint16
}

// arpMessage represents an ARP message.
type arpMessage struct {
	arpHeader
	senderHardwareAddress []byte
	senderProtocolAddress []byte
	targetHardwareAddress []byte
	targetProtocolAddress []byte
}

// bytes returns the wire representation of the ARP message.
func (m *arpMessage) bytes() ([]byte, error) {
	buf := new(bytes.Buffer)

	if err := binary.Write(buf, binary.BigEndian, m.arpHeader); err != nil {
		return nil, fmt.Errorf("binary write failed: %v", err)
	}
	buf.Write(m.senderHardwareAddress)
	buf.Write(m.senderProtocolAddress)
	buf.Write(m.targetHardwareAddress)
	buf.Write(m.targetHardwareAddress)

	return buf.Bytes(), nil
}

// gratuitousARPReply returns an ARP message that contains a gratuitous ARP
// reply from the specified sender.
func gratuitousARPReply(ip net.IP, mac net.HardwareAddr) (*arpMessage, error) {
	if ip.To4() == nil {
		return nil, fmt.Errorf("%q is not an IPv4 address", ip)
	}
	if len(mac) != 6 {
		return nil, fmt.Errorf("%q is not an Ethernet MAC address", mac)
	}

	m := &arpMessage{
		arpHeader{
			6,           // IEEE 802
			0x0800,      // Ethernet
			6,           // 48-bit MAC Address
			net.IPv4len, // 32-bit IPv4 Address
			opARPReply,  // ARP Reply
		},
		mac,
		ip.To4(),
		ethernetBroadcast,
		net.IPv4bcast,
	}

	return m, nil
}

// sendARP sends the given ARP message via the specified interface.
func sendARP(iface *net.Interface, m *arpMessage) error {
	fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_DGRAM, int(htons(syscall.ETH_P_ARP)))
	if err != nil {
		return fmt.Errorf("failed to get raw socket: %v", err)
	}
	defer syscall.Close(fd)

	if err := syscall.BindToDevice(fd, iface.Name); err != nil {
		return fmt.Errorf("failed to bind to device: %v", err)
	}

	ll := syscall.SockaddrLinklayer{
		Protocol: htons(syscall.ETH_P_ARP),
		Ifindex:  iface.Index,
		Pkttype:  0, // syscall.PACKET_HOST
		Hatype:   m.hardwareType,
		Halen:    m.hardwareAddressLength,
	}
	target := ethernetBroadcast
	if m.opcode == opARPReply {
		target = m.targetHardwareAddress
	}
	for i := 0; i < len(target); i++ {
		ll.Addr[i] = target[i]
	}

	b, err := m.bytes()
	if err != nil {
		return fmt.Errorf("failed to convert ARP message: %v", err)
	}

	if err := syscall.Bind(fd, &ll); err != nil {
		return fmt.Errorf("failed to bind: %v", err)
	}
	if err := syscall.Sendto(fd, b, 0, &ll); err != nil {
		return fmt.Errorf("failed to send: %v", err)
	}

	return nil
}

// ARPSendGratuitous sends a gratuitous ARP message via the specified interface.
func (ncc *SeesawNCC) ARPSendGratuitous(arp *ncctypes.ARPGratuitous, out *int) error {
	iface, err := net.InterfaceByName(arp.IfaceName)
	if err != nil {
		return fmt.Errorf("failed to get interface %q: %v", arp.IfaceName, err)
	}
	log.V(2).Infof("Sending gratuitous ARP for %s (%s) via %s", arp.IP, iface.HardwareAddr, iface.Name)
	m, err := gratuitousARPReply(arp.IP, iface.HardwareAddr)
	if err != nil {
		return err
	}
	return sendARP(iface, m)
}
<commit_msg>Fix target IP address in serialized ARP message<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Author: jsing@google.com (Joel Sing)

package ncc

// This file contains ARP related functions for the Seesaw Network Control
// component.

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"net"
	"syscall"
	"unsafe"

	ncctypes "github.com/google/seesaw/ncc/types"

	log "github.com/golang/glog"
)

const (
	opARPRequest = 1
	opARPReply   = 2
)

var (
	ethernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
)

func htons(p uint16) uint16 {
	var b [2]byte
	binary.BigEndian.PutUint16(b[:], p)
	return *(*uint16)(unsafe.Pointer(&b))
}

// arpHeader specifies the header for an ARP message.
type arpHeader struct {
	hardwareType          uint16
	protocolType          uint16
	hardwareAddressLength uint8
	protocolAddressLength uint8
	opcode                uint16
}

// arpMessage represents an ARP message.
type arpMessage struct {
	arpHeader
	senderHardwareAddress []byte
	senderProtocolAddress []byte
	targetHardwareAddress []byte
	targetProtocolAddress []byte
}

// bytes returns the wire representation of the ARP message.
func (m *arpMessage) bytes() ([]byte, error) {
	buf := new(bytes.Buffer)

	if err := binary.Write(buf, binary.BigEndian, m.arpHeader); err != nil {
		return nil, fmt.Errorf("binary write failed: %v", err)
	}
	buf.Write(m.senderHardwareAddress)
	buf.Write(m.senderProtocolAddress)
	buf.Write(m.targetHardwareAddress)
	buf.Write(m.targetProtocolAddress)

	return buf.Bytes(), nil
}

// gratuitousARPReply returns an ARP message that contains a gratuitous ARP
// reply from the specified sender.
func gratuitousARPReply(ip net.IP, mac net.HardwareAddr) (*arpMessage, error) {
	if ip.To4() == nil {
		return nil, fmt.Errorf("%q is not an IPv4 address", ip)
	}
	if len(mac) != 6 {
		return nil, fmt.Errorf("%q is not an Ethernet MAC address", mac)
	}

	m := &arpMessage{
		arpHeader{
			6,           // IEEE 802
			0x0800,      // Ethernet
			6,           // 48-bit MAC Address
			net.IPv4len, // 32-bit IPv4 Address
			opARPReply,  // ARP Reply
		},
		mac,
		ip.To4(),
		ethernetBroadcast,
		net.IPv4bcast,
	}

	return m, nil
}

// sendARP sends the given ARP message via the specified interface.
func sendARP(iface *net.Interface, m *arpMessage) error {
	fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_DGRAM, int(htons(syscall.ETH_P_ARP)))
	if err != nil {
		return fmt.Errorf("failed to get raw socket: %v", err)
	}
	defer syscall.Close(fd)

	if err := syscall.BindToDevice(fd, iface.Name); err != nil {
		return fmt.Errorf("failed to bind to device: %v", err)
	}

	ll := syscall.SockaddrLinklayer{
		Protocol: htons(syscall.ETH_P_ARP),
		Ifindex:  iface.Index,
		Pkttype:  0, // syscall.PACKET_HOST
		Hatype:   m.hardwareType,
		Halen:    m.hardwareAddressLength,
	}
	target := ethernetBroadcast
	if m.opcode == opARPReply {
		target = m.targetHardwareAddress
	}
	for i := 0; i < len(target); i++ {
		ll.Addr[i] = target[i]
	}

	b, err := m.bytes()
	if err != nil {
		return fmt.Errorf("failed to convert ARP message: %v", err)
	}

	if err := syscall.Bind(fd, &ll); err != nil {
		return fmt.Errorf("failed to bind: %v", err)
	}
	if err := syscall.Sendto(fd, b, 0, &ll); err != nil {
		return fmt.Errorf("failed to send: %v", err)
	}

	return nil
}

// ARPSendGratuitous sends a gratuitous ARP message via the specified interface.
func (ncc *SeesawNCC) ARPSendGratuitous(arp *ncctypes.ARPGratuitous, out *int) error {
	iface, err := net.InterfaceByName(arp.IfaceName)
	if err != nil {
		return fmt.Errorf("failed to get interface %q: %v", arp.IfaceName, err)
	}
	log.V(2).Infof("Sending gratuitous ARP for %s (%s) via %s", arp.IP, iface.HardwareAddr, iface.Name)
	m, err := gratuitousARPReply(arp.IP, iface.HardwareAddr)
	if err != nil {
		return err
	}
	return sendARP(iface, m)
}
<|endoftext|>


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

package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/kube-etcd-controller/pkg/analytics"
	"github.com/coreos/kube-etcd-controller/pkg/chaos"
	"github.com/coreos/kube-etcd-controller/pkg/controller"
	"github.com/coreos/kube-etcd-controller/pkg/util/k8sutil"
	"github.com/coreos/kube-etcd-controller/version"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/leaderelection"
	"k8s.io/kubernetes/pkg/client/record"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/labels"
)

var (
	analyticsEnabled bool
	pvProvisioner    string
	masterHost       string
	tlsInsecure      bool
	certFile         string
	keyFile          string
	caFile           string
	namespace        string

	chaosLevel int

	printVersion bool
)

var (
	leaseDuration = 15 * time.Second
	renewDuration = 5 * time.Second
	retryPeriod   = 3 * time.Second
)

func init() {
	flag.BoolVar(&analyticsEnabled, "analytics", true, "Send analytical event (Cluster Created/Deleted etc.) to Google Analytics")

	flag.StringVar(&pvProvisioner, "pv-provisioner", "kubernetes.io/gce-pd", "persistent volume provisioner type")
	flag.StringVar(&masterHost, "master", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
	flag.StringVar(&certFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
	flag.StringVar(&keyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
	flag.StringVar(&caFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
	flag.BoolVar(&tlsInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
	// chaos level will be removed once we have a formal tool to inject failures.
	flag.IntVar(&chaosLevel, "chaos-level", -1, "DO NOT USE IN PRODUCTION - level of chaos injected into the etcd clusters created by the controller.")
	flag.BoolVar(&printVersion, "version", false, "Show version and quit")
	flag.Parse()

	namespace = os.Getenv("MY_POD_NAMESPACE")
	if len(namespace) == 0 {
		namespace = "default"
	}
}

func main() {
	if printVersion {
		fmt.Println("kube-etcd-controller", version.Version)
		os.Exit(0)
	}

	if analyticsEnabled {
		analytics.Enable()
	}

	analytics.ControllerStarted()

	id, err := os.Hostname()
	if err != nil {
		logrus.Fatalf("failed to get hostname: %v", err)
	}

	leaderelection.RunOrDie(leaderelection.LeaderElectionConfig{
		EndpointsMeta: api.ObjectMeta{
			Namespace: "default",
			Name:      "etcd-controller",
		},
		Client: k8sutil.MustCreateClient(masterHost, tlsInsecure, &restclient.TLSClientConfig{
			CertFile: certFile,
			KeyFile:  keyFile,
			CAFile:   caFile,
		}),
		EventRecorder: &record.FakeRecorder{},
		Identity:      id,
		LeaseDuration: leaseDuration,
		RenewDeadline: renewDuration,
		RetryPeriod:   retryPeriod,
		Callbacks: leaderelection.LeaderCallbacks{
			OnStartedLeading: run,
			OnStoppedLeading: func() {
				logrus.Fatalf("leader election lost")
			},
		},
	})
	panic("unreachable")
}

func run(stop <-chan struct{}) {
	for {
		ctx, cancel := context.WithCancel(context.Background())

		cfg := newControllerConfig()

		switch chaosLevel {
		case 1:
			logrus.Infof("chaos level = 1: randomly kill one etcd pod every 10 seconds")
			m := chaos.NewMonkeys(cfg.KubeCli)
			ls := labels.SelectorFromSet(map[string]string{"app": "etcd"})
			go m.CrushPods(ctx, cfg.Namespace, ls, 0.1)
		default:
		}

		c := controller.New(cfg)
		err := c.Run()
		switch err {
		case controller.ErrVersionOutdated:
		default:
			logrus.Fatalf("controller Run() ended with failure: %v", err)
		}

		cancel()
	}
}

func newControllerConfig() controller.Config {
	tlsConfig := restclient.TLSClientConfig{
		CertFile: certFile,
		KeyFile:  keyFile,
		CAFile:   caFile,
	}
	kubecli := k8sutil.MustCreateClient(masterHost, tlsInsecure, &tlsConfig)
	cfg := controller.Config{
		MasterHost:    masterHost,
		PVProvisioner: pvProvisioner,
		Namespace:     namespace,
		KubeCli:       kubecli,
	}
	if len(cfg.MasterHost) == 0 {
		logrus.Info("use in cluster client from k8s library")
		cfg.MasterHost = k8sutil.MustGetInClusterMasterHost()
	}
	return cfg
}
<commit_msg>controller: fix leader election in different ns<commit_after>// Copyright 2016 The kube-etcd-controller Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/kube-etcd-controller/pkg/analytics"
	"github.com/coreos/kube-etcd-controller/pkg/chaos"
	"github.com/coreos/kube-etcd-controller/pkg/controller"
	"github.com/coreos/kube-etcd-controller/pkg/util/k8sutil"
	"github.com/coreos/kube-etcd-controller/version"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/leaderelection"
	"k8s.io/kubernetes/pkg/client/record"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/labels"
)

var (
	analyticsEnabled bool
	pvProvisioner    string
	masterHost       string
	tlsInsecure      bool
	certFile         string
	keyFile          string
	caFile           string
	namespace        string

	chaosLevel int

	printVersion bool
)

var (
	leaseDuration = 15 * time.Second
	renewDuration = 5 * time.Second
	retryPeriod   = 3 * time.Second
)

func init() {
	flag.BoolVar(&analyticsEnabled, "analytics", true, "Send analytical event (Cluster Created/Deleted etc.) to Google Analytics")

	flag.StringVar(&pvProvisioner, "pv-provisioner", "kubernetes.io/gce-pd", "persistent volume provisioner type")
	flag.StringVar(&masterHost, "master", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
	flag.StringVar(&certFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
	flag.StringVar(&keyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
	flag.StringVar(&caFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
	flag.BoolVar(&tlsInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
	// chaos level will be removed once we have a formal tool to inject failures.
	flag.IntVar(&chaosLevel, "chaos-level", -1, "DO NOT USE IN PRODUCTION - level of chaos injected into the etcd clusters created by the controller.")
	flag.BoolVar(&printVersion, "version", false, "Show version and quit")
	flag.Parse()

	namespace = os.Getenv("MY_POD_NAMESPACE")
	if len(namespace) == 0 {
		namespace = "default"
	}
}

func main() {
	if printVersion {
		fmt.Println("kube-etcd-controller", version.Version)
		os.Exit(0)
	}

	if analyticsEnabled {
		analytics.Enable()
	}

	analytics.ControllerStarted()

	id, err := os.Hostname()
	if err != nil {
		logrus.Fatalf("failed to get hostname: %v", err)
	}

	leaderelection.RunOrDie(leaderelection.LeaderElectionConfig{
		EndpointsMeta: api.ObjectMeta{
			Namespace: namespace,
			Name:      "etcd-controller",
		},
		Client: k8sutil.MustCreateClient(masterHost, tlsInsecure, &restclient.TLSClientConfig{
			CertFile: certFile,
			KeyFile:  keyFile,
			CAFile:   caFile,
		}),
		EventRecorder: &record.FakeRecorder{},
		Identity:      id,
		LeaseDuration: leaseDuration,
		RenewDeadline: renewDuration,
		RetryPeriod:   retryPeriod,
		Callbacks: leaderelection.LeaderCallbacks{
			OnStartedLeading: run,
			OnStoppedLeading: func() {
				logrus.Fatalf("leader election lost")
			},
		},
	})
	panic("unreachable")
}

func run(stop <-chan struct{}) {
	for {
		ctx, cancel := context.WithCancel(context.Background())

		cfg := newControllerConfig()

		switch chaosLevel {
		case 1:
			logrus.Infof("chaos level = 1: randomly kill one etcd pod every 10 seconds")
			m := chaos.NewMonkeys(cfg.KubeCli)
			ls := labels.SelectorFromSet(map[string]string{"app": "etcd"})
			go m.CrushPods(ctx, cfg.Namespace, ls, 0.1)
		default:
		}

		c := controller.New(cfg)
		err := c.Run()
		switch err {
		case controller.ErrVersionOutdated:
		default:
			logrus.Fatalf("controller Run() ended with failure: %v", err)
		}

		cancel()
	}
}

func newControllerConfig() controller.Config {
	tlsConfig := restclient.TLSClientConfig{
		CertFile: certFile,
		KeyFile:  keyFile,
		CAFile:   caFile,
	}
	kubecli := k8sutil.MustCreateClient(masterHost, tlsInsecure, &tlsConfig)
	cfg := controller.Config{
		MasterHost:    masterHost,
		PVProvisioner: pvProvisioner,
		Namespace:     namespace,
		KubeCli:       kubecli,
	}
	if len(cfg.MasterHost) == 0 {
		logrus.Info("use in cluster client from k8s library")
		cfg.MasterHost = k8sutil.MustGetInClusterMasterHost()
	}
	return cfg
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/concourse/baggageclaim/fs"
	"github.com/jessevdk/go-flags"
	"github.com/pivotal-golang/lager"
)

type FSMounterCommand struct {
	DiskImage string `long:"disk-image" required:"true" description:"Location of the backing file to create for the image."`

	MountPath string `long:"mount-path" required:"true" description:"Directory where the filesystem should be mounted."`

	SizeInMegabytes uint64 `long:"size-in-megabytes" default:"0" description:"Maximum size of the filesystem. Can exceed the size of the backing device."`

	Remove bool `long:"remove" default:"false" description:"Remove the filesystem instead of creating it."`

	MkfsBin string `long:"mkfs-bin" default:"mkfs.btrfs" description:"Path to mkfs.btrfs binary"`
}

func main() {
	cmd := &FSMounterCommand{}

	parser := flags.NewParser(cmd, flags.Default)
	parser.NamespaceDelimiter = "-"

	_, err := parser.Parse()
	if err != nil {
		os.Exit(1)
	}

	logger := lager.NewLogger("baggageclaim")
	sink := lager.NewWriterSink(os.Stdout, lager.DEBUG)
	logger.RegisterSink(sink)

	filesystem := fs.New(logger, cmd.DiskImage, cmd.MountPath, cmd.MkfsBin)

	if !cmd.Remove {
		if cmd.SizeInMegabytes == 0 {
			fmt.Fprintln(os.Stderr, "--size-in-megabytes or --remove must be specified")
			os.Exit(1)
		}

		err := filesystem.Create(cmd.SizeInMegabytes * 1024 * 1024)
		if err != nil {
			log.Fatalln("failed to create filesystem: ", err)
		}
	} else {
		err := filesystem.Delete()
		if err != nil {
			log.Fatalln("failed to delete filesystem: ", err)
		}
	}
}
<commit_msg>nix redundant default<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/concourse/baggageclaim/fs"
	"github.com/jessevdk/go-flags"
	"github.com/pivotal-golang/lager"
)

type FSMounterCommand struct {
	DiskImage string `long:"disk-image" required:"true" description:"Location of the backing file to create for the image."`

	MountPath string `long:"mount-path" required:"true" description:"Directory where the filesystem should be mounted."`

	SizeInMegabytes uint64 `long:"size-in-megabytes" default:"0" description:"Maximum size of the filesystem. Can exceed the size of the backing device."`

	Remove bool `long:"remove" description:"Remove the filesystem instead of creating it."`

	MkfsBin string `long:"mkfs-bin" default:"mkfs.btrfs" description:"Path to mkfs.btrfs binary"`
}

func main() {
	cmd := &FSMounterCommand{}

	parser := flags.NewParser(cmd, flags.Default)
	parser.NamespaceDelimiter = "-"

	_, err := parser.Parse()
	if err != nil {
		os.Exit(1)
	}

	logger := lager.NewLogger("baggageclaim")
	sink := lager.NewWriterSink(os.Stdout, lager.DEBUG)
	logger.RegisterSink(sink)

	filesystem := fs.New(logger, cmd.DiskImage, cmd.MountPath, cmd.MkfsBin)

	if !cmd.Remove {
		if cmd.SizeInMegabytes == 0 {
			fmt.Fprintln(os.Stderr, "--size-in-megabytes or --remove must be specified")
			os.Exit(1)
		}

		err := filesystem.Create(cmd.SizeInMegabytes * 1024 * 1024)
		if err != nil {
			log.Fatalln("failed to create filesystem: ", err)
		}
	} else {
		err := filesystem.Delete()
		if err != nil {
			log.Fatalln("failed to delete filesystem: ", err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"flag"
	"strings"
	"time"

	"github.com/peted27/gherkin/pkg/gherkin"
	"github.com/peted27/gherkin/pkg/plugins/autoban"
	"github.com/peted27/gherkin/pkg/plugins/sed"
	"github.com/peted27/gherkin/pkg/plugins/seen"
	"github.com/peted27/gherkin/pkg/plugins/slap"
	"github.com/peted27/gherkin/pkg/plugins/urltitle"
	irc "github.com/peted27/go-ircevent"
)

var (
	host        = flag.String("host", "irc.example.com", "Server host[:port]")
	ssl         = flag.Bool("ssl", true, "Enable SSL")
	nick        = flag.String("nick", "goircbot", "Bot nick")
	ident       = flag.String("ident", "goircbot", "Bot ident")
	channels    = flag.String("channels", "", "Channels to join (separated by comma)")
	debug       = flag.Bool("debug", false, "Enable debugging output")
	helpStrings = map[string]string{}
)

func main() {
	flag.Parse()

	bot := irc.IRC(*nick, *ident)

	//bot.VerboseCallbackHandler = *debug
	bot.Debug = *debug

	// using ssl? configure here
	if *ssl {
		bot.UseTLS = *ssl
		bot.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	}

	// connect the bot
	if err := bot.Connect(*host); err != nil {
		bot.Log.Printf("Error: %s\n", err)
	}

	// setup callbacks to join managed channels
	for _, ch := range strings.Split(*channels, ",") {

		bot.AddCallback("001",
			func(e *irc.Event) {
				bot.Join(ch)
				bot.Log.Printf("bot: joining channel %s\n", ch)
			})

	}

	// pong! plugin
	helpStrings["!ping"] = "auto reply with !pong"
	bot.AddCallback("PRIVMSG",
		func(e *irc.Event) {
			if !gherkin.IsCommandMessage(e) {
				return
			}

			if strings.HasPrefix(e.Arguments[1], "!ping") {
				e.Connection.Privmsg(e.Arguments[0], "pong!")
			}
		})

	// !uptime plugin
	timeInitialised := time.Now()
	helpStrings["!uptime"] = "display time since bot was launched"
	bot.AddCallback("PRIVMSG",
		func(e *irc.Event) {
			if !gherkin.IsCommandMessage(e) {
				return
			}

			if strings.HasPrefix(e.Arguments[1], "!uptime") {
				e.Connection.Action(e.Arguments[0], "running since "+timeInitialised.Format("15:04:05 (2006-01-02) MST"))
			}
		})

	// !help
	helpStrings["!help"] = "print this message"
	bot.AddCallback("PRIVMSG",
		func(e *irc.Event) {
			if !gherkin.IsCommandMessage(e) {
				return
			}

			if strings.HasPrefix(e.Arguments[1], "!help") {
				for h, c := range helpStrings {
					e.Connection.Privmsg(e.Nick, gherkin.MakeHelpString(h, c))
				}
			}
		})

	// plugin registration
	slap.Register(bot, helpStrings)
	urltitle.Register(bot, helpStrings)
	sed.Register(bot, helpStrings)
	seen.Register(bot, helpStrings)
	autoban.Register(bot, helpStrings)

	bot.Loop()

}
<commit_msg>add version<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"strings"
	"time"

	"github.com/peted27/gherkin/pkg/gherkin"
	"github.com/peted27/gherkin/pkg/plugins/autoban"
	"github.com/peted27/gherkin/pkg/plugins/sed"
	"github.com/peted27/gherkin/pkg/plugins/seen"
	"github.com/peted27/gherkin/pkg/plugins/slap"
	"github.com/peted27/gherkin/pkg/plugins/urltitle"
	irc "github.com/peted27/go-ircevent"
)

var (
	host        = flag.String("host", "irc.example.com", "Server host[:port]")
	ssl         = flag.Bool("ssl", true, "Enable SSL")
	nick        = flag.String("nick", "goircbot", "Bot nick")
	ident       = flag.String("ident", "goircbot", "Bot ident")
	channels    = flag.String("channels", "", "Channels to join (separated by comma)")
	debug       = flag.Bool("debug", false, "Enable debugging output")
	helpStrings = map[string]string{}
	version     = "0.9.0"
)

func main() {
	flag.Parse()

	bot := irc.IRC(*nick, *ident)

	//bot.VerboseCallbackHandler = *debug
	bot.Debug = *debug

	// using ssl? configure here
	if *ssl {
		bot.UseTLS = *ssl
		bot.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	}

	// connect the bot
	if err := bot.Connect(*host); err != nil {
		bot.Log.Printf("Error: %s\n", err)
	}

	// setup callbacks to join managed channels
	for _, ch := range strings.Split(*channels, ",") {

		bot.AddCallback("001",
			func(e *irc.Event) {
				bot.Join(ch)
				bot.Log.Printf("bot: joining channel %s\n", ch)
			})

	}

	// pong! plugin
	helpStrings["!ping"] = "auto reply with !pong"
	bot.AddCallback("PRIVMSG",
		func(e *irc.Event) {
			if !gherkin.IsCommandMessage(e) {
				return
			}

			if strings.HasPrefix(e.Arguments[1], "!ping") {
				e.Connection.Privmsg(e.Arguments[0], "pong!")
			}
		})

	// !uptime plugin
	timeInitialised := time.Now()
	helpStrings["!uptime"] = "display time since bot was launched"
	bot.AddCallback("PRIVMSG",
		func(e *irc.Event) {
			if !gherkin.IsCommandMessage(e) {
				return
			}

			if strings.HasPrefix(e.Arguments[1], "!uptime") {
				e.Connection.Action(e.Arguments[0], "running since "+timeInitialised.Format("15:04:05 (2006-01-02) MST"))
			}
		})

	// !version plugin
	helpStrings["!version"] = "display bot version"
	bot.AddCallback("PRIVMSG",
		func(e *irc.Event) {
			if !gherkin.IsCommandMessage(e) {
				return
			}

			if strings.HasPrefix(e.Arguments[1], "!version") {
				e.Connection.Action(e.Arguments[0], "running version "+version)
			}
		})

	// !help
	helpStrings["!help"] = "print this message"
	bot.AddCallback("PRIVMSG",
		func(e *irc.Event) {
			if !gherkin.IsCommandMessage(e) {
				return
			}

			if strings.HasPrefix(e.Arguments[1], "!help") {
				for h, c := range helpStrings {
					e.Connection.Privmsg(e.Nick, gherkin.MakeHelpString(h, c))
				}
			}
		})

	// plugin registration
	slap.Register(bot, helpStrings)
	urltitle.Register(bot, helpStrings)
	sed.Register(bot, helpStrings)
	seen.Register(bot, helpStrings)
	autoban.Register(bot, helpStrings)

	bot.Loop()

}
<|endoftext|>


<commit_before>package integration

import (
	"io/ioutil"
	"os"
	"testing"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
	"k8s.io/client-go/kubernetes"

	configapi "github.com/openshift/origin/pkg/cmd/server/apis/config"
	testutil "github.com/openshift/origin/test/util"
	testserver "github.com/openshift/origin/test/util/server"
)

func setupAudit(t *testing.T, auditConfig configapi.AuditConfig) (kubernetes.Interface, func()) {
	masterConfig, err := testserver.DefaultMasterOptions()
	if err != nil {
		t.Fatalf("error creating config: %v", err)
	}
	masterConfig.AuditConfig = auditConfig
	kubeConfigFile, err := testserver.StartConfiguredMasterAPI(masterConfig)
	if err != nil {
		t.Fatalf("error starting server: %v", err)
	}
	kubeClient, err := testutil.GetClusterAdminKubeClient(kubeConfigFile)
	if err != nil {
		t.Fatalf("error getting client: %v", err)
	}
	return kubeClient, func() {
		testserver.CleanupMasterEtcd(t, masterConfig)
	}
}

func TestBasicFunctionalityWithAudit(t *testing.T) {
	kubeClient, fn := setupAudit(t, configapi.AuditConfig{Enabled: true})
	defer fn()

	if _, err := kubeClient.CoreV1().Pods(metav1.NamespaceDefault).Watch(metav1.ListOptions{}); err != nil {
		t.Errorf("Unexpected error watching pods: %v", err)
	}

	// TODO: test oc debug, exec, rsh, port-forward
}

func TestAuditConfigEmbeded(t *testing.T) {
	auditConfig := configapi.AuditConfig{
		Enabled: true,
		PolicyConfiguration: &auditv1beta1.Policy{
			Rules: []auditv1beta1.PolicyRule{
				{Level: auditv1beta1.LevelMetadata},
			},
		},
	}
	kubeClient, fn := setupAudit(t, auditConfig)
	defer fn()

	if _, err := kubeClient.CoreV1().Pods(metav1.NamespaceDefault).Watch(metav1.ListOptions{}); err != nil {
		t.Errorf("Unexpected error watching pods: %v", err)
	}
}

func TestAuditConfigV1Alpha1File(t *testing.T) {
	testAuditConfigFile(t, []byte(`
apiVersion: audit.k8s.io/v1alpha1
kind: Policy
rules:
- level: Metadata
`))
}

func TestAuditConfigV1Beta1File(t *testing.T) {
	testAuditConfigFile(t, []byte(`
apiVersion: audit.k8s.io/v1beta1
kind: Policy
rules:
- level: Metadata
`))
}

func testAuditConfigFile(t *testing.T, policy []byte) {
	tmp, err := ioutil.TempFile("", "audit-policy")
	if err != nil {
		t.Fatalf("Cannot create a temporary file: %v", err)
	}
	defer os.Remove(tmp.Name())
	if _, err := tmp.Write(policy); err != nil {
		t.Fatalf("Cannot write to a temporary file: %v", err)
	}
	if err := tmp.Close(); err != nil {
		t.Fatalf("Cannot close a temporary file: %v", err)
	}
	auditConfig := configapi.AuditConfig{
		Enabled:    true,
		PolicyFile: tmp.Name(),
	}
	kubeClient, fn := setupAudit(t, auditConfig)
	defer fn()

	if _, err := kubeClient.CoreV1().Pods(metav1.NamespaceDefault).Watch(metav1.ListOptions{}); err != nil {
		t.Errorf("Unexpected error watching pods: %v", err)
	}
}
<commit_msg>remove tests for non-standard, non-shipped audit configuration<commit_after><|endoftext|>


<commit_before>package negroni

import (
	"log"
	"net/http"
	"os"
)

const (
	// DefaultAddress is used if no other is specified.
	DefaultAddress = ":8080"
)

// Handler handler is an interface that objects can implement to be registered to serve as middleware
// in the Negroni middleware stack.
// ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc
// passed in.
//
// If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked.
type Handler interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers.
// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}

type middleware struct {
	handler Handler
	next    *middleware
}

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}

// Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni
// middleware. The next http.HandlerFunc is automatically called after the Handler
// is executed.
func Wrap(handler http.Handler) Handler {
	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handler.ServeHTTP(rw, r)
		next(rw, r)
	})
}

// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler.
// Negroni middleware is evaluated in the order that they are added to the stack using
// the Use and UseHandler methods.
type Negroni struct {
	middleware middleware
	handlers   []Handler
}

// New returns a new Negroni instance with no middleware preconfigured.
func New(handlers ...Handler) *Negroni {
	return &Negroni{
		handlers:   handlers,
		middleware: build(handlers),
	}
}

// With returns a new Negroni instance that is a combination of the negroni
// receiver's handlers and the provided handlers.
func (n *Negroni) With(handlers ...Handler) *Negroni {
	return New(
		append(n.handlers, handlers...)...,
	)
}

// Classic returns a new Negroni instance with the default middleware already
// in the stack.
//
// Recovery - Panic Recovery Middleware
// Logger - Request/Response Logging
// Static - Static File Serving
func Classic() *Negroni {
	return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}

// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
func (n *Negroni) Use(handler Handler) {
	if handler == nil {
		panic("handler cannot be nil")
	}

	n.handlers = append(n.handlers, handler)
	n.middleware = build(n.handlers)
}

// UseFunc adds a Negroni-style handler function onto the middleware stack.
func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
	n.Use(HandlerFunc(handlerFunc))
}

// UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
func (n *Negroni) UseHandler(handler http.Handler) {
	n.Use(Wrap(handler))
}

// UseHandlerFunc adds a http.HandlerFunc-style handler function onto the middleware stack.
func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
	n.UseHandler(http.HandlerFunc(handlerFunc))
}

// Run is a convenience function that runs the negroni stack as an HTTP
// server. The addr string, if provided, takes the same format as http.ListenAndServe.
// If no address is provided but the PORT environment variable is set, the PORT value is used.
// If neither is provided, the address' value will equal the DefaultAddress constant.
func (n *Negroni) Run(addr ...string) {
	l := log.New(os.Stdout, "[negroni] ", 0)
	finalAddr := detectAddress(addr...)
	l.Printf("listening on %s", finalAddr)
	l.Fatal(http.ListenAndServe(finalAddr, n))
}

func detectAddress(addr ...string) string {
	if len(addr) > 0 {
		return addr[0]
	}
	if port := os.Getenv("PORT"); port != "" {
		return ":" + port
	}
	return DefaultAddress
}

// Returns a list of all the handlers in the current Negroni middleware chain.
func (n *Negroni) Handlers() []Handler {
	return n.handlers
}

func build(handlers []Handler) middleware {
	var next middleware

	if len(handlers) == 0 {
		return voidMiddleware()
	} else if len(handlers) > 1 {
		next = build(handlers[1:])
	} else {
		next = voidMiddleware()
	}

	return middleware{handlers[0], &next}
}

func voidMiddleware() middleware {
	return middleware{
		HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
		&middleware{},
	}
}
<commit_msg>Add function WrapFunc, used to converts a http.HandlerFunc into a negroni.Handler.<commit_after>package negroni

import (
	"log"
	"net/http"
	"os"
)

const (
	// DefaultAddress is used if no other is specified.
	DefaultAddress = ":8080"
)

// Handler handler is an interface that objects can implement to be registered to serve as middleware
// in the Negroni middleware stack.
// ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc
// passed in.
//
// If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked.
type Handler interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers.
// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}

type middleware struct {
	handler Handler
	next    *middleware
}

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}

// Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni
// middleware. The next http.HandlerFunc is automatically called after the Handler
// is executed.
func Wrap(handler http.Handler) Handler {
	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handler.ServeHTTP(rw, r)
		next(rw, r)
	})
}

// WrapFunc converts a http.HandlerFunc into a negroni.Handler so it can be used as a Negroni
// middleware. The next http.HandlerFunc is automatically called after the Handler
// is executed.
func WrapFunc(handlerFunc http.HandlerFunc) Handler {
	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handlerFunc(rw, r)
		next(rw, r)
	})
}

// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler.
// Negroni middleware is evaluated in the order that they are added to the stack using
// the Use and UseHandler methods.
type Negroni struct {
	middleware middleware
	handlers   []Handler
}

// New returns a new Negroni instance with no middleware preconfigured.
func New(handlers ...Handler) *Negroni {
	return &Negroni{
		handlers:   handlers,
		middleware: build(handlers),
	}
}

// With returns a new Negroni instance that is a combination of the negroni
// receiver's handlers and the provided handlers.
func (n *Negroni) With(handlers ...Handler) *Negroni {
	return New(
		append(n.handlers, handlers...)...,
	)
}

// Classic returns a new Negroni instance with the default middleware already
// in the stack.
//
// Recovery - Panic Recovery Middleware
// Logger - Request/Response Logging
// Static - Static File Serving
func Classic() *Negroni {
	return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}

// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
func (n *Negroni) Use(handler Handler) {
	if handler == nil {
		panic("handler cannot be nil")
	}

	n.handlers = append(n.handlers, handler)
	n.middleware = build(n.handlers)
}

// UseFunc adds a Negroni-style handler function onto the middleware stack.
func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
	n.Use(HandlerFunc(handlerFunc))
}

// UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
func (n *Negroni) UseHandler(handler http.Handler) {
	n.Use(Wrap(handler))
}

// UseHandlerFunc adds a http.HandlerFunc-style handler function onto the middleware stack.
func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
	n.UseHandler(http.HandlerFunc(handlerFunc))
}

// Run is a convenience function that runs the negroni stack as an HTTP
// server. The addr string, if provided, takes the same format as http.ListenAndServe.
// If no address is provided but the PORT environment variable is set, the PORT value is used.
// If neither is provided, the address' value will equal the DefaultAddress constant.
func (n *Negroni) Run(addr ...string) {
	l := log.New(os.Stdout, "[negroni] ", 0)
	finalAddr := detectAddress(addr...)
	l.Printf("listening on %s", finalAddr)
	l.Fatal(http.ListenAndServe(finalAddr, n))
}

func detectAddress(addr ...string) string {
	if len(addr) > 0 {
		return addr[0]
	}
	if port := os.Getenv("PORT"); port != "" {
		return ":" + port
	}
	return DefaultAddress
}

// Returns a list of all the handlers in the current Negroni middleware chain.
func (n *Negroni) Handlers() []Handler {
	return n.handlers
}

func build(handlers []Handler) middleware {
	var next middleware

	if len(handlers) == 0 {
		return voidMiddleware()
	} else if len(handlers) > 1 {
		next = build(handlers[1:])
	} else {
		next = voidMiddleware()
	}

	return middleware{handlers[0], &next}
}

func voidMiddleware() middleware {
	return middleware{
		HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
		&middleware{},
	}
}
<|endoftext|>