<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package antiaffinity

import (
	"testing"

	"k8s.io/kubernetes/pkg/admission"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
)

// ensures the hard PodAntiAffinity is denied if it defines TopologyKey other than kubernetes.io/hostname.
func TestInterPodAffinityAdmission(t *testing.T) {
	handler := NewInterPodAntiAffinity(nil)
	pod := api.Pod{
		Spec: api.PodSpec{},
	}
	tests := []struct {
		affinity      map[string]string
		errorExpected bool
	}{
		// empty affinity its success.
		{
			affinity:      map[string]string{},
			errorExpected: false,
		},
		// what ever topologyKey in preferredDuringSchedulingIgnoredDuringExecution, the admission should success.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"preferredDuringSchedulingIgnoredDuringExecution": [{
							"weight": 5,
							"podAffinityTerm": {
								"labelSelector": {
									"matchExpressions": [{
										"key": "security",
										"operator": "In",
										"values":["S2"]
										}]
									},
								"namespaces": [],
								"topologyKey": "az"
							}
						}]
					}}`,
			},
			errorExpected: false,
		},
		// valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution,
		// plus any topologyKey in preferredDuringSchedulingIgnoredDuringExecution, then admission success.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"preferredDuringSchedulingIgnoredDuringExecution": [{
							"weight": 5,
							"podAffinityTerm": {
								"labelSelector": {
									"matchExpressions": [{
										"key": "security",
										"operator": "In",
										"values":["S2"]
									}]
								},
								"namespaces": [],
								"topologyKey": "az"
							}
						}],
						"requiredDuringSchedulingIgnoredDuringExecution": [{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces": [],
							"topologyKey": "` + unversioned.LabelHostname + `"
						}]
					}}`,
			},
			errorExpected: false,
		},
		// valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission success.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"requiredDuringSchedulingIgnoredDuringExecution": [{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces":[],
							"topologyKey": "` + unversioned.LabelHostname + `"
						}]
					}}`,
			},
			errorExpected: false,
		},
		// invalid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission fails.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"requiredDuringSchedulingIgnoredDuringExecution": [{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces":[],
							"topologyKey": " zone "
						}]
					}}`,
			},
			errorExpected: true,
		},
		// invalid topologyKey in requiredDuringSchedulingRequiredDuringExecution then admission fails.
		// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
		// {
		//         affinity: map[string]string{
		//			api.AffinityAnnotationKey: `
		//				{"podAntiAffinity": {
		//					"requiredDuringSchedulingRequiredDuringExecution": [{
		//						"labelSelector": {
		//							"matchExpressions": [{
		//								"key": "security",
		//								"operator": "In",
		//								"values":["S2"]
		//							}]
		//						},
		//						"namespaces":[],
		//						"topologyKey": " zone "
		//					}]
		//				}}`,
		//			},
		//			errorExpected: true,
		//  }
		// list of requiredDuringSchedulingIgnoredDuringExecution middle element topologyKey is not valid.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"requiredDuringSchedulingIgnoredDuringExecution": [{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces":[],
							"topologyKey": "` + unversioned.LabelHostname + `"
						},
						{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces":[],
							"topologyKey": " zone "
						},
						{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces": [],
							"topologyKey": "` + unversioned.LabelHostname + `"
						}]
					}}`,
			},
			errorExpected: true,
		},
	}
	for _, test := range tests {
		pod.ObjectMeta.Annotations = test.affinity
		err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil))

		if test.errorExpected && err == nil {
			t.Errorf("Expected error for Anti Affinity %+v but did not get an error", test.affinity)
		}

		if !test.errorExpected && err != nil {
			t.Errorf("Unexpected error %v for AntiAffinity %+v", err, test.affinity)
		}
	}
}
func TestHandles(t *testing.T) {
	handler := NewInterPodAntiAffinity(nil)
	tests := map[admission.Operation]bool{
		admission.Update:  true,
		admission.Create:  true,
		admission.Delete:  false,
		admission.Connect: false,
	}
	for op, expected := range tests {
		result := handler.Handles(op)
		if result != expected {
			t.Errorf("Unexpected result for operation %s: %v\n", op, result)
		}
	}
}
<commit_msg>Add unit test to ensure no error thrown<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package antiaffinity

import (
	"testing"

	"k8s.io/kubernetes/pkg/admission"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
)

// ensures the hard PodAntiAffinity is denied if it defines TopologyKey other than kubernetes.io/hostname.
func TestInterPodAffinityAdmission(t *testing.T) {
	handler := NewInterPodAntiAffinity(nil)
	pod := api.Pod{
		Spec: api.PodSpec{},
	}
	tests := []struct {
		affinity      map[string]string
		errorExpected bool
	}{
		// empty affinity its success.
		{
			affinity:      map[string]string{},
			errorExpected: false,
		},
		// what ever topologyKey in preferredDuringSchedulingIgnoredDuringExecution, the admission should success.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"preferredDuringSchedulingIgnoredDuringExecution": [{
							"weight": 5,
							"podAffinityTerm": {
								"labelSelector": {
									"matchExpressions": [{
										"key": "security",
										"operator": "In",
										"values":["S2"]
										}]
									},
								"namespaces": [],
								"topologyKey": "az"
							}
						}]
					}}`,
			},
			errorExpected: false,
		},
		// valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution,
		// plus any topologyKey in preferredDuringSchedulingIgnoredDuringExecution, then admission success.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"preferredDuringSchedulingIgnoredDuringExecution": [{
							"weight": 5,
							"podAffinityTerm": {
								"labelSelector": {
									"matchExpressions": [{
										"key": "security",
										"operator": "In",
										"values":["S2"]
									}]
								},
								"namespaces": [],
								"topologyKey": "az"
							}
						}],
						"requiredDuringSchedulingIgnoredDuringExecution": [{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces": [],
							"topologyKey": "` + unversioned.LabelHostname + `"
						}]
					}}`,
			},
			errorExpected: false,
		},
		// valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission success.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"requiredDuringSchedulingIgnoredDuringExecution": [{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces":[],
							"topologyKey": "` + unversioned.LabelHostname + `"
						}]
					}}`,
			},
			errorExpected: false,
		},
		// invalid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission fails.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"requiredDuringSchedulingIgnoredDuringExecution": [{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces":[],
							"topologyKey": " zone "
						}]
					}}`,
			},
			errorExpected: true,
		},
		// invalid topologyKey in requiredDuringSchedulingRequiredDuringExecution then admission fails.
		// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
		// {
		//         affinity: map[string]string{
		//			api.AffinityAnnotationKey: `
		//				{"podAntiAffinity": {
		//					"requiredDuringSchedulingRequiredDuringExecution": [{
		//						"labelSelector": {
		//							"matchExpressions": [{
		//								"key": "security",
		//								"operator": "In",
		//								"values":["S2"]
		//							}]
		//						},
		//						"namespaces":[],
		//						"topologyKey": " zone "
		//					}]
		//				}}`,
		//			},
		//			errorExpected: true,
		//  }
		// list of requiredDuringSchedulingIgnoredDuringExecution middle element topologyKey is not valid.
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"requiredDuringSchedulingIgnoredDuringExecution": [{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces":[],
							"topologyKey": "` + unversioned.LabelHostname + `"
						},
						{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces":[],
							"topologyKey": " zone "
						},
						{
							"labelSelector": {
								"matchExpressions": [{
									"key": "security",
									"operator": "In",
									"values":["S2"]
								}]
							},
							"namespaces": [],
							"topologyKey": "` + unversioned.LabelHostname + `"
						}]
					}}`,
			},
			errorExpected: true,
		},
		{
			affinity: map[string]string{
				api.AffinityAnnotationKey: `
					{"podAntiAffinity": {
						"thisIsAInvalidAffinity": [{}
					}}`,
			},
			// however, we should not got error here
			errorExpected: false,
		},
	}
	for _, test := range tests {
		pod.ObjectMeta.Annotations = test.affinity
		err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil))

		if test.errorExpected && err == nil {
			t.Errorf("Expected error for Anti Affinity %+v but did not get an error", test.affinity)
		}

		if !test.errorExpected && err != nil {
			t.Errorf("Unexpected error %v for AntiAffinity %+v", err, test.affinity)
		}
	}
}
func TestHandles(t *testing.T) {
	handler := NewInterPodAntiAffinity(nil)
	tests := map[admission.Operation]bool{
		admission.Update:  true,
		admission.Create:  true,
		admission.Delete:  false,
		admission.Connect: false,
	}
	for op, expected := range tests {
		result := handler.Handles(op)
		if result != expected {
			t.Errorf("Unexpected result for operation %s: %v\n", op, result)
		}
	}
}
<|endoftext|>


<commit_before>package distnano

import (
	"encoding/json"
	"fmt"
	"log"
)

type Child struct {
	Path     []uint  `json:"path,omitempty"`
	X        *int    `json:"x,omitempty"`
	Y        *int    `json:"y,omitempty"`
	Val      *int    `json:"val,omitempty"`
	Children []Child `json:"children,omitempty"`
}

type Root struct {
	Val      *int    `json:"val,omitempty"`
	Children []Child `json:"children,omitempty"`
}

type NanocubeResponse struct {
	Layers []string `json:"layers"`
	Root   Root     `json:"root"`
}

// asKey is a function that encodes c's path or x and y values as a string to
// be used as a key in a map. This is necessary to produce quick aggregations
// on keys.
func (c Child) asKey() string {
	// Want to ignore val.
	c.Val = nil
	b, err := json.Marshal(c)
	if err != nil {
		log.Fatalf("Got %v encoding child %v", err, c)
	}
	return string(b)
}

// TODO(asubiotto): The whole error handling has to be looked at more closely.

// mergeChildren merges the children array by aggregating "val"s at the base
// level and using either path as key or x and y as keys.
func mergeChildren(dest, src []Child) []Child {
	if dest == nil || src == nil {
		if dest == nil {
			return src
		}
		return dest
	}

	// Store pointers to children accesible by key.
	destMap := make(map[string]*Child)
	for i, child := range dest {
		destMap[child.asKey()] = &dest[i]
	}

	// For each child in src, merge with the destMap.
	for i, child := range src {
		key := child.asKey()

		// If the key already exists in the map, we want to merge with that
		// key. Otherwise, we simply add it.
		if _, e := destMap[key]; e {
			// If the child doesn't have a Val, it necessarily has something in
			// its []Child field. We therefore initiate a recursive merge.
			// Otherwise, the semantics of merging is to add to the value.
			if child.Val == nil {
				destMap[key].Children =
					mergeChildren(destMap[key].Children, child.Children)
			} else {
				*(destMap[key].Val) += *(child.Val)
			}
		} else {
			destMap[key] = &src[i]
		}
	}

	// We now want to create a new []Child slice from our [string]*Child map.
	result := make([]Child, len(destMap))
	i := 0

	for _, v := range destMap {
		result[i] = *v
		i++
	}

	return result
}

// merge merges src into dest according to the API.md document found in
// github.com/laurolins/nanocube.
func merge(dest, src *NanocubeResponse) {
	// Little check for layers. TODO(asubiotto): Think about this case a bit
	// more when we have empty responses or we want to return an error.
	if dest.Layers == nil {
		dest.Layers = []string{}
	}

	if dest.Root.Val != nil {
		*(dest.Root.Val) += *(src.Root.Val)
		return
	}

	b, _ := json.Marshal(dest)
	b1, _ := json.Marshal(src)
	fmt.Printf("Merging children %v and %v\n\n", string(b), string(b1))
	dest.Root.Children = mergeChildren(dest.Root.Children, src.Root.Children)
	b, _ = json.Marshal(dest)
	fmt.Printf("Got: %v\n\n\n\n", string(b))
}
<commit_msg>Better result slice creation<commit_after>package distnano

import (
	"encoding/json"
	"fmt"
	"log"
)

type Child struct {
	Path     []uint  `json:"path,omitempty"`
	X        *int    `json:"x,omitempty"`
	Y        *int    `json:"y,omitempty"`
	Val      *int    `json:"val,omitempty"`
	Children []Child `json:"children,omitempty"`
}

type Root struct {
	Val      *int    `json:"val,omitempty"`
	Children []Child `json:"children,omitempty"`
}

type NanocubeResponse struct {
	Layers []string `json:"layers"`
	Root   Root     `json:"root"`
}

// asKey is a function that encodes c's path or x and y values as a string to
// be used as a key in a map. This is necessary to produce quick aggregations
// on keys.
func (c Child) asKey() string {
	// Want to ignore val.
	c.Val = nil
	b, err := json.Marshal(c)
	if err != nil {
		log.Fatalf("Got %v encoding child %v", err, c)
	}
	return string(b)
}

// TODO(asubiotto): The whole error handling has to be looked at more closely.

// mergeChildren merges the children array by aggregating "val"s at the base
// level and using either path as key or x and y as keys.
func mergeChildren(dest, src []Child) []Child {
	if dest == nil || src == nil {
		if dest == nil {
			return src
		}
		return dest
	}

	// Store pointers to children accesible by key.
	destMap := make(map[string]*Child)
	for i, child := range dest {
		destMap[child.asKey()] = &dest[i]
	}

	// For each child in src, merge with the destMap.
	for i, child := range src {
		key := child.asKey()

		// If the key already exists in the map, we want to merge with that
		// key. Otherwise, we simply add it.
		if _, e := destMap[key]; e {
			// If the child doesn't have a Val, it necessarily has something in
			// its []Child field. We therefore initiate a recursive merge.
			// Otherwise, the semantics of merging is to add to the value.
			if child.Val == nil {
				destMap[key].Children =
					mergeChildren(destMap[key].Children, child.Children)
			} else {
				*(destMap[key].Val) += *(child.Val)
			}
		} else {
			destMap[key] = &src[i]
		}
	}

	// We now want to create a new []Child slice from our [string]*Child map.
	result := make([]Child, 0, len(destMap))

	for _, v := range destMap {
		// Note that result is not reallocated on each iteration because its
		// capacity was specified.
		result = append(result, *v)
	}

	return result
}

// merge merges src into dest according to the API.md document found in
// github.com/laurolins/nanocube.
func merge(dest, src *NanocubeResponse) {
	// Little check for layers. TODO(asubiotto): Think about this case a bit
	// more when we have empty responses or we want to return an error.
	if dest.Layers == nil {
		dest.Layers = []string{}
	}

	if dest.Root.Val != nil {
		*(dest.Root.Val) += *(src.Root.Val)
		return
	}

	b, _ := json.Marshal(dest)
	b1, _ := json.Marshal(src)
	fmt.Printf("Merging children %v and %v\n\n", string(b), string(b1))
	dest.Root.Children = mergeChildren(dest.Root.Children, src.Root.Children)
	b, _ = json.Marshal(dest)
	fmt.Printf("Got: %v\n\n\n\n", string(b))
}
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"bytes"
	"fmt"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"runtime"
	"testing"
)

/*

	Unit tests. Skipped the tests that would have required mocking the Docker API
	for now, may make them actually create package files.

*/

func Test_majorMinorVersionOnly(t *testing.T) {
	assert.Equal(t, majorMinorVersionOnly("2.1.34"), "2.1")
	assert.Equal(t, majorMinorVersionOnly("1.9.3-p635"), "1.9")
}

func Test_rubyDownloadUrl(t *testing.T) {
	assert.Equal(t, rubyDownloadUrl("2.1.34"), "http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.34.tar.gz")
	assert.Equal(t, rubyDownloadUrl("2.0.34-p451"), "http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.34-p451.tar.gz")
}

// Could do with pushing this out to go-bindata or similar
func Test_dockerFileFromTemplate(t *testing.T) {
	dockerfile_putput := fmt.Sprintf(`FROM ubuntu:12.04
RUN apt-get update
RUN apt-get install -y ruby1.9.3 build-essential \
    libc6-dev libffi-dev libgdbm-dev libncurses5-dev \
    libreadline-dev libssl-dev libyaml-dev zlib1g-dev \
    curl
RUN ["/usr/bin/gem", "install", "fpm", "--bindir=/usr/bin", "--no-rdoc", "--no-ri"]
RUN curl http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.34.tar.gz|tar oxzC /tmp
WORKDIR /tmp/ruby-2.1.34
RUN CFLAGS="-march=native -O3" ./configure \
  --prefix=/opt/ruby2.1.34 \
  --enable-shared \
  --disable-install-doc \
  --enable-load-relative
RUN make -j%d install DESTDIR=/tmp/fpm

WORKDIR /
RUN fpm \
    -s dir \
    -t deb \
    -n ruby-2.1.34 \
    -a amd64 \
    -v 2.1.34 \
    --iteration 37s~precise \
    -d libc6-dev \
    -d libffi-dev \
    -d libgdbm-dev \
    -d libncurses5-dev \
    -d libreadline-dev \
    -d libssl-dev \
    -d libyaml-dev \
    -d zlib1g-dev \
    -C /tmp/fpm \
    -p /ruby-2.1.34_37s~precise_amd64.deb \
    opt
`, runtime.NumCPU())

	assert.Equal(t, dockerFileFromTemplate("ubuntu:12.04", "2.1.34", "amd64", "37s~precise").String(), dockerfile_putput)
}

// Could do with pushing this out to go-bindata or similar
func Test_dockerFileFromTemplate_lucid(t *testing.T) {
	dockerfile_putput := fmt.Sprintf(`FROM ubuntu:10.04
RUN echo "deb http://security.ubuntu.com/ubuntu lucid-security main" >> /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y ruby1.9.1-full build-essential \
    libc6-dev libffi-dev libgdbm-dev libncurses5-dev \
    libreadline-dev libssl-dev libyaml-dev zlib1g-dev \
    libopenssl-ruby1.9.1 ruby1.9.1-dev curl
RUN curl http://production.cf.rubygems.org/rubygems/rubygems-2.4.2.tgz |tar oxzC /tmp
RUN cd /tmp/rubygems-2.4.2 && ruby1.9.1 setup.rb
RUN gem1.9.1 install fpm --bindir=/usr/bin --no-rdoc --no-ri
RUN curl http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.34.tar.gz|tar oxzC /tmp
WORKDIR /tmp/ruby-2.1.34
RUN CFLAGS="-march=native -O3" ./configure \
  --prefix=/opt/ruby2.1.34 \
  --enable-shared \
  --disable-install-doc \
  --enable-load-relative
RUN make -j%d install DESTDIR=/tmp/fpm

WORKDIR /
RUN fpm \
    -s dir \
    -t deb \
    -n ruby-2.1.34 \
    -a amd64 \
    -v 2.1.34 \
    --iteration 37s~lucid \
    -d libc6-dev \
    -d libffi-dev \
    -d libgdbm-dev \
    -d libncurses5-dev \
    -d libreadline-dev \
    -d libssl-dev \
    -d libyaml-dev \
    -d zlib1g-dev \
    -C /tmp/fpm \
    -p /ruby-2.1.34_37s~lucid_amd64.deb \
    opt
`, runtime.NumCPU())

	assert.Equal(t, dockerFileFromTemplate("ubuntu:10.04", "2.1.34", "amd64", "37s~lucid").String(), dockerfile_putput)
}

func Test_rubyPackageFileName(t *testing.T) {
	assert.Equal(t, "ruby-2.1.0_37s~raring_amd64.deb", rubyPackageFileName("2.1.0", "37s~raring", "amd64"))
}

func Test_createTarFileFromDockerfile(t *testing.T) {
	tar_in_buffer := createTarFileFromDockerfile(bytes.NewBufferString("foo"))

	var tar_out *tar.Reader = tar.NewReader(tar_in_buffer)

	// Get the header so we can check the name of the only entry we care about
	tar_header, err := tar_out.Next()
	if err != nil {
		panic(err)
	}

	// Get the data to make sure our foo made it through
	var out_bytes []byte
	out_bytes, err = ioutil.ReadAll(tar_out)
	if err != nil {
		panic(err)
	}

	assert.Equal(t, tar_header.Name, "Dockerfile")
	assert.Equal(t, string(out_bytes), "foo")
}

func Test_copyPackageFromContainerToLocalFs(t *testing.T) {
	t.Skip()
}

func Test_buildRuby(t *testing.T) {
	t.Skip()
}
<commit_msg>Fix rubyPackageFilename test<commit_after>package main

import (
	"archive/tar"
	"bytes"
	"fmt"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"runtime"
	"testing"
)

/*

	Unit tests. Skipped the tests that would have required mocking the Docker API
	for now, may make them actually create package files.

*/

func Test_majorMinorVersionOnly(t *testing.T) {
	assert.Equal(t, majorMinorVersionOnly("2.1.34"), "2.1")
	assert.Equal(t, majorMinorVersionOnly("1.9.3-p635"), "1.9")
}

func Test_rubyDownloadUrl(t *testing.T) {
	assert.Equal(t, rubyDownloadUrl("2.1.34"), "http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.34.tar.gz")
	assert.Equal(t, rubyDownloadUrl("2.0.34-p451"), "http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.34-p451.tar.gz")
}

// Could do with pushing this out to go-bindata or similar
func Test_dockerFileFromTemplate(t *testing.T) {
	dockerfile_putput := fmt.Sprintf(`FROM ubuntu:12.04
RUN apt-get update
RUN apt-get install -y ruby1.9.3 build-essential \
    libc6-dev libffi-dev libgdbm-dev libncurses5-dev \
    libreadline-dev libssl-dev libyaml-dev zlib1g-dev \
    curl
RUN ["/usr/bin/gem", "install", "fpm", "--bindir=/usr/bin", "--no-rdoc", "--no-ri"]
RUN curl http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.34.tar.gz|tar oxzC /tmp
WORKDIR /tmp/ruby-2.1.34
RUN CFLAGS="-march=x86-64 -O3" ./configure \
  --prefix=/opt/ruby2.1.34 \
  --enable-shared \
  --disable-install-doc \
  --enable-load-relative
RUN make -j%d install DESTDIR=/tmp/fpm

WORKDIR /
RUN fpm \
    -s dir \
    -t deb \
    -n ruby-2.1.34 \
    -a amd64 \
    -v 2.1.34 \
    --iteration 37s~precise \
    -d libc6-dev \
    -d libffi-dev \
    -d libgdbm-dev \
    -d libncurses5-dev \
    -d libreadline-dev \
    -d libssl-dev \
    -d libyaml-dev \
    -d zlib1g-dev \
    -C /tmp/fpm \
    -p /ruby-2.1.34_37s~precise_amd64.deb \
    opt
`, runtime.NumCPU())

	assert.Equal(t, dockerFileFromTemplate("ubuntu:12.04", "2.1.34", "amd64", "37s~precise").String(), dockerfile_putput)
}

// Could do with pushing this out to go-bindata or similar
func Test_dockerFileFromTemplate_lucid(t *testing.T) {
	dockerfile_putput := fmt.Sprintf(`FROM ubuntu:10.04
RUN echo "deb http://security.ubuntu.com/ubuntu lucid-security main" >> /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y ruby1.9.1-full build-essential \
    libc6-dev libffi-dev libgdbm-dev libncurses5-dev \
    libreadline-dev libssl-dev libyaml-dev zlib1g-dev \
    libopenssl-ruby1.9.1 ruby1.9.1-dev curl
RUN curl http://production.cf.rubygems.org/rubygems/rubygems-2.4.2.tgz |tar oxzC /tmp
RUN cd /tmp/rubygems-2.4.2 && ruby1.9.1 setup.rb
RUN gem1.9.1 install fpm --bindir=/usr/bin --no-rdoc --no-ri
RUN curl http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.34.tar.gz|tar oxzC /tmp
WORKDIR /tmp/ruby-2.1.34
RUN CFLAGS="-march=native -O3" ./configure \
  --prefix=/opt/ruby2.1.34 \
  --enable-shared \
  --disable-install-doc \
  --enable-load-relative
RUN make -j%d install DESTDIR=/tmp/fpm

WORKDIR /
RUN fpm \
    -s dir \
    -t deb \
    -n ruby-2.1.34 \
    -a amd64 \
    -v 2.1.34 \
    --iteration 37s~lucid \
    -d libc6-dev \
    -d libffi-dev \
    -d libgdbm-dev \
    -d libncurses5-dev \
    -d libreadline-dev \
    -d libssl-dev \
    -d libyaml-dev \
    -d zlib1g-dev \
    -C /tmp/fpm \
    -p /ruby-2.1.34_37s~lucid_amd64.deb \
    opt
`, runtime.NumCPU())

	assert.Equal(t, dockerFileFromTemplate("ubuntu:10.04", "2.1.34", "amd64", "37s~lucid").String(), dockerfile_putput)
}

func Test_rubyPackageFileName(t *testing.T) {
	assert.Equal(t, "ruby-2.1.0_37s~raring_amd64.deb", rubyPackageFileName("2.1.0", "37s~raring", "amd64"))
}

func Test_createTarFileFromDockerfile(t *testing.T) {
	tar_in_buffer := createTarFileFromDockerfile(bytes.NewBufferString("foo"))

	var tar_out *tar.Reader = tar.NewReader(tar_in_buffer)

	// Get the header so we can check the name of the only entry we care about
	tar_header, err := tar_out.Next()
	if err != nil {
		panic(err)
	}

	// Get the data to make sure our foo made it through
	var out_bytes []byte
	out_bytes, err = ioutil.ReadAll(tar_out)
	if err != nil {
		panic(err)
	}

	assert.Equal(t, tar_header.Name, "Dockerfile")
	assert.Equal(t, string(out_bytes), "foo")
}

func Test_copyPackageFromContainerToLocalFs(t *testing.T) {
	t.Skip()
}

func Test_buildRuby(t *testing.T) {
	t.Skip()
}
<|endoftext|>


<commit_before>/*
Package minify is a minifier written in Go that has a built-in HTML and CSS minifier.

Usage example:

	package main

	import (
		"fmt"
		"os"
		"os/exec"

		"github.com/tdewolff/minify"
	)

	// Minifies HTML code from stdin to stdout
	// Note that reading the file into a buffer first and writing to a buffer would be faster.
	func main() {
		m := minify.NewMinifierDefault()
		m.AddCmd("text/javascript", exec.Command("java", "-jar", "build/compiler.jar"))

		if err := m.Minify("text/html", os.Stdout, os.Stdin); err != nil {
			fmt.Println("minify.Minify:", err)
		}
	}

*/
package minify // import "github.com/tdewolff/minify"

import (
	"bytes"
	"errors"
	"io"
	"os/exec"
	"strings"
)

// ErrNotExist is returned when no minifier exists for a given mediatype
var ErrNotExist = errors.New("minifier does not exist for mediatype")

////////////////////////////////////////////////////////////////

// Func is the function interface for minifiers.
// The Minifier parameter is used for embedded resources, such as JS within HTML.
type Func func(Minifier, io.Writer, io.Reader) error

// Minifier holds a map of mediatype => function to allow recursive minifier calls of the minifier functions.
type Minifier struct {
	minify map[string]Func
	Info   map[string]string
}

// NewMinifier returns a new Minifier struct with initialized map.
func NewMinifier() *Minifier {
	return &Minifier{
		map[string]Func{},
		map[string]string{},
	}
}

// NewMinifierDefault returns a new Minifier struct with initialized map.
// It loads in the default minifier functions for HTML and CSS (test/html and text/css mediatypes respectively).
func NewMinifierDefault() *Minifier {
	return &Minifier{
		map[string]Func{
			"text/html": (Minifier).HTML,
			"text/css":  (Minifier).CSS,
			"*/*":       (Minifier).Default,
		},
		map[string]string{},
	}
}

// Add adds a minify function to the mediatype => function map.
// It allows one to implement a custom minifier for a specific mediatype.
func (m *Minifier) Add(mediatype string, f Func) {
	m.minify[mediatype] = f
}

// AddCmd adds a minify function that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc.
// Be aware that running external tools will slow down minification a lot!
func (m *Minifier) AddCmd(mediatype string, cmd *exec.Cmd) error {
	m.minify[mediatype] = func(m Minifier, w io.Writer, r io.Reader) error {
		stdOut, err := cmd.StdoutPipe()
		if err != nil {
			return err
		}
		defer stdOut.Close()

		stdIn, err := cmd.StdinPipe()
		if err != nil {
			return err
		}
		defer stdIn.Close()

		if err = cmd.Start(); err != nil {
			return err
		}
		if _, err := io.Copy(stdIn, r); err != nil {
			return err
		}
		stdIn.Close()
		if _, err = io.Copy(w, stdOut); err != nil {
			return err
		}
		return cmd.Wait()
	}
	return nil
}

// Minify minifies the content of a Reader and writes it to a Writer.
// An error is returned when no such mediatype exists (ErrNotExist) or any error occurred in the minifier function.
// Mediatype may take the form of 'text', 'text/css', 'text/css;utf8'
func (m Minifier) Minify(mediatype string, w io.Writer, r io.Reader) error {
	parentInfo := m.Info
	m.Info = make(map[string]string)
	defer func() {
		m.Info = parentInfo
	}()

	m.Info["mediatype"] = mediatype
	params := strings.Split(mediatype, ";")
	for _, p := range params[1:] {
		if i := strings.IndexByte(p, '='); i != -1 {
			m.Info[strings.TrimSpace(p[:i])] = strings.TrimSpace(p[i+1:])
		}
	}

	if f, ok := m.minify[params[0]]; ok {
		if err := f(m, w, r); err != nil {
			return err
		}
		return nil
	} else if i := strings.IndexByte(params[0], '/'); i != -1 {
		if f, ok := m.minify[params[0][:i]+"/*"]; ok {
			if err := f(m, w, r); err != nil {
				return err
			}
			return nil
		} else if f, ok := m.minify["*/*"]; ok {
			if err := f(m, w, r); err != nil {
				return err
			}
			return nil
		}
	}
	return ErrNotExist
}

// MinifyBytes minifies an array of bytes. When an error occurs it return the original array and the error.
// It return an error when no such mediatype exists (ErrNotExist) or any error occurred in the minifier function.
func (m Minifier) MinifyBytes(mediatype string, v []byte) ([]byte, error) {
	b := &bytes.Buffer{}
	if err := m.Minify(mediatype, b, bytes.NewBuffer(v)); err != nil {
		return v, err
	}
	return b.Bytes(), nil
}

// MinifyString minifies a string. When an error occurs it return the original string and the error.
// It return an error when no such mediatype exists (ErrNotExist) or any error occurred in the minifier function.
func (m Minifier) MinifyString(mediatype string, v string) (string, error) {
	b := &bytes.Buffer{}
	if err := m.Minify(mediatype, b, bytes.NewBufferString(v)); err != nil {
		return v, err
	}
	return b.String(), nil
}
<commit_msg>Tweaks<commit_after>/*
Package minify is a minifier written in Go that has a built-in HTML and CSS minifier.

Usage example:

	package main

	import (
		"fmt"
		"os"
		"os/exec"

		"github.com/tdewolff/minify"
	)

	// Minifies HTML code from stdin to stdout
	// Note that reading the file into a buffer first and writing to a buffer would be faster.
	func main() {
		m := minify.NewMinifierDefault()
		m.AddCmd("text/javascript", exec.Command("java", "-jar", "build/compiler.jar"))

		if err := m.Minify("text/html", os.Stdout, os.Stdin); err != nil {
			fmt.Println("minify.Minify:", err)
		}
	}

*/
package minify // import "github.com/tdewolff/minify"

import (
	"bytes"
	"errors"
	"io"
	"os/exec"
	"strings"
)

// ErrNotExist is returned when no minifier exists for a given mediatype
var ErrNotExist = errors.New("minifier does not exist for mediatype")

////////////////////////////////////////////////////////////////

// Func is the function interface for minifiers.
// The Minifier parameter is used for embedded resources, such as JS within HTML.
type Func func(Minifier, io.Writer, io.Reader) error

// Minifier holds a map of mediatype => function to allow recursive minifier calls of the minifier functions.
type Minifier struct {
	minify map[string]Func
	Info   map[string]string
}

// NewMinifier returns a new Minifier struct with initialized map.
func NewMinifier() *Minifier {
	return &Minifier{
		map[string]Func{},
		map[string]string{},
	}
}

// NewMinifierDefault returns a new Minifier struct with initialized map.
// It loads in the default minifier functions for HTML and CSS (test/html and text/css mediatypes respectively).
func NewMinifierDefault() *Minifier {
	return &Minifier{
		map[string]Func{
			"text/html": (Minifier).HTML,
			"text/css":  (Minifier).CSS,
			"*/*":       (Minifier).Default,
		},
		map[string]string{},
	}
}

// Add adds a minify function to the mediatype => function map.
// It allows one to implement a custom minifier for a specific mediatype.
func (m *Minifier) Add(mediatype string, f Func) {
	m.minify[mediatype] = f
}

// AddCmd adds a minify function that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc.
// Be aware that running external tools will slow down minification a lot!
func (m *Minifier) AddCmd(mediatype string, cmd *exec.Cmd) error {
	m.minify[mediatype] = func(m Minifier, w io.Writer, r io.Reader) error {
		stdOut, err := cmd.StdoutPipe()
		if err != nil {
			return err
		}
		defer stdOut.Close()

		stdIn, err := cmd.StdinPipe()
		if err != nil {
			return err
		}
		defer stdIn.Close()

		if err = cmd.Start(); err != nil {
			return err
		}
		if _, err := io.Copy(stdIn, r); err != nil {
			return err
		}
		stdIn.Close()
		if _, err = io.Copy(w, stdOut); err != nil {
			return err
		}
		return cmd.Wait()
	}
	return nil
}

// Minify minifies the content of a Reader and writes it to a Writer.
// An error is returned when no such mediatype exists (ErrNotExist) or any error occurred in the minifier function.
// Mediatype may take the form of 'text', 'text/css', 'text/css;utf8'
func (m Minifier) Minify(mediatype string, w io.Writer, r io.Reader) error {
	parentInfo := m.Info
	defer func() {
		m.Info = parentInfo
	}()

	m.Info = map[string]string{"mediatype": mediatype}
	params := strings.Split(mediatype, ";")
	for _, p := range params[1:] {
		if i := strings.IndexByte(p, '='); i != -1 {
			m.Info[strings.TrimSpace(p[:i])] = strings.TrimSpace(p[i+1:])
		}
	}

	if f, ok := m.minify[params[0]]; ok {
		if err := f(m, w, r); err != nil {
			return err
		}
		return nil
	} else if i := strings.IndexByte(params[0], '/'); i != -1 {
		if f, ok := m.minify[params[0][:i]+"/*"]; ok {
			if err := f(m, w, r); err != nil {
				return err
			}
			return nil
		} else if f, ok := m.minify["*/*"]; ok {
			if err := f(m, w, r); err != nil {
				return err
			}
			return nil
		}
	}
	return ErrNotExist
}

// MinifyBytes minifies an array of bytes. When an error occurs it return the original array and the error.
// It return an error when no such mediatype exists (ErrNotExist) or any error occurred in the minifier function.
func (m Minifier) MinifyBytes(mediatype string, v []byte) ([]byte, error) {
	b := &bytes.Buffer{}
	b.Grow(len(v))
	if err := m.Minify(mediatype, b, bytes.NewReader(v)); err != nil {
		return v, err
	}
	return b.Bytes(), nil
}

// MinifyString minifies a string. When an error occurs it return the original string and the error.
// It return an error when no such mediatype exists (ErrNotExist) or any error occurred in the minifier function.
func (m Minifier) MinifyString(mediatype string, v string) (string, error) {
	b := &bytes.Buffer{}
	b.Grow(len(v))
	if err := m.Minify(mediatype, b, bytes.NewReader([]byte(v))); err != nil {
		return v, err
	}
	return b.String(), nil
}
<|endoftext|>


<commit_before>package minion

import (
	"encoding/gob"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"strings"

	"github.com/gorilla/sessions"
)

func init() {
	gob.Register(Principal{})
}

const (
	// PrincipalKey is the key used for the principal in the user session.
	PrincipalKey = "_principal"

	// RedirectKey is the key used for the original URL before redirecting to the login site.
	RedirectKey = "_redirect"
)

// ErrorFormat defines as which content type an error should be serialized
type ErrorFormat string

const (
	// ErrorAsHTML formats an error using the HTML template `error.html`.
	ErrorAsHTML ErrorFormat = "html"
	// ErrorAsJSON formats the error as JSON object.
	ErrorAsJSON ErrorFormat = "json"
)

const (
	// TemplatePrincipalKey is the name templates can use to access the principal
	TemplatePrincipalKey = "principal"

	// TemplateFlashesKey is the name templates can use to access flash messages
	TemplateFlashesKey = "flashes"
)

// Logger is a simple interface to describe something that can write log output.
type Logger interface {
	Printf(fmt string, v ...interface{})
}

// Option is a functional configuration type that can be used to tailor
// the Minion instance during creation.
type Option func(*Minion) *Minion

// Minion implements basic building blocks that most http servers require
type Minion struct {
	Debug  bool
	Logger Logger

	// Unauthorized is called for Secured handlers where no authenticated
	// principal is found in the current session. The default handler will
	// redirect the user to `UnauthorizedURL` and store the original URL
	// in the session.
	Unauthorized    func(w http.ResponseWriter, r *http.Request)
	UnauthorizedURL string

	// Forbidden is called for Secured handlers where an authenticated principal
	// does not have enough permission to view the resource. The default handler
	// will execute the HTML template `ForbiddenTemplate`.
	Forbidden         func(w http.ResponseWriter, r *http.Request)
	ForbiddenTemplate string

	// Error is called for any error that occur during the request processing, be
	// it client side errors (4xx status) or server side errors (5xx status). The
	// default handler wirr execute the HTML template `ErrorTemplate`.
	Error         func(w http.ResponseWriter, r *http.Request, code int, err error)
	ErrorTemplate string

	sessions    sessions.Store
	sessionName string

	templates *template.Template
}

// NewMinion creates a new minion instance.
func NewMinion(options ...Option) Minion {
	m := &Minion{
		Debug:  os.Getenv("DEBUG") == "true",
		Logger: log.New(os.Stderr, "", log.LstdFlags),

		UnauthorizedURL:   "/login",
		ErrorTemplate:     "500.html",
		ForbiddenTemplate: "403.html",
	}

	// default handlers
	m.Unauthorized = m.defaultUnauthorizedHandler
	m.Forbidden = m.defaultForbiddenHandler
	m.Error = m.defaultErrorHandler

	// apply functional configuration
	for _, option := range options {
		m = option(m)
	}

	return *m
}

// Session can be used in the NewMinion function to add an secure cookie based session.
func Session(name string, key []byte, options *sessions.Options) Option {
	return func(m *Minion) *Minion {
		store := sessions.NewCookieStore(key)
		store.Options = options

		m.sessions = store
		m.sessionName = name
		return m
	}
}

func (m Minion) openSession(w http.ResponseWriter, r *http.Request) (*sessions.Session, error) {
	if m.sessions == nil {
		return nil, errors.New("no session store configured")
	}

	session, err := m.sessions.Get(r, m.sessionName)
	return session, err
}

// Get retrieves a value from the active session. If the value does not
// exist in the session, a provided default is returned
func (m Minion) Get(w http.ResponseWriter, r *http.Request, name string, def interface{}) interface{} {
	session, err := m.openSession(w, r)
	if err != nil {
		return def
	}
	value, ok := session.Values[name]
	if !ok {
		return def
	}
	return value
}

// Set stores a value in the active session.
func (m Minion) Set(w http.ResponseWriter, r *http.Request, name string, value interface{}) {
	session, err := m.openSession(w, r)
	if err != nil {
		return
	}

	session.Values[name] = value
	err = session.Save(r, w)
	if err != nil {
		m.Logger.Printf("set value: session save: %v", err)
	}
}

// Delete removes a value from the active session.
func (m Minion) Delete(w http.ResponseWriter, r *http.Request, name string) {
	session, err := m.openSession(w, r)
	if err != nil {
		return
	}

	delete(session.Values, name)
	err = session.Save(r, w)
	if err != nil {
		m.Logger.Printf("delete value: session save: %v", err)
	}
}

// AddFlash adds a flash message to the session.
func (m Minion) AddFlash(w http.ResponseWriter, r *http.Request, value interface{}) {
	session, err := m.openSession(w, r)
	if err != nil {
		return
	}

	session.AddFlash(value)
	err = session.Save(r, w)
	if err != nil {
		m.Logger.Printf("add flash: session save: %v", err)
	}
}

// Flashes returns a slice of flash messages from the session.
func (m Minion) Flashes(w http.ResponseWriter, r *http.Request) []interface{} {
	session, err := m.openSession(w, r)
	if err != nil {
		return nil
	}

	flashes := session.Flashes()
	err = session.Save(r, w)
	if err != nil {
		m.Logger.Printf("get flashes: session save: %v", err)
		return nil
	}

	return flashes
}

// Secured requires that the user has at least one of the provided roles before
// the request is forwarded to the secured handler.
func (m Minion) Secured(fn http.HandlerFunc, roles ...string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		principal := m.Get(w, r, PrincipalKey, Principal{}).(Principal)
		if !principal.Authenticated {
			m.Unauthorized(w, r)
			return
		}

		if !principal.HasAnyRole(roles...) {
			m.Forbidden(w, r)
			return
		}

		fn(w, r)
	}
}

// defaultUnauthorizedHandler is the default handler for minion.Unauthorized
func (m *Minion) defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	session, err := m.openSession(w, r)
	if err != nil {
		m.Error(w, r, http.StatusBadRequest, err)
		return
	}

	session.Values[RedirectKey] = r.URL.String()
	err = session.Save(r, w)
	if err != nil {
		m.Error(w, r, http.StatusInternalServerError, err)
		return
	}

	http.Redirect(w, r, m.UnauthorizedURL, http.StatusSeeOther)
}

// defaultForbiddenHandler is the default handler for minion.Forbidden
func (m *Minion) defaultForbiddenHandler(w http.ResponseWriter, r *http.Request) {
	m.HTML(w, r, http.StatusForbidden, m.ForbiddenTemplate, V{})
}

// defaultErrorHandler is the default handler for minion.Error
func (m *Minion) defaultErrorHandler(w http.ResponseWriter, r *http.Request, code int, err error) {
	m.Logger.Printf("error: %v", err)
	m.HTML(w, r, code, m.ErrorTemplate, V{
		"code":  code,
		"error": err.Error(),
	})
}

// JSON outputs the data encoded as JSON.
func (m Minion) JSON(w http.ResponseWriter, r *http.Request, code int, data interface{}) {
	w.Header().Add("content-type", "application/json; charset=utf-8")
	w.WriteHeader(code)

	err := json.NewEncoder(w).Encode(data)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "failed to encode json: %v", err)
		m.Logger.Printf("failed to encode json: %v", err)
	}
}

// HTML outputs a rendered HTML template to the client. This function also includes
// some default variables into the template scope.
func (m *Minion) HTML(w http.ResponseWriter, r *http.Request, code int, name string, data V) {
	// reload templates in debug mode
	if m.templates == nil || m.Debug {
		fm := template.FuncMap{
			"div": func(dividend, divisor int) float64 {
				return float64(dividend) / float64(divisor)
			},
			"json": func(v interface{}) template.JS {
				b, _ := json.MarshalIndent(v, "", "  ")
				return template.JS(b)
			},
			"dict": func(values ...interface{}) (map[string]interface{}, error) {
				if len(values)%2 != 0 {
					return nil, errors.New("invalid dict call")
				}
				dict := make(map[string]interface{}, len(values)/2)
				for i := 0; i < len(values); i += 2 {
					key, ok := values[i].(string)
					if !ok {
						return nil, errors.New("dict keys must be strings")
					}
					dict[key] = values[i+1]
				}
				return dict, nil
			},
		}

		var err error
		m.templates, err = template.New("").Funcs(fm).ParseGlob("templates/*")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "failed to parse templates: %v", err)
			m.Logger.Printf("failed to parse templates: %v", err)
			return
		}
	}

	flashes := m.Flashes(w, r)
	data[TemplateFlashesKey] = flashes

	principal := m.Get(w, r, PrincipalKey, Principal{}).(Principal)
	data[TemplatePrincipalKey] = principal

	w.Header().Add("content-type", "text/html; charset=utf-8")
	err := m.templates.ExecuteTemplate(w, name, data)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "failed to execute template %q: %v", name, err)
		m.Logger.Printf("failed to execute template %q: %v", name, err)
		return
	}
}

// Principal is an entity that can be authenticated and verified.
type Principal struct {
	Authenticated bool
	ID            string
	Login         string
	Roles         string
}

// HasAnyRole checks whether the principal has any of the given roles. Use '*'
// as a wildcard role to match any.
func (u Principal) HasAnyRole(roles ...string) bool {
	if !u.Authenticated {
		return false
	}

	dedup := make(map[string]struct{})
	for _, role := range strings.Split(u.Roles, " ") {
		dedup[role] = struct{}{}
	}

	for _, role := range roles {
		if _, ok := dedup[role]; ok || role == "*" {
			return true
		}
	}

	return false
}

// BindingResult holds validation errors of the binding process from a HTML
// form to a Go struct.
type BindingResult map[string]string

// Valid returns whether the binding was successfull or not.
func (br BindingResult) Valid() bool {
	return len(br) == 0
}

// Fail marks the binding as failed and stores an error for the given field
// that caused the form binding to fail.
func (br BindingResult) Fail(field, err string) {
	br[field] = err
}

// Include copies all errors and state of a binding result
func (br BindingResult) Include(other BindingResult) {
	for field, err := range other {
		br.Fail(field, err)
	}
}

// V is a helper type to quickly build variable maps for templates.
type V map[string]interface{}

// MarshalJSON implements the json.Marshaler interface.
func (v V) MarshalJSON() ([]byte, error) {
	return json.Marshal(map[string]interface{}(v))
}
<commit_msg>only save session during HTML(), JSON() or when explicitly invoked<commit_after>package minion

import (
	"encoding/gob"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"strings"

	"github.com/gorilla/sessions"
)

func init() {
	gob.Register(Principal{})
}

const (
	// PrincipalKey is the key used for the principal in the user session.
	PrincipalKey = "_principal"

	// RedirectKey is the key used for the original URL before redirecting to the login site.
	RedirectKey = "_redirect"
)

// ErrorFormat defines as which content type an error should be serialized
type ErrorFormat string

const (
	// ErrorAsHTML formats an error using the HTML template `error.html`.
	ErrorAsHTML ErrorFormat = "html"
	// ErrorAsJSON formats the error as JSON object.
	ErrorAsJSON ErrorFormat = "json"
)

const (
	// TemplatePrincipalKey is the name templates can use to access the principal
	TemplatePrincipalKey = "principal"

	// TemplateFlashesKey is the name templates can use to access flash messages
	TemplateFlashesKey = "flashes"
)

// Logger is a simple interface to describe something that can write log output.
type Logger interface {
	Printf(fmt string, v ...interface{})
}

// Option is a functional configuration type that can be used to tailor
// the Minion instance during creation.
type Option func(*Minion) *Minion

// Minion implements basic building blocks that most http servers require
type Minion struct {
	Debug  bool
	Logger Logger

	// Unauthorized is called for Secured handlers where no authenticated
	// principal is found in the current session. The default handler will
	// redirect the user to `UnauthorizedURL` and store the original URL
	// in the session.
	Unauthorized    func(w http.ResponseWriter, r *http.Request)
	UnauthorizedURL string

	// Forbidden is called for Secured handlers where an authenticated principal
	// does not have enough permission to view the resource. The default handler
	// will execute the HTML template `ForbiddenTemplate`.
	Forbidden         func(w http.ResponseWriter, r *http.Request)
	ForbiddenTemplate string

	// Error is called for any error that occur during the request processing, be
	// it client side errors (4xx status) or server side errors (5xx status). The
	// default handler wirr execute the HTML template `ErrorTemplate`.
	Error         func(w http.ResponseWriter, r *http.Request, code int, err error)
	ErrorTemplate string

	sessions    sessions.Store
	sessionName string

	templates *template.Template
}

// NewMinion creates a new minion instance.
func NewMinion(options ...Option) Minion {
	m := &Minion{
		Debug:  os.Getenv("DEBUG") == "true",
		Logger: log.New(os.Stderr, "", log.LstdFlags),

		UnauthorizedURL:   "/login",
		ErrorTemplate:     "500.html",
		ForbiddenTemplate: "403.html",
	}

	// default handlers
	m.Unauthorized = m.defaultUnauthorizedHandler
	m.Forbidden = m.defaultForbiddenHandler
	m.Error = m.defaultErrorHandler

	// apply functional configuration
	for _, option := range options {
		m = option(m)
	}

	return *m
}

// Session can be used in the NewMinion function to add an secure cookie based session.
func Session(name string, key []byte, options *sessions.Options) Option {
	return func(m *Minion) *Minion {
		store := sessions.NewCookieStore(key)
		store.Options = options

		m.sessions = store
		m.sessionName = name
		return m
	}
}

func (m Minion) openSession(w http.ResponseWriter, r *http.Request) (*sessions.Session, error) {
	if m.sessions == nil {
		return nil, errors.New("no session store configured")
	}

	session, err := m.sessions.Get(r, m.sessionName)
	return session, err
}

// Get retrieves a value from the active session. If the value does not
// exist in the session, a provided default is returned
func (m Minion) Get(w http.ResponseWriter, r *http.Request, name string, def interface{}) interface{} {
	session, err := m.openSession(w, r)
	if err != nil {
		return def
	}
	value, ok := session.Values[name]
	if !ok {
		return def
	}
	return value
}

// Set stores a value in the active session.
func (m Minion) Set(w http.ResponseWriter, r *http.Request, name string, value interface{}) {
	session, err := m.openSession(w, r)
	if err != nil {
		return
	}

	session.Values[name] = value
}

// Delete removes a value from the active session.
func (m Minion) Delete(w http.ResponseWriter, r *http.Request, name string) {
	session, err := m.openSession(w, r)
	if err != nil {
		return
	}

	delete(session.Values, name)
}

// AddFlash adds a flash message to the session.
func (m Minion) AddFlash(w http.ResponseWriter, r *http.Request, value interface{}) {
	session, err := m.openSession(w, r)
	if err != nil {
		return
	}

	session.AddFlash(value)
}

// Flashes returns a slice of flash messages from the session.
func (m Minion) Flashes(w http.ResponseWriter, r *http.Request) []interface{} {
	session, err := m.openSession(w, r)
	if err != nil {
		return nil
	}

	flashes := session.Flashes()
	return flashes
}

func (m Minion) SaveSession(w http.ResponseWriter, r *http.Request) {
	session, err := m.openSession(w, r)
	if err != nil {
		m.Logger.Printf("session open: %v", err)
		return
	}

	err = session.Save(r, w)
	if err != nil {
		m.Logger.Printf("session save: %v", err)
		return
	}
}

// Secured requires that the user has at least one of the provided roles before
// the request is forwarded to the secured handler.
func (m Minion) Secured(fn http.HandlerFunc, roles ...string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		principal := m.Get(w, r, PrincipalKey, Principal{}).(Principal)
		if !principal.Authenticated {
			m.Unauthorized(w, r)
			return
		}

		if !principal.HasAnyRole(roles...) {
			m.Forbidden(w, r)
			return
		}

		fn(w, r)
	}
}

// defaultUnauthorizedHandler is the default handler for minion.Unauthorized
func (m *Minion) defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	session, err := m.openSession(w, r)
	if err != nil {
		m.Error(w, r, http.StatusBadRequest, err)
		return
	}

	session.Values[RedirectKey] = r.URL.String()
	err = session.Save(r, w)
	if err != nil {
		m.Error(w, r, http.StatusInternalServerError, err)
		return
	}

	http.Redirect(w, r, m.UnauthorizedURL, http.StatusSeeOther)
}

// defaultForbiddenHandler is the default handler for minion.Forbidden
func (m *Minion) defaultForbiddenHandler(w http.ResponseWriter, r *http.Request) {
	m.HTML(w, r, http.StatusForbidden, m.ForbiddenTemplate, V{})
}

// defaultErrorHandler is the default handler for minion.Error
func (m *Minion) defaultErrorHandler(w http.ResponseWriter, r *http.Request, code int, err error) {
	m.Logger.Printf("error: %v", err)
	m.HTML(w, r, code, m.ErrorTemplate, V{
		"code":  code,
		"error": err.Error(),
	})
}

// JSON outputs the data encoded as JSON.
func (m Minion) JSON(w http.ResponseWriter, r *http.Request, code int, data interface{}) {
	m.SaveSession(w, r)
	w.Header().Add("content-type", "application/json; charset=utf-8")
	w.WriteHeader(code)

	err := json.NewEncoder(w).Encode(data)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "failed to encode json: %v", err)
		m.Logger.Printf("failed to encode json: %v", err)
	}
}

// HTML outputs a rendered HTML template to the client. This function also includes
// some default variables into the template scope.
func (m *Minion) HTML(w http.ResponseWriter, r *http.Request, code int, name string, data V) {
	// reload templates in debug mode
	if m.templates == nil || m.Debug {
		fm := template.FuncMap{
			"div": func(dividend, divisor int) float64 {
				return float64(dividend) / float64(divisor)
			},
			"json": func(v interface{}) template.JS {
				b, _ := json.MarshalIndent(v, "", "  ")
				return template.JS(b)
			},
			"dict": func(values ...interface{}) (map[string]interface{}, error) {
				if len(values)%2 != 0 {
					return nil, errors.New("invalid dict call")
				}
				dict := make(map[string]interface{}, len(values)/2)
				for i := 0; i < len(values); i += 2 {
					key, ok := values[i].(string)
					if !ok {
						return nil, errors.New("dict keys must be strings")
					}
					dict[key] = values[i+1]
				}
				return dict, nil
			},
		}

		var err error
		m.templates, err = template.New("").Funcs(fm).ParseGlob("templates/*")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "failed to parse templates: %v", err)
			m.Logger.Printf("failed to parse templates: %v", err)
			return
		}
	}

	flashes := m.Flashes(w, r)
	data[TemplateFlashesKey] = flashes

	principal := m.Get(w, r, PrincipalKey, Principal{}).(Principal)
	data[TemplatePrincipalKey] = principal

	m.SaveSession(w, r)
	w.Header().Add("content-type", "text/html; charset=utf-8")
	w.WriteHeader(code)

	err := m.templates.ExecuteTemplate(w, name, data)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "failed to execute template %q: %v", name, err)
		m.Logger.Printf("failed to execute template %q: %v", name, err)
		return
	}
}

// Principal is an entity that can be authenticated and verified.
type Principal struct {
	Authenticated bool
	ID            string
	Login         string
	Roles         string
}

// HasAnyRole checks whether the principal has any of the given roles. Use '*'
// as a wildcard role to match any.
func (u Principal) HasAnyRole(roles ...string) bool {
	if !u.Authenticated {
		return false
	}

	dedup := make(map[string]struct{})
	for _, role := range strings.Split(u.Roles, " ") {
		dedup[role] = struct{}{}
	}

	for _, role := range roles {
		if _, ok := dedup[role]; ok || role == "*" {
			return true
		}
	}

	return false
}

// BindingResult holds validation errors of the binding process from a HTML
// form to a Go struct.
type BindingResult map[string]string

// Valid returns whether the binding was successfull or not.
func (br BindingResult) Valid() bool {
	return len(br) == 0
}

// Fail marks the binding as failed and stores an error for the given field
// that caused the form binding to fail.
func (br BindingResult) Fail(field, err string) {
	br[field] = err
}

// Include copies all errors and state of a binding result
func (br BindingResult) Include(other BindingResult) {
	for field, err := range other {
		br.Fail(field, err)
	}
}

// V is a helper type to quickly build variable maps for templates.
type V map[string]interface{}

// MarshalJSON implements the json.Marshaler interface.
func (v V) MarshalJSON() ([]byte, error) {
	return json.Marshal(map[string]interface{}(v))
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcs

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"
	"unicode/utf8"

	"golang.org/x/net/context"
	storagev1 "google.golang.org/api/storage/v1"
	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
)

// A request to create an object, accepted by Bucket.CreateObject.
type CreateObjectRequest struct {
	// Attributes with which the object should be created. The Name field must be
	// set; other zero-valued fields are ignored.
	//
	// Object names must:
	//
	// *  be non-empty.
	// *  be no longer than 1024 bytes.
	// *  be valid UTF-8.
	// *  not contain the code point U+000A (line feed).
	// *  not contain the code point U+000D (carriage return).
	//
	// See here for authoritative documentation:
	//     https://cloud.google.com/storage/docs/bucket-naming#objectnames
	Attrs storage.ObjectAttrs

	// A reader from which to obtain the contents of the object. Must be non-nil.
	Contents io.Reader
}

// Bucket represents a GCS bucket, pre-bound with a bucket name and necessary
// authorization information.
//
// Each method that may block accepts a context object that is used for
// deadlines and cancellation. Users need not package authorization information
// into the context object (using cloud.WithContext or similar).
type Bucket interface {
	Name() string

	// List the objects in the bucket that meet the criteria defined by the
	// query, returning a result object that contains the results and potentially
	// a cursor for retrieving the next portion of the larger set of results.
	ListObjects(ctx context.Context, query *storage.Query) (*storage.Objects, error)

	// Create a reader for the contents of the object with the given name. The
	// caller must arrange for the reader to be closed when it is no longer
	// needed.
	NewReader(ctx context.Context, objectName string) (io.ReadCloser, error)

	// Create or overwrite an object according to the supplied request. The new
	// object is guaranteed to exist immediately for the purposes of reading (and
	// eventually for listing) after this method returns a nil error. It is
	// guaranteed not to exist before req.Contents returns io.EOF.
	CreateObject(ctx context.Context, req *CreateObjectRequest) (*storage.Object, error)

	// Delete the object with the given name.
	DeleteObject(ctx context.Context, name string) error
}

type bucket struct {
	projID string
	client *http.Client
	name   string
}

func (b *bucket) Name() string {
	return b.name
}

func (b *bucket) ListObjects(ctx context.Context, query *storage.Query) (*storage.Objects, error) {
	authContext := cloud.WithContext(ctx, b.projID, b.client)
	return storage.ListObjects(authContext, b.name, query)
}

func (b *bucket) NewReader(ctx context.Context, objectName string) (io.ReadCloser, error) {
	authContext := cloud.WithContext(ctx, b.projID, b.client)
	return storage.NewReader(authContext, b.name, objectName)
}

func toRawAcls(in []storage.ACLRule) []*storagev1.ObjectAccessControl {
	out := make([]*storagev1.ObjectAccessControl, len(in))
	for i, rule := range in {
		out[i] = &storagev1.ObjectAccessControl{
			Entity: string(rule.Entity),
			Role:   string(rule.Role),
		}
	}

	return out
}

func fromRawAcls(in []*storagev1.ObjectAccessControl) []storage.ACLRule

func fromRfc3339(s string) (t time.Time, err error) {
	if s != "" {
		t, err = time.Parse(time.RFC3339, s)
	}

	return
}

func fromRawObject(
	bucketName string,
	in *storagev1.Object) (out *storage.Object, err error) {
	// Convert the easy fields.
	out = &storage.Object{
		Bucket:          bucketName,
		Name:            in.Name,
		ContentType:     in.ContentType,
		ContentLanguage: in.ContentLanguage,
		CacheControl:    in.CacheControl,
		ACL:             fromRawAcls(in.Acl),
		ContentEncoding: in.ContentEncoding,
		Size:            int64(in.Size),
		MediaLink:       in.MediaLink,
		Metadata:        in.Metadata,
		Generation:      in.Generation,
		MetaGeneration:  in.Metageneration,
		StorageClass:    in.StorageClass,
	}

	// Handle special cases.
	if in.Owner != nil {
		out.Owner = in.Owner.Entity
	}

	if out.Deleted, err = fromRfc3339(in.TimeDeleted); err != nil {
		err = fmt.Errorf("Decoding TimeDeleted field: %v", err)
		return
	}

	if out.Updated, err = fromRfc3339(in.Updated); err != nil {
		err = fmt.Errorf("Decoding Updated field: %v", err)
		return
	}

	if out.MD5, err = base64.StdEncoding.DecodeString(in.Md5Hash); err != nil {
		err = fmt.Errorf("Decoding Md5Hash field: %v", err)
		return
	}

	crc32cString, err := base64.StdEncoding.DecodeString(in.Crc32c)
	if err != nil {
		err = fmt.Errorf("Decoding Crc32c field: %v", err)
		return
	}

	if len(crc32cString) != 4 {
		err = fmt.Errorf("Wrong length for decoded Crc32c field: %d", len(crc32cString))
		return
	}

	out.CRC32C =
		uint32(crc32cString[0])<<24 |
			uint32(crc32cString[1])<<16 |
			uint32(crc32cString[2])<<8 |
			uint32(crc32cString[3])<<0

	return
}

func getRawService(authContext context.Context) *storagev1.Service

func (b *bucket) CreateObject(
	ctx context.Context,
	req *CreateObjectRequest) (o *storage.Object, err error) {
	authContext := cloud.WithContext(ctx, b.projID, b.client)
	objectsService := getRawService(authContext).Objects

	// As of 2015-02, the wrapped storage package doesn't check this for us,
	// causing silently transformed names:
	//     https://github.com/GoogleCloudPlatform/gcloud-golang/issues/111
	if !utf8.ValidString(req.Attrs.Name) {
		err = errors.New("Invalid object name: not valid UTF-8")
		return
	}

	// Set up an object struct based on the supplied attributes.
	inputObj := &storagev1.Object{
		Name:            req.Attrs.Name,
		Bucket:          b.Name(),
		ContentType:     req.Attrs.ContentType,
		ContentLanguage: req.Attrs.ContentLanguage,
		ContentEncoding: req.Attrs.ContentEncoding,
		CacheControl:    req.Attrs.CacheControl,
		Acl:             toRawAcls(req.Attrs.ACL),
		Metadata:        req.Attrs.Metadata,
	}

	// Configure a 'call' object.
	call := objectsService.Insert(b.Name(), inputObj)
	call.Media(req.Contents)
	call.Projection("full")

	// Execute the call.
	rawObject, err := call.Do()
	if err != nil {
		return
	}

	// Convert the returned object.
	o, err = fromRawObject(b.Name(), rawObject)
	if err != nil {
		return
	}

	return
}

func (b *bucket) DeleteObject(ctx context.Context, name string) error {
	authContext := cloud.WithContext(ctx, b.projID, b.client)
	return storage.DeleteObject(authContext, b.name, name)
}
<commit_msg>Implemented fromRawAcls.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcs

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"
	"unicode/utf8"

	"golang.org/x/net/context"
	storagev1 "google.golang.org/api/storage/v1"
	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
)

// A request to create an object, accepted by Bucket.CreateObject.
type CreateObjectRequest struct {
	// Attributes with which the object should be created. The Name field must be
	// set; other zero-valued fields are ignored.
	//
	// Object names must:
	//
	// *  be non-empty.
	// *  be no longer than 1024 bytes.
	// *  be valid UTF-8.
	// *  not contain the code point U+000A (line feed).
	// *  not contain the code point U+000D (carriage return).
	//
	// See here for authoritative documentation:
	//     https://cloud.google.com/storage/docs/bucket-naming#objectnames
	Attrs storage.ObjectAttrs

	// A reader from which to obtain the contents of the object. Must be non-nil.
	Contents io.Reader
}

// Bucket represents a GCS bucket, pre-bound with a bucket name and necessary
// authorization information.
//
// Each method that may block accepts a context object that is used for
// deadlines and cancellation. Users need not package authorization information
// into the context object (using cloud.WithContext or similar).
type Bucket interface {
	Name() string

	// List the objects in the bucket that meet the criteria defined by the
	// query, returning a result object that contains the results and potentially
	// a cursor for retrieving the next portion of the larger set of results.
	ListObjects(ctx context.Context, query *storage.Query) (*storage.Objects, error)

	// Create a reader for the contents of the object with the given name. The
	// caller must arrange for the reader to be closed when it is no longer
	// needed.
	NewReader(ctx context.Context, objectName string) (io.ReadCloser, error)

	// Create or overwrite an object according to the supplied request. The new
	// object is guaranteed to exist immediately for the purposes of reading (and
	// eventually for listing) after this method returns a nil error. It is
	// guaranteed not to exist before req.Contents returns io.EOF.
	CreateObject(ctx context.Context, req *CreateObjectRequest) (*storage.Object, error)

	// Delete the object with the given name.
	DeleteObject(ctx context.Context, name string) error
}

type bucket struct {
	projID string
	client *http.Client
	name   string
}

func (b *bucket) Name() string {
	return b.name
}

func (b *bucket) ListObjects(ctx context.Context, query *storage.Query) (*storage.Objects, error) {
	authContext := cloud.WithContext(ctx, b.projID, b.client)
	return storage.ListObjects(authContext, b.name, query)
}

func (b *bucket) NewReader(ctx context.Context, objectName string) (io.ReadCloser, error) {
	authContext := cloud.WithContext(ctx, b.projID, b.client)
	return storage.NewReader(authContext, b.name, objectName)
}

func toRawAcls(in []storage.ACLRule) []*storagev1.ObjectAccessControl {
	out := make([]*storagev1.ObjectAccessControl, len(in))
	for i, rule := range in {
		out[i] = &storagev1.ObjectAccessControl{
			Entity: string(rule.Entity),
			Role:   string(rule.Role),
		}
	}

	return out
}

func fromRawAcls(in []*storagev1.ObjectAccessControl) []storage.ACLRule {
	out := make([]storage.ACLRule, len(in))
	for i, rule := range in {
		out[i] = storage.ACLRule{
			Entity: storage.ACLEntity(rule.Entity),
			Role:   storage.ACLRole(rule.Role),
		}
	}

	return out
}

func fromRfc3339(s string) (t time.Time, err error) {
	if s != "" {
		t, err = time.Parse(time.RFC3339, s)
	}

	return
}

func fromRawObject(
	bucketName string,
	in *storagev1.Object) (out *storage.Object, err error) {
	// Convert the easy fields.
	out = &storage.Object{
		Bucket:          bucketName,
		Name:            in.Name,
		ContentType:     in.ContentType,
		ContentLanguage: in.ContentLanguage,
		CacheControl:    in.CacheControl,
		ACL:             fromRawAcls(in.Acl),
		ContentEncoding: in.ContentEncoding,
		Size:            int64(in.Size),
		MediaLink:       in.MediaLink,
		Metadata:        in.Metadata,
		Generation:      in.Generation,
		MetaGeneration:  in.Metageneration,
		StorageClass:    in.StorageClass,
	}

	// Handle special cases.
	if in.Owner != nil {
		out.Owner = in.Owner.Entity
	}

	if out.Deleted, err = fromRfc3339(in.TimeDeleted); err != nil {
		err = fmt.Errorf("Decoding TimeDeleted field: %v", err)
		return
	}

	if out.Updated, err = fromRfc3339(in.Updated); err != nil {
		err = fmt.Errorf("Decoding Updated field: %v", err)
		return
	}

	if out.MD5, err = base64.StdEncoding.DecodeString(in.Md5Hash); err != nil {
		err = fmt.Errorf("Decoding Md5Hash field: %v", err)
		return
	}

	crc32cString, err := base64.StdEncoding.DecodeString(in.Crc32c)
	if err != nil {
		err = fmt.Errorf("Decoding Crc32c field: %v", err)
		return
	}

	if len(crc32cString) != 4 {
		err = fmt.Errorf("Wrong length for decoded Crc32c field: %d", len(crc32cString))
		return
	}

	out.CRC32C =
		uint32(crc32cString[0])<<24 |
			uint32(crc32cString[1])<<16 |
			uint32(crc32cString[2])<<8 |
			uint32(crc32cString[3])<<0

	return
}

func getRawService(authContext context.Context) *storagev1.Service

func (b *bucket) CreateObject(
	ctx context.Context,
	req *CreateObjectRequest) (o *storage.Object, err error) {
	authContext := cloud.WithContext(ctx, b.projID, b.client)
	objectsService := getRawService(authContext).Objects

	// As of 2015-02, the wrapped storage package doesn't check this for us,
	// causing silently transformed names:
	//     https://github.com/GoogleCloudPlatform/gcloud-golang/issues/111
	if !utf8.ValidString(req.Attrs.Name) {
		err = errors.New("Invalid object name: not valid UTF-8")
		return
	}

	// Set up an object struct based on the supplied attributes.
	inputObj := &storagev1.Object{
		Name:            req.Attrs.Name,
		Bucket:          b.Name(),
		ContentType:     req.Attrs.ContentType,
		ContentLanguage: req.Attrs.ContentLanguage,
		ContentEncoding: req.Attrs.ContentEncoding,
		CacheControl:    req.Attrs.CacheControl,
		Acl:             toRawAcls(req.Attrs.ACL),
		Metadata:        req.Attrs.Metadata,
	}

	// Configure a 'call' object.
	call := objectsService.Insert(b.Name(), inputObj)
	call.Media(req.Contents)
	call.Projection("full")

	// Execute the call.
	rawObject, err := call.Do()
	if err != nil {
		return
	}

	// Convert the returned object.
	o, err = fromRawObject(b.Name(), rawObject)
	if err != nil {
		return
	}

	return
}

func (b *bucket) DeleteObject(ctx context.Context, name string) error {
	authContext := cloud.WithContext(ctx, b.projID, b.client)
	return storage.DeleteObject(authContext, b.name, name)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"

	"github.com/gdamore/tcell"
	"github.com/rivo/tview"
)

const logo = `
   __        _
  / /__   __(_)__ _      __
 / __/ | / / / _ \ | /| / /
/ /_ | |/ / /  __/ |/ |/ /
\__/ |___/_/\___/|__/|__/

`

const subtitle = `tview - Rich Widgets for Terminal UIs`

// Cover returns the cover page.
func Cover(nextSlide func()) (title string, content tview.Primitive) {
	// What's the size of the logo?
	lines := strings.Split(logo, "\n")
	logoWidth := 0
	logoHeight := len(lines)
	for _, line := range lines {
		if len(line) > logoWidth {
			logoWidth = len(line)
		}
	}
	logoBox := tview.NewTextView().
		SetTextColor(tcell.ColorGreen).
		SetDoneFunc(func(key tcell.Key) {
			nextSlide()
		})
	fmt.Fprint(logoBox, logo)

	// Create a frame for the subtitle.
	frame := tview.NewFrame(tview.NewBox()).
		SetBorders(0, 0, 0, 0, 0, 0).
		AddText(subtitle, true, tview.AlignCenter, tcell.ColorWhite)

	// Create a Flex layout that centers the logo and subtitle.
	flex := tview.NewFlex().
		SetDirection(tview.FlexRow).
		AddItem(tview.NewBox(), 0, 7, false).
		AddItem(tview.NewFlex().
			AddItem(tview.NewBox(), 0, 1, false).
			AddItem(logoBox, logoWidth, 1, true).
			AddItem(tview.NewBox(), 0, 1, false), logoHeight, 1, true).
		AddItem(frame, 0, 10, false)

	return "Start", flex
}
<commit_msg>Added navigation instructions to presentation demo. Resolves #14, resolves #10<commit_after>package main

import (
	"fmt"
	"strings"

	"github.com/gdamore/tcell"
	"github.com/rivo/tview"
)

const logo = `
   __        _
  / /__   __(_)__ _      __
 / __/ | / / / _ \ | /| / /
/ /_ | |/ / /  __/ |/ |/ /
\__/ |___/_/\___/|__/|__/

`

const (
	subtitle   = `tview - Rich Widgets for Terminal UIs`
	navigation = `Ctrl-N: Next slide    Ctrl-P: Previous slide`
)

// Cover returns the cover page.
func Cover(nextSlide func()) (title string, content tview.Primitive) {
	// What's the size of the logo?
	lines := strings.Split(logo, "\n")
	logoWidth := 0
	logoHeight := len(lines)
	for _, line := range lines {
		if len(line) > logoWidth {
			logoWidth = len(line)
		}
	}
	logoBox := tview.NewTextView().
		SetTextColor(tcell.ColorGreen).
		SetDoneFunc(func(key tcell.Key) {
			nextSlide()
		})
	fmt.Fprint(logoBox, logo)

	// Create a frame for the subtitle and navigation infos.
	frame := tview.NewFrame(tview.NewBox()).
		SetBorders(0, 0, 0, 0, 0, 0).
		AddText(subtitle, true, tview.AlignCenter, tcell.ColorWhite).
		AddText("", true, tview.AlignCenter, tcell.ColorWhite).
		AddText(navigation, true, tview.AlignCenter, tcell.ColorBlue)

	// Create a Flex layout that centers the logo and subtitle.
	flex := tview.NewFlex().
		SetDirection(tview.FlexRow).
		AddItem(tview.NewBox(), 0, 7, false).
		AddItem(tview.NewFlex().
			AddItem(tview.NewBox(), 0, 1, false).
			AddItem(logoBox, logoWidth, 1, true).
			AddItem(tview.NewBox(), 0, 1, false), logoHeight, 1, true).
		AddItem(frame, 0, 10, false)

	return "Start", flex
}
<|endoftext|>


<commit_before>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command git-p prints the status of pending commits on all branches.
//
// git-p summarizes the status of each commit, including its review
// state in Gerrit and whether or not there are any comments or TryBot
// failures.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"unicode/utf8"
)

// TODO: Provide a way to exclude branches (like archive/, etc)

// TODO: Do the right thing if the terminal is dumb.

const (
	// TODO: Support other repos.
	remoteUrl = "https://go.googlesource.com/go"
	project   = "go"
	gerritUrl = "https://go-review.googlesource.com"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [branches...]\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "With no arguments, list branches from newest to oldest.\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	branches := flag.Args()

	// Check the branch names.
	for _, b := range branches {
		if out, err := tryGit("rev-parse", b, "--"); err != nil {
			fmt.Printf("%s\n", out)
			os.Exit(1)
		}
	}

	setupPager()

	// Find the Gerrit remote name.
	remote, err := getRemote(remoteUrl)
	if err != nil {
		log.Fatal(err)
	}

	// Get commits that are available from the Gerrit remote.
	upstreams := lines(git("for-each-ref", "--format", "%(objectname)", "refs/remotes/"+remote+"/"))
	if len(upstreams) == 0 {
		log.Fatalf("no refs for remote %s", remote)
	}

	gerrit := NewGerrit(gerritUrl)

	// Pass a token through each showBranch so we can pipeline
	// fetching branch information, while displaying it in order.
	token := make(chan struct{}, 1)
	token <- struct{}{}
	// But if the output of showBranch is blocked (e.g., by
	// back-pressure from a pager), don't start new showBranches.
	// This avoids making lots of ultimately ignored requests to
	// Gerrit.
	limit := make(chan struct{}, 3)

	var head string
	if len(branches) == 0 {
		// Resolve HEAD and show it first regardless of age.
		head, _ = tryGit("symbolic-ref", "HEAD")
		if head != "" {
			token = showBranch(gerrit, head, "HEAD", remote, upstreams, token, limit)
		}

		// Get all local branches, sorted by most recent commit date.
		branches = lines(git("for-each-ref", "--format", "%(refname)", "--sort", "-committerdate", "refs/heads/"))
	}

	// Show all branches.
	for _, branch := range branches {
		if branch == head {
			continue
		}
		token = showBranch(gerrit, branch, "", remote, upstreams, token, limit)
	}

	<-token
}

func showBranch(gerrit *Gerrit, branch, extra string, remote string, upstreams []string, token, limit chan struct{}) chan struct{} {
	// Don't start too many showBranches.
	limit <- struct{}{}

	// Get the Gerrit upstream name so we can construct full
	// Change-IDs.
	upstream := upstreamOf(branch)
	if upstream == "" {
		upstream = "refs/" + remote + "/master"
	}

	// Get commits from the branch to any upstream.
	args := []string{"rev-list", branch}
	for _, u := range upstreams {
		args = append(args, "^"+u)
	}
	args = append(args, "--")
	commits := lines(git(args...))

	// Get Change-Ids from these commits.
	cids := changeIds(project, upstream, commits)

	// Fetch information on all of these changes.
	//
	// We need DETAILED_LABELS to get numeric values of labels.
	changes := make([]*GerritChanges, len(cids))
	for i, cid := range cids {
		// TODO: Would this be simpler with a single big OR query?
		if cid != "" {
			changes[i] = gerrit.QueryChanges("change:"+cid, printChangeOptions...)
		}
	}

	if len(changes) == 0 {
		<-limit
		return token
	}

	done := make(chan struct{})
	go func() {
		<-token
		// Print changes.
		fmt.Printf("\x1b[1;32m%s\x1b[0m", strings.TrimPrefix(branch, "refs/heads/"))
		if extra != "" {
			fmt.Printf(" (\x1b[1;36m%s\x1b[0m)", extra)
		}
		fmt.Printf("\n")
		for i, change := range changes {
			printChange(commits[i], change)
		}
		fmt.Println()
		<-limit
		done <- struct{}{}
	}()
	return done
}

func changeStatus(commit string, info *GerritChange) (string, []string) {
	switch info.Status {
	default:
		return fmt.Sprintf("Unknown status %q", info.Status), nil
	case "MERGED":
		return "Submitted", nil
	case "ABANDONED":
		return "Abandoned", nil
	case "DRAFT":
		return "Draft", nil
	case "NEW":
	}

	// Check for warnings on current PS. (Requires
	// CURRENT_REVISION or ALL_REVISIONS option.)
	warnings := []string{}
	curPatchSet := info.Revisions[info.CurrentRevision].Number
	// Are there unmailed changes?
	if info.CurrentRevision != commit {
		warnings = append(warnings, "Local commit differs from mailed commit")
	}
	// Are there comments on the latest PS? (Requires
	// MESSAGES option.)
	nComments := 0
	commentUsers, commentUsersSet := []string{}, map[string]bool{}
	for _, msg := range info.Messages {
		if msg.PatchSet != curPatchSet {
			continue
		}
		// Ignore automated comments.
		if strings.HasPrefix(msg.Tag, "autogenerated:gerrit:") {
			continue
		}
		// Ignore TryBot comments (Requires
		// DETAILED_ACCOUNTS option.)
		if msg.Author.Email == "gobot@golang.org" {
			continue
		}
		nComments++
		if !commentUsersSet[msg.Author.Name] {
			commentUsersSet[msg.Author.Name] = true
			commentUsers = append(commentUsers, msg.Author.Name)
		}
	}
	if nComments > 0 {
		msg := "1 comment"
		if nComments > 1 {
			msg = fmt.Sprintf("%d comments", nComments)
		}
		msg += " on latest PS from " + strings.Join(commentUsers, ", ")
		warnings = append(warnings, msg)
	}
	// Are the trybots unhappy? (Requires LABELS option.)
	if tbr := info.Labels["TryBot-Result"]; tbr != nil && tbr.Rejected != nil {
		// TODO: List failed configs
		warnings = append(warnings, "TryBots are unhappy")
	} else if tbr == nil || tbr.Approved == nil {
		warnings = append(warnings, "TryBots not run")
	}

	// Submittable? (Requires SUBMITTABLE option.)
	status := "Pending"
	if info.Submittable {
		status = "Ready"
	}

	return status, warnings
}

var printChangeOptions = []string{"SUBMITTABLE", "LABELS", "CURRENT_REVISION", "MESSAGES", "DETAILED_ACCOUNTS"}

var display = map[string]string{
	"Not mailed": "\x1b[4m", // Underline

	"Pending warn": "\x1b[1;33m", // Bright yellow
	"Ready warn":   "\x1b[1;33m", // Bright yellow

	"Ready": "\x1b[1;32m", // Bright green

	"Submitted": "\x1b[37m",   // Gray
	"Abandoned": "\x1b[9;37m", // Gray, strike-through
	"Draft":     "\x1b[37m",   // Gray
}

// printChange prints a summary of change's status and warnings.
//
// change must be retrieved with options printChangeOptions.
func printChange(commit string, change *GerritChanges) {
	logMsg := git("log", "-n1", "--oneline", commit)

	status, warnings, link := "Not mailed", []string(nil), ""
	if change != nil {
		results, err := change.Wait()
		if err != nil {
			log.Fatal(err)
		}
		if len(results) > 1 {
			log.Fatal("multiple changes found for commit %s", commit)
		}
		if len(results) == 1 {
			status, warnings = changeStatus(commit, results[0])
			//link = fmt.Sprintf("[%s/c/%d]", gerritUrl, results[0].Number)
			link = fmt.Sprintf(" [golang.org/cl/%d]", results[0].Number)
		}
	}

	var control, eControl string
	if len(warnings) != 0 {
		if c, ok := display[status+" warn"]; ok {
			control = c
		}
	}
	if control == "" {
		if c, ok := display[status]; ok {
			control = c
		}
	}
	if control != "" {
		eControl = "\x1b[0m"
	}

	hdr := fmt.Sprintf("%-10s %s", status, logMsg)
	hdrMax := 80 - len(link) - 2
	if utf8.RuneCountInString(hdr) > hdrMax {
		hdr = fmt.Sprintf("%*.*s…", hdrMax-1, hdrMax-1, hdr)
	}
	fmt.Printf("  %s%-*s%s%s\n", control, hdrMax, hdr, eControl, link)
	for _, w := range warnings {
		fmt.Printf("    %s\n", w)
	}
}
<commit_msg>git-p: improve formatting and show rejections<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command git-p prints the status of pending commits on all branches.
//
// git-p summarizes the status of each commit, including its review
// state in Gerrit and whether or not there are any comments or TryBot
// failures.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"unicode/utf8"
)

// TODO: Provide a way to exclude branches (like archive/, etc)

// TODO: Do the right thing if the terminal is dumb.

const (
	// TODO: Support other repos.
	remoteUrl = "https://go.googlesource.com/go"
	project   = "go"
	gerritUrl = "https://go-review.googlesource.com"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [branches...]\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "With no arguments, list branches from newest to oldest.\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	branches := flag.Args()

	// Check the branch names.
	for _, b := range branches {
		if out, err := tryGit("rev-parse", b, "--"); err != nil {
			fmt.Printf("%s\n", out)
			os.Exit(1)
		}
	}

	setupPager()

	// Find the Gerrit remote name.
	remote, err := getRemote(remoteUrl)
	if err != nil {
		log.Fatal(err)
	}

	// Get commits that are available from the Gerrit remote.
	upstreams := lines(git("for-each-ref", "--format", "%(objectname)", "refs/remotes/"+remote+"/"))
	if len(upstreams) == 0 {
		log.Fatalf("no refs for remote %s", remote)
	}

	gerrit := NewGerrit(gerritUrl)

	// Pass a token through each showBranch so we can pipeline
	// fetching branch information, while displaying it in order.
	token := make(chan struct{}, 1)
	token <- struct{}{}
	// But if the output of showBranch is blocked (e.g., by
	// back-pressure from a pager), don't start new showBranches.
	// This avoids making lots of ultimately ignored requests to
	// Gerrit.
	limit := make(chan struct{}, 3)

	var head string
	if len(branches) == 0 {
		// Resolve HEAD and show it first regardless of age.
		head, _ = tryGit("symbolic-ref", "HEAD")
		if head != "" {
			token = showBranch(gerrit, head, "HEAD", remote, upstreams, token, limit)
		}

		// Get all local branches, sorted by most recent commit date.
		branches = lines(git("for-each-ref", "--format", "%(refname)", "--sort", "-committerdate", "refs/heads/"))
	}

	// Show all branches.
	for _, branch := range branches {
		if branch == head {
			continue
		}
		token = showBranch(gerrit, branch, "", remote, upstreams, token, limit)
	}

	<-token
}

func showBranch(gerrit *Gerrit, branch, extra string, remote string, upstreams []string, token, limit chan struct{}) chan struct{} {
	// Don't start too many showBranches.
	limit <- struct{}{}

	// Get the Gerrit upstream name so we can construct full
	// Change-IDs.
	upstream := upstreamOf(branch)
	if upstream == "" {
		upstream = "refs/" + remote + "/master"
	}

	// Get commits from the branch to any upstream.
	args := []string{"rev-list", branch}
	for _, u := range upstreams {
		args = append(args, "^"+u)
	}
	args = append(args, "--")
	commits := lines(git(args...))

	// Get Change-Ids from these commits.
	cids := changeIds(project, upstream, commits)

	// Fetch information on all of these changes.
	//
	// We need DETAILED_LABELS to get numeric values of labels.
	changes := make([]*GerritChanges, len(cids))
	for i, cid := range cids {
		// TODO: Would this be simpler with a single big OR query?
		if cid != "" {
			changes[i] = gerrit.QueryChanges("change:"+cid, printChangeOptions...)
		}
	}

	if len(changes) == 0 {
		<-limit
		return token
	}

	done := make(chan struct{})
	go func() {
		<-token
		// Print changes.
		fmt.Printf("\x1b[1;32m%s\x1b[0m", strings.TrimPrefix(branch, "refs/heads/"))
		if extra != "" {
			fmt.Printf(" (\x1b[1;36m%s\x1b[0m)", extra)
		}
		fmt.Printf("\n")
		for i, change := range changes {
			printChange(commits[i], change)
		}
		fmt.Println()
		<-limit
		done <- struct{}{}
	}()
	return done
}

func changeStatus(commit string, info *GerritChange) (string, []string) {
	switch info.Status {
	default:
		return fmt.Sprintf("Unknown status %q", info.Status), nil
	case "MERGED":
		return "Submitted", nil
	case "ABANDONED":
		return "Abandoned", nil
	case "DRAFT":
		return "Draft", nil
	case "NEW":
	}

	// Check for warnings on current PS. (Requires
	// CURRENT_REVISION or ALL_REVISIONS option.)
	warnings := []string{}
	curPatchSet := info.Revisions[info.CurrentRevision].Number
	// Are there unmailed changes?
	if info.CurrentRevision != commit {
		warnings = append(warnings, "Local commit differs from mailed commit")
	}
	// Are there rejections?
	rejected := false
	for labelName, label := range info.Labels {
		if !label.Optional && label.Rejected != nil {
			if labelName == "Do-Not-Submit" {
				warnings = append(warnings, "Marked \"Do not submit\"")
			} else {
				warnings = append(warnings, fmt.Sprintf("Rejected by %s", label.Rejected.Name))
				rejected = true
			}
		}
	}
	// Are there comments on the latest PS? (Requires
	// MESSAGES option.)
	nComments := 0
	commentUsers, commentUsersSet := []string{}, map[string]bool{}
	for _, msg := range info.Messages {
		if msg.PatchSet != curPatchSet {
			continue
		}
		// Ignore automated comments.
		if strings.HasPrefix(msg.Tag, "autogenerated:gerrit:") {
			continue
		}
		// Ignore TryBot comments (Requires
		// DETAILED_ACCOUNTS option.)
		if msg.Author.Email == "gobot@golang.org" {
			continue
		}
		nComments++
		if !commentUsersSet[msg.Author.Name] {
			commentUsersSet[msg.Author.Name] = true
			commentUsers = append(commentUsers, msg.Author.Name)
		}
	}
	if nComments > 0 {
		msg := "1 comment"
		if nComments > 1 {
			msg = fmt.Sprintf("%d comments", nComments)
		}
		msg += " on latest PS from " + strings.Join(commentUsers, ", ")
		warnings = append(warnings, msg)
	}
	// Are the trybots unhappy? (Requires LABELS option.)
	if tbr := info.Labels["TryBot-Result"]; tbr != nil && tbr.Rejected != nil {
		// TODO: List failed configs
		warnings = append(warnings, "TryBots are unhappy")
	} else if tbr == nil || tbr.Approved == nil {
		warnings = append(warnings, "TryBots not run")
	}

	// Submittable? (Requires SUBMITTABLE option.)
	status := "Pending"
	if rejected {
		status = "Rejected"
	} else if info.Submittable {
		status = "Ready"
	}

	return status, warnings
}

var printChangeOptions = []string{"SUBMITTABLE", "LABELS", "CURRENT_REVISION", "MESSAGES", "DETAILED_ACCOUNTS"}

var display = map[string]string{
	"Not mailed": "\x1b[35m", // Magenta

	"Pending warn":  "\x1b[33m",   // Yellow
	"Ready warn":    "\x1b[33m",   // Yellow
	"Rejected warn": "\x1b[1;31m", // Bright red

	"Ready": "\x1b[32m", // Green

	"Submitted": "\x1b[37m",   // Gray
	"Abandoned": "\x1b[9;37m", // Gray, strike-through
	"Draft":     "\x1b[37m",   // Gray
}

// printChange prints a summary of change's status and warnings.
//
// change must be retrieved with options printChangeOptions.
func printChange(commit string, change *GerritChanges) {
	logMsg := git("log", "-n1", "--oneline", commit)

	status, warnings, link := "Not mailed", []string(nil), ""
	if change != nil {
		results, err := change.Wait()
		if err != nil {
			log.Fatal(err)
		}
		if len(results) > 1 {
			log.Fatal("multiple changes found for commit %s", commit)
		}
		if len(results) == 1 {
			status, warnings = changeStatus(commit, results[0])
			//link = fmt.Sprintf("[%s/c/%d]", gerritUrl, results[0].Number)
			link = fmt.Sprintf(" [golang.org/cl/%d]", results[0].Number)
		}
	}

	var control, eControl string
	if len(warnings) != 0 {
		if c, ok := display[status+" warn"]; ok {
			control = c
		}
	}
	if control == "" {
		if c, ok := display[status]; ok {
			control = c
		}
	}
	if control != "" {
		eControl = "\x1b[0m"
	}

	hdr := fmt.Sprintf("%-10s %s", status, logMsg)
	hdrMax := 80 - len(link) - 2
	if utf8.RuneCountInString(hdr) > hdrMax {
		hdr = fmt.Sprintf("%*.*s…", hdrMax-1, hdrMax-1, hdr)
	}
	fmt.Printf("  %s%-*s%s%s\n", control, hdrMax, hdr, eControl, link)
	for _, w := range warnings {
		fmt.Printf("    %s\n", w)
	}
}
<|endoftext|>


<commit_before>package git

import (
	"errors"
	"regexp"
)

type GitRemote struct {
	Name string
	URL  string
}

func Remotes() ([]GitRemote, error) {
	r := regexp.MustCompile("(.+)\t(.+github.com.+) \\(push\\)")
	output, err := execGitCmd("remote", "-v")
	if err != nil {
		return nil, errors.New("Can't load git remote")
	}

	remotes := make([]GitRemote, 0)
	for _, o := range output {
		if r.MatchString(o) {
			match := r.FindStringSubmatch(o)
			remotes = append(remotes, GitRemote{Name: match[1], URL: match[2]})
		}
	}

	if len(remotes) == 0 {
		return nil, errors.New("Can't find git remote (push)")
	}

	return remotes, nil
}

func OriginRemote() (*GitRemote, error) {
	remotes, err := Remotes()
	if err != nil {
		return nil, err
	}

	for _, r := range remotes {
		if r.Name == "origin" {
			return &r, nil
		}
	}

	return nil, errors.New("Can't find git remote orign (push)")
}
<commit_msg>Use pointers to be more efficient<commit_after>package git

import (
	"errors"
	"regexp"
)

type GitRemote struct {
	Name string
	URL  string
}

func Remotes() ([]*GitRemote, error) {
	r := regexp.MustCompile("(.+)\t(.+github.com.+) \\(push\\)")
	output, err := execGitCmd("remote", "-v")
	if err != nil {
		return nil, errors.New("Can't load git remote")
	}

	remotes := make([]*GitRemote, 0)
	for _, o := range output {
		if r.MatchString(o) {
			match := r.FindStringSubmatch(o)
			remotes = append(remotes, &GitRemote{Name: match[1], URL: match[2]})
		}
	}

	if len(remotes) == 0 {
		return nil, errors.New("Can't find git remote (push)")
	}

	return remotes, nil
}

func OriginRemote() (*GitRemote, error) {
	remotes, err := Remotes()
	if err != nil {
		return nil, err
	}

	for _, r := range remotes {
		if r.Name == "origin" {
			return r, nil
		}
	}

	return nil, errors.New("Can't find git remote orign (push)")
}
<|endoftext|>