<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
    "time"
)

var (
	registry_addr string
    style_path string
    script_path string
)

func main() {
	var (
		bind          = flag.String("b", "127.0.0.1", "address to bind on")
		port          = flag.String("p", "8080", "port to listen on")
		cpus          = flag.Int("c", 1, "CPUs to use")
		flAddr          = flag.String("registry", "localhost:5000", "address to prefix the `docker pull ...`")
		registry_path = "/tmp"
        style         = flag.String("s", "./style.css", "path to style.css file")
        script        = flag.String("j", "./script.js", "path to script.js file")
		err           error
	)
	flag.Parse()

  if len(*flAddr) > 0 && !strings.HasSuffix(*flAddr,"/") {
    registry_addr = *flAddr + "/"
  }
    style_path = *style
    script_path = *script
	runtime.GOMAXPROCS(*cpus)

	if flag.NArg() > 0 {
		if registry_path, err = filepath.Abs(flag.Args()[0]); err != nil {
			log.Fatal(err)
		}
	}
	addr := fmt.Sprintf("%s:%s", *bind, *port)
	http.Handle("/", ImageListMux{registry_path})
	log.Printf("serving image list from %q listening on %s ...", registry_path, addr)
	log.Fatal(http.ListenAndServe(addr, nil))
}

type ImageListMux struct {
	BaseDir string
}

func (ilm ImageListMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		repos, err := ilm.Repos()
		if err != nil {
			log.Println(err)
			return
		}

        style_content, err := ioutil.ReadFile(style_path)
        if err != nil { panic(err) }
        script_content, err2 := ioutil.ReadFile(script_path)
        if err2 != nil { panic(err2) }

		fmt.Fprintln(w, "");
        fmt.Fprintf(w, "", style_content, script_content);
        fmt.Fprintln(w, "Show all
")
        last_namespace := ""
        first := true
        const layout = "Jan 2, 2006"
		for _, repo := range repos {
            if last_namespace != repo.Namespace {
                if first {
                    first = false
                    fmt.Fprintf(w, "
")
                }
                namespace := ""
                if repo.Namespace == "" {namespace = "No namespace"} else {namespace = repo.Namespace}
                fmt.Fprintf(w, "

%s

", namespace)
                last_namespace = repo.Namespace

            }
			name := filepath.Clean(filepath.Join(repo.Namespace, repo.Name))
			fmt.Fprintf(w, "
docker pull %s%s:%s	 (hash %s))	%s
", registry_addr, name, repo.Tags[0].Name, repo.Tags[0].HashID, repo.Time.Format(layout)) // XXX
		}
		fmt.Fprintln(w, "

")
	} else {
		msg := fmt.Sprintf("TODO: handle %s", r.URL.String())
		fmt.Fprintln(w, msg)
		log.Println(msg)
	}
	r.Body.Close()
}

func (ilm ImageListMux) Repos() ([]Repo, error) {
	repos := []Repo{}
	err := filepath.Walk(filepath.Join(ilm.BaseDir, "repositories"), func(path string, fi os.FileInfo, err error) error {
		if fi.Mode().IsRegular() && strings.HasPrefix(filepath.Base(path), "tag_") {
			r, err := NewRepoFromTagFile(path)
			if err != nil {
				return err
			}
			if !HasRepo(r, repos) {
				repos = append(repos, r)
			}
		}
		return nil
	})
	if err != nil {
		return []Repo{}, err
	}
	return repos, nil
}

func HasRepo(r Repo, repos []Repo) bool {
	return false // XXX
}

func NewRepoFromTagFile(path string) (Repo, error) {
	t, err := NewTag(path)
	if err != nil {
		return Repo{}, nil
	}
	chunks := strings.Split(filepath.Dir(path), "/")
    info, err := os.Stat(path)
	r := Repo{
		Namespace: chunks[len(chunks)-2],
		Name:      chunks[len(chunks)-1],
		Tags:      []Tag{t},
        Time:      info.ModTime(),
	}
	if r.Namespace == "library" {
		r.Namespace = ""
	}
	return r, nil
}

type Repo struct {
	Namespace, Name string
	Tags            []Tag
    Time            time.Time
}

func NewTag(path string) (Tag, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		return Tag{}, err
	}
	return Tag{
		Name:   strings.TrimPrefix(filepath.Base(path), "tag_"),
		HashID: string(buf),
	}, nil
}

type Tag struct {
	Name   string
	HashID string
}
<commit_msg>gofmt -s -w .<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

var (
	registry_addr string
	style_path    string
	script_path   string
)

func main() {
	var (
		bind          = flag.String("b", "127.0.0.1", "address to bind on")
		port          = flag.String("p", "8080", "port to listen on")
		cpus          = flag.Int("c", 1, "CPUs to use")
		flAddr        = flag.String("registry", "localhost:5000", "address to prefix the `docker pull ...`")
		registry_path = "/tmp"
		style         = flag.String("s", "./style.css", "path to style.css file")
		script        = flag.String("j", "./script.js", "path to script.js file")
		err           error
	)
	flag.Parse()

	if len(*flAddr) > 0 && !strings.HasSuffix(*flAddr, "/") {
		registry_addr = *flAddr + "/"
	}
	style_path = *style
	script_path = *script
	runtime.GOMAXPROCS(*cpus)

	if flag.NArg() > 0 {
		if registry_path, err = filepath.Abs(flag.Args()[0]); err != nil {
			log.Fatal(err)
		}
	}
	addr := fmt.Sprintf("%s:%s", *bind, *port)
	http.Handle("/", ImageListMux{registry_path})
	log.Printf("serving image list from %q listening on %s ...", registry_path, addr)
	log.Fatal(http.ListenAndServe(addr, nil))
}

type ImageListMux struct {
	BaseDir string
}

func (ilm ImageListMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		repos, err := ilm.Repos()
		if err != nil {
			log.Println(err)
			return
		}

		style_content, err := ioutil.ReadFile(style_path)
		if err != nil {
			panic(err)
		}
		script_content, err2 := ioutil.ReadFile(script_path)
		if err2 != nil {
			panic(err2)
		}

		fmt.Fprintln(w, "")
		fmt.Fprintf(w, "", style_content, script_content)
		fmt.Fprintln(w, "Show all
")
		last_namespace := ""
		first := true
		const layout = "Jan 2, 2006"
		for _, repo := range repos {
			if last_namespace != repo.Namespace {
				if first {
					first = false
					fmt.Fprintf(w, "
")
				}
				namespace := ""
				if repo.Namespace == "" {
					namespace = "No namespace"
				} else {
					namespace = repo.Namespace
				}
				fmt.Fprintf(w, "

%s

", namespace)
				last_namespace = repo.Namespace

			}
			name := filepath.Clean(filepath.Join(repo.Namespace, repo.Name))
			fmt.Fprintf(w, "
docker pull %s%s:%s	 (hash %s))	%s
", registry_addr, name, repo.Tags[0].Name, repo.Tags[0].HashID, repo.Time.Format(layout)) // XXX
		}
		fmt.Fprintln(w, "

")
	} else {
		msg := fmt.Sprintf("TODO: handle %s", r.URL.String())
		fmt.Fprintln(w, msg)
		log.Println(msg)
	}
	r.Body.Close()
}

func (ilm ImageListMux) Repos() ([]Repo, error) {
	repos := []Repo{}
	err := filepath.Walk(filepath.Join(ilm.BaseDir, "repositories"), func(path string, fi os.FileInfo, err error) error {
		if fi.Mode().IsRegular() && strings.HasPrefix(filepath.Base(path), "tag_") {
			r, err := NewRepoFromTagFile(path)
			if err != nil {
				return err
			}
			if !HasRepo(r, repos) {
				repos = append(repos, r)
			}
		}
		return nil
	})
	if err != nil {
		return []Repo{}, err
	}
	return repos, nil
}

func HasRepo(r Repo, repos []Repo) bool {
	return false // XXX
}

func NewRepoFromTagFile(path string) (Repo, error) {
	t, err := NewTag(path)
	if err != nil {
		return Repo{}, nil
	}
	chunks := strings.Split(filepath.Dir(path), "/")
	info, err := os.Stat(path)
	r := Repo{
		Namespace: chunks[len(chunks)-2],
		Name:      chunks[len(chunks)-1],
		Tags:      []Tag{t},
		Time:      info.ModTime(),
	}
	if r.Namespace == "library" {
		r.Namespace = ""
	}
	return r, nil
}

type Repo struct {
	Namespace, Name string
	Tags            []Tag
	Time            time.Time
}

func NewTag(path string) (Tag, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		return Tag{}, err
	}
	return Tag{
		Name:   strings.TrimPrefix(filepath.Base(path), "tag_"),
		HashID: string(buf),
	}, nil
}

type Tag struct {
	Name   string
	HashID string
}
<|endoftext|>


<commit_before>// Go FIDO U2F Library
// Copyright 2015 The Go FIDO U2F Library Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package u2f

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/sha256"
	"crypto/x509"
	"encoding/asn1"
	"errors"
	"time"
)

// Registration represents a single enrolment or pairing between an
// application and a token. This data will typically be stored in a database.
type Registration struct {
	// Raw serialized registration data as received from the token.
	Raw []byte

	KeyHandle []byte
	PubKey    ecdsa.PublicKey

	// AttestationCert can be nil for Authenticate requests.
	AttestationCert *x509.Certificate
}

// Config contains configurable options for the package.
type Config struct {
	// SkipAttestationVerify controls whether the token attestation
	// certificate should be verified on registration. Ideally it should
	// always be verified. However, there is currently no public list of
	// trusted attestation root certificates so it may be necessary to skip.
	SkipAttestationVerify bool
}

// Register validates a RegisterResponse message to enrol a new token.
// An error is returned if any part of the response fails to validate.
// The returned Registration should be stored by the caller.
func Register(resp RegisterResponse, c Challenge, config *Config) (*Registration, error) {
	if config == nil {
		config = &Config{}
	}

	if time.Now().Sub(c.Timestamp) > timeout {
		return nil, errors.New("u2f: challenge has expired")
	}

	regData, err := decodeBase64(resp.RegistrationData)
	if err != nil {
		return nil, err
	}

	clientData, err := decodeBase64(resp.ClientData)
	if err != nil {
		return nil, err
	}

	reg, sig, err := parseRegistration(regData)
	if err != nil {
		return nil, err
	}

	if err := verifyClientData(clientData, c); err != nil {
		return nil, err
	}

	if err := verifyAttestationCert(*reg, config); err != nil {
		return nil, err
	}

	if err := verifyRegistrationSignature(*reg, sig, c.AppID, clientData); err != nil {
		return nil, err
	}

	return reg, nil
}

func parseRegistration(buf []byte) (*Registration, []byte, error) {
	if len(buf) < 1+65+1+1+1 {
		return nil, nil, errors.New("u2f: data is too short")
	}

	var r Registration
	r.Raw = buf

	if buf[0] != 0x05 {
		return nil, nil, errors.New("u2f: invalid reserved byte")
	}
	buf = buf[1:]

	x, y := elliptic.Unmarshal(elliptic.P256(), buf[:65])
	if x == nil {
		return nil, nil, errors.New("u2f: invalid public key")
	}
	r.PubKey.Curve = elliptic.P256()
	r.PubKey.X = x
	r.PubKey.Y = y
	buf = buf[65:]

	khLen := int(buf[0])
	buf = buf[1:]
	if len(buf) < khLen {
		return nil, nil, errors.New("u2f: invalid key handle")
	}
	r.KeyHandle = buf[:khLen]
	buf = buf[khLen:]

	// The length of the x509 cert isn't specified so it has to be inferred
	// by parsing. We can't use x509.ParseCertificate yet because it returns
	// an error if there are any trailing bytes. So parse raw asn1 as a
	// workaround to get the length.
	sig, err := asn1.Unmarshal(buf, &asn1.RawValue{})
	if err != nil {
		return nil, nil, err
	}

	buf = buf[:len(buf)-len(sig)]
	cert, err := x509.ParseCertificate(buf)
	if err != nil {
		return nil, nil, err
	}
	r.AttestationCert = cert

	return &r, sig, nil
}

// UnmarshalBinary implements encoding.BinaryMarshaler.
func (r *Registration) UnmarshalBinary(data []byte) error {
	reg, _, err := parseRegistration(data)
	if err != nil {
		return err
	}
	*r = *reg
	return nil
}

// MarshalBinary implements encoding.BinaryUnmarshaler.
func (r *Registration) MarshalBinary() ([]byte, error) {
	return r.Raw, nil
}

func verifyAttestationCert(r Registration, config *Config) error {
	if config.SkipAttestationVerify {
		return nil
	}

	opts := x509.VerifyOptions{Roots: roots}
	_, err := r.AttestationCert.Verify(opts)
	return err
}

func verifyRegistrationSignature(
	r Registration, signature []byte, appid string, clientData []byte) error {

	appParam := sha256.Sum256([]byte(appid))
	challenge := sha256.Sum256(clientData)

	buf := []byte{0}
	buf = append(buf, appParam[:]...)
	buf = append(buf, challenge[:]...)
	buf = append(buf, r.KeyHandle...)
	pk := elliptic.Marshal(r.PubKey.Curve, r.PubKey.X, r.PubKey.Y)
	buf = append(buf, pk...)

	return r.AttestationCert.CheckSignature(
		x509.ECDSAWithSHA256, buf, signature)
}

func getRegisteredKey(appID string, r Registration) RegisteredKey {
	return RegisteredKey{
		Version:   u2fVersion,
		KeyHandle: encodeBase64(r.KeyHandle),
		AppID:     appID,
	}
}

// NewWebRegisterRequest creates a request to enrol a new token.
// regs is the list of the user's existing registration. The browser will
// refuse to re-register a device if it has an existing registration.
func NewWebRegisterRequest(c *Challenge, regs []Registration) *WebRegisterRequest {
	req := RegisterRequest{
		Version:   u2fVersion,
		Challenge: encodeBase64(c.Challenge),
	}

	rr := WebRegisterRequest{
		AppID:            c.AppID,
		RegisterRequests: []RegisterRequest{req},
	}

	for _, r := range regs {
		rk := getRegisteredKey(c.AppID, r)
		rr.RegisteredKeys = append(rr.RegisteredKeys, rk)
	}

	return &rr
}
<commit_msg>Support configurable root attestation certificates<commit_after>// Go FIDO U2F Library
// Copyright 2015 The Go FIDO U2F Library Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package u2f

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/sha256"
	"crypto/x509"
	"encoding/asn1"
	"errors"
	"time"
)

// Registration represents a single enrolment or pairing between an
// application and a token. This data will typically be stored in a database.
type Registration struct {
	// Raw serialized registration data as received from the token.
	Raw []byte

	KeyHandle []byte
	PubKey    ecdsa.PublicKey

	// AttestationCert can be nil for Authenticate requests.
	AttestationCert *x509.Certificate
}

// Config contains configurable options for the package.
type Config struct {
	// SkipAttestationVerify controls whether the token attestation
	// certificate should be verified on registration. Ideally it should
	// always be verified. However, there is currently no public list of
	// trusted attestation root certificates so it may be necessary to skip.
	SkipAttestationVerify bool

	// RootAttestationCertPool overrides the default root certificates used
	// to verify client attestations. If nil, this defaults to the roots that are
	// bundled in this library.
	RootAttestationCertPool *x509.CertPool
}

// Register validates a RegisterResponse message to enrol a new token.
// An error is returned if any part of the response fails to validate.
// The returned Registration should be stored by the caller.
func Register(resp RegisterResponse, c Challenge, config *Config) (*Registration, error) {
	if config == nil {
		config = &Config{}
	}

	if time.Now().Sub(c.Timestamp) > timeout {
		return nil, errors.New("u2f: challenge has expired")
	}

	regData, err := decodeBase64(resp.RegistrationData)
	if err != nil {
		return nil, err
	}

	clientData, err := decodeBase64(resp.ClientData)
	if err != nil {
		return nil, err
	}

	reg, sig, err := parseRegistration(regData)
	if err != nil {
		return nil, err
	}

	if err := verifyClientData(clientData, c); err != nil {
		return nil, err
	}

	if err := verifyAttestationCert(*reg, config); err != nil {
		return nil, err
	}

	if err := verifyRegistrationSignature(*reg, sig, c.AppID, clientData); err != nil {
		return nil, err
	}

	return reg, nil
}

func parseRegistration(buf []byte) (*Registration, []byte, error) {
	if len(buf) < 1+65+1+1+1 {
		return nil, nil, errors.New("u2f: data is too short")
	}

	var r Registration
	r.Raw = buf

	if buf[0] != 0x05 {
		return nil, nil, errors.New("u2f: invalid reserved byte")
	}
	buf = buf[1:]

	x, y := elliptic.Unmarshal(elliptic.P256(), buf[:65])
	if x == nil {
		return nil, nil, errors.New("u2f: invalid public key")
	}
	r.PubKey.Curve = elliptic.P256()
	r.PubKey.X = x
	r.PubKey.Y = y
	buf = buf[65:]

	khLen := int(buf[0])
	buf = buf[1:]
	if len(buf) < khLen {
		return nil, nil, errors.New("u2f: invalid key handle")
	}
	r.KeyHandle = buf[:khLen]
	buf = buf[khLen:]

	// The length of the x509 cert isn't specified so it has to be inferred
	// by parsing. We can't use x509.ParseCertificate yet because it returns
	// an error if there are any trailing bytes. So parse raw asn1 as a
	// workaround to get the length.
	sig, err := asn1.Unmarshal(buf, &asn1.RawValue{})
	if err != nil {
		return nil, nil, err
	}

	buf = buf[:len(buf)-len(sig)]
	cert, err := x509.ParseCertificate(buf)
	if err != nil {
		return nil, nil, err
	}
	r.AttestationCert = cert

	return &r, sig, nil
}

// UnmarshalBinary implements encoding.BinaryMarshaler.
func (r *Registration) UnmarshalBinary(data []byte) error {
	reg, _, err := parseRegistration(data)
	if err != nil {
		return err
	}
	*r = *reg
	return nil
}

// MarshalBinary implements encoding.BinaryUnmarshaler.
func (r *Registration) MarshalBinary() ([]byte, error) {
	return r.Raw, nil
}

func verifyAttestationCert(r Registration, config *Config) error {
	if config.SkipAttestationVerify {
		return nil
	}
	rootCertPool := roots
	if config.RootAttestationCertPool != nil {
		rootCertPool = config.RootAttestationCertPool
	}

	opts := x509.VerifyOptions{Roots: rootCertPool}
	_, err := r.AttestationCert.Verify(opts)
	return err
}

func verifyRegistrationSignature(
	r Registration, signature []byte, appid string, clientData []byte) error {

	appParam := sha256.Sum256([]byte(appid))
	challenge := sha256.Sum256(clientData)

	buf := []byte{0}
	buf = append(buf, appParam[:]...)
	buf = append(buf, challenge[:]...)
	buf = append(buf, r.KeyHandle...)
	pk := elliptic.Marshal(r.PubKey.Curve, r.PubKey.X, r.PubKey.Y)
	buf = append(buf, pk...)

	return r.AttestationCert.CheckSignature(
		x509.ECDSAWithSHA256, buf, signature)
}

func getRegisteredKey(appID string, r Registration) RegisteredKey {
	return RegisteredKey{
		Version:   u2fVersion,
		KeyHandle: encodeBase64(r.KeyHandle),
		AppID:     appID,
	}
}

// NewWebRegisterRequest creates a request to enrol a new token.
// regs is the list of the user's existing registration. The browser will
// refuse to re-register a device if it has an existing registration.
func NewWebRegisterRequest(c *Challenge, regs []Registration) *WebRegisterRequest {
	req := RegisterRequest{
		Version:   u2fVersion,
		Challenge: encodeBase64(c.Challenge),
	}

	rr := WebRegisterRequest{
		AppID:            c.AppID,
		RegisterRequests: []RegisterRequest{req},
	}

	for _, r := range regs {
		rk := getRegisteredKey(c.AppID, r)
		rr.RegisteredKeys = append(rr.RegisteredKeys, rk)
	}

	return &rr
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Registry struct {
	Client  *http.Client
	BaseURL string
}

type Catalog struct {
	Repositories []string `json:"repositories"`
}

type Tags struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

func (r *Registry) VerifyV2() error {
	resp, err := r.Client.Get(r.BaseURL + "/v2/")
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	switch resp.StatusCode {
	case http.StatusUnauthorized:
		// error text in resp.Body
		return errors.New("take action based on WWW-Authenticate")
	case http.StatusNotFound:
		return errors.New("registry does not support v2 API")
	case http.StatusOK:
		break
	default:
		return errors.New(fmt.Sprintln("bad status (", r.BaseURL, "/v2/) ", resp.StatusCode))
	}

	ver := resp.Header.Get("Docker-Distribution-API-Version")
	if ver == "registry/2.0" {
		return nil
	}

	return errors.New("registry does not support v2 API")
}

func (r *Registry) Catalog() ([]string, error) {
	resp, err := r.Client.Get(r.BaseURL + "/v2/_catalog")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		// decode error text if 4xx error code
		return nil, errors.New(fmt.Sprintln("bad status ", resp.StatusCode))
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	catalog := new(Catalog)

	err = json.Unmarshal(body, &catalog)
	if err != nil {
		return nil, err
	}

	return catalog.Repositories, nil
}

func (r *Registry) Tags(img string) ([]string, error) {
	resp, err := r.Client.Get(r.BaseURL + "/v2/" + img + "/tags/list")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		// decode error text if 4xx error code
		return nil, errors.New(fmt.Sprintln("bad status ", resp.StatusCode))
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	tags := new(Tags)

	err = json.Unmarshal(body, &tags)
	if err != nil {
		return nil, err
	}

	return tags.Tags, nil
}

func main() {
	registry := &Registry{
		Client:  &http.Client{},
		BaseURL: "http://yin.mno.stratus.com:5000",
	}

	err := registry.VerifyV2()
	if err != nil {
		fmt.Println(err)
		return
	}

	images, err := registry.Catalog()
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, img := range images {
		tags, err := registry.Tags(img)
		if err != nil {
			fmt.Println(err)
			return
		}

		for _, t := range tags {
			fmt.Println(img + ":" + t)
		}
	}
}
<commit_msg>reuse the tcp connection<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Registry struct {
	Client  *http.Client
	BaseURL string
}

type Catalog struct {
	Repositories []string `json:"repositories"`
}

type Tags struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

func (r *Registry) VerifyV2() error {
	resp, err := r.Client.Get(r.BaseURL + "/v2/")
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	switch resp.StatusCode {
	case http.StatusUnauthorized:
		// error text in resp.Body
		return errors.New("take action based on WWW-Authenticate")
	case http.StatusNotFound:
		return errors.New("registry does not support v2 API")
	case http.StatusOK:
		break
	default:
		return errors.New(fmt.Sprintln("bad status (", r.BaseURL, "/v2/) ", resp.StatusCode))
	}

	// consume body so connection can be reused
	ioutil.ReadAll(resp.Body)

	ver := resp.Header.Get("Docker-Distribution-API-Version")
	if ver == "registry/2.0" {
		return nil
	}

	return errors.New("registry does not support v2 API")
}

func (r *Registry) Catalog() ([]string, error) {
	resp, err := r.Client.Get(r.BaseURL + "/v2/_catalog")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		// decode error text if 4xx error code
		return nil, errors.New(fmt.Sprintln("bad status ", resp.StatusCode))
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	catalog := new(Catalog)

	err = json.Unmarshal(body, &catalog)
	if err != nil {
		return nil, err
	}

	return catalog.Repositories, nil
}

func (r *Registry) Tags(img string) ([]string, error) {
	resp, err := r.Client.Get(r.BaseURL + "/v2/" + img + "/tags/list")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		// decode error text if 4xx error code
		return nil, errors.New(fmt.Sprintln("bad status ", resp.StatusCode))
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	tags := new(Tags)

	err = json.Unmarshal(body, &tags)
	if err != nil {
		return nil, err
	}

	return tags.Tags, nil
}

func main() {
	registry := &Registry{
		Client:  &http.Client{Transport: &http.Transport{}},
		BaseURL: "http://yin.mno.stratus.com:5000",
	}

	err := registry.VerifyV2()
	if err != nil {
		fmt.Println(err)
		return
	}

	images, err := registry.Catalog()
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, img := range images {
		tags, err := registry.Tags(img)
		if err != nil {
			fmt.Println(err)
			return
		}

		for _, t := range tags {
			fmt.Println(img + ":" + t)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"

	"github.com/bmizerany/mc"
	"github.com/elazarl/goproxy"
	"github.com/jackc/pgx"
)

var (
	cn    *mc.Conn
	pool  *pgx.ConnPool
	proxy *goproxy.ProxyHttpServer
)

func urlHasPrefix(prefix string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		isGET := req.Method == http.MethodGet
		hasPrefix := strings.HasPrefix(req.URL.Path, prefix)
		isSearch := strings.HasPrefix(req.URL.Path, "/packages/search/")
		return isGET && hasPrefix && !isSearch
	}
}

func pathIs(path string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		return req.Method == http.MethodGet && req.URL.Path == path
	}
}

func getEnv(key, def string) string {
	k := os.Getenv(key)
	if k == "" {
		return def
	}
	return k
}

func main() {
	memcachedURL := getEnv("MEMCACHEDCLOUD_SERVERS", "localhost:11211")
	var err error
	cn, err = mc.Dial("tcp", memcachedURL)
	if err != nil {
		log.Fatalf("Memcached connection error: %s", err)
	}

	memcachedUsername := os.Getenv("MEMCACHEDCLOUD_USERNAME")
	memcachedPassword := os.Getenv("MEMCACHEDCLOUD_PASSWORD")
	if memcachedUsername != "" && memcachedPassword != "" {
		if err := cn.Auth(memcachedUsername, memcachedPassword); err != nil {
			log.Fatalf("Memcached auth error: %s", err)
		}
	}

	pgxcfg, err := pgx.ParseURI(os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatalf("Parse URI error: %s", err)
	}
	pool, err = pgx.NewConnPool(pgx.ConnPoolConfig{
		ConnConfig:     pgxcfg,
		MaxConnections: 20,
		AfterConnect: func(conn *pgx.Conn) error {
			_, err := conn.Prepare("getPackage", `SELECT name, url FROM packages WHERE name = $1`)
			return err
		},
	})
	if err != nil {
		log.Fatalf("Connection error: %s", err)
	}
	defer pool.Close()

	binary, err := exec.LookPath("node")
	if err != nil {
		log.Fatalf("Could not lookup node path: %s", err)
	}

	cmd := exec.Command(binary, "--expose_gc", "index.js")
	env := os.Environ()
	env = append([]string{"PORT=3001"}, env...)
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		log.Fatalf("Could not start node: %s", err)
	}
	go func() {
		if err := cmd.Wait(); err != nil {
			log.Fatalf("Node process failed: %s", err)
		}
	}()

	proxy = goproxy.NewProxyHttpServer()
	proxy.Verbose = false
	proxy.NonproxyHandler = http.HandlerFunc(nonProxy)
	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			if r.Host != "registry.bower.io" {
				response := goproxy.NewResponse(r, "application/json", http.StatusGone, "")
				return r, response
			}

			return r, nil
		})

	proxy.OnRequest(pathIs("/packages")).DoFunc(listPackages)
	proxy.OnRequest(urlHasPrefix("/packages/")).DoFunc(getPackage)

	port := getEnv("PORT", "3000")
	log.Println("Starting web server at port", port)
	log.Fatal(http.ListenAndServe(":"+port, proxy))
}

func nonProxy(w http.ResponseWriter, req *http.Request) {
	req.URL.Scheme = "http"
	req.URL.Host = "localhost:3001"
	proxy.ServeHTTP(w, req)
}

type Package struct {
	Name string `json:"name"`
	URL  string `json:"url"`
}

func getPackage(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	elements := strings.Split(r.URL.Path, "/")
	packageName := elements[len(elements)-1]

	var name, url string
	if err := pool.QueryRow("getPackage", packageName).Scan(&name, &url); err != nil {
		if err == pgx.ErrNoRows {
			return r, goproxy.NewResponse(r, "text/html", http.StatusNotFound, "Package not found")
		}
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}

	data, err := json.Marshal(Package{Name: name, URL: url})
	if err != nil {
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}
	response := goproxy.NewResponse(r, "application/json", http.StatusOK, string(data))
	response.Header.Add("Cache-Control", "public, max-age=86400")
	return r, response
}

func listPackages(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	val, _, _, err := cn.Get("packages")
	if err != nil {
		return r, nil
	}
	response := goproxy.NewResponse(r, "application/json", http.StatusOK, val)
	response.Header.Add("Cache-Control", "public, max-age=86400")
	return r, response
}
<commit_msg>Revert "Fully deprecate old registry"<commit_after>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"

	"github.com/bmizerany/mc"
	"github.com/elazarl/goproxy"
	"github.com/jackc/pgx"
)

var (
	cn    *mc.Conn
	pool  *pgx.ConnPool
	proxy *goproxy.ProxyHttpServer
)

func urlHasPrefix(prefix string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		isGET := req.Method == http.MethodGet
		hasPrefix := strings.HasPrefix(req.URL.Path, prefix)
		isSearch := strings.HasPrefix(req.URL.Path, "/packages/search/")
		return isGET && hasPrefix && !isSearch
	}
}

func pathIs(path string) goproxy.ReqConditionFunc {
	return func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		return req.Method == http.MethodGet && req.URL.Path == path
	}
}

func getEnv(key, def string) string {
	k := os.Getenv(key)
	if k == "" {
		return def
	}
	return k
}

func main() {
	memcachedURL := getEnv("MEMCACHEDCLOUD_SERVERS", "localhost:11211")
	var err error
	cn, err = mc.Dial("tcp", memcachedURL)
	if err != nil {
		log.Fatalf("Memcached connection error: %s", err)
	}

	memcachedUsername := os.Getenv("MEMCACHEDCLOUD_USERNAME")
	memcachedPassword := os.Getenv("MEMCACHEDCLOUD_PASSWORD")
	if memcachedUsername != "" && memcachedPassword != "" {
		if err := cn.Auth(memcachedUsername, memcachedPassword); err != nil {
			log.Fatalf("Memcached auth error: %s", err)
		}
	}

	pgxcfg, err := pgx.ParseURI(os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatalf("Parse URI error: %s", err)
	}
	pool, err = pgx.NewConnPool(pgx.ConnPoolConfig{
		ConnConfig:     pgxcfg,
		MaxConnections: 20,
		AfterConnect: func(conn *pgx.Conn) error {
			_, err := conn.Prepare("getPackage", `SELECT name, url FROM packages WHERE name = $1`)
			return err
		},
	})
	if err != nil {
		log.Fatalf("Connection error: %s", err)
	}
	defer pool.Close()

	binary, err := exec.LookPath("node")
	if err != nil {
		log.Fatalf("Could not lookup node path: %s", err)
	}

	cmd := exec.Command(binary, "--expose_gc", "index.js")
	env := os.Environ()
	env = append([]string{"PORT=3001"}, env...)
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		log.Fatalf("Could not start node: %s", err)
	}
	go func() {
		if err := cmd.Wait(); err != nil {
			log.Fatalf("Node process failed: %s", err)
		}
	}()

	proxy = goproxy.NewProxyHttpServer()
	proxy.Verbose = false
	proxy.NonproxyHandler = http.HandlerFunc(nonProxy)
	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			if r.Method == "GET" && r.Host != "registry.bower.io" {
				response := goproxy.NewResponse(r, "application/json", http.StatusPermanentRedirect, "")
				target := "https://registry.bower.io" + r.URL.Path
				if len(r.URL.RawQuery) > 0 {
					target += "?" + r.URL.RawQuery
				}
				response.Header.Set("Location", target)
				return r, response
			}

			return r, nil
		})

	proxy.OnRequest(pathIs("/packages")).DoFunc(listPackages)
	proxy.OnRequest(urlHasPrefix("/packages/")).DoFunc(getPackage)

	port := getEnv("PORT", "3000")
	log.Println("Starting web server at port", port)
	log.Fatal(http.ListenAndServe(":"+port, proxy))
}

func nonProxy(w http.ResponseWriter, req *http.Request) {
	req.URL.Scheme = "http"
	req.URL.Host = "localhost:3001"
	proxy.ServeHTTP(w, req)
}

type Package struct {
	Name string `json:"name"`
	URL  string `json:"url"`
}

func getPackage(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	elements := strings.Split(r.URL.Path, "/")
	packageName := elements[len(elements)-1]

	var name, url string
	if err := pool.QueryRow("getPackage", packageName).Scan(&name, &url); err != nil {
		if err == pgx.ErrNoRows {
			return r, goproxy.NewResponse(r, "text/html", http.StatusNotFound, "Package not found")
		}
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}

	data, err := json.Marshal(Package{Name: name, URL: url})
	if err != nil {
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}
	response := goproxy.NewResponse(r, "application/json", http.StatusOK, string(data))
	response.Header.Add("Cache-Control", "public, max-age=86400")
	return r, response
}

func listPackages(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	val, _, _, err := cn.Get("packages")
	if err != nil {
		return r, nil
	}
	response := goproxy.NewResponse(r, "application/json", http.StatusOK, val)
	response.Header.Add("Cache-Control", "public, max-age=86400")
	return r, response
}
<|endoftext|>


<commit_before>package main

import "os"
import "github.com/docker/engine-api/client"
import "math/rand"
import "os/exec"
import "strconv"
import "github.com/seehuhn/mt19937"
import "time"
import "os/user"

var cli *client.Client

type ExecRequest struct {
	Image string
	Cmd []string
	SourceFileName string
}

type Judge struct {
	Code string
	Compile *ExecRequest
	Exec ExecRequest
	Time int64
	Mem  int64
	TCCount int // The number of test cases
}

type JudgeResult int

const (
	Accepted            JudgeResult = 0
	WrongAnswer         JudgeResult = 1
	CompileError        JudgeResult = 2
	TimeLimitExceeded   JudgeResult = 3
	MemoryLimitExceeded JudgeResult = 4
	RuntimeError        JudgeResult = 5
	InternalError       JudgeResult = 6
	Judging             JudgeResult = 7
	CompileTimeLimitExceeded JudgeResult = 8
	CompileMemoryLimitExceeded JudgeResult = 9
)

type JudgeStatus struct {
	Case     *string
	JR       JudgeResult
	Mem      int64
	Time     int64
	Msg      *string
}

func CreateInternalError(msg string) JudgeStatus {
	return JudgeStatus{nil, InternalError, 0, 0, &msg}
}

const BASE_RAND_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

func RandomName() string {
	rng := rand.New(mt19937.New())
	rng.Seed(time.Now().UnixNano())

	res := make([]byte, 0, 32)
	for i := 0; i < 32; i++ {
		res = append(res, BASE_RAND_STRING[rng.Intn(len(BASE_RAND_STRING))])
	}

	return string(res)
}

func (j *Judge) Run(ch chan<- JudgeStatus, tests <-chan struct {
	Name string
	In  string
	Out string
}) {
	// Close a channel to send results of judging
	defer close(ch)

	// Identity
	id := RandomName()

	// Working Directory
	path := workingDirectory + "/" + id

	err := os.Mkdir(path, 0664)

	if err != nil {
		ch <- CreateInternalError("Failed to create a directory." + err.Error())

		return
	}

	defer os.RemoveAll(path)

	fp, err := os.Create(path + "/" + j.Compile.SourceFileName)

	if err != nil {
		ch <- CreateInternalError("Failed to create source file." + err.Error())

		return
	}

	l, err := fp.Write([]byte(j.Code))

	if err != nil {
		ch <- CreateInternalError("Failed to write your code on your file." + err.Error())

		return
	}

	if l != len(j.Code) {
		ch <- CreateInternalError("Failed to write your code on your file.")

		return
	}

	// User
	_, err = exec.Command("useradd", "--no-create-home", id).Output()

	if err != nil {
		ch <- CreateInternalError("Failed to create a directory to build your code." + err.Error())

		return
	}

	uid, err := user.Lookup(id)

	if err != nil {
		ch <- CreateInternalError("Failed to look up a user." + err.Error())
	}

	defer exec.Command("userdel", id)

	// Compile
	if j.Compile != nil {
		exe, err := NewExecutor(id, 512 * 1024 * 1024, j.Compile.Cmd, j.Compile.Image, []string{path + ":" + "/work"}, uid.Uid)

		if err != nil {
			ch <- CreateInternalError("Failed to create a Docker container to compile your code." + err.Error())

			return
		}

		res := exe.Run(10000, "")

		exe.Delete()
		if res.Status != ExecFinished {
			switch res.Status {
			case ExecError:
				ch <- CreateInternalError("Failed to execute a compiler." + res.Stderr)

				return
			case ExecMemoryLimitExceeded:
				ch <- JudgeStatus{JR: CompileMemoryLimitExceeded}

				return
			case ExecTimeLimitExceeded:
				ch <- JudgeStatus{JR: CompileTimeLimitExceeded}

				return
			}
		}

		if res.ExitCode != 0 {
			msg := res.Stdout + res.Stderr
			ch <- JudgeStatus{JR: CompileError, Msg: &msg}

			return
		}
	}

	exe, err := NewExecutor(id, j.Mem, j.Exec.Cmd, j.Exec.Image, []string{path + ":" + "/work:ro"}, uid.Uid)

	if err != nil {
		ch <- CreateInternalError("Failed to create a Docker container to judge." + err.Error())

		return
	}

	defer exe.Delete()

	tcCounter := 0
	for tc, res := <-tests; res; tc, res = <-tests {
		res := exe.Run(j.Time, tc.In)

		if res.Status != ExecFinished {
			switch res.Status {
			case ExecError:
				msg := "Failed to execute your code." + res.Stderr
				ch <- JudgeStatus{Case: &tc.Name, JR: InternalError, Msg: &msg}
			case ExecMemoryLimitExceeded:
				ch <- JudgeStatus{Case: &tc.Name, JR: MemoryLimitExceeded}
			case ExecTimeLimitExceeded:
				ch <- JudgeStatus{Case: &tc.Name, JR: TimeLimitExceeded}
			}
		}else {
			if res.ExitCode != 0 {
				ch <- JudgeStatus{Case: &tc.Name, JR: RuntimeError}
			}else {
				if res.Stdout == tc.Out {
					ch <- JudgeStatus{Case: &tc.Name, JR: Accepted}
				}else {
					ch <- JudgeStatus{Case: &tc.Name, JR: WrongAnswer}
				}
			}
		}

		tcCounter++

		msg := strconv.FormatInt(int64(tcCounter), 10) + "/" + strconv.FormatInt(int64(j.TCCount), 10)
		ch <- JudgeStatus{JR: Judging, Msg: &msg}
	}

}
<commit_msg>Sun May 22 18:57:21 JST 2016<commit_after>package main

import "os"
import "github.com/docker/engine-api/client"
import "math/rand"
import "os/exec"
import "strconv"
import "github.com/seehuhn/mt19937"
import "time"
import "os/user"

var cli *client.Client

type ExecRequest struct {
	Image string
	Cmd []string
	SourceFileName string
}

type Judge struct {
	Code string
	Compile *ExecRequest
	Exec ExecRequest
	Time int64
	Mem  int64
	TCCount int // The number of test cases
}

type JudgeResult int

const (
	Accepted            JudgeResult = 0
	WrongAnswer         JudgeResult = 1
	CompileError        JudgeResult = 2
	TimeLimitExceeded   JudgeResult = 3
	MemoryLimitExceeded JudgeResult = 4
	RuntimeError        JudgeResult = 5
	InternalError       JudgeResult = 6
	Judging             JudgeResult = 7
	CompileTimeLimitExceeded JudgeResult = 8
	CompileMemoryLimitExceeded JudgeResult = 9
)

type JudgeStatus struct {
	Case     *string
	JR       JudgeResult
	Mem      int64
	Time     int64
	Msg      *string
}

func CreateInternalError(msg string) JudgeStatus {
	return JudgeStatus{nil, InternalError, 0, 0, &msg}
}

const BASE_RAND_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

func RandomName() string {
	rng := rand.New(mt19937.New())
	rng.Seed(time.Now().UnixNano())

	res := make([]byte, 0, 32)
	for i := 0; i < 32; i++ {
		res = append(res, BASE_RAND_STRING[rng.Intn(len(BASE_RAND_STRING))])
	}

	return string(res)
}

func (j *Judge) Run(ch chan<- JudgeStatus, tests <-chan struct {
	Name string
	In  string
	Out string
}) {
	// Close a channel to send results of judging
	defer close(ch)

	// Identity
	id := RandomName()

	// User
	_, err = exec.Command("useradd", "--no-create-home", id).Output()

	if err != nil {
		ch <- CreateInternalError("Failed to create a directory to build your code. " + err.Error())

		return
	}

	uid, err := user.Lookup(id)

	if err != nil {
		ch <- CreateInternalError("Failed to look up a user. " + err.Error())
	}

	defer exec.Command("userdel", id)

	// Working Directory
	path := workingDirectory + "/" + id

	err := os.Mkdir(path, 0664)

	if err != nil {
		ch <- CreateInternalError("Failed to create a directory. " + err.Error())

		return
	}

	defer os.RemoveAll(path)

	err = os.Chown(path, uid.Uid, uid.Gid)

	if err != nil {
		ch <- CreateInternalError("Failed to chown the directory. " + err.Error())

		return
	}

	// Source File
	fp, err := os.Create(path + "/" + j.Compile.SourceFileName)

	if err != nil {
		ch <- CreateInternalError("Failed to create source file." + err.Error())

		return
	}

	l, err := fp.Write([]byte(j.Code))

	if err != nil {
		ch <- CreateInternalError("Failed to write your code on your file. " + err.Error())

		return
	}

	if l != len(j.Code) {
		ch <- CreateInternalError("Failed to write your code on your file.")

		return
	}

	fp.Close()

	err = os.Chmod(path + "/" + j.Compile.SourceFileName, 0644)

	if err != nil {
		ch <- CreateInternalError("Failed to chmod the source file. " + err.Error())

		return
	}

	// Compile
	if j.Compile != nil {
		exe, err := NewExecutor(id, 512 * 1024 * 1024, j.Compile.Cmd, j.Compile.Image, []string{path + ":" + "/work"}, uid.Uid)

		if err != nil {
			ch <- CreateInternalError("Failed to create a Docker container to compile your code." + err.Error())

			return
		}

		res := exe.Run(10000, "")

		exe.Delete()
		if res.Status != ExecFinished {
			switch res.Status {
			case ExecError:
				ch <- CreateInternalError("Failed to execute a compiler." + res.Stderr)

				return
			case ExecMemoryLimitExceeded:
				ch <- JudgeStatus{JR: CompileMemoryLimitExceeded}

				return
			case ExecTimeLimitExceeded:
				ch <- JudgeStatus{JR: CompileTimeLimitExceeded}

				return
			}
		}

		if res.ExitCode != 0 {
			msg := res.Stdout + res.Stderr
			ch <- JudgeStatus{JR: CompileError, Msg: &msg}

			return
		}
	}

	exe, err := NewExecutor(id, j.Mem, j.Exec.Cmd, j.Exec.Image, []string{path + ":" + "/work:ro"}, uid.Uid)

	if err != nil {
		ch <- CreateInternalError("Failed to create a Docker container to judge." + err.Error())

		return
	}

	defer exe.Delete()

	tcCounter := 0
	for tc, res := <-tests; res; tc, res = <-tests {
		res := exe.Run(j.Time, tc.In)

		if res.Status != ExecFinished {
			switch res.Status {
			case ExecError:
				msg := "Failed to execute your code." + res.Stderr
				ch <- JudgeStatus{Case: &tc.Name, JR: InternalError, Msg: &msg}
			case ExecMemoryLimitExceeded:
				ch <- JudgeStatus{Case: &tc.Name, JR: MemoryLimitExceeded}
			case ExecTimeLimitExceeded:
				ch <- JudgeStatus{Case: &tc.Name, JR: TimeLimitExceeded}
			}
		}else {
			if res.ExitCode != 0 {
				ch <- JudgeStatus{Case: &tc.Name, JR: RuntimeError}
			}else {
				if res.Stdout == tc.Out {
					ch <- JudgeStatus{Case: &tc.Name, JR: Accepted}
				}else {
					ch <- JudgeStatus{Case: &tc.Name, JR: WrongAnswer}
				}
			}
		}

		tcCounter++

		msg := strconv.FormatInt(int64(tcCounter), 10) + "/" + strconv.FormatInt(int64(j.TCCount), 10)
		ch <- JudgeStatus{JR: Judging, Msg: &msg}
	}

}
<|endoftext|>


<commit_before>// The requests package contains logic for loading and unmarshalling
// data contained within web requests.  The most common uses for this
// library are as follows:
//
//     params, err := requests.New(request).Params()
//
//     err := requests.New(request).Unmarshal(structPtr)
//
// For parameter parsing, the requests package uses the following
// logic:
//
// 1. A map[string]interface{} is created to store parameters.
// 2. If there are URL parameters, they are appended to the
// map[string]interface{} using standard urlencode unmarshalling.
// 3. If the request body is non-empty:
//   1. Look up a codec matching the request's Content-Type header.
//   2. If no matching codec is found, fall back on urlencoded data.
//   3. Unmarshal values from the request body and append them to the
//   map[string]interface{}.
//
// The return value is the map[string]interface{} generated during
// that process.
//
// For the Unmarshal process, the requests package uses a combination
// of reflection (to check field tags) and interfaces to figure out
// which values (from the above params) should be applied to which
// fields in the target struct.  Unmarshalling to non-struct types is
// not supported.
package requests

import "net/http"

type Request struct {
	httpRequest *http.Request
	body        interface{}
	params      map[string]interface{}
}

func New(request *http.Request) *Request {
	return &Request{httpRequest: request}
}
<commit_msg>godoc.org documentation format cleanup, step 1<commit_after>// The requests package contains logic for loading and unmarshalling
// data contained within web requests.  The most common uses for this
// library are as follows:
//
//     params, err := requests.New(request).Params()
//
//     err := requests.New(request).Unmarshal(structPtr)
//
// For parameter parsing, the requests package uses the following
// logic:
//
//   1. A map[string]interface{} is created to store parameters.
//   2. If there are URL parameters, they are appended to the
//      map[string]interface{} using standard urlencode unmarshalling.
//   3. If the request body is non-empty:
//     1. Look up a codec matching the request's Content-Type header.
//     2. If no matching codec is found, fall back on urlencoded data.
//     3. Unmarshal values from the request body and append them to the
//        map[string]interface{}.
//
// The return value is the map[string]interface{} generated during
// that process.
//
// For the Unmarshal process, the requests package uses a combination
// of reflection (to check field tags) and interfaces to figure out
// which values (from the above params) should be applied to which
// fields in the target struct.  Unmarshalling to non-struct types is
// not supported.
package requests

import "net/http"

type Request struct {
	httpRequest *http.Request
	body        interface{}
	params      map[string]interface{}
}

func New(request *http.Request) *Request {
	return &Request{httpRequest: request}
}
<|endoftext|>


<commit_before>/*
 * (c) 2014, Tonnerre Lombard <tonnerre@ancient-solutions.com>,
 *	     Starship Factory. All rights reserved.
 *
 * Redistribution and use in source  and binary forms, with or without
 * modification, are permitted  provided that the following conditions
 * are met:
 *
 * * Redistributions of  source code  must retain the  above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this  list of conditions and the  following disclaimer in
 *   the  documentation  and/or  other  materials  provided  with  the
 *   distribution.
 * * Neither  the name  of the Starship Factory  nor the  name  of its
 *   contributors may  be used to endorse or  promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS"  AND ANY EXPRESS  OR IMPLIED WARRANTIES  OF MERCHANTABILITY
 * AND FITNESS  FOR A PARTICULAR  PURPOSE ARE DISCLAIMED. IN  NO EVENT
 * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED  TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT  LIABILITY,  OR  TORT  (INCLUDING NEGLIGENCE  OR  OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package x509keyserver

import (
	"code.google.com/p/goprotobuf/proto"
	"crypto/x509"
	"crypto/x509/pkix"
	"database/cassandra"
	"encoding/binary"
	"errors"
	"fmt"
	"time"
)

// Object for retrieving X.509 certificates from the Cassandra database.
type X509KeyDB struct {
	db *cassandra.RetryCassandraClient
}

// List of all column names in the certificate column family.
var certificate_DisplayColumns [][]byte = [][]byte{
	[]byte("subject"), []byte("issuer"), []byte("expires"),
}
var certificate_AllColumns [][]byte = [][]byte{
	[]byte("subject"), []byte("issuer"), []byte("expires"), []byte("der_certificate"),
}

func formatCertSubject(name pkix.Name) []byte {
	var ret, val string

	for _, val = range name.Country {
		ret += fmt.Sprintf("/C=%s", val)
	}
	for _, val = range name.Province {
		ret += fmt.Sprintf("/SP=%s", val)
	}
	for _, val = range name.Locality {
		ret += fmt.Sprintf("/L=%s", val)
	}
	for _, val = range name.StreetAddress {
		ret += fmt.Sprintf("/A=%s", val)
	}
	for _, val = range name.Organization {
		ret += fmt.Sprintf("/O=%s", val)
	}
	for _, val = range name.OrganizationalUnit {
		ret += fmt.Sprintf("/OU=%s", val)
	}
	return []byte(fmt.Sprintf("%s/CN=%s", ret, name.CommonName))
}

// Connect to the X.509 key database given as "dbserver" and "keyspace".
func NewX509KeyDB(dbserver, keyspace string) (*X509KeyDB, error) {
	var client *cassandra.RetryCassandraClient
	var ire *cassandra.InvalidRequestException
	var err error

	client, err = cassandra.NewRetryCassandraClient(dbserver)
	if err != nil {
		return nil, err
	}

	ire, err = client.SetKeyspace(keyspace)
	if ire != nil {
		return nil, errors.New(ire.Why)
	}
	if err != nil {
		return nil, err
	}

	return &X509KeyDB{
		db: client,
	}, nil
}

// List the next "count" known certificates starting from "start_index".
func (db *X509KeyDB) ListCertificates(start_index uint64, count int32) ([]*X509KeyData, error) {
	var ret []*X509KeyData
	var cp *cassandra.ColumnParent = cassandra.NewColumnParent()
	var pred *cassandra.SlicePredicate = cassandra.NewSlicePredicate()
	var kr *cassandra.KeyRange = cassandra.NewKeyRange()
	var r []*cassandra.KeySlice
	var ks *cassandra.KeySlice
	var ire *cassandra.InvalidRequestException
	var ue *cassandra.UnavailableException
	var te *cassandra.TimedOutException
	var err error

	cp.ColumnFamily = "certificate"
	pred.ColumnNames = certificate_DisplayColumns

	if start_index > 0 {
		binary.BigEndian.PutUint64(kr.StartKey, start_index)
	} else {
		kr.StartKey = make([]byte, 0)
	}
	kr.EndKey = make([]byte, 0)
	kr.Count = count

	r, ire, ue, te, err = db.db.GetRangeSlices(cp, pred, kr,
		cassandra.ConsistencyLevel_ONE)
	if ire != nil {
		return ret, errors.New(ire.Why)
	}
	if ue != nil {
		return ret, errors.New("Unavailable")
	}
	if te != nil {
		return ret, errors.New("Timed out")
	}
	if err != nil {
		return ret, err
	}

	for _, ks = range r {
		var rv *X509KeyData = new(X509KeyData)
		var cos *cassandra.ColumnOrSuperColumn
		rv.Index = proto.Uint64(binary.BigEndian.Uint64(ks.Key))

		for _, cos = range ks.Columns {
			var col *cassandra.Column = cos.Column
			if col == nil {
				continue
			}

			if string(col.Name) == "subject" {
				rv.Subject = proto.String(string(col.Value))
			} else if string(col.Name) == "issuer" {
				rv.Issuer = proto.String(string(col.Value))
			} else if string(col.Name) == "issuer" {
				rv.Issuer = proto.String(string(col.Value))
			} else if string(col.Name) == "expires" {
				rv.Expires = proto.Uint64(binary.BigEndian.Uint64(col.Value))
			} else {
				return ret, errors.New("Unexpected column: " + string(col.Name))
			}
		}

		ret = append(ret, rv)
	}

	return ret, nil
}

// Retrieve the certificate with the given index number from the database.
func (db *X509KeyDB) RetrieveCertificateByIndex(index uint64) (*x509.Certificate, error) {
	var cp *cassandra.ColumnPath = cassandra.NewColumnPath()
	var r *cassandra.ColumnOrSuperColumn
	var ire *cassandra.InvalidRequestException
	var nfe *cassandra.NotFoundException
	var ue *cassandra.UnavailableException
	var te *cassandra.TimedOutException
	var key []byte
	var err error

	binary.BigEndian.PutUint64(key, index)

	cp.ColumnFamily = "certificate"
	cp.Column = []byte("der_certificate")

	r, ire, nfe, ue, te, err = db.db.Get(key, cp, cassandra.ConsistencyLevel_ONE)
	if ire != nil {
		return nil, errors.New(ire.Why)
	}
	if nfe != nil {
		return nil, errors.New("Certificate not found")
	}
	if ue != nil {
		return nil, errors.New("Unavailable")
	}
	if te != nil {
		return nil, errors.New("Timed out")
	}
	if err != nil {
		return nil, err
	}

	if r.Column == nil {
		return nil, errors.New("Column not found")
	}

	return x509.ParseCertificate(r.Column.Value)
}

// Add all relevant data for the given X.509 certificate.
func (db *X509KeyDB) AddX509Certificate(cert *x509.Certificate) error {
	var now time.Time = time.Now()
	var mmap = make(map[string]map[string][]*cassandra.Mutation)
	var mutation *cassandra.Mutation
	var expires uint64
	var key []byte = make([]byte, 8)
	var ire *cassandra.InvalidRequestException
	var ue *cassandra.UnavailableException
	var te *cassandra.TimedOutException
	var err error

	binary.BigEndian.PutUint64(key, cert.SerialNumber.Uint64())
	mmap[string(key)] = make(map[string][]*cassandra.Mutation)
	mmap[string(key)]["certificate"] = make([]*cassandra.Mutation, 0)

	mutation = cassandra.NewMutation()
	mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn()
	mutation.ColumnOrSupercolumn.Column = cassandra.NewColumn()
	mutation.ColumnOrSupercolumn.Column.Name = []byte("subject")
	mutation.ColumnOrSupercolumn.Column.Value = formatCertSubject(cert.Subject)
	mutation.ColumnOrSupercolumn.Column.Timestamp = now.UnixNano()
	mmap[string(key)]["certificate"] = append(
		mmap[string(key)]["certificate"], mutation)

	mutation = cassandra.NewMutation()
	mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn()
	mutation.ColumnOrSupercolumn.Column = cassandra.NewColumn()
	mutation.ColumnOrSupercolumn.Column.Name = []byte("issuer")
	mutation.ColumnOrSupercolumn.Column.Value = formatCertSubject(cert.Issuer)
	mutation.ColumnOrSupercolumn.Column.Timestamp = now.UnixNano()
	mmap[string(key)]["certificate"] = append(
		mmap[string(key)]["certificate"], mutation)

	expires = uint64(cert.NotAfter.Unix())

	mutation = cassandra.NewMutation()
	mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn()
	mutation.ColumnOrSupercolumn.Column = cassandra.NewColumn()
	mutation.ColumnOrSupercolumn.Column.Name = []byte("expires")
	mutation.ColumnOrSupercolumn.Column.Value = make([]byte, 8)
	binary.BigEndian.PutUint64(mutation.ColumnOrSupercolumn.Column.Value, expires)
	mutation.ColumnOrSupercolumn.Column.Timestamp = now.UnixNano()
	mmap[string(key)]["certificate"] = append(
		mmap[string(key)]["certificate"], mutation)

	// Commit the data into the database.
	ire, ue, te, err = db.db.BatchMutate(mmap, cassandra.ConsistencyLevel_QUORUM)
	if ire != nil {
		return errors.New(ire.Why)
	}
	if ue != nil {
		return errors.New("Unavailable")
	}
	if te != nil {
		return errors.New("Timed out")
	}
	return err
}
<commit_msg>Actually store the raw certificate.<commit_after>/*
 * (c) 2014, Tonnerre Lombard <tonnerre@ancient-solutions.com>,
 *	     Starship Factory. All rights reserved.
 *
 * Redistribution and use in source  and binary forms, with or without
 * modification, are permitted  provided that the following conditions
 * are met:
 *
 * * Redistributions of  source code  must retain the  above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this  list of conditions and the  following disclaimer in
 *   the  documentation  and/or  other  materials  provided  with  the
 *   distribution.
 * * Neither  the name  of the Starship Factory  nor the  name  of its
 *   contributors may  be used to endorse or  promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS"  AND ANY EXPRESS  OR IMPLIED WARRANTIES  OF MERCHANTABILITY
 * AND FITNESS  FOR A PARTICULAR  PURPOSE ARE DISCLAIMED. IN  NO EVENT
 * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED  TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT  LIABILITY,  OR  TORT  (INCLUDING NEGLIGENCE  OR  OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package x509keyserver

import (
	"code.google.com/p/goprotobuf/proto"
	"crypto/x509"
	"crypto/x509/pkix"
	"database/cassandra"
	"encoding/binary"
	"errors"
	"fmt"
	"time"
)

// Object for retrieving X.509 certificates from the Cassandra database.
type X509KeyDB struct {
	db *cassandra.RetryCassandraClient
}

// List of all column names in the certificate column family.
var certificate_DisplayColumns [][]byte = [][]byte{
	[]byte("subject"), []byte("issuer"), []byte("expires"),
}
var certificate_AllColumns [][]byte = [][]byte{
	[]byte("subject"), []byte("issuer"), []byte("expires"), []byte("der_certificate"),
}

func formatCertSubject(name pkix.Name) []byte {
	var ret, val string

	for _, val = range name.Country {
		ret += fmt.Sprintf("/C=%s", val)
	}
	for _, val = range name.Province {
		ret += fmt.Sprintf("/SP=%s", val)
	}
	for _, val = range name.Locality {
		ret += fmt.Sprintf("/L=%s", val)
	}
	for _, val = range name.StreetAddress {
		ret += fmt.Sprintf("/A=%s", val)
	}
	for _, val = range name.Organization {
		ret += fmt.Sprintf("/O=%s", val)
	}
	for _, val = range name.OrganizationalUnit {
		ret += fmt.Sprintf("/OU=%s", val)
	}
	return []byte(fmt.Sprintf("%s/CN=%s", ret, name.CommonName))
}

// Connect to the X.509 key database given as "dbserver" and "keyspace".
func NewX509KeyDB(dbserver, keyspace string) (*X509KeyDB, error) {
	var client *cassandra.RetryCassandraClient
	var ire *cassandra.InvalidRequestException
	var err error

	client, err = cassandra.NewRetryCassandraClient(dbserver)
	if err != nil {
		return nil, err
	}

	ire, err = client.SetKeyspace(keyspace)
	if ire != nil {
		return nil, errors.New(ire.Why)
	}
	if err != nil {
		return nil, err
	}

	return &X509KeyDB{
		db: client,
	}, nil
}

// List the next "count" known certificates starting from "start_index".
func (db *X509KeyDB) ListCertificates(start_index uint64, count int32) ([]*X509KeyData, error) {
	var ret []*X509KeyData
	var cp *cassandra.ColumnParent = cassandra.NewColumnParent()
	var pred *cassandra.SlicePredicate = cassandra.NewSlicePredicate()
	var kr *cassandra.KeyRange = cassandra.NewKeyRange()
	var r []*cassandra.KeySlice
	var ks *cassandra.KeySlice
	var ire *cassandra.InvalidRequestException
	var ue *cassandra.UnavailableException
	var te *cassandra.TimedOutException
	var err error

	cp.ColumnFamily = "certificate"
	pred.ColumnNames = certificate_DisplayColumns

	if start_index > 0 {
		binary.BigEndian.PutUint64(kr.StartKey, start_index)
	} else {
		kr.StartKey = make([]byte, 0)
	}
	kr.EndKey = make([]byte, 0)
	kr.Count = count

	r, ire, ue, te, err = db.db.GetRangeSlices(cp, pred, kr,
		cassandra.ConsistencyLevel_ONE)
	if ire != nil {
		return ret, errors.New(ire.Why)
	}
	if ue != nil {
		return ret, errors.New("Unavailable")
	}
	if te != nil {
		return ret, errors.New("Timed out")
	}
	if err != nil {
		return ret, err
	}

	for _, ks = range r {
		var rv *X509KeyData = new(X509KeyData)
		var cos *cassandra.ColumnOrSuperColumn
		rv.Index = proto.Uint64(binary.BigEndian.Uint64(ks.Key))

		for _, cos = range ks.Columns {
			var col *cassandra.Column = cos.Column
			if col == nil {
				continue
			}

			if string(col.Name) == "subject" {
				rv.Subject = proto.String(string(col.Value))
			} else if string(col.Name) == "issuer" {
				rv.Issuer = proto.String(string(col.Value))
			} else if string(col.Name) == "issuer" {
				rv.Issuer = proto.String(string(col.Value))
			} else if string(col.Name) == "expires" {
				rv.Expires = proto.Uint64(binary.BigEndian.Uint64(col.Value))
			} else {
				return ret, errors.New("Unexpected column: " + string(col.Name))
			}
		}

		ret = append(ret, rv)
	}

	return ret, nil
}

// Retrieve the certificate with the given index number from the database.
func (db *X509KeyDB) RetrieveCertificateByIndex(index uint64) (*x509.Certificate, error) {
	var cp *cassandra.ColumnPath = cassandra.NewColumnPath()
	var r *cassandra.ColumnOrSuperColumn
	var ire *cassandra.InvalidRequestException
	var nfe *cassandra.NotFoundException
	var ue *cassandra.UnavailableException
	var te *cassandra.TimedOutException
	var key []byte
	var err error

	binary.BigEndian.PutUint64(key, index)

	cp.ColumnFamily = "certificate"
	cp.Column = []byte("der_certificate")

	r, ire, nfe, ue, te, err = db.db.Get(key, cp, cassandra.ConsistencyLevel_ONE)
	if ire != nil {
		return nil, errors.New(ire.Why)
	}
	if nfe != nil {
		return nil, errors.New("Certificate not found")
	}
	if ue != nil {
		return nil, errors.New("Unavailable")
	}
	if te != nil {
		return nil, errors.New("Timed out")
	}
	if err != nil {
		return nil, err
	}

	if r.Column == nil {
		return nil, errors.New("Column not found")
	}

	return x509.ParseCertificate(r.Column.Value)
}

// Add all relevant data for the given X.509 certificate.
func (db *X509KeyDB) AddX509Certificate(cert *x509.Certificate) error {
	var now time.Time = time.Now()
	var mmap = make(map[string]map[string][]*cassandra.Mutation)
	var mutation *cassandra.Mutation
	var expires uint64
	var key []byte = make([]byte, 8)
	var ire *cassandra.InvalidRequestException
	var ue *cassandra.UnavailableException
	var te *cassandra.TimedOutException
	var err error

	binary.BigEndian.PutUint64(key, cert.SerialNumber.Uint64())
	mmap[string(key)] = make(map[string][]*cassandra.Mutation)
	mmap[string(key)]["certificate"] = make([]*cassandra.Mutation, 0)

	mutation = cassandra.NewMutation()
	mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn()
	mutation.ColumnOrSupercolumn.Column = cassandra.NewColumn()
	mutation.ColumnOrSupercolumn.Column.Name = []byte("subject")
	mutation.ColumnOrSupercolumn.Column.Value = formatCertSubject(cert.Subject)
	mutation.ColumnOrSupercolumn.Column.Timestamp = now.UnixNano()
	mmap[string(key)]["certificate"] = append(
		mmap[string(key)]["certificate"], mutation)

	mutation = cassandra.NewMutation()
	mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn()
	mutation.ColumnOrSupercolumn.Column = cassandra.NewColumn()
	mutation.ColumnOrSupercolumn.Column.Name = []byte("issuer")
	mutation.ColumnOrSupercolumn.Column.Value = formatCertSubject(cert.Issuer)
	mutation.ColumnOrSupercolumn.Column.Timestamp = now.UnixNano()
	mmap[string(key)]["certificate"] = append(
		mmap[string(key)]["certificate"], mutation)

	expires = uint64(cert.NotAfter.Unix())

	mutation = cassandra.NewMutation()
	mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn()
	mutation.ColumnOrSupercolumn.Column = cassandra.NewColumn()
	mutation.ColumnOrSupercolumn.Column.Name = []byte("expires")
	mutation.ColumnOrSupercolumn.Column.Value = make([]byte, 8)
	binary.BigEndian.PutUint64(mutation.ColumnOrSupercolumn.Column.Value, expires)
	mutation.ColumnOrSupercolumn.Column.Timestamp = now.UnixNano()
	mmap[string(key)]["certificate"] = append(
		mmap[string(key)]["certificate"], mutation)

	mutation = cassandra.NewMutation()
	mutation.ColumnOrSupercolumn = cassandra.NewColumnOrSuperColumn()
	mutation.ColumnOrSupercolumn.Column = cassandra.NewColumn()
	mutation.ColumnOrSupercolumn.Column.Name = []byte("der_certificate")
	mutation.ColumnOrSupercolumn.Column.Value = cert.Raw
	mutation.ColumnOrSupercolumn.Column.Timestamp = now.UnixNano()
	mmap[string(key)]["certificate"] = append(
		mmap[string(key)]["certificate"], mutation)

	// Commit the data into the database.
	ire, ue, te, err = db.db.BatchMutate(mmap, cassandra.ConsistencyLevel_QUORUM)
	if ire != nil {
		return errors.New(ire.Why)
	}
	if ue != nil {
		return errors.New("Unavailable")
	}
	if te != nil {
		return errors.New("Timed out")
	}
	return err
}
<|endoftext|>


<commit_before><commit_msg>Applied requested changes<commit_after><|endoftext|>


<commit_before>package echo

import (
	"bufio"
	"net"
	"net/http"
)

type (
	// Response wraps an http.ResponseWriter and implements its interface to be used
	// by an HTTP handler to construct an HTTP response.
	// See: https://golang.org/pkg/net/http/#ResponseWriter
	Response struct {
		echo        *Echo
		beforeFuncs []func()
		Writer      http.ResponseWriter
		Status      int
		Size        int64
		Committed   bool
	}
)

// NewResponse creates a new instance of Response.
func NewResponse(w http.ResponseWriter, e *Echo) (r *Response) {
	return &Response{Writer: w, echo: e}
}

// Header returns the header map for the writer that will be sent by
// WriteHeader. Changing the header after a call to WriteHeader (or Write) has
// no effect unless the modified headers were declared as trailers by setting
// the "Trailer" header before the call to WriteHeader (see example)
// To suppress implicit response headers, set their value to nil.
// Example: https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
func (r *Response) Header() http.Header {
	return r.Writer.Header()
}

// Before registers a function which is called just before the response is written.
func (r *Response) Before(fn func()) {
	r.beforeFuncs = append(r.beforeFuncs, fn)
}

// WriteHeader sends an HTTP response header with status code. If WriteHeader is
// not called explicitly, the first call to Write will trigger an implicit
// WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly
// used to send error codes.
func (r *Response) WriteHeader(code int) {
	if r.Committed {
		r.echo.Logger.Warn("response already committed")
		return
	}
	for _, fn := range r.beforeFuncs {
		fn()
	}
	r.Status = code
	r.Writer.WriteHeader(code)
	r.Committed = true
}

// Write writes the data to the connection as part of an HTTP reply.
func (r *Response) Write(b []byte) (n int, err error) {
	if !r.Committed {
		r.WriteHeader(http.StatusOK)
	}
	n, err = r.Writer.Write(b)
	r.Size += int64(n)
	return
}

// Flush implements the http.Flusher interface to allow an HTTP handler to flush
// buffered data to the client.
// See [http.Flusher](https://golang.org/pkg/net/http/#Flusher)
func (r *Response) Flush() {
	r.Writer.(http.Flusher).Flush()
}

// Hijack implements the http.Hijacker interface to allow an HTTP handler to
// take over the connection.
// See [http.Hijacker](https://golang.org/pkg/net/http/#Hijacker)
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return r.Writer.(http.Hijacker).Hijack()
}

// CloseNotify implements the http.CloseNotifier interface to allow detecting
// when the underlying connection has gone away.
// This mechanism can be used to cancel long operations on the server if the
// client has disconnected before the response is ready.
// See [http.CloseNotifier](https://golang.org/pkg/net/http/#CloseNotifier)
func (r *Response) CloseNotify() <-chan bool {
	return r.Writer.(http.CloseNotifier).CloseNotify()
}

func (r *Response) reset(w http.ResponseWriter) {
	r.Writer = w
	r.Size = 0
	r.Status = http.StatusOK
	r.Committed = false
}
<commit_msg>Fixed Response#Before() Implemented Response#After()<commit_after>package echo

import (
	"bufio"
	"net"
	"net/http"
	"strconv"
)

type (
	// Response wraps an http.ResponseWriter and implements its interface to be used
	// by an HTTP handler to construct an HTTP response.
	// See: https://golang.org/pkg/net/http/#ResponseWriter
	Response struct {
		echo          *Echo
		contentLength int64
		beforeFuncs   []func()
		afterFuncs    []func()
		Writer        http.ResponseWriter
		Status        int
		Size          int64
		Committed     bool
	}
)

// NewResponse creates a new instance of Response.
func NewResponse(w http.ResponseWriter, e *Echo) (r *Response) {
	return &Response{Writer: w, echo: e}
}

// Header returns the header map for the writer that will be sent by
// WriteHeader. Changing the header after a call to WriteHeader (or Write) has
// no effect unless the modified headers were declared as trailers by setting
// the "Trailer" header before the call to WriteHeader (see example)
// To suppress implicit response headers, set their value to nil.
// Example: https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
func (r *Response) Header() http.Header {
	return r.Writer.Header()
}

// Before registers a function which is called just before the response is written.
func (r *Response) Before(fn func()) {
	r.beforeFuncs = append(r.beforeFuncs, fn)
}

// After registers a function which is called just after the response is written.
// If the `Content-Length` is unknown, none of the after function is executed.
func (r *Response) After(fn func()) {
	r.afterFuncs = append(r.afterFuncs, fn)
}

// WriteHeader sends an HTTP response header with status code. If WriteHeader is
// not called explicitly, the first call to Write will trigger an implicit
// WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly
// used to send error codes.
func (r *Response) WriteHeader(code int) {
	if r.Committed {
		r.echo.Logger.Warn("response already committed")
		return
	}
	for _, fn := range r.beforeFuncs {
		fn()
	}
	r.Status = code
	r.Writer.WriteHeader(code)
	r.Committed = true
	r.contentLength, _ = strconv.ParseInt(r.Header().Get(HeaderContentLength), 10, 0)
}

// Write writes the data to the connection as part of an HTTP reply.
func (r *Response) Write(b []byte) (n int, err error) {
	if !r.Committed {
		r.WriteHeader(http.StatusOK)
	}
	n, err = r.Writer.Write(b)
	r.Size += int64(n)
	if r.Size == r.contentLength {
		for _, fn := range r.afterFuncs {
			fn()
		}
	}
	return
}

// Flush implements the http.Flusher interface to allow an HTTP handler to flush
// buffered data to the client.
// See [http.Flusher](https://golang.org/pkg/net/http/#Flusher)
func (r *Response) Flush() {
	r.Writer.(http.Flusher).Flush()
}

// Hijack implements the http.Hijacker interface to allow an HTTP handler to
// take over the connection.
// See [http.Hijacker](https://golang.org/pkg/net/http/#Hijacker)
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return r.Writer.(http.Hijacker).Hijack()
}

// CloseNotify implements the http.CloseNotifier interface to allow detecting
// when the underlying connection has gone away.
// This mechanism can be used to cancel long operations on the server if the
// client has disconnected before the response is ready.
// See [http.CloseNotifier](https://golang.org/pkg/net/http/#CloseNotifier)
func (r *Response) CloseNotify() <-chan bool {
	return r.Writer.(http.CloseNotifier).CloseNotify()
}

func (r *Response) reset(w http.ResponseWriter) {
	r.contentLength = 0
	r.beforeFuncs = nil
	r.afterFuncs = nil
	r.Writer = w
	r.Size = 0
	r.Status = http.StatusOK
	r.Committed = false
}
<|endoftext|>


<commit_before>package rest

import (
	"fmt"
	"log"
	"net/http"
	"sort"
	"sync"

	"github.com/gorilla/mux"
)

// Address is the address and port to bind to (e.g. ":8080").
type Address string

// FilePath represents a file path.
type FilePath string

// API is the top-level interface encapsulating an HTTP REST server. It's responsible for
// registering ResourceHandlers and routing requests. Use NewAPI to retrieve an instance.
type API interface {
	// Start begins serving requests. This will block unless it fails, in which case an
	// error will be returned. This will validate any defined Rules. If any Rules are
	// invalid, it will panic.
	Start(Address) error

	// StartTLS begins serving requests received over HTTPS connections. This will block
	// unless it fails, in which case an error will be returned. Files containing a
	// certificate and matching private key for the server must be provided. If the
	// certificate is signed by a certificate authority, the certFile should be the
	// concatenation of the server's certificate followed by the CA's certificate. This
	// will validate any defined Rules. If any Rules are invalid, it will panic.
	StartTLS(Address, FilePath, FilePath) error

	// RegisterResourceHandler binds the provided ResourceHandler to the appropriate REST
	// endpoints and applies any specified middleware. Endpoints will have the following
	// base URL: /api/:version/resourceName.
	RegisterResourceHandler(ResourceHandler, ...RequestMiddleware)

	// RegisterResponseSerializer registers the provided ResponseSerializer with the given
	// format. If the format has already been registered, it will be overwritten.
	RegisterResponseSerializer(string, ResponseSerializer)

	// UnregisterResponseSerializer unregisters the ResponseSerializer with the provided
	// format. If the format hasn't been registered, this is a no-op.
	UnregisterResponseSerializer(string)

	// AvailableFormats returns a slice containing all of the available serialization
	// formats currently available.
	AvailableFormats() []string

	// responseSerializer returns a ResponseSerializer for the given format type. If the
	// format is not implemented, the returned serializer will be nil and the error set.
	responseSerializer(string) (ResponseSerializer, error)
}

// RequestMiddleware is a function that returns a HandlerFunc wrapping the provided HandlerFunc.
// This allows injecting custom logic to operate on requests (e.g. performing authentication).
type RequestMiddleware func(http.HandlerFunc) http.HandlerFunc

// newAuthMiddleware returns a RequestMiddleware used to authenticate requests.
func newAuthMiddleware(authenticate func(*http.Request) error) RequestMiddleware {
	return func(wrapped http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if err := authenticate(r); err != nil {
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte(err.Error()))
				return
			}
			wrapped(w, r)
		}
	}
}

// muxAPI is an implementation of the API interface which relies on the gorilla/mux
// package to handle request dispatching (see http://www.gorillatoolkit.org/pkg/mux).
type muxAPI struct {
	router             *mux.Router
	mu                 sync.RWMutex
	handler            *requestHandler
	serializerRegistry map[string]ResponseSerializer
	resourceHandlers   []ResourceHandler
}

// NewAPI returns a newly allocated API instance.
func NewAPI() API {
	r := mux.NewRouter()
	restAPI := &muxAPI{
		router:             r,
		serializerRegistry: map[string]ResponseSerializer{"json": &jsonSerializer{}},
		resourceHandlers:   make([]ResourceHandler, 0),
	}
	restAPI.handler = &requestHandler{restAPI}
	return restAPI
}

// Start begins serving requests. This will block unless it fails, in which case an error will be
// returned.
func (r *muxAPI) Start(addr Address) error {
	r.validateRules()
	return http.ListenAndServe(string(addr), r.router)
}

// StartTLS begins serving requests received over HTTPS connections. This will block unless it
// fails, in which case an error will be returned. Files containing a certificate and matching
// private key for the server must be provided. If the certificate is signed by a certificate
// authority, the certFile should be the concatenation of the server's certificate followed by
// the CA's certificate.
func (r *muxAPI) StartTLS(addr Address, certFile, keyFile FilePath) error {
	r.validateRules()
	return http.ListenAndServeTLS(string(addr), string(certFile), string(keyFile), r.router)
}

// RegisterResourceHandler binds the provided ResourceHandler to the appropriate REST endpoints and
// applies any specified middleware. Endpoints will have the following base URL:
// /api/:version/resourceName.
func (r *muxAPI) RegisterResourceHandler(h ResourceHandler, middleware ...RequestMiddleware) {
	h = resourceHandlerProxy{h}
	resource := h.ResourceName()
	middleware = append(middleware, newAuthMiddleware(h.Authenticate))

	r.router.HandleFunc(
		h.CreateURI(), applyMiddleware(r.handler.handleCreate(h), middleware),
	).Methods("POST").Name(resource + ":create")
	log.Printf("Registered create handler at POST %s", h.CreateURI())

	r.router.HandleFunc(
		h.ReadListURI(), applyMiddleware(r.handler.handleReadList(h), middleware),
	).Methods("GET").Name(resource + ":readList")
	log.Printf("Registered read list handler at GET %s", h.ReadListURI())

	r.router.HandleFunc(
		h.ReadURI(), applyMiddleware(r.handler.handleRead(h), middleware),
	).Methods("GET").Name(resource + ":read")
	log.Printf("Registered read handler at GET %s", h.ReadURI())

	r.router.HandleFunc(
		h.UpdateURI(), applyMiddleware(r.handler.handleUpdate(h), middleware),
	).Methods("PUT").Name(resource + ":update")
	log.Printf("Registered update handler at UPDATE %s", h.UpdateURI())

	r.router.HandleFunc(
		h.DeleteURI(), applyMiddleware(r.handler.handleDelete(h), middleware),
	).Methods("DELETE").Name(resource + ":delete")
	log.Printf("Registered delete handler at DELETE %s", h.DeleteURI())

	r.resourceHandlers = append(r.resourceHandlers, h)
}

// RegisterResponseSerializer registers the provided ResponseSerializer with the given format. If the
// format has already been registered, it will be overwritten.
func (r *muxAPI) RegisterResponseSerializer(format string, serializer ResponseSerializer) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.serializerRegistry[format] = serializer
}

// UnregisterResponseSerializer unregisters the ResponseSerializer with the provided format. If the
// format hasn't been registered, this is a no-op.
func (r *muxAPI) UnregisterResponseSerializer(format string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	delete(r.serializerRegistry, format)
}

// AvailableFormats returns a slice containing all of the available serialization formats
// currently available.
func (r *muxAPI) AvailableFormats() []string {
	r.mu.RLock()
	defer r.mu.RUnlock()
	formats := make([]string, 0, len(r.serializerRegistry))
	for format := range r.serializerRegistry {
		formats = append(formats, format)
	}
	sort.Strings(formats)
	return formats
}

// responseSerializer returns a ResponseSerializer for the given format type. If the format
// is not implemented, the returned serializer will be nil and the error set.
func (r *muxAPI) responseSerializer(format string) (ResponseSerializer, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	if serializer, ok := r.serializerRegistry[format]; ok {
		return serializer, nil
	}
	return nil, fmt.Errorf("Format not implemented: %s", format)
}

// applyMiddleware wraps the HandlerFunc with the provided RequestMiddleware and returns the
// function composition.
func applyMiddleware(h http.HandlerFunc, middleware []RequestMiddleware) http.HandlerFunc {
	for _, m := range middleware {
		h = m(h)
	}

	return h
}

// validateRules verifies that the Rules for each ResourceHandler registered with the muxAPI
// are valid, meaning they specify fields that exist and correct types. If a Rule is invalid,
// this will panic.
func (r *muxAPI) validateRules() {
	for _, handler := range r.resourceHandlers {
		rules := handler.Rules()
		if rules == nil || rules.Size() == 0 {
			continue
		}

		if err := rules.Validate(); err != nil {
			panic(err)
		}
	}
}
<commit_msg>Fix update list handler name<commit_after>package rest

import (
	"fmt"
	"log"
	"net/http"
	"sort"
	"sync"

	"github.com/gorilla/mux"
)

// Address is the address and port to bind to (e.g. ":8080").
type Address string

// FilePath represents a file path.
type FilePath string

// API is the top-level interface encapsulating an HTTP REST server. It's responsible for
// registering ResourceHandlers and routing requests. Use NewAPI to retrieve an instance.
type API interface {
	// Start begins serving requests. This will block unless it fails, in which case an
	// error will be returned. This will validate any defined Rules. If any Rules are
	// invalid, it will panic.
	Start(Address) error

	// StartTLS begins serving requests received over HTTPS connections. This will block
	// unless it fails, in which case an error will be returned. Files containing a
	// certificate and matching private key for the server must be provided. If the
	// certificate is signed by a certificate authority, the certFile should be the
	// concatenation of the server's certificate followed by the CA's certificate. This
	// will validate any defined Rules. If any Rules are invalid, it will panic.
	StartTLS(Address, FilePath, FilePath) error

	// RegisterResourceHandler binds the provided ResourceHandler to the appropriate REST
	// endpoints and applies any specified middleware. Endpoints will have the following
	// base URL: /api/:version/resourceName.
	RegisterResourceHandler(ResourceHandler, ...RequestMiddleware)

	// RegisterResponseSerializer registers the provided ResponseSerializer with the given
	// format. If the format has already been registered, it will be overwritten.
	RegisterResponseSerializer(string, ResponseSerializer)

	// UnregisterResponseSerializer unregisters the ResponseSerializer with the provided
	// format. If the format hasn't been registered, this is a no-op.
	UnregisterResponseSerializer(string)

	// AvailableFormats returns a slice containing all of the available serialization
	// formats currently available.
	AvailableFormats() []string

	// responseSerializer returns a ResponseSerializer for the given format type. If the
	// format is not implemented, the returned serializer will be nil and the error set.
	responseSerializer(string) (ResponseSerializer, error)
}

// RequestMiddleware is a function that returns a HandlerFunc wrapping the provided HandlerFunc.
// This allows injecting custom logic to operate on requests (e.g. performing authentication).
type RequestMiddleware func(http.HandlerFunc) http.HandlerFunc

// newAuthMiddleware returns a RequestMiddleware used to authenticate requests.
func newAuthMiddleware(authenticate func(*http.Request) error) RequestMiddleware {
	return func(wrapped http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if err := authenticate(r); err != nil {
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte(err.Error()))
				return
			}
			wrapped(w, r)
		}
	}
}

// muxAPI is an implementation of the API interface which relies on the gorilla/mux
// package to handle request dispatching (see http://www.gorillatoolkit.org/pkg/mux).
type muxAPI struct {
	router             *mux.Router
	mu                 sync.RWMutex
	handler            *requestHandler
	serializerRegistry map[string]ResponseSerializer
	resourceHandlers   []ResourceHandler
}

// NewAPI returns a newly allocated API instance.
func NewAPI() API {
	r := mux.NewRouter()
	restAPI := &muxAPI{
		router:             r,
		serializerRegistry: map[string]ResponseSerializer{"json": &jsonSerializer{}},
		resourceHandlers:   make([]ResourceHandler, 0),
	}
	restAPI.handler = &requestHandler{restAPI}
	return restAPI
}

// Start begins serving requests. This will block unless it fails, in which case an error will be
// returned.
func (r *muxAPI) Start(addr Address) error {
	r.validateRules()
	return http.ListenAndServe(string(addr), r.router)
}

// StartTLS begins serving requests received over HTTPS connections. This will block unless it
// fails, in which case an error will be returned. Files containing a certificate and matching
// private key for the server must be provided. If the certificate is signed by a certificate
// authority, the certFile should be the concatenation of the server's certificate followed by
// the CA's certificate.
func (r *muxAPI) StartTLS(addr Address, certFile, keyFile FilePath) error {
	r.validateRules()
	return http.ListenAndServeTLS(string(addr), string(certFile), string(keyFile), r.router)
}

// RegisterResourceHandler binds the provided ResourceHandler to the appropriate REST endpoints and
// applies any specified middleware. Endpoints will have the following base URL:
// /api/:version/resourceName.
func (r *muxAPI) RegisterResourceHandler(h ResourceHandler, middleware ...RequestMiddleware) {
	h = resourceHandlerProxy{h}
	resource := h.ResourceName()
	middleware = append(middleware, newAuthMiddleware(h.Authenticate))

	r.router.HandleFunc(
		h.CreateURI(), applyMiddleware(r.handler.handleCreate(h), middleware),
	).Methods("POST").Name(resource + ":create")
	log.Printf("Registered create handler at POST %s", h.CreateURI())

	r.router.HandleFunc(
		h.ReadListURI(), applyMiddleware(r.handler.handleReadList(h), middleware),
	).Methods("GET").Name(resource + ":readList")
	log.Printf("Registered read list handler at GET %s", h.ReadListURI())

	r.router.HandleFunc(
		h.ReadURI(), applyMiddleware(r.handler.handleRead(h), middleware),
	).Methods("GET").Name(resource + ":read")
	log.Printf("Registered read handler at GET %s", h.ReadURI())

	r.router.HandleFunc(
		h.UpdateListURI(), applyMiddleware(r.handler.handleUpdateList(h), middleware),
	).Methods("PUT").Name(resource + ":updateList")
	log.Printf("Registered update list handler at PUT %s", h.UpdateListURI())

	r.router.HandleFunc(
		h.UpdateURI(), applyMiddleware(r.handler.handleUpdate(h), middleware),
	).Methods("PUT").Name(resource + ":update")
	log.Printf("Registered update handler at PUT %s", h.UpdateURI())

	r.router.HandleFunc(
		h.DeleteURI(), applyMiddleware(r.handler.handleDelete(h), middleware),
	).Methods("DELETE").Name(resource + ":delete")
	log.Printf("Registered delete handler at DELETE %s", h.DeleteURI())

	r.resourceHandlers = append(r.resourceHandlers, h)
}

// RegisterResponseSerializer registers the provided ResponseSerializer with the given format. If the
// format has already been registered, it will be overwritten.
func (r *muxAPI) RegisterResponseSerializer(format string, serializer ResponseSerializer) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.serializerRegistry[format] = serializer
}

// UnregisterResponseSerializer unregisters the ResponseSerializer with the provided format. If the
// format hasn't been registered, this is a no-op.
func (r *muxAPI) UnregisterResponseSerializer(format string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	delete(r.serializerRegistry, format)
}

// AvailableFormats returns a slice containing all of the available serialization formats
// currently available.
func (r *muxAPI) AvailableFormats() []string {
	r.mu.RLock()
	defer r.mu.RUnlock()
	formats := make([]string, 0, len(r.serializerRegistry))
	for format := range r.serializerRegistry {
		formats = append(formats, format)
	}
	sort.Strings(formats)
	return formats
}

// responseSerializer returns a ResponseSerializer for the given format type. If the format
// is not implemented, the returned serializer will be nil and the error set.
func (r *muxAPI) responseSerializer(format string) (ResponseSerializer, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	if serializer, ok := r.serializerRegistry[format]; ok {
		return serializer, nil
	}
	return nil, fmt.Errorf("Format not implemented: %s", format)
}

// applyMiddleware wraps the HandlerFunc with the provided RequestMiddleware and returns the
// function composition.
func applyMiddleware(h http.HandlerFunc, middleware []RequestMiddleware) http.HandlerFunc {
	for _, m := range middleware {
		h = m(h)
	}

	return h
}

// validateRules verifies that the Rules for each ResourceHandler registered with the muxAPI
// are valid, meaning they specify fields that exist and correct types. If a Rule is invalid,
// this will panic.
func (r *muxAPI) validateRules() {
	for _, handler := range r.resourceHandlers {
		rules := handler.Rules()
		if rules == nil || rules.Size() == 0 {
			continue
		}

		if err := rules.Validate(); err != nil {
			panic(err)
		}
	}
}
<|endoftext|>


<commit_before>package lib_test

import (
	"fmt"
	"net"
	"net/http"
	"testing"

	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil/assert"
)

func getFixtureNic() brain.NetworkInterface {
	ip := net.IPv4(127, 0, 0, 2)
	return brain.NetworkInterface{
		Label:            "",
		Mac:              "00:00:00:00:00",
		ID:               1,
		VlanNum:          1,
		IPs:              []net.IP{ip},
		ExtraIPs:         map[string]net.IP{},
		VirtualMachineID: 1,
	}
}

func TestAddIP(t *testing.T) {
	local1 := net.IPv4(127, 0, 0, 1)
	tests := []struct {
		name       string
		serverName lib.VirtualMachineName
		nicID      int
		spec       brain.IPCreateRequest
		created    brain.IPCreateRequest
		shouldErr  bool
	}{
		{
			name:       "add one ip",
			serverName: lib.VirtualMachineName{Account: "test", Group: "testo", VirtualMachine: "testing"},
			nicID:      252,
			spec:       brain.IPCreateRequest{Addresses: 1, Family: "ipv4", Reason: "jeff", Contiguous: false},
			created:    brain.IPCreateRequest{IPs: brain.IPs{local1}},
		},
		{
			name:       "add two ips",
			serverName: lib.VirtualMachineName{Account: "borm", Group: "galp", VirtualMachine: "sklep"},
			nicID:      564,
			spec:       brain.IPCreateRequest{Addresses: 1, Family: "ipv4", Reason: "jeff", Contiguous: false},
			created:    brain.IPCreateRequest{IPs: brain.IPs{local1}},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			specMap := map[string]interface{}{
				"addresses":  float64(test.spec.Addresses),
				"family":     test.spec.Family,
				"reason":     test.spec.Reason,
				"contiguous": test.spec.Contiguous,
			}
			vmUrl := fmt.Sprintf("/accounts/%s/groups/%s/virtual_machines/%s", test.serverName.Account, test.serverName.Group, test.serverName.VirtualMachine)
			ipcreateUrl := vmUrl + fmt.Sprintf("/nics/%d/ip_create", test.nicID)
			vm := brain.VirtualMachine{
				NetworkInterfaces: []brain.NetworkInterface{
					{
						ID: test.nicID,
					},
				},
			}

			rts := testutil.RequestTestSpec{
				MuxHandlers: &testutil.MuxHandlers{
					Brain: testutil.Mux{
						vmUrl: func(wr http.ResponseWriter, r *http.Request) {
							assert.All(
								assert.Auth(lib.TokenType(lib.BrainEndpoint)),
								assert.Method("GET"),
							)(t, test.name, r)

							testutil.WriteJSON(t, wr, vm)
						},
						ipcreateUrl: func(wr http.ResponseWriter, r *http.Request) {
							assert.All(
								assert.Auth(lib.TokenType(lib.BrainEndpoint)),
								assert.Method("POST"),
								assert.BodyUnmarshalEqual(specMap),
							)(t, test.name, r)

							testutil.WriteJSON(t, wr, test.created)
						},
					},
				},
			}

			rts.Run(t, test.name, true, func(client lib.Client) {
				ips, err := client.AddIP(test.serverName, test.spec)
				if err != nil && !test.shouldErr {
					t.Errorf("Unexpected error: %v", err)
				} else if err == nil && test.shouldErr {
					t.Errorf("Error expected but not returned")
				}

				assert.Equal(t, test.name, test.created.IPs, ips)
			})
		})
	}
}
<commit_msg>actually pretend to add two IPs<commit_after>package lib_test

import (
	"fmt"
	"net"
	"net/http"
	"testing"

	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil"
	"github.com/BytemarkHosting/bytemark-client/lib/testutil/assert"
)

func getFixtureNic() brain.NetworkInterface {
	ip := net.IPv4(127, 0, 0, 2)
	return brain.NetworkInterface{
		Label:            "",
		Mac:              "00:00:00:00:00",
		ID:               1,
		VlanNum:          1,
		IPs:              []net.IP{ip},
		ExtraIPs:         map[string]net.IP{},
		VirtualMachineID: 1,
	}
}

func TestAddIP(t *testing.T) {
	local1 := net.IPv4(127, 0, 0, 1)
	local1 := net.IPv4(127, 0, 0, 2)
	tests := []struct {
		name       string
		serverName lib.VirtualMachineName
		nicID      int
		spec       brain.IPCreateRequest
		created    brain.IPCreateRequest
		shouldErr  bool
	}{
		{
			name:       "add one ip",
			serverName: lib.VirtualMachineName{Account: "test", Group: "testo", VirtualMachine: "testing"},
			nicID:      252,
			spec:       brain.IPCreateRequest{Addresses: 1, Family: "ipv4", Reason: "jeff", Contiguous: false},
			created:    brain.IPCreateRequest{IPs: brain.IPs{local1}},
		},
		{
			name:       "add two ips",
			serverName: lib.VirtualMachineName{Account: "borm", Group: "galp", VirtualMachine: "sklep"},
			nicID:      564,
			spec:       brain.IPCreateRequest{Addresses: 2, Family: "ipv4", Reason: "jeff", Contiguous: false},
			created:    brain.IPCreateRequest{IPs: brain.IPs{local1, local2}},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			specMap := map[string]interface{}{
				"addresses":  float64(test.spec.Addresses),
				"family":     test.spec.Family,
				"reason":     test.spec.Reason,
				"contiguous": test.spec.Contiguous,
			}
			vmUrl := fmt.Sprintf("/accounts/%s/groups/%s/virtual_machines/%s", test.serverName.Account, test.serverName.Group, test.serverName.VirtualMachine)
			ipcreateUrl := vmUrl + fmt.Sprintf("/nics/%d/ip_create", test.nicID)
			vm := brain.VirtualMachine{
				NetworkInterfaces: []brain.NetworkInterface{
					{
						ID: test.nicID,
					},
				},
			}

			rts := testutil.RequestTestSpec{
				MuxHandlers: &testutil.MuxHandlers{
					Brain: testutil.Mux{
						vmUrl: func(wr http.ResponseWriter, r *http.Request) {
							assert.All(
								assert.Auth(lib.TokenType(lib.BrainEndpoint)),
								assert.Method("GET"),
							)(t, test.name, r)

							testutil.WriteJSON(t, wr, vm)
						},
						ipcreateUrl: func(wr http.ResponseWriter, r *http.Request) {
							assert.All(
								assert.Auth(lib.TokenType(lib.BrainEndpoint)),
								assert.Method("POST"),
								assert.BodyUnmarshalEqual(specMap),
							)(t, test.name, r)

							testutil.WriteJSON(t, wr, test.created)
						},
					},
				},
			}

			rts.Run(t, test.name, true, func(client lib.Client) {
				ips, err := client.AddIP(test.serverName, test.spec)
				if err != nil && !test.shouldErr {
					t.Errorf("Unexpected error: %v", err)
				} else if err == nil && test.shouldErr {
					t.Errorf("Error expected but not returned")
				}

				assert.Equal(t, test.name, test.created.IPs, ips)
			})
		})
	}
}
<|endoftext|>