<commit_before>// Copyright 2016-2017, Cyrill @ Schumacher.fm and the CaddyESI Contributors
//
// 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 caddyesi

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"sort"
	"strconv"

	"github.com/SchumacherFM/caddyesi/bufpool"
	"github.com/SchumacherFM/caddyesi/esitag"
	"github.com/corestoreio/errors"
	"github.com/corestoreio/log"
	loghttp "github.com/corestoreio/log/http"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"golang.org/x/sync/singleflight"
)

// Middleware implements the Tag tag middleware
type Middleware struct {
	Group singleflight.Group
	// Root the Server root
	Root string
	//FileSys  jails the requests to site root with a mock file system
	FileSys http.FileSystem
	// Next HTTP handler in the chain
	Next httpserver.Handler

	// PathConfigs The list of Tag configurations for each path prefix and theirs
	// caches.
	PathConfigs
	// coalesce guarantees the execution of one backend request when n-external
	// incoming requests occur. Pointer type not needed.
	coalesce singleflight.Group
}

// ServeHTTP implements the http.Handler interface.
func (mw *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	cfg := mw.PathConfigs.ConfigForPath(r)
	if cfg == nil {
		return mw.Next.ServeHTTP(w, r) // exit early
	}
	if !cfg.IsRequestAllowed(r) {
		if cfg.Log.IsDebug() {
			cfg.Log.Debug("caddyesi.Middleware.ServeHTTP.IsRequestAllowed",
				log.Bool("benchIsResponseAllowed", false), loghttp.Request("request", r), log.Stringer("config", cfg),
			)
		}
		return mw.Next.ServeHTTP(w, r) // go on ...
	}
	if err := handleHeaderCommands(cfg, w, r); err != nil {
		// clears the Tag tags
		return http.StatusInternalServerError, err
	}

	pageID, entities := cfg.ESITagsByRequest(r)
	if entities == nil || len(entities) == 0 {
		// Slow path because Tag cache tag is empty and we need to analyse the
		// buffer.
		return mw.serveBuffered(cfg, pageID, w, r)
	}

	////////////////////////////////////////////////////////////////////////////////
	// Proceed from map, filled with the parsed Tag tags.

	chanTags := make(chan esitag.DataTags)
	go func() {
		var coaChanTags chan esitag.DataTags
		if entities.HasCoalesce() {
			coaChanTags = make(chan esitag.DataTags)
			var coaEnt esitag.Entities
			coaEnt, entities = entities.SplitCoalesce()
			// variable entities will be reused after go func() to query the
			// non-coalesce resources.

			var scr *http.Request
			if cfg.Log.IsInfo() || cfg.Log.IsDebug() {
				scr = loghttp.ShallowCloneRequest(r)
			}

			go func() {
				coaID := coaEnt.UniqueID()
				coaRes, _, _ := mw.coalesce.Do(strconv.FormatUint(coaID, 10), func() (interface{}, error) {
					cTags, err := coaEnt.QueryResources(r)
					if err != nil {
						if cfg.Log.IsInfo() {
							cfg.Log.Info("caddyesi.Middleware.ServeHTTP.coaEnt.QueryResources.Error",
								log.Err(err), log.Stringer("config", cfg), log.Uint64("page_id", pageID),
								log.Uint64("entities_coalesce_id", coaID), loghttp.Request("request", scr),
							)
						}
					}
					if cfg.Log.IsDebug() {
						cfg.Log.Info("caddyesi.Middleware.ServeHTTP.coaEnt.QueryResources.Once",
							log.Uint64("page_id", pageID), log.Uint64("entities_coalesce_id", coaID),
							log.Stringer("coalesce_entities", coaEnt), log.Stringer("non_coalesce_entities", entities),
							loghttp.Request("request", scr),
						)
					}
					return cTags, nil
				})
				coaChanTags <- coaRes.(esitag.DataTags)
				close(coaChanTags)
			}()
		}

		// trigger the DoRequests and query all backend resources in
		// parallel. Errors are mostly of cancelled client requests which
		// the context propagates.
		tags, err := entities.QueryResources(r)
		if err != nil {
			if cfg.Log.IsInfo() {
				cfg.Log.Info("caddyesi.Middleware.ServeHTTP.entities.QueryResources.Error",
					log.Err(err), loghttp.Request("request", r), log.Stringer("config", cfg),
					log.Uint64("page_id", pageID),
				)
			}
		}
		if coaChanTags != nil {
			ct := <-coaChanTags
			tags = append(tags, ct...)
		}
		chanTags <- tags
		close(chanTags)
	}()

	return mw.Next.ServeHTTP(responseWrapInjector(chanTags, w), r)
}

// serveBuffered creates a http.ResponseWriter buffer, calls the next handler,
// waits until the buffer has been filled, parses the buffer for Tag tags,
// queries the resources and injects the data from the resources into the output
// towards the http.ResponseWriter.Write.
func (mw *Middleware) serveBuffered(cfg *PathConfig, pageID uint64, w http.ResponseWriter, r *http.Request) (int, error) {

	buf := bufpool.Get()
	defer bufpool.Put(buf)

	bufResW := responseWrapBuffer(buf, w)

	// We must wait until every single byte has been written into the buffer.
	code, err := mw.Next.ServeHTTP(bufResW, r)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	// Only plain text response is benchIsResponseAllowed, so detect content type
	if !isResponseAllowed(buf.Bytes()) {
		bufResW.TriggerRealWrite(0)
		if _, err := bufResW.Write(buf.Bytes()); err != nil {
			return http.StatusInternalServerError, err
		}
		return code, nil
	}

	bufRdr := bytes.NewReader(buf.Bytes())

	// Parse the buffer to find Tag tags. First buffer Read happens within this
	// Group.Do block. We make sure with the Group.Do call that Tag tags for a
	// specific page ID gets only parsed once, even if multiple requests are
	// coming in to for same page. Therefore you should make sure that your
	// pageID has been calculated correctly.

	// run a performance load test to see if it's worth to switch to Group.DoChan
	groupEntitiesResult, err, shared := mw.Group.Do(strconv.FormatUint(pageID, 10), func() (interface{}, error) {

		var body io.Reader = bufRdr
		var bodyBuf *bytes.Buffer
		if cfg.Log.IsDebug() {
			bodyBuf = new(bytes.Buffer)
			body = io.TeeReader(body, bodyBuf)
		}

		entities, err := esitag.Parse(body)
		if cfg.Log.IsDebug() {
			cfg.Log.Debug("caddyesi.Middleware.ServeHTTP.ESITagsByRequest.Parse",
				log.Err(err), log.Uint64("page_id", pageID), log.Int("tag_count", len(entities)),
				loghttp.Request("request", r), log.Stringer("content", bodyBuf),
			)
		}
		if err != nil {
			return nil, errors.Wrapf(err, "[caddyesi] Grouped parsing failed ID %d", pageID)
		}
		cfg.UpsertESITags(pageID, entities)

		return entities, nil
	})
	if err != nil {
		if cfg.Log.IsDebug() {
			cfg.Log.Debug("caddyesi.Middleware.ServeHTTP.Group.Do.Error",
				log.Err(err), log.String("scope", cfg.Scope),
				log.Bool("shared", shared), log.Uint64("page_id", pageID), loghttp.Request("request", r),
			)
		}
		return http.StatusInternalServerError, err
	}

	// Trigger the queries to the resource backends in parallel
	// TODO(CyS) Coalesce requests

	tags, err := (groupEntitiesResult.(esitag.Entities)).QueryResources(r)
	if err != nil {
		if cfg.Log.IsDebug() {
			cfg.Log.Debug("caddyesi.Middleware.ServeHTTP.esiEntities.QueryResources.Error",
				log.Err(err), loghttp.Request("request", r), log.Stringer("config", cfg),
				log.Uint64("page_id", pageID),
			)
		}
		// Reported errors are mostly because of incorrect template syntax. Those gets
		// reported during first parsing.
		return http.StatusInternalServerError, err
	}

	// Calculates the correct Content-Length and enables now the real writing to the
	// client.
	bufResW.TriggerRealWrite(tags.DataLen())

	// restore original order as occurred in the HTML document.
	sort.Sort(tags)

	if _, err := bufRdr.Seek(0, 0); err != nil { // Reset io.Reader
		return http.StatusInternalServerError, err
	}
	// read the 2nd time from the buffer to finally inject the content from the resource backends
	// into the HTML page
	if _, _, err := tags.InjectContent(bufRdr, bufResW, 0); err != nil {
		return http.StatusInternalServerError, err
	}

	return code, err
}

// handleHeaderCommands allows to execute certain commands to influence the
// behaviour of the Tag tag middleware.
func handleHeaderCommands(pc *PathConfig, w http.ResponseWriter, r *http.Request) (err error) {
	if pc.CmdHeaderName == "" {
		return nil
	}
	var logLevel string

	switch r.Header.Get(pc.CmdHeaderName) {
	case `purge`:
		prevItemsInMap := pc.purgeESICache()
		w.Header().Set(pc.CmdHeaderName, fmt.Sprintf("purge-ok-%d", prevItemsInMap))
	case `log-debug`:
		logLevel = "debug"
	case `log-info`:
		logLevel = "info"
	case `log-none`:
		logLevel = "none"
	}

	if logLevel != "" {
		// TODO: check for race conditions
		pc.esiMU.Lock()
		prevLevel := pc.LogLevel
		pc.LogLevel = logLevel
		err = setupLogger(pc)
		pc.esiMU.Unlock()
		if err != nil {
			return errors.Wrap(err, "[caddyesi] handleHeaderCommands.setupLogger")
		}
		w.Header().Set(pc.CmdHeaderName, fmt.Sprintf("log-%s-ok", prevLevel))
	}

	return nil
}
<commit_msg>caddyesi: Move Request cloning out of the goroutine<commit_after>// Copyright 2016-2017, Cyrill @ Schumacher.fm and the CaddyESI Contributors
//
// 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 caddyesi

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"sort"
	"strconv"

	"github.com/SchumacherFM/caddyesi/bufpool"
	"github.com/SchumacherFM/caddyesi/esitag"
	"github.com/corestoreio/errors"
	"github.com/corestoreio/log"
	loghttp "github.com/corestoreio/log/http"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"golang.org/x/sync/singleflight"
)

// Middleware implements the Tag tag middleware
type Middleware struct {
	Group singleflight.Group
	// Root the Server root
	Root string
	//FileSys  jails the requests to site root with a mock file system
	FileSys http.FileSystem
	// Next HTTP handler in the chain
	Next httpserver.Handler

	// PathConfigs The list of Tag configurations for each path prefix and theirs
	// caches.
	PathConfigs
	// coalesce guarantees the execution of one backend request when n-external
	// incoming requests occur. Pointer type not needed.
	coalesce singleflight.Group
}

// ServeHTTP implements the http.Handler interface.
func (mw *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	cfg := mw.PathConfigs.ConfigForPath(r)
	if cfg == nil {
		return mw.Next.ServeHTTP(w, r) // exit early
	}
	if !cfg.IsRequestAllowed(r) {
		if cfg.Log.IsDebug() {
			cfg.Log.Debug("caddyesi.Middleware.ServeHTTP.IsRequestAllowed",
				log.Bool("benchIsResponseAllowed", false), loghttp.Request("request", r), log.Stringer("config", cfg),
			)
		}
		return mw.Next.ServeHTTP(w, r) // go on ...
	}
	if err := handleHeaderCommands(cfg, w, r); err != nil {
		// clears the Tag tags
		return http.StatusInternalServerError, err
	}

	pageID, entities := cfg.ESITagsByRequest(r)
	if entities == nil || len(entities) == 0 {
		// Slow path because Tag cache tag is empty and we need to analyse the
		// buffer.
		return mw.serveBuffered(cfg, pageID, w, r)
	}

	////////////////////////////////////////////////////////////////////////////////
	// Proceed from map, filled with the parsed Tag tags.

	var logR *http.Request
	if cfg.Log.IsInfo() || cfg.Log.IsDebug() {
		logR = loghttp.ShallowCloneRequest(r)
	}

	chanTags := make(chan esitag.DataTags)
	go func() {
		var coaChanTags chan esitag.DataTags
		if entities.HasCoalesce() {
			coaChanTags = make(chan esitag.DataTags)
			var coaEnt esitag.Entities
			coaEnt, entities = entities.SplitCoalesce()
			// variable entities will be reused after go func() to query the
			// non-coalesce resources.

			go func() {
				coaID := coaEnt.UniqueID()
				coaRes, _, _ := mw.coalesce.Do(strconv.FormatUint(coaID, 10), func() (interface{}, error) {
					cTags, err := coaEnt.QueryResources(r)
					if err != nil {
						if cfg.Log.IsInfo() {
							cfg.Log.Info("caddyesi.Middleware.ServeHTTP.coaEnt.QueryResources.Error",
								log.Err(err), log.Stringer("config", cfg), log.Uint64("page_id", pageID),
								log.Uint64("entities_coalesce_id", coaID), loghttp.Request("request", logR),
							)
						}
					}
					if cfg.Log.IsDebug() {
						cfg.Log.Info("caddyesi.Middleware.ServeHTTP.coaEnt.QueryResources.Once",
							log.Uint64("page_id", pageID), log.Uint64("entities_coalesce_id", coaID),
							log.Stringer("coalesce_entities", coaEnt), log.Stringer("non_coalesce_entities", entities),
							loghttp.Request("request", logR),
						)
					}
					return cTags, nil
				})
				coaChanTags <- coaRes.(esitag.DataTags)
				close(coaChanTags)
			}()
		}

		// trigger the DoRequests and query all backend resources in
		// parallel. Errors are mostly of cancelled client requests which
		// the context propagates.
		tags, err := entities.QueryResources(r)
		if err != nil {
			if cfg.Log.IsInfo() {
				cfg.Log.Info("caddyesi.Middleware.ServeHTTP.entities.QueryResources.Error",
					log.Err(err), loghttp.Request("request", r), log.Stringer("config", cfg),
					log.Uint64("page_id", pageID),
				)
			}
		}
		if coaChanTags != nil {
			ct := <-coaChanTags
			tags = append(tags, ct...)
		}
		chanTags <- tags
		close(chanTags)
	}()

	return mw.Next.ServeHTTP(responseWrapInjector(chanTags, w), r)
}

// serveBuffered creates a http.ResponseWriter buffer, calls the next handler,
// waits until the buffer has been filled, parses the buffer for Tag tags,
// queries the resources and injects the data from the resources into the output
// towards the http.ResponseWriter.Write.
func (mw *Middleware) serveBuffered(cfg *PathConfig, pageID uint64, w http.ResponseWriter, r *http.Request) (int, error) {

	buf := bufpool.Get()
	defer bufpool.Put(buf)

	bufResW := responseWrapBuffer(buf, w)

	// We must wait until every single byte has been written into the buffer.
	code, err := mw.Next.ServeHTTP(bufResW, r)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	// Only plain text response is benchIsResponseAllowed, so detect content type
	if !isResponseAllowed(buf.Bytes()) {
		bufResW.TriggerRealWrite(0)
		if _, err := bufResW.Write(buf.Bytes()); err != nil {
			return http.StatusInternalServerError, err
		}
		return code, nil
	}

	bufRdr := bytes.NewReader(buf.Bytes())

	// Parse the buffer to find Tag tags. First buffer Read happens within this
	// Group.Do block. We make sure with the Group.Do call that Tag tags for a
	// specific page ID gets only parsed once, even if multiple requests are
	// coming in to for same page. Therefore you should make sure that your
	// pageID has been calculated correctly.

	// run a performance load test to see if it's worth to switch to Group.DoChan
	groupEntitiesResult, err, shared := mw.Group.Do(strconv.FormatUint(pageID, 10), func() (interface{}, error) {

		var body io.Reader = bufRdr
		var bodyBuf *bytes.Buffer
		if cfg.Log.IsDebug() {
			bodyBuf = new(bytes.Buffer)
			body = io.TeeReader(body, bodyBuf)
		}

		entities, err := esitag.Parse(body)
		if cfg.Log.IsDebug() {
			cfg.Log.Debug("caddyesi.Middleware.ServeHTTP.ESITagsByRequest.Parse",
				log.Err(err), log.Uint64("page_id", pageID), log.Int("tag_count", len(entities)),
				loghttp.Request("request", r), log.Stringer("content", bodyBuf),
			)
		}
		if err != nil {
			return nil, errors.Wrapf(err, "[caddyesi] Grouped parsing failed ID %d", pageID)
		}
		cfg.UpsertESITags(pageID, entities)

		return entities, nil
	})
	if err != nil {
		if cfg.Log.IsDebug() {
			cfg.Log.Debug("caddyesi.Middleware.ServeHTTP.Group.Do.Error",
				log.Err(err), log.String("scope", cfg.Scope),
				log.Bool("shared", shared), log.Uint64("page_id", pageID), loghttp.Request("request", r),
			)
		}
		return http.StatusInternalServerError, err
	}

	// Trigger the queries to the resource backends in parallel
	// TODO(CyS) Coalesce requests

	tags, err := (groupEntitiesResult.(esitag.Entities)).QueryResources(r)
	if err != nil {
		if cfg.Log.IsDebug() {
			cfg.Log.Debug("caddyesi.Middleware.ServeHTTP.esiEntities.QueryResources.Error",
				log.Err(err), loghttp.Request("request", r), log.Stringer("config", cfg),
				log.Uint64("page_id", pageID),
			)
		}
		// Reported errors are mostly because of incorrect template syntax. Those gets
		// reported during first parsing.
		return http.StatusInternalServerError, err
	}

	// Calculates the correct Content-Length and enables now the real writing to the
	// client.
	bufResW.TriggerRealWrite(tags.DataLen())

	// restore original order as occurred in the HTML document.
	sort.Sort(tags)

	if _, err := bufRdr.Seek(0, 0); err != nil { // Reset io.Reader
		return http.StatusInternalServerError, err
	}
	// read the 2nd time from the buffer to finally inject the content from the resource backends
	// into the HTML page
	if _, _, err := tags.InjectContent(bufRdr, bufResW, 0); err != nil {
		return http.StatusInternalServerError, err
	}

	return code, err
}

// handleHeaderCommands allows to execute certain commands to influence the
// behaviour of the Tag tag middleware.
func handleHeaderCommands(pc *PathConfig, w http.ResponseWriter, r *http.Request) (err error) {
	if pc.CmdHeaderName == "" {
		return nil
	}
	var logLevel string

	switch r.Header.Get(pc.CmdHeaderName) {
	case `purge`:
		prevItemsInMap := pc.purgeESICache()
		w.Header().Set(pc.CmdHeaderName, fmt.Sprintf("purge-ok-%d", prevItemsInMap))
	case `log-debug`:
		logLevel = "debug"
	case `log-info`:
		logLevel = "info"
	case `log-none`:
		logLevel = "none"
	}

	if logLevel != "" {
		// TODO: check for race conditions
		pc.esiMU.Lock()
		prevLevel := pc.LogLevel
		pc.LogLevel = logLevel
		err = setupLogger(pc)
		pc.esiMU.Unlock()
		if err != nil {
			return errors.Wrap(err, "[caddyesi] handleHeaderCommands.setupLogger")
		}
		w.Header().Set(pc.CmdHeaderName, fmt.Sprintf("log-%s-ok", prevLevel))
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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 recipe
package integration

import (
	"math/rand"
	"reflect"
	"testing"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
)

type clusterV3 struct {
	*cluster
	conns []*grpc.ClientConn
}

// newClusterGRPC returns a launched cluster with a grpc client connection
// for each cluster member.
func newClusterGRPC(t *testing.T, cfg *clusterConfig) *clusterV3 {
	cfg.useV3 = true
	cfg.useGRPC = true
	clus := &clusterV3{cluster: NewClusterByConfig(t, cfg)}
	for _, m := range clus.Members {
		conn, err := NewGRPCClient(m)
		if err != nil {
			t.Fatal(err)
		}
		clus.conns = append(clus.conns, conn)
	}
	clus.Launch(t)
	return clus
}

func (c *clusterV3) Terminate(t *testing.T) {
	for _, conn := range c.conns {
		if err := conn.Close(); err != nil {
			t.Error(err)
		}
	}
	c.cluster.Terminate(t)
}

func (c *clusterV3) RandConn() *grpc.ClientConn {
	return c.conns[rand.Intn(len(c.conns))]
}

// TestV3PutOverwrite puts a key with the v3 api to a random cluster member,
// overwrites it, then checks that the change was applied.
func TestV3PutOverwrite(t *testing.T) {
	clus := newClusterGRPC(t, &clusterConfig{size: 3})
	defer clus.Terminate(t)

	kvc := pb.NewKVClient(clus.RandConn())
	key := []byte("foo")
	reqput := &pb.PutRequest{Key: key, Value: []byte("bar")}

	respput, err := kvc.Put(context.TODO(), reqput)
	if err != nil {
		t.Fatalf("couldn't put key (%v)", err)
	}

	// overwrite
	reqput.Value = []byte("baz")
	respput2, err := kvc.Put(context.TODO(), reqput)
	if err != nil {
		t.Fatalf("couldn't put key (%v)", err)
	}
	if respput2.Header.Revision <= respput.Header.Revision {
		t.Fatalf("expected newer revision on overwrite, got %v <= %v",
			respput2.Header.Revision, respput.Header.Revision)
	}

	reqrange := &pb.RangeRequest{Key: key}
	resprange, err := kvc.Range(context.TODO(), reqrange)
	if err != nil {
		t.Fatalf("couldn't get key (%v)", err)
	}
	if len(resprange.Kvs) != 1 {
		t.Fatalf("expected 1 key, got %v", len(resprange.Kvs))
	}

	kv := resprange.Kvs[0]
	if kv.ModRevision <= kv.CreateRevision {
		t.Errorf("expected modRev > createRev, got %d <= %d",
			kv.ModRevision, kv.CreateRevision)
	}
	if !reflect.DeepEqual(reqput.Value, kv.Value) {
		t.Errorf("expected value %v, got %v", reqput.Value, kv.Value)
	}
}

// TestV3DeleteRange tests various edge cases in the DeleteRange API.
func TestV3DeleteRange(t *testing.T) {
	tests := []struct {
		keySet []string
		begin  string
		end    string

		wantSet [][]byte
	}{
		// delete middle
		{
			[]string{"foo", "foo/abc", "fop"},
			"foo/", "fop",
			[][]byte{[]byte("foo"), []byte("fop")},
		},
		// no delete
		{
			[]string{"foo", "foo/abc", "fop"},
			"foo/", "foo/",
			[][]byte{[]byte("foo"), []byte("foo/abc"), []byte("fop")},
		},
		// delete first
		{
			[]string{"foo", "foo/abc", "fop"},
			"fo", "fop",
			[][]byte{[]byte("fop")},
		},
		// delete tail
		{
			[]string{"foo", "foo/abc", "fop"},
			"foo/", "fos",
			[][]byte{[]byte("foo")},
		},
		// delete exact
		{
			[]string{"foo", "foo/abc", "fop"},
			"foo/abc", "",
			[][]byte{[]byte("foo"), []byte("fop")},
		},
		// delete none, [x,x)
		{
			[]string{"foo"},
			"foo", "foo",
			[][]byte{[]byte("foo")},
		},
	}

	for i, tt := range tests {
		clus := newClusterGRPC(t, &clusterConfig{size: 3})
		kvc := pb.NewKVClient(clus.RandConn())

		ks := tt.keySet
		for j := range ks {
			reqput := &pb.PutRequest{Key: []byte(ks[j]), Value: []byte{}}
			_, err := kvc.Put(context.TODO(), reqput)
			if err != nil {
				t.Fatalf("couldn't put key (%v)", err)
			}
		}

		dreq := &pb.DeleteRangeRequest{
			Key:      []byte(tt.begin),
			RangeEnd: []byte(tt.end)}
		dresp, err := kvc.DeleteRange(context.TODO(), dreq)
		if err != nil {
			t.Fatalf("couldn't delete range on test %d (%v)", i, err)
		}

		rreq := &pb.RangeRequest{Key: []byte{0x0}, RangeEnd: []byte{0xff}}
		rresp, err := kvc.Range(context.TODO(), rreq)
		if err != nil {
			t.Errorf("couldn't get range on test %v (%v)", i, err)
		}
		if dresp.Header.Revision != rresp.Header.Revision {
			t.Errorf("expected revision %v, got %v",
				dresp.Header.Revision, rresp.Header.Revision)
		}

		keys := [][]byte{}
		for j := range rresp.Kvs {
			keys = append(keys, rresp.Kvs[j].Key)
		}
		if reflect.DeepEqual(tt.wantSet, keys) == false {
			t.Errorf("expected %v on test %v, got %v", tt.wantSet, i, keys)
		}

		// can't defer because tcp ports will be in use
		clus.Terminate(t)
	}
}
<commit_msg>integration: add WatchFromCurrentRevision test<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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 recipe
package integration

import (
	"math/rand"
	"reflect"
	"testing"
	"time"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/storage/storagepb"
)

type clusterV3 struct {
	*cluster
	conns []*grpc.ClientConn
}

// newClusterGRPC returns a launched cluster with a grpc client connection
// for each cluster member.
func newClusterGRPC(t *testing.T, cfg *clusterConfig) *clusterV3 {
	cfg.useV3 = true
	cfg.useGRPC = true
	clus := &clusterV3{cluster: NewClusterByConfig(t, cfg)}
	for _, m := range clus.Members {
		conn, err := NewGRPCClient(m)
		if err != nil {
			t.Fatal(err)
		}
		clus.conns = append(clus.conns, conn)
	}
	clus.Launch(t)
	return clus
}

func (c *clusterV3) Terminate(t *testing.T) {
	for _, conn := range c.conns {
		if err := conn.Close(); err != nil {
			t.Error(err)
		}
	}
	c.cluster.Terminate(t)
}

func (c *clusterV3) RandConn() *grpc.ClientConn {
	return c.conns[rand.Intn(len(c.conns))]
}

// TestV3PutOverwrite puts a key with the v3 api to a random cluster member,
// overwrites it, then checks that the change was applied.
func TestV3PutOverwrite(t *testing.T) {
	clus := newClusterGRPC(t, &clusterConfig{size: 3})
	defer clus.Terminate(t)

	kvc := pb.NewKVClient(clus.RandConn())
	key := []byte("foo")
	reqput := &pb.PutRequest{Key: key, Value: []byte("bar")}

	respput, err := kvc.Put(context.TODO(), reqput)
	if err != nil {
		t.Fatalf("couldn't put key (%v)", err)
	}

	// overwrite
	reqput.Value = []byte("baz")
	respput2, err := kvc.Put(context.TODO(), reqput)
	if err != nil {
		t.Fatalf("couldn't put key (%v)", err)
	}
	if respput2.Header.Revision <= respput.Header.Revision {
		t.Fatalf("expected newer revision on overwrite, got %v <= %v",
			respput2.Header.Revision, respput.Header.Revision)
	}

	reqrange := &pb.RangeRequest{Key: key}
	resprange, err := kvc.Range(context.TODO(), reqrange)
	if err != nil {
		t.Fatalf("couldn't get key (%v)", err)
	}
	if len(resprange.Kvs) != 1 {
		t.Fatalf("expected 1 key, got %v", len(resprange.Kvs))
	}

	kv := resprange.Kvs[0]
	if kv.ModRevision <= kv.CreateRevision {
		t.Errorf("expected modRev > createRev, got %d <= %d",
			kv.ModRevision, kv.CreateRevision)
	}
	if !reflect.DeepEqual(reqput.Value, kv.Value) {
		t.Errorf("expected value %v, got %v", reqput.Value, kv.Value)
	}
}

// TestV3DeleteRange tests various edge cases in the DeleteRange API.
func TestV3DeleteRange(t *testing.T) {
	tests := []struct {
		keySet []string
		begin  string
		end    string

		wantSet [][]byte
	}{
		// delete middle
		{
			[]string{"foo", "foo/abc", "fop"},
			"foo/", "fop",
			[][]byte{[]byte("foo"), []byte("fop")},
		},
		// no delete
		{
			[]string{"foo", "foo/abc", "fop"},
			"foo/", "foo/",
			[][]byte{[]byte("foo"), []byte("foo/abc"), []byte("fop")},
		},
		// delete first
		{
			[]string{"foo", "foo/abc", "fop"},
			"fo", "fop",
			[][]byte{[]byte("fop")},
		},
		// delete tail
		{
			[]string{"foo", "foo/abc", "fop"},
			"foo/", "fos",
			[][]byte{[]byte("foo")},
		},
		// delete exact
		{
			[]string{"foo", "foo/abc", "fop"},
			"foo/abc", "",
			[][]byte{[]byte("foo"), []byte("fop")},
		},
		// delete none, [x,x)
		{
			[]string{"foo"},
			"foo", "foo",
			[][]byte{[]byte("foo")},
		},
	}

	for i, tt := range tests {
		clus := newClusterGRPC(t, &clusterConfig{size: 3})
		kvc := pb.NewKVClient(clus.RandConn())

		ks := tt.keySet
		for j := range ks {
			reqput := &pb.PutRequest{Key: []byte(ks[j]), Value: []byte{}}
			_, err := kvc.Put(context.TODO(), reqput)
			if err != nil {
				t.Fatalf("couldn't put key (%v)", err)
			}
		}

		dreq := &pb.DeleteRangeRequest{
			Key:      []byte(tt.begin),
			RangeEnd: []byte(tt.end)}
		dresp, err := kvc.DeleteRange(context.TODO(), dreq)
		if err != nil {
			t.Fatalf("couldn't delete range on test %d (%v)", i, err)
		}

		rreq := &pb.RangeRequest{Key: []byte{0x0}, RangeEnd: []byte{0xff}}
		rresp, err := kvc.Range(context.TODO(), rreq)
		if err != nil {
			t.Errorf("couldn't get range on test %v (%v)", i, err)
		}
		if dresp.Header.Revision != rresp.Header.Revision {
			t.Errorf("expected revision %v, got %v",
				dresp.Header.Revision, rresp.Header.Revision)
		}

		keys := [][]byte{}
		for j := range rresp.Kvs {
			keys = append(keys, rresp.Kvs[j].Key)
		}
		if reflect.DeepEqual(tt.wantSet, keys) == false {
			t.Errorf("expected %v on test %v, got %v", tt.wantSet, i, keys)
		}

		// can't defer because tcp ports will be in use
		clus.Terminate(t)
	}
}

// TestV3WatchFromCurrentRevision tests Watch APIs from current revision.
func TestV3WatchFromCurrentRevision(t *testing.T) {
	tests := []struct {
		putKeys      []string
		watchRequest *pb.WatchRequest

		wresps []*pb.WatchResponse
	}{
		// watch the key, matching
		{
			[]string{"foo"},
			&pb.WatchRequest{CreateRequest: &pb.WatchCreateRequest{Key: []byte("foo")}},

			[]*pb.WatchResponse{
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: true,
				},
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: false,
					Events: []*storagepb.Event{
						{
							Type: storagepb.PUT,
							Kv:   &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
						},
					},
				},
			},
		},
		// watch the key, non-matching
		{
			[]string{"foo"},
			&pb.WatchRequest{CreateRequest: &pb.WatchCreateRequest{Key: []byte("helloworld")}},

			[]*pb.WatchResponse{
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: true,
				},
			},
		},
		// watch the prefix, matching
		{
			[]string{"fooLong"},
			&pb.WatchRequest{CreateRequest: &pb.WatchCreateRequest{Prefix: []byte("foo")}},

			[]*pb.WatchResponse{
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: true,
				},
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: false,
					Events: []*storagepb.Event{
						{
							Type: storagepb.PUT,
							Kv:   &storagepb.KeyValue{Key: []byte("fooLong"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
						},
					},
				},
			},
		},
		// watch the prefix, non-matching
		{
			[]string{"foo"},
			&pb.WatchRequest{CreateRequest: &pb.WatchCreateRequest{Prefix: []byte("helloworld")}},

			[]*pb.WatchResponse{
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: true,
				},
			},
		},
		// multiple puts, one watcher with matching key
		{
			[]string{"foo", "foo", "foo"},
			&pb.WatchRequest{CreateRequest: &pb.WatchCreateRequest{Key: []byte("foo")}},

			[]*pb.WatchResponse{
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: true,
				},
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: false,
					Events: []*storagepb.Event{
						{
							Type: storagepb.PUT,
							Kv:   &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
						},
					},
				},
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: false,
					Events: []*storagepb.Event{
						{
							Type: storagepb.PUT,
							Kv:   &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 2, Version: 2},
						},
					},
				},
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: false,
					Events: []*storagepb.Event{
						{
							Type: storagepb.PUT,
							Kv:   &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 3, Version: 3},
						},
					},
				},
			},
		},
		// multiple puts, one watcher with matching prefix
		{
			[]string{"foo", "foo", "foo"},
			&pb.WatchRequest{CreateRequest: &pb.WatchCreateRequest{Prefix: []byte("foo")}},

			[]*pb.WatchResponse{
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: true,
				},
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: false,
					Events: []*storagepb.Event{
						{
							Type: storagepb.PUT,
							Kv:   &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
						},
					},
				},
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: false,
					Events: []*storagepb.Event{
						{
							Type: storagepb.PUT,
							Kv:   &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 2, Version: 2},
						},
					},
				},
				{
					Header:  &pb.ResponseHeader{Revision: 1},
					Created: false,
					Events: []*storagepb.Event{
						{
							Type: storagepb.PUT,
							Kv:   &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 3, Version: 3},
						},
					},
				},
			},
		},

		// TODO: watch and receive multiple-events from synced (need Txn)
	}

	for i, tt := range tests {
		clus := newClusterGRPC(t, &clusterConfig{size: 3})

		wAPI := pb.NewWatchClient(clus.RandConn())
		wStream, err := wAPI.Watch(context.TODO())
		if err != nil {
			t.Fatalf("#%d: wAPI.Watch error: %v", i, err)
		}

		if err := wStream.Send(tt.watchRequest); err != nil {
			t.Fatalf("#%d: wStream.Send error: %v", i, err)
		}

		kvc := pb.NewKVClient(clus.RandConn())
		for _, k := range tt.putKeys {
			if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte(k), Value: []byte("bar")}); err != nil {
				t.Fatalf("#%d: couldn't put key (%v)", i, err)
			}
		}

		var createdWatchId int64
		for j, wresp := range tt.wresps {
			resp, err := wStream.Recv()
			if err != nil {
				t.Errorf("#%d.%d: wStream.Recv error: %v", i, j, err)
			}

			if resp.Header == nil {
				t.Fatalf("#%d.%d: unexpected nil resp.Header", i, j)
			}
			if resp.Header.Revision != wresp.Header.Revision {
				t.Logf("[TODO - skip for now] #%d.%d: resp.Header.Revision got = %d, want = %d", i, j, resp.Header.Revision, wresp.Header.Revision)
			}

			if wresp.Created != resp.Created {
				t.Errorf("#%d.%d: resp.Created got = %v, want = %v", i, j, resp.Created, wresp.Created)
			}
			if resp.Created {
				createdWatchId = resp.WatchId
			}
			if resp.WatchId != createdWatchId {
				t.Errorf("#%d.%d: resp.WatchId got = %d, want = %d", i, j, resp.WatchId, createdWatchId)
			}

			if !reflect.DeepEqual(resp.Events, wresp.Events) {
				t.Errorf("#%d.%d: resp.Events got = %+v, want = %+v", i, j, resp.Events, wresp.Events)
			}
		}

		rCh := make(chan *pb.WatchResponse)
		go func() {
			resp, _ := wStream.Recv()
			rCh <- resp
		}()
		select {
		case nr := <-rCh:
			t.Errorf("#%d: unexpected response is received %+v", i, nr)
		case <-time.After(2 * time.Second):
		}
		wStream.CloseSend()
		rv, ok := <-rCh
		if rv != nil || !ok {
			t.Errorf("#%d: rv, ok got = %v %v, want = nil true", i, rv, ok)
		}

		// can't defer because tcp ports will be in use
		clus.Terminate(t)
	}
}
<|endoftext|>


<commit_before>package bugsnag

import (
	"net/http"
	"strings"
)

type (
	beforeFunc func(*Event, *Configuration) error

	// MiddlewareStacks keep middleware in the correct order. They are
	// called in reverse order, so if you add a new middleware it will
	// be called before all existing middleware.
	middlewareStack struct {
		before []beforeFunc
	}
)

// AddMiddleware adds a new middleware to the outside of the existing ones,
// when the middlewareStack is Run it will be run before all middleware that
// have been added before.
func (stack *middlewareStack) OnBeforeNotify(middleware beforeFunc) {
	stack.before = append(stack.before, middleware)
}

// Run causes all the middleware to be run. If they all permit it the next callback
// will be called with all the middleware on the stack.
func (stack *middlewareStack) Run(event *Event, config *Configuration, next func() error) error {
	// run all the before filters in reverse order
	for i := range stack.before {
		before := stack.before[len(stack.before)-i-1]

		err := stack.runBeforeFilter(before, event, config)
		if err != nil {
			return err
		}
	}

	return next()
}

func (stack *middlewareStack) runBeforeFilter(f beforeFunc, event *Event, config *Configuration) error {
	defer func() {
		if err := recover(); err != nil {
			config.logf("bugsnag/middleware: unexpected panic: %v", err)
		}
	}()

	return f(event, config)
}

// catchMiddlewarePanic is used to log any panics that happen inside Middleware,
// we wouldn't want to not notify Bugsnag in this case.
func catchMiddlewarePanic(event *Event, config *Configuration, next func() error) {
}

// httpRequestMiddleware is added OnBeforeNotify by default. It takes information
// from an http.Request passed in as rawData, and adds it to the Event. You can
// use this as a template for writing your own Middleware.
func httpRequestMiddleware(event *Event, config *Configuration) error {
	for _, datum := range event.RawData {
		if request, ok := datum.(*http.Request); ok {
			proto := "http://"
			if request.TLS != nil {
				proto = "https://"
			}

			event.MetaData.Update(MetaData{
				"request": {
					"clientIp":   request.RemoteAddr,
					"httpMethod": request.Method,
					"url":        proto + request.Host + request.RequestURI,
					"params":     request.URL.Query(),
				},
			})

			// Add headers as a separate tab.
			event.MetaData.AddStruct("Headers", request.Header)

			// Default context to Path
			if event.Context == "" {
				event.Context = request.URL.Path
			}

			// Default user.id to IP so that users-affected works.
			if event.User == nil {
				ip := request.RemoteAddr
				if idx := strings.LastIndex(ip, ":"); idx != -1 {
					ip = ip[:idx]
				}
				event.User = &User{Id: ip}
			}
		}
	}
	return nil
}
<commit_msg>Put headers on the request<commit_after>package bugsnag

import (
	"net/http"
	"strings"
)

type (
	beforeFunc func(*Event, *Configuration) error

	// MiddlewareStacks keep middleware in the correct order. They are
	// called in reverse order, so if you add a new middleware it will
	// be called before all existing middleware.
	middlewareStack struct {
		before []beforeFunc
	}
)

// AddMiddleware adds a new middleware to the outside of the existing ones,
// when the middlewareStack is Run it will be run before all middleware that
// have been added before.
func (stack *middlewareStack) OnBeforeNotify(middleware beforeFunc) {
	stack.before = append(stack.before, middleware)
}

// Run causes all the middleware to be run. If they all permit it the next callback
// will be called with all the middleware on the stack.
func (stack *middlewareStack) Run(event *Event, config *Configuration, next func() error) error {
	// run all the before filters in reverse order
	for i := range stack.before {
		before := stack.before[len(stack.before)-i-1]

		err := stack.runBeforeFilter(before, event, config)
		if err != nil {
			return err
		}
	}

	return next()
}

func (stack *middlewareStack) runBeforeFilter(f beforeFunc, event *Event, config *Configuration) error {
	defer func() {
		if err := recover(); err != nil {
			config.logf("bugsnag/middleware: unexpected panic: %v", err)
		}
	}()

	return f(event, config)
}

// catchMiddlewarePanic is used to log any panics that happen inside Middleware,
// we wouldn't want to not notify Bugsnag in this case.
func catchMiddlewarePanic(event *Event, config *Configuration, next func() error) {
}

// httpRequestMiddleware is added OnBeforeNotify by default. It takes information
// from an http.Request passed in as rawData, and adds it to the Event. You can
// use this as a template for writing your own Middleware.
func httpRequestMiddleware(event *Event, config *Configuration) error {
	for _, datum := range event.RawData {
		if request, ok := datum.(*http.Request); ok {
			proto := "http://"
			if request.TLS != nil {
				proto = "https://"
			}

			event.MetaData.Update(MetaData{
				"request": {
					"clientIp":   request.RemoteAddr,
					"httpMethod": request.Method,
					"url":        proto + request.Host + request.RequestURI,
					"params":     request.URL.Query(),
					"headers",    request.Header,
				},
			})

			// Default context to Path
			if event.Context == "" {
				event.Context = request.URL.Path
			}

			// Default user.id to IP so that users-affected works.
			if event.User == nil {
				ip := request.RemoteAddr
				if idx := strings.LastIndex(ip, ":"); idx != -1 {
					ip = ip[:idx]
				}
				event.User = &User{Id: ip}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package v2

import (
	"fmt"

	"code.cloudfoundry.org/cli/actor/v2action"
	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/v2/shared"
	"code.cloudfoundry.org/cli/util/configv3"
)

//go:generate counterfeiter . TargetActor
type TargetActor interface {
	GetOrganizationByName(orgName string) (v2action.Organization, v2action.Warnings, error)
	GetOrganizationSpaces(orgGUID string) ([]v2action.Space, v2action.Warnings, error)
	GetSpaceByName(orgGUID string, spaceName string) (v2action.Space, v2action.Warnings, error)
}

type TargetCommand struct {
	Organization    string      `short:"o" description:"Organization"`
	Space           string      `short:"s" description:"Space"`
	usage           interface{} `usage:"CF_NAME target [-o ORG] [-s SPACE]"`
	relatedCommands interface{} `related_commands:"create-org, create-space, login, orgs, spaces"`

	Config command.Config
	Actor  TargetActor
	UI     command.UI
}

func (cmd *TargetCommand) Setup(config command.Config, ui command.UI) error {
	cmd.Config = config
	cmd.UI = ui

	ccClient, uaaClient, err := shared.NewClients(config, ui)
	if err != nil {
		return err
	}
	cmd.Actor = v2action.NewActor(ccClient, uaaClient, config)

	return nil
}

func (cmd *TargetCommand) notifyCLIUpdateIfNeeded() {
	err := command.MinimumAPIVersionCheck(cmd.Config.BinaryVersion(), cmd.Config.MinCLIVersion())
	if _, ok := err.(command.MinimumAPIVersionNotMetError); ok {
		cmd.UI.DisplayTextWithFlavor("Cloud Foundry API version {{.APIVersion}} requires CLI version {{.MinCLIVersion}}. You are currently on version {{.BinaryVersion}}. To upgrade your CLI, please visit: https://github.com/cloudfoundry/cli#downloads",
			map[string]interface{}{
				"APIVersion":    cmd.Config.APIVersion(),
				"MinCLIVersion": cmd.Config.MinCLIVersion(),
				"BinaryVersion": cmd.Config.BinaryVersion(),
			})
	}
}

func (cmd *TargetCommand) Execute(args []string) error {
	cmd.notifyCLIUpdateIfNeeded()

	err := command.CheckTarget(cmd.Config, false, false)
	if err != nil {
		return err
	}

	user, err := cmd.Config.CurrentUser()
	if err != nil {
		return shared.CurrentUserError{
			Message: err.Error(),
		}
	}

	err = cmd.setOrg()
	if err != nil {
		return err
	}

	err = cmd.setSpace()
	if err != nil {
		return err
	}

	return cmd.displayTargetTable(user)
}

// Setting space
func (cmd *TargetCommand) setSpace() error {
	if cmd.Space == "" {
		return nil
	}
	emptyOrg := configv3.Organization{}
	if cmd.Config.TargetedOrganization() == emptyOrg {
		return shared.NoOrgTargetedError{}
	}

	space, warnings, err := cmd.Actor.GetSpaceByName(cmd.Config.TargetedOrganization().GUID, cmd.Space)
	cmd.UI.DisplayWarnings(warnings)

	if err != nil {
		return shared.SpaceTargetError{
			Message:   err.Error(),
			SpaceName: cmd.Space,
		}
	}

	cmd.Config.SetSpaceInformation(space.GUID, space.Name, space.AllowSSH)
	return nil
}

// Setting organization
func (cmd *TargetCommand) setOrg() error {
	if cmd.Organization == "" {
		return nil
	}

	var (
		org      v2action.Organization
		warnings v2action.Warnings
	)

	org, warnings, err := cmd.Actor.GetOrganizationByName(cmd.Organization)
	cmd.UI.DisplayWarnings(warnings)

	if err != nil {
		return shared.OrgTargetError{
			Message: err.Error(),
		}
	}

	cmd.Config.SetOrganizationInformation(org.GUID, cmd.Organization)

	cmd.Config.UnsetSpaceInformation()

	err = cmd.autoTargetSpace(org.GUID)
	if err != nil {
		return err
	}

	return nil
}

// Auto-target the space if there is only one space in the org
// and no space arg was provided.
func (cmd *TargetCommand) autoTargetSpace(orgGUID string) error {
	// if the user provided a space as an argument, we don't want to autoTarget
	if cmd.Space != "" {
		return nil
	}

	spaces, warnings, err := cmd.Actor.GetOrganizationSpaces(orgGUID)
	cmd.UI.DisplayWarnings(warnings)

	if err != nil {
		return shared.GetOrgSpacesError{
			Message: err.Error(),
		}
	}

	if len(spaces) == 1 {
		space := spaces[0]
		cmd.Config.SetSpaceInformation(space.GUID, space.Name, space.AllowSSH)
	}

	return nil
}

func (cmd *TargetCommand) displayTargetTable(user configv3.User) error {
	apiEndpoint := cmd.UI.TranslateText("{{.APIEndpoint}} (API version: {{.APIVersionString}})", map[string]interface{}{
		"APIEndpoint":      cmd.Config.Target(),
		"APIVersionString": cmd.Config.APIVersion(),
	})

	table := [][]string{
		{cmd.UI.TranslateText("API endpoint:"), apiEndpoint},
		{cmd.UI.TranslateText("User:"), user.Name},
	}

	emptyOrg := configv3.Organization{}
	if cmd.Config.TargetedOrganization() == emptyOrg {
		cmd.UI.DisplayTable("", table, 3)
		command := fmt.Sprintf("%s target -o ORG -s SPACE", cmd.Config.BinaryName())

		cmd.UI.DisplayTextWithFlavor("No org or space targeted, use '{{.CFTargetCommand}}'",
			map[string]interface{}{
				"CFTargetCommand": command,
			})
		return nil
	}

	table = append(table, []string{
		cmd.UI.TranslateText("Org:"), cmd.Config.TargetedOrganization().Name,
	})

	emptySpace := configv3.Space{}
	if cmd.Config.TargetedSpace() == emptySpace {
		spaceCommand := fmt.Sprintf("%s target -s SPACE", cmd.Config.BinaryName())

		noSpaceTargeted := cmd.UI.TranslateText("No space targeted, use '{{.CFTargetCommand}}'",
			map[string]interface{}{
				"CFTargetCommand": spaceCommand,
			})

		table = append(table, []string{
			cmd.UI.TranslateText("Space:"), noSpaceTargeted,
		})
	} else {
		table = append(table, []string{
			cmd.UI.TranslateText("Space:"), cmd.Config.TargetedSpace().Name,
		})
	}

	cmd.UI.DisplayTable("", table, 3)

	return nil
}
<commit_msg>improved readability by moving checks to the calling function<commit_after>package v2

import (
	"fmt"

	"code.cloudfoundry.org/cli/actor/v2action"
	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/v2/shared"
	"code.cloudfoundry.org/cli/util/configv3"
)

//go:generate counterfeiter . TargetActor
type TargetActor interface {
	GetOrganizationByName(orgName string) (v2action.Organization, v2action.Warnings, error)
	GetOrganizationSpaces(orgGUID string) ([]v2action.Space, v2action.Warnings, error)
	GetSpaceByName(orgGUID string, spaceName string) (v2action.Space, v2action.Warnings, error)
}

type TargetCommand struct {
	Organization    string      `short:"o" description:"Organization"`
	Space           string      `short:"s" description:"Space"`
	usage           interface{} `usage:"CF_NAME target [-o ORG] [-s SPACE]"`
	relatedCommands interface{} `related_commands:"create-org, create-space, login, orgs, spaces"`

	Config command.Config
	Actor  TargetActor
	UI     command.UI
}

func (cmd *TargetCommand) Setup(config command.Config, ui command.UI) error {
	cmd.Config = config
	cmd.UI = ui

	ccClient, uaaClient, err := shared.NewClients(config, ui)
	if err != nil {
		return err
	}
	cmd.Actor = v2action.NewActor(ccClient, uaaClient, config)

	return nil
}

func (cmd *TargetCommand) Execute(args []string) error {
	cmd.notifyCLIUpdateIfNeeded()

	err := command.CheckTarget(cmd.Config, false, false)
	if err != nil {
		return err
	}

	user, err := cmd.Config.CurrentUser()
	if err != nil {
		return shared.CurrentUserError{
			Message: err.Error(),
		}
	}

	if cmd.Organization != "" {
		err = cmd.setOrg()
		if err != nil {
			return err
		}

		if cmd.Space == "" {
			err = cmd.autoTargetSpace(cmd.Config.TargetedOrganization().GUID)
			if err != nil {
				return err
			}
		}
	}

	if cmd.Space != "" {
		err = cmd.setSpace()
		if err != nil {
			return err
		}
	}

	return cmd.displayTargetTable(user)
}

func (cmd *TargetCommand) notifyCLIUpdateIfNeeded() {
	err := command.MinimumAPIVersionCheck(cmd.Config.BinaryVersion(), cmd.Config.MinCLIVersion())
	if _, ok := err.(command.MinimumAPIVersionNotMetError); ok {
		cmd.UI.DisplayTextWithFlavor("Cloud Foundry API version {{.APIVersion}} requires CLI version {{.MinCLIVersion}}. You are currently on version {{.BinaryVersion}}. To upgrade your CLI, please visit: https://github.com/cloudfoundry/cli#downloads",
			map[string]interface{}{
				"APIVersion":    cmd.Config.APIVersion(),
				"MinCLIVersion": cmd.Config.MinCLIVersion(),
				"BinaryVersion": cmd.Config.BinaryVersion(),
			})
	}
}

// Setting organization
func (cmd *TargetCommand) setOrg() error {
	var (
		org      v2action.Organization
		warnings v2action.Warnings
	)

	org, warnings, err := cmd.Actor.GetOrganizationByName(cmd.Organization)
	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return shared.OrgTargetError{
			Message: err.Error(),
		}
	}

	cmd.Config.SetOrganizationInformation(org.GUID, cmd.Organization)
	cmd.Config.UnsetSpaceInformation()

	return nil
}

// Auto-target the space if there is only one space in the org
// and no space arg was provided.
func (cmd *TargetCommand) autoTargetSpace(orgGUID string) error {
	spaces, warnings, err := cmd.Actor.GetOrganizationSpaces(orgGUID)
	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return shared.GetOrgSpacesError{
			Message: err.Error(),
		}
	}

	if len(spaces) == 1 {
		space := spaces[0]
		cmd.Config.SetSpaceInformation(space.GUID, space.Name, space.AllowSSH)
	}

	return nil
}

// Setting space
func (cmd *TargetCommand) setSpace() error {
	emptyOrg := configv3.Organization{}
	if cmd.Config.TargetedOrganization() == emptyOrg {
		return shared.NoOrgTargetedError{}
	}

	space, warnings, err := cmd.Actor.GetSpaceByName(cmd.Config.TargetedOrganization().GUID, cmd.Space)
	cmd.UI.DisplayWarnings(warnings)
	if err != nil {
		return shared.SpaceTargetError{
			Message:   err.Error(),
			SpaceName: cmd.Space,
		}
	}

	cmd.Config.SetSpaceInformation(space.GUID, space.Name, space.AllowSSH)

	return nil
}

func (cmd *TargetCommand) displayTargetTable(user configv3.User) error {
	apiEndpoint := cmd.UI.TranslateText("{{.APIEndpoint}} (API version: {{.APIVersionString}})", map[string]interface{}{
		"APIEndpoint":      cmd.Config.Target(),
		"APIVersionString": cmd.Config.APIVersion(),
	})

	table := [][]string{
		{cmd.UI.TranslateText("API endpoint:"), apiEndpoint},
		{cmd.UI.TranslateText("User:"), user.Name},
	}

	emptyOrg := configv3.Organization{}
	if cmd.Config.TargetedOrganization() == emptyOrg {
		cmd.UI.DisplayTable("", table, 3)
		command := fmt.Sprintf("%s target -o ORG -s SPACE", cmd.Config.BinaryName())

		cmd.UI.DisplayTextWithFlavor("No org or space targeted, use '{{.CFTargetCommand}}'",
			map[string]interface{}{
				"CFTargetCommand": command,
			})
		return nil
	}

	table = append(table, []string{
		cmd.UI.TranslateText("Org:"), cmd.Config.TargetedOrganization().Name,
	})

	emptySpace := configv3.Space{}
	if cmd.Config.TargetedSpace() == emptySpace {
		spaceCommand := fmt.Sprintf("%s target -s SPACE", cmd.Config.BinaryName())

		noSpaceTargeted := cmd.UI.TranslateText("No space targeted, use '{{.CFTargetCommand}}'",
			map[string]interface{}{
				"CFTargetCommand": spaceCommand,
			})

		table = append(table, []string{
			cmd.UI.TranslateText("Space:"), noSpaceTargeted,
		})
	} else {
		table = append(table, []string{
			cmd.UI.TranslateText("Space:"), cmd.Config.TargetedSpace().Name,
		})
	}

	cmd.UI.DisplayTable("", table, 3)

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

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

// ErrNetworkAlreadyExists is the error returned by CreateNetwork when the
// network already exists.
var ErrNetworkAlreadyExists = errors.New("network already exists")

// Network represents a network.
//
// See https://goo.gl/FDkCdQ for more details.
type Network struct {
	Name      string      `json:"name"`
	ID        string      `json:"id"`
	Type      string      `json:"type"`
	Endpoints []*Endpoint `json:"endpoints"`
}

// Endpoint represents an endpoint.
//
// See https://goo.gl/FDkCdQ for more details.
type Endpoint struct {
	Name    string `json:"name"`
	ID      string `json:"id"`
	Network string `json:"network"`
}

// ListNetworks returns all networks.
//
// See https://goo.gl/4hCNtZ for more details.
func (c *Client) ListNetworks() ([]Network, error) {
	resp, err := c.do("GET", "/networks", doOptions{})
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var networks []Network
	if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil {
		return nil, err
	}
	return networks, nil
}

// NetworkInfo returns information about a network by its ID.
//
// See https://goo.gl/4hCNtZ for more details.
func (c *Client) NetworkInfo(id string) (*Network, error) {
	path := "/networks/" + id
	resp, err := c.do("GET", path, doOptions{})
	if err != nil {
		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
			return nil, &NoSuchNetwork{ID: id}
		}
		return nil, err
	}
	defer resp.Body.Close()
	var network Network
	if err := json.NewDecoder(resp.Body).Decode(&network); err != nil {
		return nil, err
	}
	return &network, nil
}

// CreateNetworkOptions specify parameters to the CreateNetwork function and
// (for now) is the expected body of the "create network" http request message
//
// See https://goo.gl/FDkCdQ for more details.
type CreateNetworkOptions struct {
	Name        string                 `json:"name"`
	NetworkType string                 `json:"network_type"`
	Options     map[string]interface{} `json:"options"`
}

// CreateNetwork creates a new network, returning the network instance,
// or an error in case of failure.
//
// See https://goo.gl/FDkCdQ for more details.
func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) {
	resp, err := c.do(
		"POST",
		"/networks",
		doOptions{
			data: opts,
		},
	)
	if err != nil {
		if e, ok := err.(*Error); ok && e.Status == http.StatusConflict {
			return nil, ErrNetworkAlreadyExists
		}
		return nil, err
	}
	defer resp.Body.Close()

	type createNetworkResponse struct {
		ID string
	}
	var (
		network Network
		cnr     createNetworkResponse
	)
	if err := json.NewDecoder(resp.Body).Decode(&cnr); err != nil {
		return nil, err
	}

	network.Name = opts.Name
	network.ID = cnr.ID
	network.Type = opts.NetworkType

	return &network, nil
}

// RemoveNetwork removes a network or an error in case of failure.
//
// See https://goo.gl/FDkCdQ for more details.
func (c *Client) RemoveNetwork(id string) error {
        resp, err := c.do("DELETE", "/networks/"+id, doOptions{})
        if err != nil {
                if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
                        return &NoSuchNetwork{ID: id}
                }
                return err
        }
        resp.Body.Close()
        return nil
}

// NoSuchNetwork is the error returned when a given network does not exist.
type NoSuchNetwork struct {
	ID string
}

func (err *NoSuchNetwork) Error() string {
	return fmt.Sprintf("No such network: %s", err.ID)
}
<commit_msg>Convert spaces to tabs<commit_after>// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

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

// ErrNetworkAlreadyExists is the error returned by CreateNetwork when the
// network already exists.
var ErrNetworkAlreadyExists = errors.New("network already exists")

// Network represents a network.
//
// See https://goo.gl/FDkCdQ for more details.
type Network struct {
	Name      string      `json:"name"`
	ID        string      `json:"id"`
	Type      string      `json:"type"`
	Endpoints []*Endpoint `json:"endpoints"`
}

// Endpoint represents an endpoint.
//
// See https://goo.gl/FDkCdQ for more details.
type Endpoint struct {
	Name    string `json:"name"`
	ID      string `json:"id"`
	Network string `json:"network"`
}

// ListNetworks returns all networks.
//
// See https://goo.gl/4hCNtZ for more details.
func (c *Client) ListNetworks() ([]Network, error) {
	resp, err := c.do("GET", "/networks", doOptions{})
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var networks []Network
	if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil {
		return nil, err
	}
	return networks, nil
}

// NetworkInfo returns information about a network by its ID.
//
// See https://goo.gl/4hCNtZ for more details.
func (c *Client) NetworkInfo(id string) (*Network, error) {
	path := "/networks/" + id
	resp, err := c.do("GET", path, doOptions{})
	if err != nil {
		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
			return nil, &NoSuchNetwork{ID: id}
		}
		return nil, err
	}
	defer resp.Body.Close()
	var network Network
	if err := json.NewDecoder(resp.Body).Decode(&network); err != nil {
		return nil, err
	}
	return &network, nil
}

// CreateNetworkOptions specify parameters to the CreateNetwork function and
// (for now) is the expected body of the "create network" http request message
//
// See https://goo.gl/FDkCdQ for more details.
type CreateNetworkOptions struct {
	Name        string                 `json:"name"`
	NetworkType string                 `json:"network_type"`
	Options     map[string]interface{} `json:"options"`
}

// CreateNetwork creates a new network, returning the network instance,
// or an error in case of failure.
//
// See https://goo.gl/FDkCdQ for more details.
func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) {
	resp, err := c.do(
		"POST",
		"/networks",
		doOptions{
			data: opts,
		},
	)
	if err != nil {
		if e, ok := err.(*Error); ok && e.Status == http.StatusConflict {
			return nil, ErrNetworkAlreadyExists
		}
		return nil, err
	}
	defer resp.Body.Close()

	type createNetworkResponse struct {
		ID string
	}
	var (
		network Network
		cnr     createNetworkResponse
	)
	if err := json.NewDecoder(resp.Body).Decode(&cnr); err != nil {
		return nil, err
	}

	network.Name = opts.Name
	network.ID = cnr.ID
	network.Type = opts.NetworkType

	return &network, nil
}

// RemoveNetwork removes a network or an error in case of failure.
//
// See https://goo.gl/FDkCdQ for more details.
func (c *Client) RemoveNetwork(id string) error {
	resp, err := c.do("DELETE", "/networks/"+id, doOptions{})
	if err != nil {
		if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
			return &NoSuchNetwork{ID: id}
		}
		return err
	}
	resp.Body.Close()
	return nil
}

// NoSuchNetwork is the error returned when a given network does not exist.
type NoSuchNetwork struct {
	ID string
}

func (err *NoSuchNetwork) Error() string {
	return fmt.Sprintf("No such network: %s", err.ID)
}
<|endoftext|>


<commit_before>package ginpongo2

import (
	"github.com/flosch/pongo2"
	. "github.com/gin-gonic/gin"
	"net/http"
)

func Pongo2() HandlerFunc {
	return func(c *Context) {
		c.Next()

		templateName, templateNameError := c.Get("template")
		templateNameValue, isString := templateName.(string)

		if templateNameError == nil && isString {
			templateData, templateDataError := c.Get("data")
			var template = pongo2.Must(pongo2.FromFile(templateNameValue))
			err := template.ExecuteWriter(getContext(templateData, templateDataError), c.Writer)
			if err != nil {
				http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
			}
		}
	}
}

func getContext(templateData interface{}, err error) pongo2.Context {
	if templateData == nil || err != nil {
		return nil
	}
	contextData, isMap := templateData.(map[string]interface{})
	if isMap {
		return contextData
	}
	return nil
}
<commit_msg>Refactored to improve error handling<commit_after>package ginpongo2

import (
	"github.com/flosch/pongo2"
	. "github.com/gin-gonic/gin"
	"net/http"
	"fmt"
)

func Pongo2() HandlerFunc {
	return func(c *Context) {
		c.Next()

		name, err := stringFromContext(c, "template")
		if err != nil {
			http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
		}

		data, _ := c.Get("data")

		template := pongo2.Must(pongo2.FromFile(name))
		err = template.ExecuteWriter(convertContext(data), c.Writer)
		if err != nil {
			http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
		}
	}
}

func stringFromContext(c *Context, input string) (string, error) {
	raw, ok := c.Get(input)
	if ok {
		strVal, ok := raw.(string)
		if ok {
			return strVal, nil
		}
	}
	return "", fmt.Errorf("No data for context variable: %s", input)
}

func convertContext(thing interface{}) pongo2.Context {
	if thing != nil {
		context, isMap := thing.(map[string]interface{})
		if isMap {
			return context
		}
	}
	return nil
}

func getContext(templateData interface{}, err error) pongo2.Context {
	if templateData == nil || err != nil {
		return nil
	}
	contextData, isMap := templateData.(map[string]interface{})
	if isMap {
		return contextData
	}
	return nil
}
<|endoftext|>


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

// +build go1.1

package interfaces

// A helper interface for a general CSV writer. Conforms to encoding/csv Writer
// in the standard go library as well as the Writer implemented by this
// package.
type Writer interface {
	// Currently no errors are possible.
	Error() error

	// Flush writes any buffered data to the underlying io.Writer.
	// To check if an error occurred during the Flush, call Error.
	Flush()

	// Writer writes a single CSV record to w along with any necessary quoting.
	// A record is a slice of strings with each string being one field.
	Write(record []string) error

	// WriteAll writes multiple CSV records to w using Write and then calls Flush.
	WriteAll(records [][]string) error
}
<commit_msg>docs(interfaces): correct incorrect Error() text<commit_after>// Copyright 2014 Jens Rantil. All rights reserved.  Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.

// +build go1.1

package interfaces

// A helper interface for a general CSV writer. Conforms to encoding/csv Writer
// in the standard go library as well as the Writer implemented by this
// package.
type Writer interface {
  // Error reports any error that has occurred during a previous Write or
  // Flush.
	Error() error

	// Flush writes any buffered data to the underlying io.Writer.
	// To check if an error occurred during the Flush, call Error.
	Flush()

	// Writer writes a single CSV record to w along with any necessary quoting.
	// A record is a slice of strings with each string being one field.
	Write(record []string) error

	// WriteAll writes multiple CSV records to w using Write and then calls Flush.
	WriteAll(records [][]string) error
}
<|endoftext|>


<commit_before>package commands

import (
	"bufio"
	"os"
	"strings"

	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

var (
	prePushCmd = &cobra.Command{
		Use:   "pre-push",
		Short: "Implements the Git pre-push hook",
		Run:   prePushCommand,
	}
	prePushDryRun        = false
	prePushDeleteBranch  = "(delete)"
	prePushMissingErrMsg = "%s is an LFS pointer to %s, which does not exist in .git/lfs/objects.\n\nRun 'git lfs fsck' to verify Git LFS objects."
)

// prePushCommand is run through Git's pre-push hook. The pre-push hook passes
// two arguments on the command line:
//
//   1. Name of the remote to which the push is being done
//   2. URL to which the push is being done
//
// The hook receives commit information on stdin in the form:
//   <local ref> <local sha1> <remote ref> <remote sha1>
//
// In the typical case, prePushCommand will get a list of git objects being
// pushed by using the following:
//
//    git rev-list --objects <local sha1> ^<remote sha1>
//
// If any of those git objects are associated with Git LFS objects, those
// objects will be pushed to the Git LFS API.
//
// In the case of pushing a new branch, the list of git objects will be all of
// the git objects in this branch.
//
// In the case of deleting a branch, no attempts to push Git LFS objects will be
// made.
func prePushCommand(cmd *cobra.Command, args []string) {

	if len(args) == 0 {
		Print("This should be run through Git's pre-push hook.  Run `git lfs update` to install it.")
		os.Exit(1)
	}

	lfs.Config.CurrentRemote = args[0]

	// We can be passed multiple lines of refs
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if len(line) == 0 {
			continue
		}

		left, right := decodeRefs(line)
		if left == prePushDeleteBranch {
			return
		}

		// Just use scanner here
		scanOpt := &lfs.ScanRefsOptions{ScanMode: lfs.ScanLeftToRemoteMode, RemoteName: lfs.Config.CurrentRemote}
		pointers, err := lfs.ScanRefs(left, right, scanOpt)
		if err != nil {
			Panic(err, "Error scanning for Git LFS files")
		}

		totalSize := int64(0)
		for _, p := range pointers {
			totalSize += p.Size
		}

		// Objects to skip because they're missing locally but on server
		var skipObjects map[string]struct{}

		if !prePushDryRun {
			// Do this as a pre-flight check since upload queue starts immediately
			skipObjects = prePushCheckForMissingObjects(pointers)
		}

		uploadQueue := lfs.NewUploadQueue(len(pointers), totalSize, prePushDryRun)

		for _, pointer := range pointers {
			if prePushDryRun {
				Print("push %s [%s]", pointer.Name, pointer.Oid)
				continue
			}

			if _, skip := skipObjects[pointer.Oid]; skip {
				// object missing locally but on server, don't bother
				continue
			}

			u, err := lfs.NewUploadable(pointer.Oid, pointer.Name)
			if err != nil {
				if lfs.IsCleanPointerError(err) {
					Exit(prePushMissingErrMsg, pointer.Name, lfs.ErrorGetContext(err, "pointer").(*lfs.Pointer).Oid)
				} else if Debugging || lfs.IsFatalError(err) {
					Panic(err, err.Error())
				} else {
					Exit(err.Error())
				}
			}

			uploadQueue.Add(u)
		}

		if !prePushDryRun {
			uploadQueue.Wait()
			for _, err := range uploadQueue.Errors() {
				if Debugging || lfs.IsFatalError(err) {
					LoggedError(err, err.Error())
				} else {
					Error(err.Error())
				}
			}

			if len(uploadQueue.Errors()) > 0 {
				os.Exit(2)
			}
		}
	}
}

func prePushCheckForMissingObjects(pointers []*lfs.WrappedPointer) (objectsOnServer map[string]struct{}) {
	var missingLocalObjects []*lfs.WrappedPointer
	var missingSize int64
	var skipObjects = make(map[string]struct{}, len(pointers))
	for _, pointer := range pointers {
		if !lfs.ObjectExistsOfSize(pointer.Oid, pointer.Size) {
			// We think we need to push this but we don't have it
			// Store for server checking later
			missingLocalObjects = append(missingLocalObjects, pointer)
			missingSize += pointer.Size
		}
	}
	if len(missingLocalObjects) == 0 {
		return nil
	}

	checkQueue := lfs.NewDownloadCheckQueue(len(missingLocalObjects), missingSize, false)
	for _, p := range missingLocalObjects {
		checkQueue.Add(lfs.NewDownloadCheckable(p))
	}
	// this channel is filled with oids for which Check() succeeded & Transfer() was called
	transferc := checkQueue.Watch()
	done := make(chan int)
	go func() {
		for oid := range transferc {
			skipObjects[oid] = struct{}{}
		}
		done <- 1
	}()
	// Currently this is needed to flush the batch but is not enough to sync transferc completely
	checkQueue.Wait()
	<-done
	return skipObjects
}

// decodeRefs pulls the sha1s out of the line read from the pre-push
// hook's stdin.
func decodeRefs(input string) (string, string) {
	refs := strings.Split(strings.TrimSpace(input), " ")
	var left, right string

	if len(refs) > 1 {
		left = refs[1]
	}

	if len(refs) > 3 {
		right = "^" + refs[3]
	}

	return left, right
}

func init() {
	prePushCmd.Flags().BoolVarP(&prePushDryRun, "dry-run", "d", false, "Do everything except actually send the updates")
	RootCmd.AddCommand(prePushCmd)
}
<commit_msg>Continue rather than return when encountering delete branch in pre-push<commit_after>package commands

import (
	"bufio"
	"os"
	"strings"

	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra"
)

var (
	prePushCmd = &cobra.Command{
		Use:   "pre-push",
		Short: "Implements the Git pre-push hook",
		Run:   prePushCommand,
	}
	prePushDryRun        = false
	prePushDeleteBranch  = "(delete)"
	prePushMissingErrMsg = "%s is an LFS pointer to %s, which does not exist in .git/lfs/objects.\n\nRun 'git lfs fsck' to verify Git LFS objects."
)

// prePushCommand is run through Git's pre-push hook. The pre-push hook passes
// two arguments on the command line:
//
//   1. Name of the remote to which the push is being done
//   2. URL to which the push is being done
//
// The hook receives commit information on stdin in the form:
//   <local ref> <local sha1> <remote ref> <remote sha1>
//
// In the typical case, prePushCommand will get a list of git objects being
// pushed by using the following:
//
//    git rev-list --objects <local sha1> ^<remote sha1>
//
// If any of those git objects are associated with Git LFS objects, those
// objects will be pushed to the Git LFS API.
//
// In the case of pushing a new branch, the list of git objects will be all of
// the git objects in this branch.
//
// In the case of deleting a branch, no attempts to push Git LFS objects will be
// made.
func prePushCommand(cmd *cobra.Command, args []string) {

	if len(args) == 0 {
		Print("This should be run through Git's pre-push hook.  Run `git lfs update` to install it.")
		os.Exit(1)
	}

	lfs.Config.CurrentRemote = args[0]

	// We can be passed multiple lines of refs
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		if len(line) == 0 {
			continue
		}

		left, right := decodeRefs(line)
		if left == prePushDeleteBranch {
			continue
		}

		// Just use scanner here
		scanOpt := &lfs.ScanRefsOptions{ScanMode: lfs.ScanLeftToRemoteMode, RemoteName: lfs.Config.CurrentRemote}
		pointers, err := lfs.ScanRefs(left, right, scanOpt)
		if err != nil {
			Panic(err, "Error scanning for Git LFS files")
		}

		totalSize := int64(0)
		for _, p := range pointers {
			totalSize += p.Size
		}

		// Objects to skip because they're missing locally but on server
		var skipObjects map[string]struct{}

		if !prePushDryRun {
			// Do this as a pre-flight check since upload queue starts immediately
			skipObjects = prePushCheckForMissingObjects(pointers)
		}

		uploadQueue := lfs.NewUploadQueue(len(pointers), totalSize, prePushDryRun)

		for _, pointer := range pointers {
			if prePushDryRun {
				Print("push %s [%s]", pointer.Name, pointer.Oid)
				continue
			}

			if _, skip := skipObjects[pointer.Oid]; skip {
				// object missing locally but on server, don't bother
				continue
			}

			u, err := lfs.NewUploadable(pointer.Oid, pointer.Name)
			if err != nil {
				if lfs.IsCleanPointerError(err) {
					Exit(prePushMissingErrMsg, pointer.Name, lfs.ErrorGetContext(err, "pointer").(*lfs.Pointer).Oid)
				} else if Debugging || lfs.IsFatalError(err) {
					Panic(err, err.Error())
				} else {
					Exit(err.Error())
				}
			}

			uploadQueue.Add(u)
		}

		if !prePushDryRun {
			uploadQueue.Wait()
			for _, err := range uploadQueue.Errors() {
				if Debugging || lfs.IsFatalError(err) {
					LoggedError(err, err.Error())
				} else {
					Error(err.Error())
				}
			}

			if len(uploadQueue.Errors()) > 0 {
				os.Exit(2)
			}
		}
	}
}

func prePushCheckForMissingObjects(pointers []*lfs.WrappedPointer) (objectsOnServer map[string]struct{}) {
	var missingLocalObjects []*lfs.WrappedPointer
	var missingSize int64
	var skipObjects = make(map[string]struct{}, len(pointers))
	for _, pointer := range pointers {
		if !lfs.ObjectExistsOfSize(pointer.Oid, pointer.Size) {
			// We think we need to push this but we don't have it
			// Store for server checking later
			missingLocalObjects = append(missingLocalObjects, pointer)
			missingSize += pointer.Size
		}
	}
	if len(missingLocalObjects) == 0 {
		return nil
	}

	checkQueue := lfs.NewDownloadCheckQueue(len(missingLocalObjects), missingSize, false)
	for _, p := range missingLocalObjects {
		checkQueue.Add(lfs.NewDownloadCheckable(p))
	}
	// this channel is filled with oids for which Check() succeeded & Transfer() was called
	transferc := checkQueue.Watch()
	done := make(chan int)
	go func() {
		for oid := range transferc {
			skipObjects[oid] = struct{}{}
		}
		done <- 1
	}()
	// Currently this is needed to flush the batch but is not enough to sync transferc completely
	checkQueue.Wait()
	<-done
	return skipObjects
}

// decodeRefs pulls the sha1s out of the line read from the pre-push
// hook's stdin.
func decodeRefs(input string) (string, string) {
	refs := strings.Split(strings.TrimSpace(input), " ")
	var left, right string

	if len(refs) > 1 {
		left = refs[1]
	}

	if len(refs) > 3 {
		right = "^" + refs[3]
	}

	return left, right
}

func init() {
	prePushCmd.Flags().BoolVarP(&prePushDryRun, "dry-run", "d", false, "Do everything except actually send the updates")
	RootCmd.AddCommand(prePushCmd)
}
<|endoftext|>


<commit_before>// Package new generates micro service templates
package new

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"

	"github.com/micro/cli"
	tmpl "github.com/micro/micro/internal/template"
)

type config struct {
	// foo
	Alias string
	// go.micro
	Namespace string
	// api, srv, web, fnc
	Type string
	// go.micro.srv.foo
	FQDN string
	// github.com/micro/foo
	Dir string
	// $GOPATH/src/github.com/micro/foo
	GoDir string
	// $GOPATH
	GoPath string
	// Files
	Files []file
	// Comments
	Comments []string
}

type file struct {
	Path string
	Tmpl string
}

func write(c config, file, tmpl string) error {
	fn := template.FuncMap{
		"title": strings.Title,
	}

	fmt.Println("creating", file)
	f, err := os.Create(file)
	if err != nil {
		return err
	}
	defer f.Close()

	t, err := template.New("f").Funcs(fn).Parse(tmpl)
	if err != nil {
		return err
	}

	return t.Execute(f, c)
}

func create(c config) error {
	// check if dir exists
	if _, err := os.Stat(c.GoDir); !os.IsNotExist(err) {
		return fmt.Errorf("%s already exists", c.GoDir)
	}

	fmt.Println("creating service", c.FQDN)

	// write the files
	for _, file := range c.Files {
		f := filepath.Join(c.GoDir, file.Path)
		dir := filepath.Dir(f)

		if _, err := os.Stat(dir); os.IsNotExist(err) {
			fmt.Println("creating", dir)
			if err := os.MkdirAll(dir, 0755); err != nil {
				return err
			}
		}

		if err := write(c, f, file.Tmpl); err != nil {
			return err
		}
	}

	for _, comment := range c.Comments {
		fmt.Println(comment)
	}

	return nil
}

func run(ctx *cli.Context) {
	namespace := ctx.String("namespace")
	alias := ctx.String("alias")
	fqdn := ctx.String("fqdn")
	atype := ctx.String("type")
	dir := ctx.Args().First()

	if len(dir) == 0 {
		fmt.Println("specify service name")
		return
	}

	if len(namespace) == 0 {
		fmt.Println("namespace not defined")
		return
	}

	if len(atype) == 0 {
		fmt.Println("type not defined")
		return
	}

	// check if the path is absolute, we don't want this
	// we want to a relative path so we can install in GOPATH
	if path.IsAbs(dir) {
		fmt.Println("require relative path as service will be installed in GOPATH")
		return
	}

	goPath := os.Getenv("GOPATH")

	// don't know GOPATH, runaway....
	if len(goPath) == 0 {
		fmt.Println("unknown GOPATH")
		return
	}

	// attempt to split path if not windows
	if runtime.GOOS != "windows" {
		goPath = strings.Split(goPath, ":")[0]
	}

	goDir := filepath.Join(goPath, "src", path.Clean(dir))

	if len(alias) == 0 {
		// set as last part
		alias = filepath.Base(dir)
	}

	if len(fqdn) == 0 {
		fqdn = strings.Join([]string{namespace, atype, alias}, ".")
	}

	var c config

	switch atype {
	case "fnc":
		// create srv config
		c = config{
			Alias:     alias,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Files: []file{
				{"main.go", tmpl.MainFNC},
				{"handler/example.go", tmpl.HandlerFNC},
				{"subscriber/example.go", tmpl.SubscriberFNC},
				{"proto/example/example.proto", tmpl.ProtoFNC},
				{"Dockerfile", tmpl.DockerFNC},
				{"README.md", tmpl.ReadmeFNC},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file example.proto:\n",
				"cd " + goDir,
				"protoc --proto_path=. --go_out=. --micro_out=. proto/example/example.proto",
			},
		}
	case "srv":
		// create srv config
		c = config{
			Alias:     alias,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Files: []file{
				{"main.go", tmpl.MainSRV},
				{"handler/example.go", tmpl.HandlerSRV},
				{"subscriber/example.go", tmpl.SubscriberSRV},
				{"proto/example/example.proto", tmpl.ProtoSRV},
				{"Dockerfile", tmpl.DockerSRV},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file example.proto:\n",
				"cd " + goDir,
				"protoc --proto_path=. --go_out=. --micro_out=. proto/example/example.proto",
			},
		}
	case "api":
		// create api config
		c = config{
			Alias:     alias,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Files: []file{
				{"main.go", tmpl.MainAPI},
				{"client/example.go", tmpl.WrapperAPI},
				{"handler/example.go", tmpl.HandlerAPI},
				{"proto/example/example.proto", tmpl.ProtoAPI},
				{"Dockerfile", tmpl.DockerSRV},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file example.proto:\n",
				"cd " + goDir,
				"protoc --proto_path=. --go_out=. --micro_out=. proto/example/example.proto",
			},
		}
	case "web":
		// create srv config
		c = config{
			Alias:     alias,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Files: []file{
				{"main.go", tmpl.MainWEB},
				{"handler/handler.go", tmpl.HandlerWEB},
				{"html/index.html", tmpl.HTMLWEB},
				{"Dockerfile", tmpl.DockerWEB},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{},
		}
	default:
		fmt.Println("Unknown type", atype)
		return
	}

	if err := create(c); err != nil {
		fmt.Println(err)
		return
	}
}

func Commands() []cli.Command {
	return []cli.Command{
		{
			Name:  "new",
			Usage: "Create a new micro service by specifying a directory path relative to your $GOPATH",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "namespace",
					Usage: "Namespace for the service e.g com.example",
					Value: "go.micro",
				},
				cli.StringFlag{
					Name:  "type",
					Usage: "Type of service e.g api, fnc, srv, web",
					Value: "srv",
				},
				cli.StringFlag{
					Name:  "fqdn",
					Usage: "FQDN of service e.g com.example.srv.service (defaults to namespace.type.alias)",
				},
				cli.StringFlag{
					Name:  "alias",
					Usage: "Alias is the short name used as part of combined name if specified",
				},
			},
			Action: func(c *cli.Context) {
				run(c)
			},
		},
	}
}
<commit_msg>use treeprint<commit_after>// Package new generates micro service templates
package new

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"

	"github.com/micro/cli"
	tmpl "github.com/micro/micro/internal/template"
	"github.com/xlab/treeprint"
)

type config struct {
	// foo
	Alias string
	// go.micro
	Namespace string
	// api, srv, web, fnc
	Type string
	// go.micro.srv.foo
	FQDN string
	// github.com/micro/foo
	Dir string
	// $GOPATH/src/github.com/micro/foo
	GoDir string
	// $GOPATH
	GoPath string
	// Files
	Files []file
	// Comments
	Comments []string
}

type file struct {
	Path string
	Tmpl string
}

func write(c config, file, tmpl string) error {
	fn := template.FuncMap{
		"title": strings.Title,
	}

	f, err := os.Create(file)
	if err != nil {
		return err
	}
	defer f.Close()

	t, err := template.New("f").Funcs(fn).Parse(tmpl)
	if err != nil {
		return err
	}

	return t.Execute(f, c)
}

func create(c config) error {
	// check if dir exists
	if _, err := os.Stat(c.GoDir); !os.IsNotExist(err) {
		return fmt.Errorf("%s already exists", c.GoDir)
	}

	fmt.Println("Creating service", c.FQDN, "in", c.GoDir, "\n")

	t := treeprint.New()

	nodes := map[string]treeprint.Tree{}
	nodes[c.GoDir] = t

	// write the files
	for _, file := range c.Files {
		f := filepath.Join(c.GoDir, file.Path)
		dir := filepath.Dir(f)

		b, ok := nodes[dir]
		if !ok {
			d, _ := filepath.Rel(c.GoDir, dir)
			b = t.AddBranch(d)
			nodes[dir] = b
		}

		if _, err := os.Stat(dir); os.IsNotExist(err) {
			if err := os.MkdirAll(dir, 0755); err != nil {
				return err
			}
		}

		p := filepath.Base(f)

		b.AddNode(p)
		if err := write(c, f, file.Tmpl); err != nil {
			return err
		}
	}

	// print tree
	fmt.Println(t.String())

	for _, comment := range c.Comments {
		fmt.Println(comment)
	}

	return nil
}

func run(ctx *cli.Context) {
	namespace := ctx.String("namespace")
	alias := ctx.String("alias")
	fqdn := ctx.String("fqdn")
	atype := ctx.String("type")
	dir := ctx.Args().First()

	if len(dir) == 0 {
		fmt.Println("specify service name")
		return
	}

	if len(namespace) == 0 {
		fmt.Println("namespace not defined")
		return
	}

	if len(atype) == 0 {
		fmt.Println("type not defined")
		return
	}

	// check if the path is absolute, we don't want this
	// we want to a relative path so we can install in GOPATH
	if path.IsAbs(dir) {
		fmt.Println("require relative path as service will be installed in GOPATH")
		return
	}

	goPath := os.Getenv("GOPATH")

	// don't know GOPATH, runaway....
	if len(goPath) == 0 {
		fmt.Println("unknown GOPATH")
		return
	}

	// attempt to split path if not windows
	if runtime.GOOS != "windows" {
		goPath = strings.Split(goPath, ":")[0]
	}

	goDir := filepath.Join(goPath, "src", path.Clean(dir))

	if len(alias) == 0 {
		// set as last part
		alias = filepath.Base(dir)
	}

	if len(fqdn) == 0 {
		fqdn = strings.Join([]string{namespace, atype, alias}, ".")
	}

	var c config

	switch atype {
	case "fnc":
		// create srv config
		c = config{
			Alias:     alias,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Files: []file{
				{"main.go", tmpl.MainFNC},
				{"handler/example.go", tmpl.HandlerFNC},
				{"subscriber/example.go", tmpl.SubscriberFNC},
				{"proto/example/example.proto", tmpl.ProtoFNC},
				{"Dockerfile", tmpl.DockerFNC},
				{"README.md", tmpl.ReadmeFNC},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file example.proto:\n",
				"cd " + goDir,
				"protoc --proto_path=. --go_out=. --micro_out=. proto/example/example.proto",
			},
		}
	case "srv":
		// create srv config
		c = config{
			Alias:     alias,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Files: []file{
				{"main.go", tmpl.MainSRV},
				{"handler/example.go", tmpl.HandlerSRV},
				{"subscriber/example.go", tmpl.SubscriberSRV},
				{"proto/example/example.proto", tmpl.ProtoSRV},
				{"Dockerfile", tmpl.DockerSRV},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file example.proto:\n",
				"cd " + goDir,
				"protoc --proto_path=. --go_out=. --micro_out=. proto/example/example.proto",
			},
		}
	case "api":
		// create api config
		c = config{
			Alias:     alias,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Files: []file{
				{"main.go", tmpl.MainAPI},
				{"client/example.go", tmpl.WrapperAPI},
				{"handler/example.go", tmpl.HandlerAPI},
				{"proto/example/example.proto", tmpl.ProtoAPI},
				{"Dockerfile", tmpl.DockerSRV},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file example.proto:\n",
				"cd " + goDir,
				"protoc --proto_path=. --go_out=. --micro_out=. proto/example/example.proto",
			},
		}
	case "web":
		// create srv config
		c = config{
			Alias:     alias,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Files: []file{
				{"main.go", tmpl.MainWEB},
				{"handler/handler.go", tmpl.HandlerWEB},
				{"html/index.html", tmpl.HTMLWEB},
				{"Dockerfile", tmpl.DockerWEB},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{},
		}
	default:
		fmt.Println("Unknown type", atype)
		return
	}

	if err := create(c); err != nil {
		fmt.Println(err)
		return
	}
}

func Commands() []cli.Command {
	return []cli.Command{
		{
			Name:  "new",
			Usage: "Create a new micro service by specifying a directory path relative to your $GOPATH",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "namespace",
					Usage: "Namespace for the service e.g com.example",
					Value: "go.micro",
				},
				cli.StringFlag{
					Name:  "type",
					Usage: "Type of service e.g api, fnc, srv, web",
					Value: "srv",
				},
				cli.StringFlag{
					Name:  "fqdn",
					Usage: "FQDN of service e.g com.example.srv.service (defaults to namespace.type.alias)",
				},
				cli.StringFlag{
					Name:  "alias",
					Usage: "Alias is the short name used as part of combined name if specified",
				},
			},
			Action: func(c *cli.Context) {
				run(c)
			},
		},
	}
}
<|endoftext|>