<commit_before>/*
Copyright 2014 The Kubernetes 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 cert

import (
	"bytes"
	"crypto"
	cryptorand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"net"
	"path"
	"strings"
	"time"

	"k8s.io/client-go/util/keyutil"
)

const duration365d = time.Hour * 24 * 365

// Config contains the basic fields required for creating a certificate
type Config struct {
	CommonName   string
	Organization []string
	AltNames     AltNames
	Usages       []x509.ExtKeyUsage
}

// AltNames contains the domain names and IP addresses that will be added
// to the API Server's x509 certificate SubAltNames field. The values will
// be passed directly to the x509.Certificate object.
type AltNames struct {
	DNSNames []string
	IPs      []net.IP
}

// NewSelfSignedCACert creates a CA certificate
func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, error) {
	now := time.Now()
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		NotBefore:             now.UTC(),
		NotAfter:              now.Add(duration365d * 10).UTC(),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name
// You may also specify additional subject alt names (either ip or dns names) for the certificate.
func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
	return GenerateSelfSignedCertKeyWithFixtures(host, alternateIPs, alternateDNS, "")
}

// GenerateSelfSignedCertKeyWithFixtures creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name. You may also specify additional subject alt names (either ip or dns names)
// for the certificate.
//
// If fixtureDirectory is non-empty, it is a directory path which can contain pre-generated certs. The format is:
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
// Certs/keys not existing in that directory are created.
func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, alternateDNS []string, fixtureDirectory string) ([]byte, []byte, error) {
	validFrom := time.Now().Add(-time.Hour) // valid an hour earlier to avoid flakes due to clock skew
	maxAge := time.Hour * 24 * 365          // one year self-signed certs

	baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
	certFixturePath := path.Join(fixtureDirectory, baseName+".crt")
	keyFixturePath := path.Join(fixtureDirectory, baseName+".key")
	if len(fixtureDirectory) > 0 {
		cert, err := ioutil.ReadFile(certFixturePath)
		if err == nil {
			key, err := ioutil.ReadFile(keyFixturePath)
			if err == nil {
				return cert, key, nil
			}
			return nil, nil, fmt.Errorf("cert %s can be read, but key %s cannot: %v", certFixturePath, keyFixturePath, err)
		}
		maxAge = 100 * time.Hour * 24 * 365 // 100 years fixtures
	}

	caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	caTemplate := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
		},
		NotBefore: validFrom,
		NotAfter:  validFrom.Add(maxAge),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	caCertificate, err := x509.ParseCertificate(caDERBytes)
	if err != nil {
		return nil, nil, err
	}

	priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(2),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
		},
		NotBefore: validFrom,
		NotAfter:  validFrom.Add(maxAge),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	template.IPAddresses = append(template.IPAddresses, alternateIPs...)
	template.DNSNames = append(template.DNSNames, alternateDNS...)

	derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	// Generate cert, followed by ca
	certBuffer := bytes.Buffer{}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
		return nil, nil, err
	}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
		return nil, nil, err
	}

	// Generate key
	keyBuffer := bytes.Buffer{}
	if err := pem.Encode(&keyBuffer, &pem.Block{Type: keyutil.RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
		return nil, nil, err
	}

	if len(fixtureDirectory) > 0 {
		if err := ioutil.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
			return nil, nil, fmt.Errorf("failed to write cert fixture to %s: %v", certFixturePath, err)
		}
		if err := ioutil.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0644); err != nil {
			return nil, nil, fmt.Errorf("failed to write key fixture to %s: %v", certFixturePath, err)
		}
	}

	return certBuffer.Bytes(), keyBuffer.Bytes(), nil
}

func ipsToStrings(ips []net.IP) []string {
	ss := make([]string, 0, len(ips))
	for _, ip := range ips {
		ss = append(ss, ip.String())
	}
	return ss
}
<commit_msg>fix cert path generation in windows<commit_after>/*
Copyright 2014 The Kubernetes 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 cert

import (
	"bytes"
	"crypto"
	cryptorand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"net"
	"path/filepath"
	"strings"
	"time"

	"k8s.io/client-go/util/keyutil"
)

const duration365d = time.Hour * 24 * 365

// Config contains the basic fields required for creating a certificate
type Config struct {
	CommonName   string
	Organization []string
	AltNames     AltNames
	Usages       []x509.ExtKeyUsage
}

// AltNames contains the domain names and IP addresses that will be added
// to the API Server's x509 certificate SubAltNames field. The values will
// be passed directly to the x509.Certificate object.
type AltNames struct {
	DNSNames []string
	IPs      []net.IP
}

// NewSelfSignedCACert creates a CA certificate
func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, error) {
	now := time.Now()
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		NotBefore:             now.UTC(),
		NotAfter:              now.Add(duration365d * 10).UTC(),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name
// You may also specify additional subject alt names (either ip or dns names) for the certificate.
func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
	return GenerateSelfSignedCertKeyWithFixtures(host, alternateIPs, alternateDNS, "")
}

// GenerateSelfSignedCertKeyWithFixtures creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name. You may also specify additional subject alt names (either ip or dns names)
// for the certificate.
//
// If fixtureDirectory is non-empty, it is a directory path which can contain pre-generated certs. The format is:
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
// Certs/keys not existing in that directory are created.
func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, alternateDNS []string, fixtureDirectory string) ([]byte, []byte, error) {
	validFrom := time.Now().Add(-time.Hour) // valid an hour earlier to avoid flakes due to clock skew
	maxAge := time.Hour * 24 * 365          // one year self-signed certs

	baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
	certFixturePath := filepath.Join(fixtureDirectory, baseName+".crt")
	keyFixturePath := filepath.Join(fixtureDirectory, baseName+".key")
	if len(fixtureDirectory) > 0 {
		cert, err := ioutil.ReadFile(certFixturePath)
		if err == nil {
			key, err := ioutil.ReadFile(keyFixturePath)
			if err == nil {
				return cert, key, nil
			}
			return nil, nil, fmt.Errorf("cert %s can be read, but key %s cannot: %v", certFixturePath, keyFixturePath, err)
		}
		maxAge = 100 * time.Hour * 24 * 365 // 100 years fixtures
	}

	caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	caTemplate := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
		},
		NotBefore: validFrom,
		NotAfter:  validFrom.Add(maxAge),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	caCertificate, err := x509.ParseCertificate(caDERBytes)
	if err != nil {
		return nil, nil, err
	}

	priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(2),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
		},
		NotBefore: validFrom,
		NotAfter:  validFrom.Add(maxAge),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	template.IPAddresses = append(template.IPAddresses, alternateIPs...)
	template.DNSNames = append(template.DNSNames, alternateDNS...)

	derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	// Generate cert, followed by ca
	certBuffer := bytes.Buffer{}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
		return nil, nil, err
	}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
		return nil, nil, err
	}

	// Generate key
	keyBuffer := bytes.Buffer{}
	if err := pem.Encode(&keyBuffer, &pem.Block{Type: keyutil.RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
		return nil, nil, err
	}

	if len(fixtureDirectory) > 0 {
		if err := ioutil.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
			return nil, nil, fmt.Errorf("failed to write cert fixture to %s: %v", certFixturePath, err)
		}
		if err := ioutil.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0644); err != nil {
			return nil, nil, fmt.Errorf("failed to write key fixture to %s: %v", certFixturePath, err)
		}
	}

	return certBuffer.Bytes(), keyBuffer.Bytes(), nil
}

func ipsToStrings(ips []net.IP) []string {
	ss := make([]string, 0, len(ips))
	for _, ip := range ips {
		ss = append(ss, ip.String())
	}
	return ss
}
<|endoftext|>


<commit_before>//
// Here we implement extra flags to extend pflag/viper binding.
//
// https://godoc.org/github.com/spf13/pflag#Value
//
// type Value interface {
//     String() string
//     Set(string) error
//     Type() string
// }
//

package venom

import (
	"time"
)

var (
	DefaultTimeFormat = time.RFC3339
)

//
// time.Time
//
type timeValue time.Time

func newTimeValue(val time.Time, p *time.Time) *timeValue {
	*p = val
	return (*timeValue)(p)
}

func (v *timeValue) Set(s string) error {
	val, err := time.Parse(DefaultTimeFormat, s)
	*v = timeValue(val)
	return err
}

func (v *timeValue) Type() string {
	return "time"
}

func (v *timeValue) String() string {
	return time.Time(*v).Format(DefaultTimeFormat)
}

//
// time.Duration
//
type durationValue time.Duration

func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
	*p = val
	return (*durationValue)(p)
}

func (v *durationValue) Set(s string) error {
	val, err := time.ParseDuration(s)
	*v = durationValue(val)
	return err
}

func (v *durationValue) Type() string {
	return "duration"
}

func (v *durationValue) String() string {
	return time.Duration(*v).String()
}

type mapStringStringValue struct {
	value   *map[string]string
	changed bool
}

func newMapStringStringValue(val map[string]string, p *map[string]string) *mapStringStringValue {
	mssv := new(mapStringStringValue)
	mssv.value = p
	*mssv.value = val
	return mssv
}

func (v *mapStringStringValue) Set(s string) error {
	val, err := parseMapStringString(s, ",", "=")
	if err != nil {
		return err
	}

	if !v.changed {
		*v.value = val
	} else {
		for k := range val {
			(*v.value)[k] = val[k]
		}
	}
	v.changed = true
	return nil
}

func (v *mapStringStringValue) Type() string {
	return "stringMap"
}

func (v *mapStringStringValue) String() string {
	str, _ := serializeMapStringString(*v.value, ",", "=")
	return "{" + str + "}"
}
<commit_msg>Panic<commit_after>//
// Here we implement extra flags to extend pflag/viper binding.
//
// https://godoc.org/github.com/spf13/pflag#Value
//
// type Value interface {
//     String() string
//     Set(string) error
//     Type() string
// }
//

package venom

import (
	"time"
)

var (
	DefaultTimeFormat = time.RFC3339
)

//
// time.Time
//
type timeValue time.Time

func newTimeValue(val time.Time, p *time.Time) *timeValue {
	*p = val
	return (*timeValue)(p)
}

func (v *timeValue) Set(s string) error {
	val, err := time.Parse(DefaultTimeFormat, s)
	*v = timeValue(val)
	return err
}

func (v *timeValue) Type() string {
	return "time"
}

func (v *timeValue) String() string {
	return time.Time(*v).Format(DefaultTimeFormat)
}

//
// time.Duration
//
type durationValue time.Duration

func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
	*p = val
	return (*durationValue)(p)
}

func (v *durationValue) Set(s string) error {
	val, err := time.ParseDuration(s)
	*v = durationValue(val)
	return err
}

func (v *durationValue) Type() string {
	return "duration"
}

func (v *durationValue) String() string {
	return time.Duration(*v).String()
}

type mapStringStringValue struct {
	value   *map[string]string
	changed bool
}

func newMapStringStringValue(val map[string]string, p *map[string]string) *mapStringStringValue {
	mssv := new(mapStringStringValue)
	mssv.value = p
	*mssv.value = val
	return mssv
}

func (v *mapStringStringValue) Set(s string) error {
	val, err := parseMapStringString(s, ",", "=")
	if err != nil {
		return err
	}

	if !v.changed {
		*v.value = val
	} else {
		for k := range val {
			(*v.value)[k] = val[k]
		}
	}
	v.changed = true
	return nil
}

func (v *mapStringStringValue) Type() string {
	return "stringMap"
}

func (v *mapStringStringValue) String() string {
	str, err := serializeMapStringString(*v.value, ",", "=")
	if err != nil {
		panic(err)
	}
	return "{" + str + "}"
}
<|endoftext|>


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

package main

import (
	"fmt"
	"log"
	"os/user"

	"github.com/codegangsta/cli"
)

// A pointer to the current context.
var currentContext *cli.Context

// Returns the version string
func version() string {
	const (
		major = 1
		minor = 2
		patch = 0
	)
	return fmt.Sprintf("%v.%v.%v", major, minor, patch)
}

func defaultCommitAuthor() string {
	current, err := user.Current()
	if err != nil {
		log.Printf("Cannot determine current user: %s", err)
		return ""
	}

	if current.Name != "" {
		return current.Name
	}
	return current.Username
}

// It returns an application with all the flags and subcommands already in
// place.
func newApp() *cli.App {
	app := cli.NewApp()

	app.Name = "zypper-docker"
	app.Usage = "Patching Docker images safely"
	app.Version = version()

	app.CommandNotFound = func(context *cli.Context, cmd string) {
		fmt.Printf("Incorrect usage: command '%v' does not exist.\n\n", cmd)
		cli.ShowAppHelp(context)
	}

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "no-gpg-checks",
			Usage: "Ignore GPG check failures and continue",
		},
		cli.BoolFlag{
			Name:  "gpg-auto-import-keys",
			Usage: "If new repository signing key is found, do not ask what to do; trust and import it automatically",
		},
		cli.BoolFlag{
			Name:  "f, force",
			Usage: "Ignore all the local caches",
		},
		cli.BoolFlag{
			Name:  "d, debug",
			Usage: "Show all the logged messages on stdout",
		},
		cli.StringSliceFlag{
			Name:  "add-host",
			Usage: "Add a custom host-to-IP mapping (host:ip)",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "images",
			Usage:     "List all the images based on either OpenSUSE or SLES",
			Action:    getCmd("images", imagesCmd),
			ArgsUsage: " ",
		},
		{
			Name:      "list-updates",
			ShortName: "lu",
			Aliases:   []string{"lu"},
			Usage:     "List all the available updates",
			Action:    getCmd("list-updates", listUpdatesCmd),
			ArgsUsage: `<image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to use.
If the tag has not been provided, then "latest" is the one that will be used.`,
		},
		{
			Name:      "list-updates-container",
			ShortName: "luc",
			Aliases:   []string{"luc"},
			Usage:     "List all the available updates for the given container",
			Action:    getCmd("list-updates-container", listUpdatesContainerCmd),
			ArgsUsage: `<container-id>

Where <container-id> is either the container ID or the name of the container
to be used.`,
		},
		{
			Name:      "update",
			ShortName: "up",
			Aliases:   []string{"up"},
			Usage:     "Install the available updates",
			Action:    getCmd("update", updateCmd),
			ArgsUsage: `<image> <new-image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to
update. Since zypper-docker does not overwrite images, <new-image> is the name
of the image that will be created on this operation. This new image will be the
same as the old one plus the applied updates.

If the tag has not been provided on either <image> or <new-image>, then
"latest" is the one that will be used.`,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "l, auto-agree-with-licenses",
					Usage: "Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install.",
				},
				cli.BoolFlag{
					Name:  "no-recommends",
					Usage: "By default, zypper installs also packages recommended by the requested ones. This option causes the recommended packages to be ignored and only the required ones to be installed.",
				},
				cli.BoolFlag{
					Name:  "replacefiles",
					Usage: "Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error.",
				},
				cli.StringFlag{
					Name:  "author",
					Value: defaultCommitAuthor(),
					Usage: "Commit author to associate with the new layer (e.g., \"John Doe <john.doe@example.com>\")",
				},
				cli.StringFlag{
					Name:  "message",
					Value: "[zypper-docker] update",
					Usage: "Commit message to associated with the new layer",
				},
			},
		},
		{
			Name:      "list-patches",
			ShortName: "lp",
			Aliases:   []string{"lp"},
			Usage:     "List all the available patches",
			Action:    getCmd("list-patches", listPatchesCmd),
			ArgsUsage: `<image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to use.
If the tag has not been provided, then "latest" is the one that will be used.`,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "bugzilla",
					Value: "",
					Usage: "List available needed patches for all Bugzilla issues, or issues whose number matches the given string (--bugzilla=#).",
				},
				cli.StringFlag{
					Name:  "cve",
					Value: "",
					Usage: "List available needed patches for all CVE issues, or issues whose number matches the given string (--cve=#).",
				},
				cli.StringFlag{
					Name:  "date",
					Value: "",
					Usage: "List patches issued up to, but not including, the specified date (YYYY-MM-DD).",
				},
				cli.StringFlag{
					Name:  "issues",
					Value: "",
					Usage: "Look for issues whose number, summary, or description matches the specified string (--issue=string).",
				},
				cli.StringFlag{
					Name:  "g, category",
					Value: "",
					Usage: "List only patches with this category.",
				},
				cli.StringFlag{
					Name:  "severity",
					Value: "",
					Usage: "List only patches with this severity.",
				},
			},
		},
		{
			Name:      "list-patches-container",
			ShortName: "lpc",
			Aliases:   []string{"lpc"},
			Usage:     "List all the available patches for the given container",
			Action:    listPatchesContainerCmd,
			ArgsUsage: `<container-id>

Where <container-id> is either the container ID or the name of the container
to be used.`,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "b, bugzilla",
					Value: "",
					Usage: "List available needed patches for all Bugzilla issues, or issues whose number matches the given string (--bugzilla=#).",
				},
				cli.StringFlag{
					Name:  "cve",
					Value: "",
					Usage: "List available needed patches for all CVE issues, or issues whose number matches the given string (--cve=#).",
				},
				cli.StringFlag{
					Name:  "date",
					Value: "",
					Usage: "List patches issued up to, but not including, the specified date (YYYY-MM-DD).",
				},
				cli.StringFlag{
					Name:  "issues",
					Value: "",
					Usage: "Look for issues whose number, summary, or description matches the specified string (--issue=string).",
				},
				cli.StringFlag{
					Name:  "g, category",
					Value: "",
					Usage: "List only patches with this category.",
				},
			},
		},
		{
			Name:   "patch",
			Usage:  "Install the available patches",
			Action: getCmd("patch", patchCmd),
			ArgsUsage: `<image> <new-image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to
patch. Since zypper-docker does not overwrite images, <new-image> is the name
of the image that will be created on this operation. This new image will be the
same as the old one plus the applied patches.

If the tag has not been provided on either <image> or <new-image>, then
"latest" is the one that will be used.`,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "bugzilla",
					Value: "",
					Usage: "Install available needed patches for all Bugzilla issues, or issues whose number matches the given string (--bugzilla=#).",
				},
				cli.StringFlag{
					Name:  "cve",
					Value: "",
					Usage: "Install available needed patches for all CVE issues, or issues whose number matches the given string (--cve=#).",
				},
				cli.StringFlag{
					Name:  "date",
					Value: "",
					Usage: "Install patches issued up to, but not including, the specified date (YYYY-MM-DD).",
				},
				cli.StringFlag{
					Name:  "g, category",
					Value: "",
					Usage: "Install only patches with this category.",
				},
				cli.BoolFlag{
					Name:  "l, auto-agree-with-licenses",
					Usage: "Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install.",
				},
				cli.BoolFlag{
					Name:  "no-recommends",
					Usage: "By default, zypper installs also packages recommended by the requested ones. This option causes the recommended packages to be ignored and only the required ones to be installed.",
				},
				cli.BoolFlag{
					Name:  "replacefiles",
					Usage: "Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error.",
				},
				cli.StringFlag{
					Name:  "author",
					Value: defaultCommitAuthor(),
					Usage: "Commit author to associate with the new layer (e.g., \"John Doe <john.doe@example.com>\")",
				},
				cli.StringFlag{
					Name:  "message",
					Value: "[zypper-docker] patch",
					Usage: "Commit message to associated with the new layer",
				},
			},
		},
		{
			Name:      "patch-check",
			ShortName: "pchk",
			Aliases:   []string{"pchk"},
			Usage:     "Check for patches",
			Action:    getCmd("patch-check", patchCheckCmd),
			ArgsUsage: `<image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to use.
If the tag has not been provided, then "latest" is the one that will be used.`,
		},
		{
			Name:      "patch-check-container",
			ShortName: "pchkc",
			Aliases:   []string{"pchkc"},
			Usage:     "Check for patches available for the given container",
			Action:    getCmd("patch-check-container", patchCheckContainerCmd),
			ArgsUsage: `<container-id>

Where <container-id> is either the container ID or the name of the container
to be used.`,
		},
		{
			Name:      "ps",
			Usage:     "List all the containers that are outdated",
			Action:    getCmd("ps", psCmd),
			ArgsUsage: " ",
		},
	}
	return app
}
<commit_msg>Fixes a bug that causes lpc command to log to stdout<commit_after>// Copyright (c) 2015 SUSE LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
	"os/user"

	"github.com/codegangsta/cli"
)

// A pointer to the current context.
var currentContext *cli.Context

// Returns the version string
func version() string {
	const (
		major = 1
		minor = 2
		patch = 0
	)
	return fmt.Sprintf("%v.%v.%v", major, minor, patch)
}

func defaultCommitAuthor() string {
	current, err := user.Current()
	if err != nil {
		log.Printf("Cannot determine current user: %s", err)
		return ""
	}

	if current.Name != "" {
		return current.Name
	}
	return current.Username
}

// It returns an application with all the flags and subcommands already in
// place.
func newApp() *cli.App {
	app := cli.NewApp()

	app.Name = "zypper-docker"
	app.Usage = "Patching Docker images safely"
	app.Version = version()

	app.CommandNotFound = func(context *cli.Context, cmd string) {
		fmt.Printf("Incorrect usage: command '%v' does not exist.\n\n", cmd)
		cli.ShowAppHelp(context)
	}

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "no-gpg-checks",
			Usage: "Ignore GPG check failures and continue",
		},
		cli.BoolFlag{
			Name:  "gpg-auto-import-keys",
			Usage: "If new repository signing key is found, do not ask what to do; trust and import it automatically",
		},
		cli.BoolFlag{
			Name:  "f, force",
			Usage: "Ignore all the local caches",
		},
		cli.BoolFlag{
			Name:  "d, debug",
			Usage: "Show all the logged messages on stdout",
		},
		cli.StringSliceFlag{
			Name:  "add-host",
			Usage: "Add a custom host-to-IP mapping (host:ip)",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "images",
			Usage:     "List all the images based on either OpenSUSE or SLES",
			Action:    getCmd("images", imagesCmd),
			ArgsUsage: " ",
		},
		{
			Name:      "list-updates",
			ShortName: "lu",
			Aliases:   []string{"lu"},
			Usage:     "List all the available updates",
			Action:    getCmd("list-updates", listUpdatesCmd),
			ArgsUsage: `<image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to use.
If the tag has not been provided, then "latest" is the one that will be used.`,
		},
		{
			Name:      "list-updates-container",
			ShortName: "luc",
			Aliases:   []string{"luc"},
			Usage:     "List all the available updates for the given container",
			Action:    getCmd("list-updates-container", listUpdatesContainerCmd),
			ArgsUsage: `<container-id>

Where <container-id> is either the container ID or the name of the container
to be used.`,
		},
		{
			Name:      "update",
			ShortName: "up",
			Aliases:   []string{"up"},
			Usage:     "Install the available updates",
			Action:    getCmd("update", updateCmd),
			ArgsUsage: `<image> <new-image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to
update. Since zypper-docker does not overwrite images, <new-image> is the name
of the image that will be created on this operation. This new image will be the
same as the old one plus the applied updates.

If the tag has not been provided on either <image> or <new-image>, then
"latest" is the one that will be used.`,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "l, auto-agree-with-licenses",
					Usage: "Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install.",
				},
				cli.BoolFlag{
					Name:  "no-recommends",
					Usage: "By default, zypper installs also packages recommended by the requested ones. This option causes the recommended packages to be ignored and only the required ones to be installed.",
				},
				cli.BoolFlag{
					Name:  "replacefiles",
					Usage: "Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error.",
				},
				cli.StringFlag{
					Name:  "author",
					Value: defaultCommitAuthor(),
					Usage: "Commit author to associate with the new layer (e.g., \"John Doe <john.doe@example.com>\")",
				},
				cli.StringFlag{
					Name:  "message",
					Value: "[zypper-docker] update",
					Usage: "Commit message to associated with the new layer",
				},
			},
		},
		{
			Name:      "list-patches",
			ShortName: "lp",
			Aliases:   []string{"lp"},
			Usage:     "List all the available patches",
			Action:    getCmd("list-patches", listPatchesCmd),
			ArgsUsage: `<image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to use.
If the tag has not been provided, then "latest" is the one that will be used.`,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "bugzilla",
					Value: "",
					Usage: "List available needed patches for all Bugzilla issues, or issues whose number matches the given string (--bugzilla=#).",
				},
				cli.StringFlag{
					Name:  "cve",
					Value: "",
					Usage: "List available needed patches for all CVE issues, or issues whose number matches the given string (--cve=#).",
				},
				cli.StringFlag{
					Name:  "date",
					Value: "",
					Usage: "List patches issued up to, but not including, the specified date (YYYY-MM-DD).",
				},
				cli.StringFlag{
					Name:  "issues",
					Value: "",
					Usage: "Look for issues whose number, summary, or description matches the specified string (--issue=string).",
				},
				cli.StringFlag{
					Name:  "g, category",
					Value: "",
					Usage: "List only patches with this category.",
				},
				cli.StringFlag{
					Name:  "severity",
					Value: "",
					Usage: "List only patches with this severity.",
				},
			},
		},
		{
			Name:      "list-patches-container",
			ShortName: "lpc",
			Aliases:   []string{"lpc"},
			Usage:     "List all the available patches for the given container",
			Action:    getCmd("list-patches-container", listPatchesContainerCmd),
			ArgsUsage: `<container-id>

Where <container-id> is either the container ID or the name of the container
to be used.`,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "b, bugzilla",
					Value: "",
					Usage: "List available needed patches for all Bugzilla issues, or issues whose number matches the given string (--bugzilla=#).",
				},
				cli.StringFlag{
					Name:  "cve",
					Value: "",
					Usage: "List available needed patches for all CVE issues, or issues whose number matches the given string (--cve=#).",
				},
				cli.StringFlag{
					Name:  "date",
					Value: "",
					Usage: "List patches issued up to, but not including, the specified date (YYYY-MM-DD).",
				},
				cli.StringFlag{
					Name:  "issues",
					Value: "",
					Usage: "Look for issues whose number, summary, or description matches the specified string (--issue=string).",
				},
				cli.StringFlag{
					Name:  "g, category",
					Value: "",
					Usage: "List only patches with this category.",
				},
			},
		},
		{
			Name:   "patch",
			Usage:  "Install the available patches",
			Action: getCmd("patch", patchCmd),
			ArgsUsage: `<image> <new-image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to
patch. Since zypper-docker does not overwrite images, <new-image> is the name
of the image that will be created on this operation. This new image will be the
same as the old one plus the applied patches.

If the tag has not been provided on either <image> or <new-image>, then
"latest" is the one that will be used.`,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "bugzilla",
					Value: "",
					Usage: "Install available needed patches for all Bugzilla issues, or issues whose number matches the given string (--bugzilla=#).",
				},
				cli.StringFlag{
					Name:  "cve",
					Value: "",
					Usage: "Install available needed patches for all CVE issues, or issues whose number matches the given string (--cve=#).",
				},
				cli.StringFlag{
					Name:  "date",
					Value: "",
					Usage: "Install patches issued up to, but not including, the specified date (YYYY-MM-DD).",
				},
				cli.StringFlag{
					Name:  "g, category",
					Value: "",
					Usage: "Install only patches with this category.",
				},
				cli.BoolFlag{
					Name:  "l, auto-agree-with-licenses",
					Usage: "Automatically say yes to third party license confirmation prompt. By using this option, you choose to agree with licenses of all third-party software this command will install.",
				},
				cli.BoolFlag{
					Name:  "no-recommends",
					Usage: "By default, zypper installs also packages recommended by the requested ones. This option causes the recommended packages to be ignored and only the required ones to be installed.",
				},
				cli.BoolFlag{
					Name:  "replacefiles",
					Usage: "Install the packages even if they replace files from other, already installed, packages. Default is to treat file conflicts as an error.",
				},
				cli.StringFlag{
					Name:  "author",
					Value: defaultCommitAuthor(),
					Usage: "Commit author to associate with the new layer (e.g., \"John Doe <john.doe@example.com>\")",
				},
				cli.StringFlag{
					Name:  "message",
					Value: "[zypper-docker] patch",
					Usage: "Commit message to associated with the new layer",
				},
			},
		},
		{
			Name:      "patch-check",
			ShortName: "pchk",
			Aliases:   []string{"pchk"},
			Usage:     "Check for patches",
			Action:    getCmd("patch-check", patchCheckCmd),
			ArgsUsage: `<image>

Where <image> is the name of the openSUSE/SUSE Linux Enterprise image to use.
If the tag has not been provided, then "latest" is the one that will be used.`,
		},
		{
			Name:      "patch-check-container",
			ShortName: "pchkc",
			Aliases:   []string{"pchkc"},
			Usage:     "Check for patches available for the given container",
			Action:    getCmd("patch-check-container", patchCheckContainerCmd),
			ArgsUsage: `<container-id>

Where <container-id> is either the container ID or the name of the container
to be used.`,
		},
		{
			Name:      "ps",
			Usage:     "List all the containers that are outdated",
			Action:    getCmd("ps", psCmd),
			ArgsUsage: " ",
		},
	}
	return app
}
<|endoftext|>


<commit_before>// Flake generates unique identifiers that are roughly sortable by time. Flake can
// run on a cluster of machines and still generate unique IDs without requiring
// worker coordination.
//
// A Flake ID is a 64-bit integer will the following components:
//  - 41 bits is the timestamp with millisecond precision
//  - 10 bits is the host id (uses IP modulo 2^10)
//  - 13 bits is an auto-incrementing sequence for ID requests within the same millisecond
//
// Note: In order to make a millisecond timestamp fit within 41 bits, a custom
// epoch of Jan 1, 2015 00:00:00 is used.

package flake

import (
	"net"
	"os"
	"strconv"
	"sync"
	"time"
)

const (
	HostBits     = 10
	SequenceBits = 13
)

var (
	// Custom Epoch so the timestamp can fit into 41 bits.
	// Jan 1, 2015 00:00:00 UTC
	Epoch       time.Time = time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)
	MaxHostId   uint64    = (1 << HostBits) - 1
	MaxSequence uint64    = (1 << SequenceBits) - 1
)

// Id represents a unique k-ordered Id
type Id uint64

// String formats the Id as a base36 string
func (id Id) String() string {
	return strconv.FormatUint(uint64(id), 36)
}

// Uint64 formats the Id as an unsigned integer
func (id Id) Uint64() uint64 {
	return uint64(id)
}

// Flake is a unique Id generator
type Flake struct {
	prevTime uint64
	hostId   uint64
	sequence uint64
	mu       sync.Mutex
}

// New returns a new Id generator and a possible error condition
func New() (*Flake, error) {
	hostId, err := getHostId()
	if err != nil {
		return nil, err
	}

	return &Flake{
		sequence: 0,
		prevTime: getTimestamp(),
		hostId:   hostId,
	}, nil
}

// NextId returns a new Id from the generator
func (f *Flake) NextId() Id {
	now := getTimestamp()

	f.mu.Lock()
	sequence := f.sequence

	// Use the sequence number if the id request is in the same millisecond as
	// the previous request.
	if now <= f.prevTime {
		now = f.prevTime
		sequence++
	} else {
		sequence = 0
	}

	// Bump the timestamp by 1ms if we run out of sequence bits.
	if sequence > MaxSequence {
		now++
		sequence = 0
	}

	f.prevTime = now
	f.sequence = sequence
	f.mu.Unlock()

	timestamp := now << (HostBits + SequenceBits)
	hostid := f.hostId << SequenceBits
	return Id(timestamp | hostid | sequence)
}

// getTimestamp returns the timestamp in milliseconds adjusted for the custom
// epoch
func getTimestamp() uint64 {
	return uint64(time.Since(Epoch).Nanoseconds() / 1e6)
}

// getHostId returns the host id using the IP address of the machine
func getHostId() (uint64, error) {
	h, err := os.Hostname()
	if err != nil {
		return 0, err
	}

	addrs, err := net.LookupIP(h)
	if err != nil {
		return 0, err
	}

	a := addrs[0].To4()
	ip := uint64((a[0] * 1 << 24) + (a[1] * 1 << 16) + (a[2] * 1 << 8) + a[3])
	return ip % MaxHostId, nil
}
<commit_msg>fix go vet warnings<commit_after>// Flake generates unique identifiers that are roughly sortable by time. Flake can
// run on a cluster of machines and still generate unique IDs without requiring
// worker coordination.
//
// A Flake ID is a 64-bit integer will the following components:
//  - 41 bits is the timestamp with millisecond precision
//  - 10 bits is the host id (uses IP modulo 2^10)
//  - 13 bits is an auto-incrementing sequence for ID requests within the same millisecond
//
// Note: In order to make a millisecond timestamp fit within 41 bits, a custom
// epoch of Jan 1, 2015 00:00:00 is used.

package flake

import (
	"net"
	"os"
	"strconv"
	"sync"
	"time"
)

const (
	HostBits     = 10
	SequenceBits = 13
)

var (
	// Custom Epoch so the timestamp can fit into 41 bits.
	// Jan 1, 2015 00:00:00 UTC
	Epoch       time.Time = time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)
	MaxHostId   uint64    = (1 << HostBits) - 1
	MaxSequence uint64    = (1 << SequenceBits) - 1
)

// Id represents a unique k-ordered Id
type Id uint64

// String formats the Id as a base36 string
func (id Id) String() string {
	return strconv.FormatUint(uint64(id), 36)
}

// Uint64 formats the Id as an unsigned integer
func (id Id) Uint64() uint64 {
	return uint64(id)
}

// Flake is a unique Id generator
type Flake struct {
	prevTime uint64
	hostId   uint64
	sequence uint64
	mu       sync.Mutex
}

// New returns a new Id generator and a possible error condition
func New() (*Flake, error) {
	hostId, err := getHostId()
	if err != nil {
		return nil, err
	}

	return &Flake{
		sequence: 0,
		prevTime: getTimestamp(),
		hostId:   hostId,
	}, nil
}

// NextId returns a new Id from the generator
func (f *Flake) NextId() Id {
	now := getTimestamp()

	f.mu.Lock()
	sequence := f.sequence

	// Use the sequence number if the id request is in the same millisecond as
	// the previous request.
	if now <= f.prevTime {
		now = f.prevTime
		sequence++
	} else {
		sequence = 0
	}

	// Bump the timestamp by 1ms if we run out of sequence bits.
	if sequence > MaxSequence {
		now++
		sequence = 0
	}

	f.prevTime = now
	f.sequence = sequence
	f.mu.Unlock()

	timestamp := now << (HostBits + SequenceBits)
	hostid := f.hostId << SequenceBits
	return Id(timestamp | hostid | sequence)
}

// getTimestamp returns the timestamp in milliseconds adjusted for the custom
// epoch
func getTimestamp() uint64 {
	return uint64(time.Since(Epoch).Nanoseconds() / 1e6)
}

// getHostId returns the host id using the IP address of the machine
func getHostId() (uint64, error) {
	h, err := os.Hostname()
	if err != nil {
		return 0, err
	}

	addrs, err := net.LookupIP(h)
	if err != nil {
		return 0, err
	}

	a := addrs[0].To4()
	ip := uint64(a[0]) << 24 + uint64(a[1]) << 16 + uint64(a[2]) << 8 + uint64(a[3])
	return ip % MaxHostId, nil
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"

	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/pkg/shutdown"
)

/*
	ish: the Inexusable/Insecure/Internet SHell.
*/
func main() {
	defer shutdown.Exit()

	name := os.Getenv("NAME")
	port := os.Getenv("PORT")
	addr := ":" + port
	if name == "" {
		name = "ish-service"
	}

	log.Println("Application", name, "(an ish instance) running")

	l, err := net.Listen("tcp", addr)
	if err != nil {
		shutdown.Fatal(err)
	}
	defer l.Close()
	log.Println("Listening on", addr)

	hb, err := discoverd.AddServiceAndRegister(name, addr)
	if err != nil {
		shutdown.Fatal(err)
	}
	shutdown.BeforeExit(func() { hb.Close() })

	http.HandleFunc("/ish", ish)
	if err := http.Serve(l, nil); err != nil {
		shutdown.Fatal(err)
	}
}

func ish(resp http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		http.Error(resp, "405 Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}
	body, _ := ioutil.ReadAll(req.Body)

	cmd := exec.Command("/bin/sh", "-c", string(body)) // no bash in busybox
	cmd.Stdout = io.MultiWriter(resp, os.Stdout)
	cmd.Stderr = io.MultiWriter(resp, os.Stderr)
	cmd.Run()
}
<commit_msg>test/apps/ish: Don't log startup messages<commit_after>package main

import (
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"

	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/pkg/shutdown"
)

/*
	ish: the Inexusable/Insecure/Internet SHell.
*/
func main() {
	defer shutdown.Exit()

	name := os.Getenv("NAME")
	port := os.Getenv("PORT")
	addr := ":" + port
	if name == "" {
		name = "ish-service"
	}

	l, err := net.Listen("tcp", addr)
	if err != nil {
		shutdown.Fatal(err)
	}
	defer l.Close()

	hb, err := discoverd.AddServiceAndRegister(name, addr)
	if err != nil {
		shutdown.Fatal(err)
	}
	shutdown.BeforeExit(func() { hb.Close() })

	http.HandleFunc("/ish", ish)
	if err := http.Serve(l, nil); err != nil {
		shutdown.Fatal(err)
	}
}

func ish(resp http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		http.Error(resp, "405 Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}
	body, _ := ioutil.ReadAll(req.Body)

	cmd := exec.Command("/bin/sh", "-c", string(body)) // no bash in busybox
	cmd.Stdout = io.MultiWriter(resp, os.Stdout)
	cmd.Stderr = io.MultiWriter(resp, os.Stderr)
	cmd.Run()
}
<|endoftext|>


<commit_before>// Package cookbook reads data from Chef cookbooks stored on disk. It can
// currently retrieve the cookbook's name and version as well as a list of all
// cookbook files.
package cookbook

import (
	"path"

	"github.com/mlafeldt/chef-runner/cookbook/metadata"
	"github.com/mlafeldt/chef-runner/util"
)

// A Cookbook is a Chef cookbook stored on disk.
type Cookbook struct {
	Path    string
	Name    string
	Version string
}

// NewCookbook returns a Cookbook that is located at cookbookPath.
func NewCookbook(cookbookPath string) (*Cookbook, error) {
	cb := Cookbook{Path: cookbookPath}

	metadataPath := path.Join(cookbookPath, metadata.Filename)
	if util.FileExist(metadataPath) {
		metadata, err := metadata.ParseFile(metadataPath)
		if err != nil {
			return nil, err
		}
		cb.Name = metadata.Name
		cb.Version = metadata.Version
	}

	return &cb, nil
}

// String returns the cookbook's name and version.
func (cb Cookbook) String() string {
	// TODO: check if fields are actually set
	return cb.Name + " " + cb.Version
}

// Files returns the names of all cookbook files. Other files are ignored.
func (cb Cookbook) Files() []string {
	fileList := [...]string{
		"README.md",
		"metadata.json",
		"metadata.rb",
		"attributes",
		"definitions",
		"files",
		"libraries",
		"providers",
		"recipes",
		"resources",
		"templates",
	}

	var files []string
	for _, f := range fileList {
		name := path.Join(cb.Path, f)
		if util.FileExist(name) {
			files = append(files, name)
		}
	}

	return files
}
<commit_msg>Add ability to strip cookbooks<commit_after>// Package cookbook reads and manipulates data from Chef cookbooks stored on
// disk.
package cookbook

import (
	"io/ioutil"
	"os"
	"path"

	"github.com/mlafeldt/chef-runner/cookbook/metadata"
	"github.com/mlafeldt/chef-runner/util"
)

// A Cookbook is a Chef cookbook stored on disk.
type Cookbook struct {
	Path    string
	Name    string
	Version string
}

// NewCookbook returns a Cookbook that is located at cookbookPath.
func NewCookbook(cookbookPath string) (*Cookbook, error) {
	cb := Cookbook{Path: cookbookPath}

	metadataPath := path.Join(cookbookPath, metadata.Filename)
	if util.FileExist(metadataPath) {
		metadata, err := metadata.ParseFile(metadataPath)
		if err != nil {
			return nil, err
		}
		cb.Name = metadata.Name
		cb.Version = metadata.Version
	}

	return &cb, nil
}

// String returns the cookbook's name and version.
func (cb Cookbook) String() string {
	// TODO: check if fields are actually set
	return cb.Name + " " + cb.Version
}

// Files returns the names of all cookbook files. Other files are ignored.
func (cb Cookbook) Files() []string {
	fileList := [...]string{
		"README.md",
		"metadata.json",
		"metadata.rb",
		"attributes",
		"definitions",
		"files",
		"libraries",
		"providers",
		"recipes",
		"resources",
		"templates",
	}

	var files []string
	for _, f := range fileList {
		name := path.Join(cb.Path, f)
		if util.FileExist(name) {
			files = append(files, name)
		}
	}

	return files
}

// Strip removes all non-cookbook files from the cookbook.
func (cb Cookbook) Strip() error {
	cbFiles := make(map[string]bool)
	for _, f := range cb.Files() {
		cbFiles[f] = true
	}

	files, err := ioutil.ReadDir(cb.Path)
	if err != nil {
		return err
	}

	for _, f := range files {
		name := path.Join(cb.Path, f.Name())
		if _, keep := cbFiles[name]; !keep {
			if err := os.RemoveAll(name); err != nil {
				return err
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Package forge provides an api to deal with parsing configuration files
//
// Config file example:
//
//     # example.cfg
//     top_level = "a string";
//     primary {
//       primary_int = 500;
//       sub_section {
//         sub_float = 50.5;  # End of line comment
//       }
//     }
//     secondary {
//       secondary_bool = true;
//       secondary_null = null;
//
//       # Reference other config value
//       local_ref = .secondary_null;
//       global_ref = primary.sub_section.sub_float;
//
//       # Include all files matching the provided pattern
//       include "/etc/app/*.cfg";
//     }
//
//
// Config file format:
//
//     IDENTIFIER: [_a-zA-Z]+
//
//     BOOL: 'true' | 'false'
//     NULL: 'null'
//     INTEGER: [0-9]+
//     FLOAT: INTEGER '.' INTEGER
//     STRING: '"' .* '"'
//     REFERENCE: [IDENTIFIER] ('.' IDENTIFIER)+
//     VALUE: BOOL | NULL | INTEGER | FLOAT | STRING | REFERENCE
//
//     INCLUDE: 'include ' STRING ';'
//     DIRECTIVE: (IDENTIFIER '=' VALUE | INCLUDE) ';'
//     SECTION: IDENTIFIER '{' (DIRECTIVE | SECTION)* '}'
//     COMMENT: '#' .* NEWLINE '\n'
//
//     CONFIG_FILE: (COMMENT | DIRECTIVE | SECTION)*
//
//
// Values
//  * String:
//      Any value enclosed in double quotes (single quotes not allowed) (e.g. "string")
//  * Integer:
//      Any number without decimal places (e.g. 500)
//  * Float:
//      Any number with decimal places (e.g. 500.55)
//  * Boolean:
//      The identifiers 'true' or 'false' of any case (e.g. TRUE, True, true, FALSE, False, false)
//  * Null:
//      The identifier 'null' of any case (e.g. NULL, Null, null)
//  * Global reference:
//      An identifier which may contain periods, the references are resolved from the global
//      section (e.g. global_value, section.sub_section.value)
//  * Local reference:
//      An identifier which main contain periods which starts with a period, the references
//      are resolved from the settings current section (e.g. .value, .sub_section.value)
//
//
// Directives
//  * Comment:
//      A comment is a pound symbol ('#') followed by any text any which ends with a newline (e.g. '# I am a comment\n')
//      A comment can either be on a line of it's own or at the end of any line. Nothing can come after the comment
//      until after the newline.
//  * Directive:
//      A directive is a setting, a identifier and a value. They are in the format '<identifier> = <value>;'
//      All directives must end in a semicolon. The value can be any of the types defined above.
//  * Section:
//      A section is a grouping of directives under a common name. They are in the format '<section_name> { <directives> }'.
//      All sections must be wrapped in brackets ('{', '}') and must all have a name. They do not end in a semicolon.
//      Sections may be left empty, they do not have to contain any directives.
//  * Include:
//      An include statement tells the config parser to include the contents of another config file where the include
//      statement is defined. Includes are in the format 'include "<pattern>";'. The <pattern> can be any glob
//      like pattern which is compatible with `path.filepath.Match` http://golang.org/pkg/path/filepath/#Match
package forge

import (
	"bytes"
	"io"
	"strings"

	"github.com/brettlangdon/forge/config"
	"github.com/brettlangdon/forge/parser"
)

// Parse a forge SectionValue from a string
func ParseString(data string) (*config.SectionValue, error) {
	return parser.ParseReader(strings.NewReader(data))
}

// Parse a forge SectionValue from a byte array
func ParseBytes(data []byte) (*config.SectionValue, error) {
	return parser.ParseReader(bytes.NewReader(data))
}

// Parse a forge SectionValue from a filename
func ParseFile(filename string) (*config.SectionValue, error) {
	return parser.ParseFile(filename)
}

// Parse a forge SectionValue from a io.Reader
func ParseReader(reader io.Reader) (*config.SectionValue, error) {
	return parser.ParseReader(reader)
}
<commit_msg>fix up comments again<commit_after>// Package forge provides an api to deal with parsing configuration files
//
// Config file example:
//
//     # example.cfg
//     top_level = "a string";
//     primary {
//       primary_int = 500;
//       sub_section {
//         sub_float = 50.5;  # End of line comment
//       }
//     }
//     secondary {
//       secondary_bool = true;
//       secondary_null = null;
//
//       # Reference other config value
//       local_ref = .secondary_null;
//       global_ref = primary.sub_section.sub_float;
//
//       # Include all files matching the provided pattern
//       include "/etc/app/*.cfg";
//     }
//
//
// Config file format:
//
//     IDENTIFIER: [_a-zA-Z]+
//
//     BOOL: 'true' | 'false'
//     NULL: 'null'
//     INTEGER: [0-9]+
//     FLOAT: INTEGER '.' INTEGER
//     STRING: '"' .* '"'
//     REFERENCE: [IDENTIFIER] ('.' IDENTIFIER)+
//     VALUE: BOOL | NULL | INTEGER | FLOAT | STRING | REFERENCE
//
//     INCLUDE: 'include ' STRING ';'
//     DIRECTIVE: (IDENTIFIER '=' VALUE | INCLUDE) ';'
//     SECTION: IDENTIFIER '{' (DIRECTIVE | SECTION)* '}'
//     COMMENT: '#' .* NEWLINE '\n'
//
//     CONFIG_FILE: (COMMENT | DIRECTIVE | SECTION)*
//
//
// Values
//  * String:
//      Any value enclosed in double quotes (single quotes not allowed) (e.g. "string")
//  * Integer:
//      Any number without decimal places (e.g. 500)
//  * Float:
//      Any number with decimal places (e.g. 500.55)
//  * Boolean:
//      The identifiers 'true' or 'false' of any case (e.g. TRUE, True, true, FALSE, False, false)
//  * Null:
//      The identifier 'null' of any case (e.g. NULL, Null, null)
//  * Global reference:
//      An identifier which may contain periods, the references are resolved from the global
//      section (e.g. global_value, section.sub_section.value)
//  * Local reference:
//      An identifier which main contain periods which starts with a period, the references
//      are resolved from the settings current section (e.g. .value, .sub_section.value)
//
//
// Directives
//  * Comment:
//      A comment is a pound symbol ('#') followed by any text any which ends with a newline (e.g. '# I am a comment\n')
//      A comment can either be on a line of it's own or at the end of any line. Nothing can come after the comment
//      until after the newline.
//  * Directive:
//      A directive is a setting, a identifier and a value. They are in the format '<identifier> = <value>;'
//      All directives must end in a semicolon. The value can be any of the types defined above.
//  * Section:
//      A section is a grouping of directives under a common name. They are in the format '<section_name> { <directives> }'.
//      All sections must be wrapped in brackets ('{', '}') and must all have a name. They do not end in a semicolon.
//      Sections may be left empty, they do not have to contain any directives.
//  * Include:
//      An include statement tells the config parser to include the contents of another config file where the include
//      statement is defined. Includes are in the format 'include "<pattern>";'. The <pattern> can be any glob
//      like pattern which is compatible with `path.filepath.Match` http://golang.org/pkg/path/filepath/#Match
package forge

import (
	"bytes"
	"io"
	"strings"

	"github.com/brettlangdon/forge/config"
	"github.com/brettlangdon/forge/parser"
)

// ParseString will parse a forge SectionValue from a string
func ParseString(data string) (*config.SectionValue, error) {
	return parser.ParseReader(strings.NewReader(data))
}

// ParseBytes will parse a forge SectionValue from a byte array
func ParseBytes(data []byte) (*config.SectionValue, error) {
	return parser.ParseReader(bytes.NewReader(data))
}

// ParseFile will parse a forge SectionValue from a filename
func ParseFile(filename string) (*config.SectionValue, error) {
	return parser.ParseFile(filename)
}

// ParseReader will parse a forge SectionValue from a io.Reader
func ParseReader(reader io.Reader) (*config.SectionValue, error) {
	return parser.ParseReader(reader)
}
<|endoftext|>


<commit_before>// Package odin ...
package odin

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/models"
)

const (
	NANOBOX  = "https://api.nanobox.io/v1/"
	BONESALT = "https://api.bonesalt.com/v1/"
	DEV      = "http://api.nanobox.dev:8080/v1/"
	SIM      = "http://api.nanobox.sim/v1/"
)

var (
	// set the default endpoint to nanobox
	endpoint = "nanobox"
)

// sets the odin endpoint
func SetEndpoint(stage string) {
	endpoint = stage
}

// Auth ...
func Auth(username, password string) (string, error) {

	//
	params := url.Values{}
	params.Set("password", password)

	//
	resBody := map[string]string{}

	//
	if err := doRequest("GET", fmt.Sprintf("users/%s/auth_token", username), params, nil, &resBody); err != nil {
		return "", err
	}

	return resBody["authentication_token"], nil
}

// App ...
func App(slug string) (models.App, error) {
	app := models.App{}

	return app, doRequest("GET", "apps/"+slug, nil, nil, &app)
}

// Deploy ...
func Deploy(appID, id, boxfile, message string) error {

	//
	body := map[string]map[string]string{
		"deploy": {
			"boxfile_content": boxfile,
			"build_id":        id,
			"commit_message":  message,
		},
	}

	return doRequest("POST", fmt.Sprintf("apps/%s/deploys", appID), nil, body, nil)
}

// EstablishTunnel ...
func EstablishTunnel(appID, id string) (string, string, int, error) {
	r := struct {
		Token string `json:"token"`
		Url   string `json:"url"`
		Port  int    `json:"port"`
	}{}

	err := doRequest("GET", fmt.Sprintf("apps/%s/tunnels/%s", appID, id), nil, nil, &r)

	return r.Token, r.Url, r.Port, err
}

// EstablishConsole ...
// protocol ssh/docker
func EstablishConsole(appID, id string) (string, string, string, error) {
	r := map[string]string{}
	err := doRequest("GET", fmt.Sprintf("apps/%s/consoles/%s", appID, id), nil, nil, &r)

	return r["token"], r["url"], r["protocol"], err
}

// GetWarehouse ...
func GetWarehouse(appID string) (string, string, error) {
	r := map[string]string{}
	err := doRequest("GET", fmt.Sprintf("apps/%s/services/warehouse", appID), nil, nil, &r)

	return r["token"], r["url"], err
}

func GetPreviousBuild(appID string) (string, error) {
	r := []map[string]string{}
	err := doRequest("GET", fmt.Sprintf("apps/%s/deploys", appID), nil, nil, &r)
	if err != nil {
		return "", err
	}

	if len(r) > 0 {
		return r[0]["build_id"], nil
	}

	return "", nil
}

// doRequest ...
func doRequest(method, path string, params url.Values, requestBody, responseBody interface{}) error {

	var rbodyReader io.Reader

	//
	if requestBody != nil {
		jsonBytes, err := json.Marshal(requestBody)
		if err != nil {
			return err
		}
		rbodyReader = bytes.NewBuffer(jsonBytes)
	}

	auth, _ := models.LoadAuthByEndpoint(endpoint)

	if params == nil {
		params = url.Values{}
	}
	params.Set("auth_token", auth.Key)

	// fetch the correct url from the endpoint
	url := odinURL()

	//
	lumber.Debug("%s%s?%s\n", url, path, params.Encode())
	req, err := http.NewRequest(method, fmt.Sprintf("%s%s?%s", url, path, params.Encode()), rbodyReader)
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")
	lumber.Trace("REQ: %s %s %s", req.Method, req.URL, req.Proto)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	lumber.Debug("RES: %d %s %s %s (%s)", res.StatusCode, req.Method, req.URL, req.Proto, res.Header.Get("Content-Length"))

	// print the body even if status is not 2XX
	if responseBody != nil {
		b, err := ioutil.ReadAll(res.Body)
		lumber.Debug("response body: '%s'\n", b)
		err = json.Unmarshal(b, responseBody)
		if err != nil {
			return err
		}
	}

	if res.StatusCode == 401 {
		return fmt.Errorf("Unauthorized")
	}

	if res.StatusCode == 404 {
		return fmt.Errorf("Not Found")
	}

	if res.StatusCode == 500 {
		return fmt.Errorf("Internal Server Error")
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return fmt.Errorf("bad exit response(%d %s %s %s (%s))", res.StatusCode, req.Method, req.URL, req.Proto, res.Header.Get("Content-Length"))
	}

	return nil
}

func odinURL() string {
	switch endpoint {
	case "bonesalt":
		return BONESALT
	case "dev":
		return DEV
	case "sim":
		return SIM
	default:
		return NANOBOX
	}
}
<commit_msg>make odin a bit better about reporting its errors<commit_after>// Package odin ...
package odin

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/models"
)

const (
	NANOBOX  = "https://api.nanobox.io/v1/"
	BONESALT = "https://api.bonesalt.com/v1/"
	DEV      = "http://api.nanobox.dev:8080/v1/"
	SIM      = "http://api.nanobox.sim/v1/"
)

var (
	// set the default endpoint to nanobox
	endpoint = "nanobox"
)

// sets the odin endpoint
func SetEndpoint(stage string) {
	endpoint = stage
}

// Auth ...
func Auth(username, password string) (string, error) {

	//
	params := url.Values{}
	params.Set("password", password)

	//
	resBody := map[string]string{}

	//
	if err := doRequest("GET", fmt.Sprintf("users/%s/auth_token", username), params, nil, &resBody); err != nil {
		return "", err
	}

	return resBody["authentication_token"], nil
}

// App ...
func App(slug string) (models.App, error) {
	app := models.App{}

	return app, doRequest("GET", "apps/"+slug, nil, nil, &app)
}

// Deploy ...
func Deploy(appID, id, boxfile, message string) error {

	//
	body := map[string]map[string]string{
		"deploy": {
			"boxfile_content": boxfile,
			"build_id":        id,
			"commit_message":  message,
		},
	}

	return doRequest("POST", fmt.Sprintf("apps/%s/deploys", appID), nil, body, nil)
}

// EstablishTunnel ...
func EstablishTunnel(appID, id string) (string, string, int, error) {
	r := struct {
		Token string `json:"token"`
		Url   string `json:"url"`
		Port  int    `json:"port"`
	}{}

	err := doRequest("GET", fmt.Sprintf("apps/%s/tunnels/%s", appID, id), nil, nil, &r)

	return r.Token, r.Url, r.Port, err
}

// EstablishConsole ...
// protocol ssh/docker
func EstablishConsole(appID, id string) (string, string, string, error) {
	r := map[string]string{}
	err := doRequest("GET", fmt.Sprintf("apps/%s/consoles/%s", appID, id), nil, nil, &r)

	return r["token"], r["url"], r["protocol"], err
}

// GetWarehouse ...
func GetWarehouse(appID string) (string, string, error) {
	r := map[string]string{}
	err := doRequest("GET", fmt.Sprintf("apps/%s/services/warehouse", appID), nil, nil, &r)

	return r["token"], r["url"], err
}

func GetPreviousBuild(appID string) (string, error) {
	r := []map[string]string{}
	err := doRequest("GET", fmt.Sprintf("apps/%s/deploys", appID), nil, nil, &r)
	if err != nil {
		return "", err
	}

	if len(r) > 0 {
		return r[0]["build_id"], nil
	}

	return "", nil
}

// doRequest ...
func doRequest(method, path string, params url.Values, requestBody, responseBody interface{}) error {

	var rbodyReader io.Reader

	//
	if requestBody != nil {
		jsonBytes, err := json.Marshal(requestBody)
		if err != nil {
			return err
		}
		rbodyReader = bytes.NewBuffer(jsonBytes)
	}

	auth, _ := models.LoadAuthByEndpoint(endpoint)

	if params == nil {
		params = url.Values{}
	}
	params.Set("auth_token", auth.Key)

	// fetch the correct url from the endpoint
	url := odinURL()

	//
	lumber.Debug("%s%s?%s\n", url, path, params.Encode())
	req, err := http.NewRequest(method, fmt.Sprintf("%s%s?%s", url, path, params.Encode()), rbodyReader)
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")
	lumber.Trace("REQ: %s %s %s", req.Method, req.URL, req.Proto)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	lumber.Debug("RES: %d %s %s %s (%s)", res.StatusCode, req.Method, req.URL, req.Proto, res.Header.Get("Content-Length"))

	// print the body even if status is not 2XX
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}

	if responseBody != nil {
		lumber.Debug("response body: '%s'\n", b)
		err = json.Unmarshal(b, responseBody)
		if err != nil {
			return err
		}
	}

	if res.StatusCode == 401 {
		return fmt.Errorf("Unauthorized")
	}

	if res.StatusCode == 404 {
		return fmt.Errorf("Not Found")
	}

	if res.StatusCode == 500 {
		return fmt.Errorf("Internal Server Error")
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return fmt.Errorf("bad exit response(%d %s %s %s (%s) %s)", res.StatusCode, req.Method, req.URL, req.Proto, res.Header.Get("Content-Length"), b)
	}

	return nil
}

func odinURL() string {
	switch endpoint {
	case "bonesalt":
		return BONESALT
	case "dev":
		return DEV
	case "sim":
		return SIM
	default:
		return NANOBOX
	}
}
<|endoftext|>


<commit_before>package util

import (
	"errors"
	"io"
	"strconv"
	"unsafe"
)

const (
	STATE_INIT                 = 0
	STATE_PARSE_TEXT_TOKEN     = 1
	STATE_PARSE_BINARY_HEADER  = 3
	STATE_PARSE_BINARY_PAYLOAD = 4
	STATE_LOOKING_FOR_LF       = 5
	SYMBOL_CR                  = 0x0A
)

const (
	MAX_TOKENS_PER_MSG   = 128
	MAX_RECV_BUFFER_SIZE = 4096
	MAX_TEXT_TOKEN_LEN   = 256
	MAX_BINARY_TOKEN_LEN = 0x80000
	START_ASCII_RANGE    = 0x21
	END_ASCII_RANGE      = 0x7E
)

var ERR_TOK_TOO_MANY_TOKENS = errors.New("Too many tokens")
var ERR_TOK_TOKEN_TOO_LONG = errors.New("Token is too long")
var ERR_TOK_PARSING_ERROR = errors.New("Error during token parsing")

type Tokenizer struct {
	buffer []byte
	bufPos int
	bufLen int
	reader io.Reader
}

func UnsafeBytesToString(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func NewTokenizer(reader io.Reader) *Tokenizer {
	tok := Tokenizer{
		buffer: make([]byte, MAX_RECV_BUFFER_SIZE),
		bufPos: 0,
		bufLen: 0,
		reader: reader,
	}
	return &tok
}

func (tok *Tokenizer) ReadTokens() ([]string, error) {
	result := make([]string, 0, MAX_TOKENS_PER_MSG)
	var err error
	var token []byte = make([]byte, 0, 64)
	var binTokenLen int
	var state int = STATE_PARSE_TEXT_TOKEN

	for {
		if tok.bufPos >= tok.bufLen {
			// Read more data from the network reader
			tok.bufPos = 0
			tok.bufLen, err = tok.reader.Read(tok.buffer)
			if nil != err {
				return nil, err
			}
		}

		// Tokenize content of the buffer
		for tok.bufPos < tok.bufLen {
			if state == STATE_PARSE_BINARY_PAYLOAD {
				availableBytes := tok.bufLen - tok.bufPos
				if availableBytes > binTokenLen {
					availableBytes = binTokenLen
				}
				token = append(token, tok.buffer[tok.bufPos:tok.bufPos+availableBytes]...)
				binTokenLen -= availableBytes

				tok.bufPos += availableBytes

				if binTokenLen <= 0 {
					// Binary token complete
					state = STATE_PARSE_TEXT_TOKEN
					result = append(result, UnsafeBytesToString(token))
					if len(result) > MAX_TOKENS_PER_MSG {
						return nil, ERR_TOK_TOO_MANY_TOKENS
					}
					token = make([]byte, 0, 64)
				}
				continue
			}

			val := tok.buffer[tok.bufPos]
			tok.bufPos += 1

			if val >= START_ASCII_RANGE && val <= END_ASCII_RANGE {
				token = append(token, val)
			} else if len(token) > 0 {
				if token[0] == '$' {
					binTokenLen, err = strconv.Atoi(UnsafeBytesToString(token[1:]))
					if err == nil && (binTokenLen < 1 || binTokenLen > MAX_BINARY_TOKEN_LEN) {
						return nil, ERR_TOK_PARSING_ERROR
					}
					state = STATE_PARSE_BINARY_PAYLOAD
					token = make([]byte, 0, binTokenLen)
				} else {
					result = append(result, UnsafeBytesToString(token))
					if len(result) > MAX_TOKENS_PER_MSG {
						return nil, ERR_TOK_TOO_MANY_TOKENS
					}
					if val == SYMBOL_CR {
						return result, nil
					}
					token = make([]byte, 0, 64)
				}
			} else {
				if val == SYMBOL_CR {
					return result, nil
				}
			}
			if len(token) > MAX_TEXT_TOKEN_LEN {
				return nil, ERR_TOK_TOKEN_TOO_LONG
			}
		}
	}
}
<commit_msg>Buffer size tuning.<commit_after>package util

import (
	"errors"
	"io"
	"strconv"
	"unsafe"
)

const (
	STATE_INIT                 = 0
	STATE_PARSE_TEXT_TOKEN     = 1
	STATE_PARSE_BINARY_HEADER  = 3
	STATE_PARSE_BINARY_PAYLOAD = 4
	STATE_LOOKING_FOR_LF       = 5
	SYMBOL_CR                  = 0x0A
)

const (
	MAX_TOKENS_PER_MSG    = 32
	MAX_RECV_BUFFER_SIZE  = 4096
	MAX_TEXT_TOKEN_LEN    = 256
	MAX_BINARY_TOKEN_LEN  = 0x80000
	START_ASCII_RANGE     = 0x21
	END_ASCII_RANGE       = 0x7E
	INIT_TOKEN_BUFFER_LEN = 48
)

var ERR_TOK_TOO_MANY_TOKENS = errors.New("Too many tokens")
var ERR_TOK_TOKEN_TOO_LONG = errors.New("Token is too long")
var ERR_TOK_PARSING_ERROR = errors.New("Error during token parsing")

type Tokenizer struct {
	buffer []byte
	bufPos int
	bufLen int
	reader io.Reader
}

func UnsafeBytesToString(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func NewTokenizer(reader io.Reader) *Tokenizer {
	tok := Tokenizer{
		buffer: make([]byte, MAX_RECV_BUFFER_SIZE),
		bufPos: 0,
		bufLen: 0,
		reader: reader,
	}
	return &tok
}

func (tok *Tokenizer) ReadTokens() ([]string, error) {
	result := make([]string, 0, MAX_TOKENS_PER_MSG)
	var err error
	var token []byte = make([]byte, 0, INIT_TOKEN_BUFFER_LEN)
	var binTokenLen int
	var state int = STATE_PARSE_TEXT_TOKEN

	for {
		if tok.bufPos >= tok.bufLen {
			// Read more data from the network reader
			tok.bufPos = 0
			tok.bufLen, err = tok.reader.Read(tok.buffer)
			if nil != err {
				return nil, err
			}
		}

		// Tokenize content of the buffer
		for tok.bufPos < tok.bufLen {
			if state == STATE_PARSE_BINARY_PAYLOAD {
				availableBytes := tok.bufLen - tok.bufPos
				if availableBytes > binTokenLen {
					availableBytes = binTokenLen
				}
				token = append(token, tok.buffer[tok.bufPos:tok.bufPos+availableBytes]...)
				binTokenLen -= availableBytes

				tok.bufPos += availableBytes

				if binTokenLen <= 0 {
					// Binary token complete
					state = STATE_PARSE_TEXT_TOKEN
					result = append(result, UnsafeBytesToString(token))
					if len(result) > MAX_TOKENS_PER_MSG {
						return nil, ERR_TOK_TOO_MANY_TOKENS
					}
					token = make([]byte, 0, INIT_TOKEN_BUFFER_LEN)
				}
				continue
			}

			val := tok.buffer[tok.bufPos]
			tok.bufPos += 1

			if val >= START_ASCII_RANGE && val <= END_ASCII_RANGE {
				token = append(token, val)
			} else if len(token) > 0 {
				if token[0] == '$' {
					binTokenLen, err = strconv.Atoi(UnsafeBytesToString(token[1:]))
					if err == nil && (binTokenLen < 1 || binTokenLen > MAX_BINARY_TOKEN_LEN) {
						return nil, ERR_TOK_PARSING_ERROR
					}
					state = STATE_PARSE_BINARY_PAYLOAD
					token = make([]byte, 0, binTokenLen)
				} else {
					result = append(result, UnsafeBytesToString(token))
					if len(result) > MAX_TOKENS_PER_MSG {
						return nil, ERR_TOK_TOO_MANY_TOKENS
					}
					if val == SYMBOL_CR {
						return result, nil
					}
					token = make([]byte, 0, INIT_TOKEN_BUFFER_LEN)
				}
			} else {
				if val == SYMBOL_CR {
					return result, nil
				}
			}
			if len(token) > MAX_TEXT_TOKEN_LEN {
				return nil, ERR_TOK_TOKEN_TOO_LONG
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"image"
	"io"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type ErrorResponse struct {
	Msg string `json:"error"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if domain := os.Getenv("ALLOWED_ORIGIN"); domain != "" {
		if origin := r.Header.Get("Origin"); origin == domain {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	} else {
		auth := r.Header.Get("X-Vip-Token")
		if auth != authToken {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}

	if r.Method == "OPTIONS" {
		return
	}

	h(w, r)
}

func fileKey(bucket string, width int, height int) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x-%dx%d", hash.Sum(nil), width, height)
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard limit in MB on files
	var limit int64 = 5
	if r.ContentLength > limit<<20 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("The file size limit is %dMB", limit),
		})
		return
	}

	raw, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	data := bytes.NewReader(raw)

	image, format, err := image.Decode(data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	width := image.Bounds().Size().X
	height := image.Bounds().Size().Y

	key := fileKey(bucket, width, height)

	data.Seek(0, 0)

	err = storage.PutReader(bucket, key, data,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		if secure {
			uri.Scheme = "https"
		} else {
			uri.Scheme = "http"
		}
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, key)

	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<commit_msg>import<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"image"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type ErrorResponse struct {
	Msg string `json:"error"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if domain := os.Getenv("ALLOWED_ORIGIN"); domain != "" {
		if origin := r.Header.Get("Origin"); origin == domain {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	} else {
		auth := r.Header.Get("X-Vip-Token")
		if auth != authToken {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}

	if r.Method == "OPTIONS" {
		return
	}

	h(w, r)
}

func fileKey(bucket string, width int, height int) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x-%dx%d", hash.Sum(nil), width, height)
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard limit in MB on files
	var limit int64 = 5
	if r.ContentLength > limit<<20 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("The file size limit is %dMB", limit),
		})
		return
	}

	raw, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	data := bytes.NewReader(raw)

	image, format, err := image.Decode(data)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	width := image.Bounds().Size().X
	height := image.Bounds().Size().Y

	key := fileKey(bucket, width, height)

	data.Seek(0, 0)

	err = storage.PutReader(bucket, key, data,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		if secure {
			uri.Scheme = "https"
		} else {
			uri.Scheme = "http"
		}
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, key)

	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<|endoftext|>


<commit_before>package fs

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"
	"syscall"

	git "github.com/libgit2/git2go"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
)

type treeFS struct {
	repo *git.Repository
	opts GitFSOptions
}

type GitFSOptions struct {
	Lazy    bool
	Disk    bool
	TempDir string
}

// NewTreeFS creates a git Tree FS. The treeish should resolve to tree SHA1.
func NewTreeFSRoot(repo *git.Repository, treeish string, opts *GitFSOptions) (nodefs.Node, error) {
	obj, err := repo.RevparseSingle(treeish)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	var treeId *git.Oid
	switch obj.Type() {
	case git.ObjectCommit:
		commit, err := repo.LookupCommit(obj.Id())
		if err != nil {
			return nil, err
		}
		treeId = commit.TreeId()
	case git.ObjectTree:
		treeId = obj.Id()
	default:
		return nil, fmt.Errorf("gitfs: unsupported object type %d", obj.Type())
	}

	if opts == nil {
		opts = &GitFSOptions{
			Lazy: true,
			Disk: false,
		}
	}
	if opts.TempDir == "" {
		opts.TempDir, err = ioutil.TempDir("", "gitfs")
		if err != nil {
			return nil, err
		}
	}

	t := &treeFS{
		repo: repo,
		opts: *opts,
	}
	root := t.newDirNode(treeId)
	return root, nil
}

func (t *treeFS) onMount(root *dirNode) {
	tree, err := t.repo.LookupTree(root.id)
	if err != nil {
		panic(err)
	}
	defer tree.Free()
	if root.Inode() == nil {
		panic("nil?")
	}
	t.recurse(tree, root)
	if err != nil {
		panic(err)
	}
}

type mutableLink struct {
	nodefs.Node
	content []byte
}

func (n *mutableLink) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = fuse.S_IFLNK
	return fuse.OK
}

func (n *mutableLink) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return n.content, fuse.OK
}

type gitNode struct {
	fs *treeFS
	id *git.Oid
	nodefs.Node
}

type dirNode struct {
	gitNode
}

func (n *dirNode) OnMount(conn *nodefs.FileSystemConnector) {
	n.fs.onMount(n)
}

func (n *dirNode) Symlink(name string, content string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
	l := &mutableLink{nodefs.NewDefaultNode(), []byte(content)}
	return n.Inode().NewChild(name, false, l), fuse.OK
}

func (n *dirNode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
	ch := n.Inode().GetChild(name)
	if ch == nil {
		return fuse.ENOENT
	}

	if _, ok := ch.Node().(*mutableLink); !ok {
		return fuse.EPERM
	}

	n.Inode().RmChild(name)
	return fuse.OK
}

type blobNode struct {
	gitNode
	mode git.Filemode
	size uint64
}

type linkNode struct {
	gitNode
	target []byte
}

func (n *linkNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = fuse.S_IFLNK
	return fuse.OK
}

func (n *linkNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return n.target, fuse.OK
}

func (n *blobNode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}

	ctor := n.LoadMemory
	if n.fs.opts.Disk {
		ctor = n.LoadDisk
	}

	if !n.fs.opts.Lazy {
		f, err := ctor()
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		return f, fuse.OK
	}

	return &lazyBlobFile{
		ctor: ctor,
		node: n,
	}, fuse.OK
}

func (n *blobNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = uint32(n.mode)
	out.Size = uint64(n.size)
	return fuse.OK
}

func (t *treeFS) newLinkNode(id *git.Oid) (nodefs.Node, error) {
	n := &linkNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}

	blob, err := t.repo.LookupBlob(id)
	if err != nil {
		return nil, err
	}
	defer blob.Free()
	n.target = append([]byte{}, blob.Contents()...)
	return n, nil
}

func (n *blobNode) LoadMemory() (nodefs.File, error) {
	blob, err := n.fs.repo.LookupBlob(n.id)
	if err != nil {
		return nil, err
	}
	return &memoryFile{
		File: nodefs.NewDefaultFile(),
		blob: blob,
	}, nil
}

type lazyBlobFile struct {
	mu sync.Mutex
	nodefs.File
	ctor func() (nodefs.File, error)
	node *blobNode
}

func (f *lazyBlobFile) SetInode(n *nodefs.Inode) {
}

func (f *lazyBlobFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File == nil {
		g, err := f.ctor()
		if err != nil {
			log.Printf("opening blob for %s: %v", f.node.id.String(), err)
			return nil, fuse.EIO
		}
		f.File = g
	}
	return f.File.Read(dest, off)
}

func (f *lazyBlobFile) Flush() fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File != nil {
		return f.File.Flush()
	}
	return fuse.OK
}

func (f *lazyBlobFile) Release() {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File != nil {
		f.File.Release()
	}
}

type memoryFile struct {
	nodefs.File
	blob *git.Blob
}

func (f *memoryFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	b := f.blob.Contents()
	end := off + int64(len(dest))
	if end > int64(len(b)) {
		end = int64(len(b))
	}
	return fuse.ReadResultData(b[off:end]), fuse.OK
}

func (f *memoryFile) Release() {
	f.blob.Free()
}

func (n *blobNode) LoadDisk() (nodefs.File, error) {
	p := filepath.Join(n.fs.opts.TempDir, n.id.String())
	if _, err := os.Lstat(p); os.IsNotExist(err) {
		blob, err := n.fs.repo.LookupBlob(n.id)
		if err != nil {
			return nil, err
		}
		defer blob.Free()

		// TODO - atomic, use content store to share content.
		if err := ioutil.WriteFile(p, blob.Contents(), 0644); err != nil {
			return nil, err
		}
	}
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}

	return nodefs.NewLoopbackFile(f), nil
}

func (t *treeFS) newBlobNode(id *git.Oid, mode git.Filemode) (nodefs.Node, error) {
	n := &blobNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}
	odb, err := t.repo.Odb()
	if err != nil {
		return nil, err
	}
	defer odb.Free()
	obj, err := odb.Read(id)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	n.size = obj.Len()
	n.mode = mode
	return n, nil
}

func (t *treeFS) newDirNode(id *git.Oid) nodefs.Node {
	n := &dirNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}
	return n
}

func (t *treeFS) recurse(tree *git.Tree, n nodefs.Node) error {
	for i := uint64(0); ; i++ {
		e := tree.EntryByIndex(i)
		if e == nil {
			break
		}
		isdir := e.Filemode&syscall.S_IFDIR != 0
		var chNode nodefs.Node
		if isdir {
			chNode = t.newDirNode(e.Id)
		} else if e.Filemode&^07777 == syscall.S_IFLNK {
			l, err := t.newLinkNode(e.Id)
			if err != nil {
				return err
			}
			chNode = l
		} else if e.Filemode&^07777 == syscall.S_IFREG {
			b, err := t.newBlobNode(e.Id, e.Filemode)
			if err != nil {
				return err
			}
			chNode = b
		} else {
			panic(e)
		}
		n.Inode().NewChild(e.Name, isdir, chNode)
		if isdir {
			tree, err := t.repo.LookupTree(e.Id)
			if err != nil {
				return err
			}

			if err := t.recurse(tree, chNode); err != nil {
				return nil
			}
		}
	}
	return nil
}
<commit_msg>fs: use git2go's Odb.ReadHeader to extract object size.<commit_after>package fs

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"
	"syscall"

	git "github.com/libgit2/git2go"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
)

type treeFS struct {
	repo *git.Repository
	opts GitFSOptions
}

type GitFSOptions struct {
	Lazy    bool
	Disk    bool
	TempDir string
}

// NewTreeFS creates a git Tree FS. The treeish should resolve to tree SHA1.
func NewTreeFSRoot(repo *git.Repository, treeish string, opts *GitFSOptions) (nodefs.Node, error) {
	obj, err := repo.RevparseSingle(treeish)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	var treeId *git.Oid
	switch obj.Type() {
	case git.ObjectCommit:
		commit, err := repo.LookupCommit(obj.Id())
		if err != nil {
			return nil, err
		}
		treeId = commit.TreeId()
	case git.ObjectTree:
		treeId = obj.Id()
	default:
		return nil, fmt.Errorf("gitfs: unsupported object type %d", obj.Type())
	}

	if opts == nil {
		opts = &GitFSOptions{
			Lazy: true,
			Disk: false,
		}
	}
	if opts.TempDir == "" {
		opts.TempDir, err = ioutil.TempDir("", "gitfs")
		if err != nil {
			return nil, err
		}
	}

	t := &treeFS{
		repo: repo,
		opts: *opts,
	}
	root := t.newDirNode(treeId)
	return root, nil
}

func (t *treeFS) onMount(root *dirNode) {
	tree, err := t.repo.LookupTree(root.id)
	if err != nil {
		panic(err)
	}
	defer tree.Free()
	if root.Inode() == nil {
		panic("nil?")
	}
	t.recurse(tree, root)
	if err != nil {
		panic(err)
	}
}

type mutableLink struct {
	nodefs.Node
	content []byte
}

func (n *mutableLink) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = fuse.S_IFLNK
	return fuse.OK
}

func (n *mutableLink) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return n.content, fuse.OK
}

type gitNode struct {
	fs *treeFS
	id *git.Oid
	nodefs.Node
}

type dirNode struct {
	gitNode
}

func (n *dirNode) OnMount(conn *nodefs.FileSystemConnector) {
	n.fs.onMount(n)
}

func (n *dirNode) Symlink(name string, content string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
	l := &mutableLink{nodefs.NewDefaultNode(), []byte(content)}
	return n.Inode().NewChild(name, false, l), fuse.OK
}

func (n *dirNode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
	ch := n.Inode().GetChild(name)
	if ch == nil {
		return fuse.ENOENT
	}

	if _, ok := ch.Node().(*mutableLink); !ok {
		return fuse.EPERM
	}

	n.Inode().RmChild(name)
	return fuse.OK
}

type blobNode struct {
	gitNode
	mode git.Filemode
	size uint64
}

type linkNode struct {
	gitNode
	target []byte
}

func (n *linkNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = fuse.S_IFLNK
	return fuse.OK
}

func (n *linkNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return n.target, fuse.OK
}

func (n *blobNode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}

	ctor := n.LoadMemory
	if n.fs.opts.Disk {
		ctor = n.LoadDisk
	}

	if !n.fs.opts.Lazy {
		f, err := ctor()
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		return f, fuse.OK
	}

	return &lazyBlobFile{
		ctor: ctor,
		node: n,
	}, fuse.OK
}

func (n *blobNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = uint32(n.mode)
	out.Size = uint64(n.size)
	return fuse.OK
}

func (t *treeFS) newLinkNode(id *git.Oid) (nodefs.Node, error) {
	n := &linkNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}

	blob, err := t.repo.LookupBlob(id)
	if err != nil {
		return nil, err
	}
	defer blob.Free()
	n.target = append([]byte{}, blob.Contents()...)
	return n, nil
}

func (n *blobNode) LoadMemory() (nodefs.File, error) {
	blob, err := n.fs.repo.LookupBlob(n.id)
	if err != nil {
		return nil, err
	}
	return &memoryFile{
		File: nodefs.NewDefaultFile(),
		blob: blob,
	}, nil
}

type lazyBlobFile struct {
	mu sync.Mutex
	nodefs.File
	ctor func() (nodefs.File, error)
	node *blobNode
}

func (f *lazyBlobFile) SetInode(n *nodefs.Inode) {
}

func (f *lazyBlobFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File == nil {
		g, err := f.ctor()
		if err != nil {
			log.Printf("opening blob for %s: %v", f.node.id.String(), err)
			return nil, fuse.EIO
		}
		f.File = g
	}
	return f.File.Read(dest, off)
}

func (f *lazyBlobFile) Flush() fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File != nil {
		return f.File.Flush()
	}
	return fuse.OK
}

func (f *lazyBlobFile) Release() {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File != nil {
		f.File.Release()
	}
}

type memoryFile struct {
	nodefs.File
	blob *git.Blob
}

func (f *memoryFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	b := f.blob.Contents()
	end := off + int64(len(dest))
	if end > int64(len(b)) {
		end = int64(len(b))
	}
	return fuse.ReadResultData(b[off:end]), fuse.OK
}

func (f *memoryFile) Release() {
	f.blob.Free()
}

func (n *blobNode) LoadDisk() (nodefs.File, error) {
	p := filepath.Join(n.fs.opts.TempDir, n.id.String())
	if _, err := os.Lstat(p); os.IsNotExist(err) {
		blob, err := n.fs.repo.LookupBlob(n.id)
		if err != nil {
			return nil, err
		}
		defer blob.Free()

		// TODO - atomic, use content store to share content.
		if err := ioutil.WriteFile(p, blob.Contents(), 0644); err != nil {
			return nil, err
		}
	}
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}

	return nodefs.NewLoopbackFile(f), nil
}

func (t *treeFS) newBlobNode(id *git.Oid, mode git.Filemode) (nodefs.Node, error) {
	n := &blobNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}
	odb, err := t.repo.Odb()
	if err != nil {
		return nil, err
	}
	defer odb.Free()
	sz, _, err := odb.ReadHeader(id)
	if err != nil {
		return nil, err
	}

	n.size = sz
	n.mode = mode
	return n, nil
}

func (t *treeFS) newDirNode(id *git.Oid) nodefs.Node {
	n := &dirNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}
	return n
}

func (t *treeFS) recurse(tree *git.Tree, n nodefs.Node) error {
	for i := uint64(0); ; i++ {
		e := tree.EntryByIndex(i)
		if e == nil {
			break
		}
		isdir := e.Filemode&syscall.S_IFDIR != 0
		var chNode nodefs.Node
		if isdir {
			chNode = t.newDirNode(e.Id)
		} else if e.Filemode&^07777 == syscall.S_IFLNK {
			l, err := t.newLinkNode(e.Id)
			if err != nil {
				return err
			}
			chNode = l
		} else if e.Filemode&^07777 == syscall.S_IFREG {
			b, err := t.newBlobNode(e.Id, e.Filemode)
			if err != nil {
				return err
			}
			chNode = b
		} else {
			panic(e)
		}
		n.Inode().NewChild(e.Name, isdir, chNode)
		if isdir {
			tree, err := t.repo.LookupTree(e.Id)
			if err != nil {
				return err
			}

			if err := t.recurse(tree, chNode); err != nil {
				return nil
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package dice

import (
	"fmt"
	"math/rand"
	"regexp"
	"sort"
	"strconv"
)

type FudgeRoller struct{}

var fudgePattern = regexp.MustCompile(`([0-9]+)df?([+-][0-9]+)?($|\s)`)

func (FudgeRoller) Pattern() *regexp.Regexp { return fudgePattern }

type FudgeResult struct {
	basicRollResult
	Rolls []int
	Total int
}

func (r FudgeResult) String() string {
	return fmt.Sprintf("%d %v", r.Total, r.Rolls)
}

func (r FudgeResult) Int() int {
	return r.Total
}

func (FudgeRoller) Roll(matches []string) (RollResult, error) {
	dice, err := strconv.ParseInt(matches[1], 10, 0)
	if err != nil {
		return nil, err
	}

	result := FudgeResult{
		basicRollResult: basicRollResult{matches[0]},
		Rolls:           make([]int, dice),
		Total:           0,
	}

	if matches[2] != "" {
		bonus, err := strconv.ParseInt(matches[2], 10, 0)
		if err != nil {
			return nil, err
		}
		result.Total += int(bonus)
	}

	for i := 0; i < len(result.Rolls); i++ {
		roll := rand.Intn(3) - 1
		result.Rolls[i] = roll
	}

	sort.Ints(result.Rolls)

	for i := 0; i < len(result.Rolls); i++ {
		result.Total += result.Rolls[i]
	}

	return result, nil
}

func init() {
	addRollHandler(FudgeRoller{})
}
<commit_msg>Add the roll totals in each ~roll~ saving one loop.<commit_after>package dice

import (
	"fmt"
	"math/rand"
	"regexp"
	"sort"
	"strconv"
)

type FudgeRoller struct{}

var fudgePattern = regexp.MustCompile(`([0-9]+)df?([+-][0-9]+)?($|\s)`)

func (FudgeRoller) Pattern() *regexp.Regexp { return fudgePattern }

type FudgeResult struct {
	basicRollResult
	Rolls []int
	Total int
}

func (r FudgeResult) String() string {
	return fmt.Sprintf("%d %v", r.Total, r.Rolls)
}

func (r FudgeResult) Int() int {
	return r.Total
}

func (FudgeRoller) Roll(matches []string) (RollResult, error) {
	dice, err := strconv.ParseInt(matches[1], 10, 0)
	if err != nil {
		return nil, err
	}

	result := FudgeResult{
		basicRollResult: basicRollResult{matches[0]},
		Rolls:           make([]int, dice),
		Total:           0,
	}

	if matches[2] != "" {
		bonus, err := strconv.ParseInt(matches[2], 10, 0)
		if err != nil {
			return nil, err
		}
		result.Total += int(bonus)
	}

	for i := 0; i < len(result.Rolls); i++ {
		result.Rolls[i] = rand.Intn(3) - 1
		result.Total += result.Rolls[i]
	}

	sort.Ints(result.Rolls)

	return result, nil
}

func init() {
	addRollHandler(FudgeRoller{})
}
<|endoftext|>