<commit_before>package agent

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/fnproject/fn/api/id"
	"github.com/fnproject/fn/api/models"
	"github.com/go-openapi/strfmt"
	"github.com/opentracing/opentracing-go"
	"github.com/patrickmn/go-cache"
	"github.com/sirupsen/logrus"
)

type Call interface {
	// Model will return the underlying models.Call configuration for this call.
	// TODO we could respond to async correctly from agent but layering, this
	// is only because the front end has different responses based on call type.
	// try to discourage use elsewhere until this gets pushed down more...
	Model() *models.Call

	// Start will be called before this call is executed, it may be used to
	// guarantee mutual exclusion, check docker permissions, update timestamps,
	// etc.
	// TODO Start and End can likely be unexported as they are only used in the agent,
	// and on a type which is constructed in a specific agent. meh.
	Start(ctx context.Context) error

	// End will be called immediately after attempting a call execution,
	// regardless of whether the execution failed or not. An error will be passed
	// to End, which if nil indicates a successful execution. Any error returned
	// from End will be returned as the error from Submit.
	End(ctx context.Context, err error) error
}

// TODO build w/o closures... lazy
type CallOpt func(a *agent, c *call) error

func FromRequest(appName, path string, req *http.Request) CallOpt {
	return func(a *agent, c *call) error {
		// TODO we need to add a little timeout to these 2 things
		app, err := a.app(req.Context(), appName)
		if err != nil {
			return err
		}

		route, err := a.route(req.Context(), appName, path)
		if err != nil {
			return err
		}

		params, match := matchRoute(route.Path, path)
		if !match {
			return errors.New("route does not match") // TODO wtf, can we ignore match?
		}

		if route.Format == "" {
			route.Format = "default"
		}

		id := id.New().String()

		// baseVars are the vars on the route & app, not on this specific request [for hot functions]
		baseVars := make(map[string]string, len(app.Config)+len(route.Config)+3)
		baseVars["FN_FORMAT"] = route.Format
		baseVars["APP_NAME"] = appName
		baseVars["ROUTE"] = route.Path
		baseVars["MEMORY_MB"] = fmt.Sprintf("%d", route.Memory)

		// app config
		for k, v := range app.Config {
			k = toEnvName("", k)
			baseVars[k] = v
		}
		for k, v := range route.Config {
			k = toEnvName("", k)
			baseVars[k] = v
		}

		// envVars contains the full set of env vars, per request + base
		envVars := make(map[string]string, len(baseVars)+len(params)+len(req.Header)+3)

		for k, v := range baseVars {
			envVars[k] = v
		}

		envVars["CALL_ID"] = id
		envVars["METHOD"] = req.Method
		envVars["REQUEST_URL"] = fmt.Sprintf("%v://%v%v", func() string {
			if req.TLS == nil {
				return "http"
			}
			return "https"
		}(), req.Host, req.URL.String())

		// params
		for _, param := range params {
			envVars[toEnvName("PARAM", param.Key)] = param.Value
		}

		var headerVars = make(map[string]string)
		for k, v := range req.Header {
			joinedHeader := strings.Join(v,",")
			headerVars[toEnvName("HEADER",k)] = joinedHeader
		}

		// add all the env vars we build to the request headers
		// TODO should we save req.Headers and copy OVER app.Config / route.Config ?
		for k, v := range envVars {
			req.Header.Add(k, v)
		}

		for k,v := range headerVars {
			envVars[k] = v
		}

		// TODO this relies on ordering of opts, but tests make sure it works, probably re-plumb/destroy headers
		if rw, ok := c.w.(http.ResponseWriter); ok {
			rw.Header().Add("FN_CALL_ID", id)
			for k, vs := range route.Headers {
				for _, v := range vs {
					// pre-write in these headers to response
					rw.Header().Add(k, v)
				}
			}
		}

		c.Call = &models.Call{
			ID:      id,
			AppName: appName,
			Path:    route.Path,
			Image:   route.Image,
			// Delay: 0,
			Type:   route.Type,
			Format: route.Format,
			// Payload: TODO,
			Priority:    new(int32), // TODO this is crucial, apparently
			Timeout:     route.Timeout,
			IdleTimeout: route.IdleTimeout,
			Memory:      route.Memory,
			BaseEnv:     baseVars,
			EnvVars:     envVars,
			CreatedAt:   strfmt.DateTime(time.Now()),
			URL:         req.URL.String(), // TODO we should probably strip host/port
			Method:      req.Method,
		}

		// TODO if these made it to here we have a problemo. error instead?
		if c.Timeout <= 0 {
			c.Timeout = models.DefaultRouteTimeout
		}
		if c.IdleTimeout <= 0 {
			c.IdleTimeout = models.DefaultIdleTimeout
		}

		c.req = req
		return nil
	}
}

func FromModel(mCall *models.Call) CallOpt {
	return func(a *agent, c *call) error {
		c.Call = mCall

		// NOTE this adds content length based on payload length
		req, err := http.NewRequest(c.Method, c.URL, strings.NewReader(c.Payload))
		if err != nil {
			return err
		}
		for k, v := range c.EnvVars {
			// TODO if we don't store env as []string headers are messed up
			req.Header.Set(k, v)
		}

		c.req = req
		// TODO anything else really?
		return nil
	}
}

// TODO this should be required
func WithWriter(w io.Writer) CallOpt {
	return func(a *agent, c *call) error {
		c.w = w
		return nil
	}
}

// GetCall builds a Call that can be used to submit jobs to the agent.
//
// TODO we could make this package level just moving the cache around. meh.
// TODO where to put this? async and sync both call this
func (a *agent) GetCall(opts ...CallOpt) (Call, error) {
	var c call

	for _, o := range opts {
		err := o(a, &c)
		if err != nil {
			return nil, err
		}
	}

	// TODO typed errors to test
	if c.req == nil || c.Call == nil {
		return nil, errors.New("no model or request provided for call")
	}

	// TODO move func logger here
	// TODO add log store interface (yagni?)
	c.ds = a.ds
	c.mq = a.mq

	return &c, nil
}

type call struct {
	*models.Call

	ds     models.Datastore
	mq     models.MessageQueue
	w      io.Writer
	req    *http.Request
	stderr io.WriteCloser
}

func (c *call) Model() *models.Call { return c.Call }

func (c *call) Start(ctx context.Context) error {
	span, ctx := opentracing.StartSpanFromContext(ctx, "agent_call_start")
	defer span.Finish()

	// TODO discuss this policy. cold has not yet started the container,
	// hot just has to dispatch
	//
	// make sure we have at least half our timeout to run, or timeout here
	deadline, ok := ctx.Deadline()
	need := time.Now().Add(time.Duration(c.Timeout) * time.Second) // > deadline, always
	// need.Sub(deadline) = elapsed time
	if ok && need.Sub(deadline) > (time.Duration(c.Timeout)*time.Second)/2 {
		return context.DeadlineExceeded
	}

	c.StartedAt = strfmt.DateTime(time.Now())
	c.Status = "running"

	if c.Type == models.TypeAsync {
		// XXX (reed): make sure MQ reservation is lengthy. to skirt MQ semantics,
		// we could add a new message to MQ w/ delay of call.Timeout and delete the
		// old one (in that order), after marking the call as running in the db
		// (see below)

		// XXX (reed): should we store the updated started_at + status? we could
		// use this so that if we pick up a call from mq and find its status is
		// running to avoid running the call twice and potentially mark it as
		// errored (built in long running task detector, so to speak...)

		err := c.mq.Delete(ctx, c.Call)
		if err != nil {
			return err // let another thread try this
		}
	}
	return nil
}

func (c *call) End(ctx context.Context, err error) error {
	span, ctx := opentracing.StartSpanFromContext(ctx, "agent_call_end")
	defer span.Finish()

	c.CompletedAt = strfmt.DateTime(time.Now())

	switch err {
	case nil:
		c.Status = "success"
	case context.DeadlineExceeded:
		c.Status = "timeout"
	default:
		// XXX (reed): should we append the error to logs? Error field?
		c.Status = "error"
	}

	if c.Type == models.TypeAsync {
		// XXX (reed): delete MQ message, eventually
	}

	// TODO since the function itself can reply directly to a client (or logs),
	// this means that we could potentially store an error / timeout status for a
	// call that ran successfully [by a user's perspective]
	// TODO this should be update, really
	if err := c.ds.InsertCall(ctx, c.Call); err != nil {
		// TODO we should just log this error not return it to user? just issue storing call status but call is run
		logrus.WithError(err).Error("error inserting call into datastore")
	}

	// return the original error so that this is returned from Submit (for sync)
	// TODO we could just skip over (and log) and End errors and return slot.exec error
	// in submit instead of doing this, it's a bit odd. thoughts?
	return err
}

func (a *agent) route(ctx context.Context, appName, path string) (*models.Route, error) {
	key := routeCacheKey(appName, path)
	route, ok := a.cache.Get(key)
	if ok {
		return route.(*models.Route), nil
	}

	resp, err := a.singleflight.Do(key,
		func() (interface{}, error) { return a.ds.GetRoute(ctx, appName, path) },
	)
	if err != nil {
		return nil, err
	}
	route = resp.(*models.Route)
	a.cache.Set(key, route, cache.DefaultExpiration)
	return route.(*models.Route), nil
}

func (a *agent) app(ctx context.Context, appName string) (*models.App, error) {
	key := appCacheKey(appName)
	app, ok := a.cache.Get(key)
	if ok {
		return app.(*models.App), nil
	}

	resp, err := a.singleflight.Do(key,
		func() (interface{}, error) { return a.ds.GetApp(ctx, appName) },
	)
	if err != nil {
		return nil, err
	}
	app = resp.(*models.App)
	a.cache.Set(key, app, cache.DefaultExpiration)
	return app.(*models.App), nil
}

func routeCacheKey(appname, path string) string {
	return "r:" + appname + "\x00" + path
}

func appCacheKey(appname string) string {
	return "a:" + appname
}

func fakeHandler(http.ResponseWriter, *http.Request, Params) {}

// TODO what is this stuff anyway?
func matchRoute(baseRoute, route string) (Params, bool) {
	tree := &node{}
	tree.addRoute(baseRoute, fakeHandler)
	handler, p, _ := tree.getValue(route)
	if handler == nil {
		return nil, false
	}

	return p, true
}

func toEnvName(envtype, name string) string {
	name = strings.Replace(name, "-", "_", -1)
	if envtype == "" {
		return name
	}
	return fmt.Sprintf("%s_%s", envtype, name)
}
<commit_msg>review feedback<commit_after>package agent

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/fnproject/fn/api/id"
	"github.com/fnproject/fn/api/models"
	"github.com/go-openapi/strfmt"
	"github.com/opentracing/opentracing-go"
	"github.com/patrickmn/go-cache"
	"github.com/sirupsen/logrus"
)

type Call interface {
	// Model will return the underlying models.Call configuration for this call.
	// TODO we could respond to async correctly from agent but layering, this
	// is only because the front end has different responses based on call type.
	// try to discourage use elsewhere until this gets pushed down more...
	Model() *models.Call

	// Start will be called before this call is executed, it may be used to
	// guarantee mutual exclusion, check docker permissions, update timestamps,
	// etc.
	// TODO Start and End can likely be unexported as they are only used in the agent,
	// and on a type which is constructed in a specific agent. meh.
	Start(ctx context.Context) error

	// End will be called immediately after attempting a call execution,
	// regardless of whether the execution failed or not. An error will be passed
	// to End, which if nil indicates a successful execution. Any error returned
	// from End will be returned as the error from Submit.
	End(ctx context.Context, err error) error
}

// TODO build w/o closures... lazy
type CallOpt func(a *agent, c *call) error

func FromRequest(appName, path string, req *http.Request) CallOpt {
	return func(a *agent, c *call) error {
		// TODO we need to add a little timeout to these 2 things
		app, err := a.app(req.Context(), appName)
		if err != nil {
			return err
		}

		route, err := a.route(req.Context(), appName, path)
		if err != nil {
			return err
		}

		params, match := matchRoute(route.Path, path)
		if !match {
			return errors.New("route does not match") // TODO wtf, can we ignore match?
		}

		if route.Format == "" {
			route.Format = "default"
		}

		id := id.New().String()

		// baseVars are the vars on the route & app, not on this specific request [for hot functions]
		baseVars := make(map[string]string, len(app.Config)+len(route.Config)+3)
		baseVars["FN_FORMAT"] = route.Format
		baseVars["APP_NAME"] = appName
		baseVars["ROUTE"] = route.Path
		baseVars["MEMORY_MB"] = fmt.Sprintf("%d", route.Memory)

		// app config
		for k, v := range app.Config {
			k = toEnvName("", k)
			baseVars[k] = v
		}
		for k, v := range route.Config {
			k = toEnvName("", k)
			baseVars[k] = v
		}

		// envVars contains the full set of env vars, per request + base
		envVars := make(map[string]string, len(baseVars)+len(params)+len(req.Header)+3)

		for k, v := range baseVars {
			envVars[k] = v
		}

		envVars["CALL_ID"] = id
		envVars["METHOD"] = req.Method
		envVars["REQUEST_URL"] = fmt.Sprintf("%v://%v%v", func() string {
			if req.TLS == nil {
				return "http"
			}
			return "https"
		}(), req.Host, req.URL.String())

		// params
		for _, param := range params {
			envVars[toEnvName("PARAM", param.Key)] = param.Value
		}

		headerVars := make(map[string]string,len(req.Header))

		for k, v := range req.Header {
			headerVars[toEnvName("HEADER",k)] = strings.Join(v, ", ")
		}

		// add all the env vars we build to the request headers
		// TODO should we save req.Headers and copy OVER app.Config / route.Config ?
		for k, v := range envVars {
			req.Header.Add(k, v)
		}

		for k,v := range headerVars {
			envVars[k] = v
		}

		// TODO this relies on ordering of opts, but tests make sure it works, probably re-plumb/destroy headers
		if rw, ok := c.w.(http.ResponseWriter); ok {
			rw.Header().Add("FN_CALL_ID", id)
			for k, vs := range route.Headers {
				for _, v := range vs {
					// pre-write in these headers to response
					rw.Header().Add(k, v)
				}
			}
		}

		c.Call = &models.Call{
			ID:      id,
			AppName: appName,
			Path:    route.Path,
			Image:   route.Image,
			// Delay: 0,
			Type:   route.Type,
			Format: route.Format,
			// Payload: TODO,
			Priority:    new(int32), // TODO this is crucial, apparently
			Timeout:     route.Timeout,
			IdleTimeout: route.IdleTimeout,
			Memory:      route.Memory,
			BaseEnv:     baseVars,
			EnvVars:     envVars,
			CreatedAt:   strfmt.DateTime(time.Now()),
			URL:         req.URL.String(), // TODO we should probably strip host/port
			Method:      req.Method,
		}

		// TODO if these made it to here we have a problemo. error instead?
		if c.Timeout <= 0 {
			c.Timeout = models.DefaultRouteTimeout
		}
		if c.IdleTimeout <= 0 {
			c.IdleTimeout = models.DefaultIdleTimeout
		}

		c.req = req
		return nil
	}
}

func FromModel(mCall *models.Call) CallOpt {
	return func(a *agent, c *call) error {
		c.Call = mCall

		// NOTE this adds content length based on payload length
		req, err := http.NewRequest(c.Method, c.URL, strings.NewReader(c.Payload))
		if err != nil {
			return err
		}
		for k, v := range c.EnvVars {
			// TODO if we don't store env as []string headers are messed up
			req.Header.Set(k, v)
		}

		c.req = req
		// TODO anything else really?
		return nil
	}
}

// TODO this should be required
func WithWriter(w io.Writer) CallOpt {
	return func(a *agent, c *call) error {
		c.w = w
		return nil
	}
}

// GetCall builds a Call that can be used to submit jobs to the agent.
//
// TODO we could make this package level just moving the cache around. meh.
// TODO where to put this? async and sync both call this
func (a *agent) GetCall(opts ...CallOpt) (Call, error) {
	var c call

	for _, o := range opts {
		err := o(a, &c)
		if err != nil {
			return nil, err
		}
	}

	// TODO typed errors to test
	if c.req == nil || c.Call == nil {
		return nil, errors.New("no model or request provided for call")
	}

	// TODO move func logger here
	// TODO add log store interface (yagni?)
	c.ds = a.ds
	c.mq = a.mq

	return &c, nil
}

type call struct {
	*models.Call

	ds     models.Datastore
	mq     models.MessageQueue
	w      io.Writer
	req    *http.Request
	stderr io.WriteCloser
}

func (c *call) Model() *models.Call { return c.Call }

func (c *call) Start(ctx context.Context) error {
	span, ctx := opentracing.StartSpanFromContext(ctx, "agent_call_start")
	defer span.Finish()

	// TODO discuss this policy. cold has not yet started the container,
	// hot just has to dispatch
	//
	// make sure we have at least half our timeout to run, or timeout here
	deadline, ok := ctx.Deadline()
	need := time.Now().Add(time.Duration(c.Timeout) * time.Second) // > deadline, always
	// need.Sub(deadline) = elapsed time
	if ok && need.Sub(deadline) > (time.Duration(c.Timeout)*time.Second)/2 {
		return context.DeadlineExceeded
	}

	c.StartedAt = strfmt.DateTime(time.Now())
	c.Status = "running"

	if c.Type == models.TypeAsync {
		// XXX (reed): make sure MQ reservation is lengthy. to skirt MQ semantics,
		// we could add a new message to MQ w/ delay of call.Timeout and delete the
		// old one (in that order), after marking the call as running in the db
		// (see below)

		// XXX (reed): should we store the updated started_at + status? we could
		// use this so that if we pick up a call from mq and find its status is
		// running to avoid running the call twice and potentially mark it as
		// errored (built in long running task detector, so to speak...)

		err := c.mq.Delete(ctx, c.Call)
		if err != nil {
			return err // let another thread try this
		}
	}
	return nil
}

func (c *call) End(ctx context.Context, err error) error {
	span, ctx := opentracing.StartSpanFromContext(ctx, "agent_call_end")
	defer span.Finish()

	c.CompletedAt = strfmt.DateTime(time.Now())

	switch err {
	case nil:
		c.Status = "success"
	case context.DeadlineExceeded:
		c.Status = "timeout"
	default:
		// XXX (reed): should we append the error to logs? Error field?
		c.Status = "error"
	}

	if c.Type == models.TypeAsync {
		// XXX (reed): delete MQ message, eventually
	}

	// TODO since the function itself can reply directly to a client (or logs),
	// this means that we could potentially store an error / timeout status for a
	// call that ran successfully [by a user's perspective]
	// TODO this should be update, really
	if err := c.ds.InsertCall(ctx, c.Call); err != nil {
		// TODO we should just log this error not return it to user? just issue storing call status but call is run
		logrus.WithError(err).Error("error inserting call into datastore")
	}

	// return the original error so that this is returned from Submit (for sync)
	// TODO we could just skip over (and log) and End errors and return slot.exec error
	// in submit instead of doing this, it's a bit odd. thoughts?
	return err
}

func (a *agent) route(ctx context.Context, appName, path string) (*models.Route, error) {
	key := routeCacheKey(appName, path)
	route, ok := a.cache.Get(key)
	if ok {
		return route.(*models.Route), nil
	}

	resp, err := a.singleflight.Do(key,
		func() (interface{}, error) { return a.ds.GetRoute(ctx, appName, path) },
	)
	if err != nil {
		return nil, err
	}
	route = resp.(*models.Route)
	a.cache.Set(key, route, cache.DefaultExpiration)
	return route.(*models.Route), nil
}

func (a *agent) app(ctx context.Context, appName string) (*models.App, error) {
	key := appCacheKey(appName)
	app, ok := a.cache.Get(key)
	if ok {
		return app.(*models.App), nil
	}

	resp, err := a.singleflight.Do(key,
		func() (interface{}, error) { return a.ds.GetApp(ctx, appName) },
	)
	if err != nil {
		return nil, err
	}
	app = resp.(*models.App)
	a.cache.Set(key, app, cache.DefaultExpiration)
	return app.(*models.App), nil
}

func routeCacheKey(appname, path string) string {
	return "r:" + appname + "\x00" + path
}

func appCacheKey(appname string) string {
	return "a:" + appname
}

func fakeHandler(http.ResponseWriter, *http.Request, Params) {}

// TODO what is this stuff anyway?
func matchRoute(baseRoute, route string) (Params, bool) {
	tree := &node{}
	tree.addRoute(baseRoute, fakeHandler)
	handler, p, _ := tree.getValue(route)
	if handler == nil {
		return nil, false
	}

	return p, true
}

func toEnvName(envtype, name string) string {
	name = strings.Replace(name, "-", "_", -1)
	if envtype == "" {
		return name
	}
	return fmt.Sprintf("%s_%s", envtype, name)
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"

	"github.com/leancloud/lean-cli/api/regions"
	"github.com/leancloud/lean-cli/utils"
	"github.com/leancloud/lean-cli/version"
	"github.com/levigross/grequests"
)

var routerCache = make(map[string]regions.Region)

// GetAppRegion will query the app router, and return the app's region.
// The result is cached in process memory.
func GetAppRegion(appID string) (regions.Region, error) {
	if r, ok := routerCache[appID]; ok {
		return r, nil
	}
	resp, err := grequests.Get("https://app-router.leancloud.cn/1/route?appId="+appID, &grequests.RequestOptions{
		UserAgent: "LeanCloud-CLI/" + version.Version,
	})
	if err != nil {
		return regions.Invalid, err
	}
	if !resp.Ok {
		return regions.Invalid, fmt.Errorf("query app router failed: %d", resp.StatusCode)
	}

	var result struct {
		APIServer string `json:"api_server"`
	}
	if err = resp.JSON(&result); err != nil {
		return regions.Invalid, err
	}

	switch result.APIServer {
	case "us-api.leancloud.cn":
		routerCache[appID] = regions.US
		return regions.US, nil
	case "api.leancloud.cn":
		routerCache[appID] = regions.CN
		return regions.CN, nil
	case "e1-api.leancloud.cn":
		routerCache[appID] = regions.TAB
		return regions.TAB, nil
	default:
		return regions.Invalid, fmt.Errorf("invalid region server: %s", result.APIServer)
	}
}

func saveRouterCache() error {
	data, err := json.MarshalIndent(routerCache, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(filepath.Join(utils.ConfigDir(), "leancloud", "app_router.json"), data, 0644)
}

func init() {
	data, err := ioutil.ReadFile(filepath.Join(utils.ConfigDir(), "leancloud", "app_router.json"))
	if err != nil {
		return
	}
	json.Unmarshal(data, &routerCache)
}
<commit_msg>:card_file_box: Error when missing app region cache<commit_after>package api

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"path/filepath"

	"github.com/leancloud/lean-cli/api/regions"
	"github.com/leancloud/lean-cli/utils"
)

var routerCache = make(map[string]regions.Region)

func GetAppRegion(appID string) (regions.Region, error) {
	if r, ok := routerCache[appID]; ok {
		return r, nil
	} else {
		return regions.Invalid, errors.New("应用配置信息不完整，请重新运行 `lean switch` 关联应用")
	}
}

func saveRouterCache() error {
	data, err := json.MarshalIndent(routerCache, "", "  ")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(filepath.Join(utils.ConfigDir(), "leancloud", "app_router.json"), data, 0644)
}

func init() {
	data, err := ioutil.ReadFile(filepath.Join(utils.ConfigDir(), "leancloud", "app_router.json"))
	if err != nil {
		return
	}
	json.Unmarshal(data, &routerCache)
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

// +build !windows,!solaris,!aix

package test

// Session functional tests.

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
	"testing"

	"golang.org/x/crypto/ssh"
)

func TestRunCommandSuccess(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()
	err = session.Run("true")
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
}

func TestHostKeyCheck(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()

	conf := clientConfig()
	hostDB := hostKeyDB()
	conf.HostKeyCallback = hostDB.Check

	// change the keys.
	hostDB.keys[ssh.KeyAlgoRSA][25]++
	hostDB.keys[ssh.KeyAlgoDSA][25]++
	hostDB.keys[ssh.KeyAlgoECDSA256][25]++

	conn, err := server.TryDial(conf)
	if err == nil {
		conn.Close()
		t.Fatalf("dial should have failed.")
	} else if !strings.Contains(err.Error(), "host key mismatch") {
		t.Fatalf("'host key mismatch' not found in %v", err)
	}
}

func TestRunCommandStdin(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	r, w := io.Pipe()
	defer r.Close()
	defer w.Close()
	session.Stdin = r

	err = session.Run("true")
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
}

func TestRunCommandStdinError(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	r, w := io.Pipe()
	defer r.Close()
	session.Stdin = r
	pipeErr := errors.New("closing write end of pipe")
	w.CloseWithError(pipeErr)

	err = session.Run("true")
	if err != pipeErr {
		t.Fatalf("expected %v, found %v", pipeErr, err)
	}
}

func TestRunCommandFailed(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()
	err = session.Run(`bash -c "kill -9 $$"`)
	if err == nil {
		t.Fatalf("session succeeded: %v", err)
	}
}

func TestRunCommandWeClosed(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	err = session.Shell()
	if err != nil {
		t.Fatalf("shell failed: %v", err)
	}
	err = session.Close()
	if err != nil {
		t.Fatalf("shell failed: %v", err)
	}
}

func TestFuncLargeRead(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("unable to create new session: %s", err)
	}

	stdout, err := session.StdoutPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdout pipe: %s", err)
	}

	err = session.Start("dd if=/dev/urandom bs=2048 count=1024")
	if err != nil {
		t.Fatalf("unable to execute remote command: %s", err)
	}

	buf := new(bytes.Buffer)
	n, err := io.Copy(buf, stdout)
	if err != nil {
		t.Fatalf("error reading from remote stdout: %s", err)
	}

	if n != 2048*1024 {
		t.Fatalf("Expected %d bytes but read only %d from remote command", 2048, n)
	}
}

func TestKeyChange(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conf := clientConfig()
	hostDB := hostKeyDB()
	conf.HostKeyCallback = hostDB.Check
	conf.RekeyThreshold = 1024
	conn := server.Dial(conf)
	defer conn.Close()

	for i := 0; i < 4; i++ {
		session, err := conn.NewSession()
		if err != nil {
			t.Fatalf("unable to create new session: %s", err)
		}

		stdout, err := session.StdoutPipe()
		if err != nil {
			t.Fatalf("unable to acquire stdout pipe: %s", err)
		}

		err = session.Start("dd if=/dev/urandom bs=1024 count=1")
		if err != nil {
			t.Fatalf("unable to execute remote command: %s", err)
		}
		buf := new(bytes.Buffer)
		n, err := io.Copy(buf, stdout)
		if err != nil {
			t.Fatalf("error reading from remote stdout: %s", err)
		}

		want := int64(1024)
		if n != want {
			t.Fatalf("Expected %d bytes but read only %d from remote command", want, n)
		}
	}

	if changes := hostDB.checkCount; changes < 4 {
		t.Errorf("got %d key changes, want 4", changes)
	}
}

func TestInvalidTerminalMode(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	if err = session.RequestPty("vt100", 80, 40, ssh.TerminalModes{255: 1984}); err == nil {
		t.Fatalf("req-pty failed: successful request with invalid mode")
	}
}

func TestValidTerminalMode(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	stdout, err := session.StdoutPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdout pipe: %s", err)
	}

	stdin, err := session.StdinPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdin pipe: %s", err)
	}

	tm := ssh.TerminalModes{ssh.ECHO: 0}
	if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
		t.Fatalf("req-pty failed: %s", err)
	}

	err = session.Shell()
	if err != nil {
		t.Fatalf("session failed: %s", err)
	}

	stdin.Write([]byte("stty -a && exit\n"))

	var buf bytes.Buffer
	if _, err := io.Copy(&buf, stdout); err != nil {
		t.Fatalf("reading failed: %s", err)
	}

	if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "-echo ") {
		t.Fatalf("terminal mode failure: expected -echo in stty output, got %s", sttyOutput)
	}
}

func TestWindowChange(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	stdout, err := session.StdoutPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdout pipe: %s", err)
	}

	stdin, err := session.StdinPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdin pipe: %s", err)
	}

	tm := ssh.TerminalModes{ssh.ECHO: 0}
	if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
		t.Fatalf("req-pty failed: %s", err)
	}

	if err := session.WindowChange(100, 100); err != nil {
		t.Fatalf("window-change failed: %s", err)
	}

	err = session.Shell()
	if err != nil {
		t.Fatalf("session failed: %s", err)
	}

	stdin.Write([]byte("stty size && exit\n"))

	var buf bytes.Buffer
	if _, err := io.Copy(&buf, stdout); err != nil {
		t.Fatalf("reading failed: %s", err)
	}

	if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "100 100") {
		t.Fatalf("terminal WindowChange failure: expected \"100 100\" stty output, got %s", sttyOutput)
	}
}

func testOneCipher(t *testing.T, cipher string, cipherOrder []string) {
	server := newServer(t)
	defer server.Shutdown()
	conf := clientConfig()
	conf.Ciphers = []string{cipher}
	// Don't fail if sshd doesn't have the cipher.
	conf.Ciphers = append(conf.Ciphers, cipherOrder...)
	conn, err := server.TryDial(conf)
	if err != nil {
		t.Fatalf("TryDial: %v", err)
	}
	defer conn.Close()

	numBytes := 4096

	// Exercise sending data to the server
	if _, _, err := conn.Conn.SendRequest("drop-me", false, make([]byte, numBytes)); err != nil {
		t.Fatalf("SendRequest: %v", err)
	}

	// Exercise receiving data from the server
	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("NewSession: %v", err)
	}

	out, err := session.Output(fmt.Sprintf("dd if=/dev/zero of=/dev/stdout bs=%d count=1", numBytes))
	if err != nil {
		t.Fatalf("Output: %v", err)
	}

	if len(out) != numBytes {
		t.Fatalf("got %d bytes, want %d bytes", len(out), numBytes)
	}
}

var deprecatedCiphers = []string{
	"aes128-cbc", "3des-cbc",
	"arcfour128", "arcfour256",
}

func TestCiphers(t *testing.T) {
	var config ssh.Config
	config.SetDefaults()
	cipherOrder := append(config.Ciphers, deprecatedCiphers...)

	for _, ciph := range cipherOrder {
		t.Run(ciph, func(t *testing.T) {
			testOneCipher(t, ciph, cipherOrder)
		})
	}
}

func TestMACs(t *testing.T) {
	var config ssh.Config
	config.SetDefaults()
	macOrder := config.MACs

	for _, mac := range macOrder {
		server := newServer(t)
		defer server.Shutdown()
		conf := clientConfig()
		conf.MACs = []string{mac}
		// Don't fail if sshd doesn't have the MAC.
		conf.MACs = append(conf.MACs, macOrder...)
		if conn, err := server.TryDial(conf); err == nil {
			conn.Close()
		} else {
			t.Fatalf("failed for MAC %q", mac)
		}
	}
}

func TestKeyExchanges(t *testing.T) {
	var config ssh.Config
	config.SetDefaults()
	kexOrder := config.KeyExchanges
	for _, kex := range kexOrder {
		server := newServer(t)
		defer server.Shutdown()
		conf := clientConfig()
		// Don't fail if sshd doesn't have the kex.
		conf.KeyExchanges = append([]string{kex}, kexOrder...)
		conn, err := server.TryDial(conf)
		if err == nil {
			conn.Close()
		} else {
			t.Errorf("failed for kex %q", kex)
		}
	}
}

func TestClientAuthAlgorithms(t *testing.T) {
	for _, key := range []string{
		"rsa",
		"dsa",
		"ecdsa",
		"ed25519",
	} {
		server := newServer(t)
		conf := clientConfig()
		conf.SetDefaults()
		conf.Auth = []ssh.AuthMethod{
			ssh.PublicKeys(testSigners[key]),
		}

		conn, err := server.TryDial(conf)
		if err == nil {
			conn.Close()
		} else {
			t.Errorf("failed for key %q", key)
		}

		server.Shutdown()
	}
}
<commit_msg>ssh/test: use t.Run for MAC/kex/pubkey tests<commit_after>// Copyright 2012 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.

// +build !windows,!solaris,!aix

package test

// Session functional tests.

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
	"testing"

	"golang.org/x/crypto/ssh"
)

func TestRunCommandSuccess(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()
	err = session.Run("true")
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
}

func TestHostKeyCheck(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()

	conf := clientConfig()
	hostDB := hostKeyDB()
	conf.HostKeyCallback = hostDB.Check

	// change the keys.
	hostDB.keys[ssh.KeyAlgoRSA][25]++
	hostDB.keys[ssh.KeyAlgoDSA][25]++
	hostDB.keys[ssh.KeyAlgoECDSA256][25]++

	conn, err := server.TryDial(conf)
	if err == nil {
		conn.Close()
		t.Fatalf("dial should have failed.")
	} else if !strings.Contains(err.Error(), "host key mismatch") {
		t.Fatalf("'host key mismatch' not found in %v", err)
	}
}

func TestRunCommandStdin(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	r, w := io.Pipe()
	defer r.Close()
	defer w.Close()
	session.Stdin = r

	err = session.Run("true")
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
}

func TestRunCommandStdinError(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	r, w := io.Pipe()
	defer r.Close()
	session.Stdin = r
	pipeErr := errors.New("closing write end of pipe")
	w.CloseWithError(pipeErr)

	err = session.Run("true")
	if err != pipeErr {
		t.Fatalf("expected %v, found %v", pipeErr, err)
	}
}

func TestRunCommandFailed(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()
	err = session.Run(`bash -c "kill -9 $$"`)
	if err == nil {
		t.Fatalf("session succeeded: %v", err)
	}
}

func TestRunCommandWeClosed(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	err = session.Shell()
	if err != nil {
		t.Fatalf("shell failed: %v", err)
	}
	err = session.Close()
	if err != nil {
		t.Fatalf("shell failed: %v", err)
	}
}

func TestFuncLargeRead(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("unable to create new session: %s", err)
	}

	stdout, err := session.StdoutPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdout pipe: %s", err)
	}

	err = session.Start("dd if=/dev/urandom bs=2048 count=1024")
	if err != nil {
		t.Fatalf("unable to execute remote command: %s", err)
	}

	buf := new(bytes.Buffer)
	n, err := io.Copy(buf, stdout)
	if err != nil {
		t.Fatalf("error reading from remote stdout: %s", err)
	}

	if n != 2048*1024 {
		t.Fatalf("Expected %d bytes but read only %d from remote command", 2048, n)
	}
}

func TestKeyChange(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conf := clientConfig()
	hostDB := hostKeyDB()
	conf.HostKeyCallback = hostDB.Check
	conf.RekeyThreshold = 1024
	conn := server.Dial(conf)
	defer conn.Close()

	for i := 0; i < 4; i++ {
		session, err := conn.NewSession()
		if err != nil {
			t.Fatalf("unable to create new session: %s", err)
		}

		stdout, err := session.StdoutPipe()
		if err != nil {
			t.Fatalf("unable to acquire stdout pipe: %s", err)
		}

		err = session.Start("dd if=/dev/urandom bs=1024 count=1")
		if err != nil {
			t.Fatalf("unable to execute remote command: %s", err)
		}
		buf := new(bytes.Buffer)
		n, err := io.Copy(buf, stdout)
		if err != nil {
			t.Fatalf("error reading from remote stdout: %s", err)
		}

		want := int64(1024)
		if n != want {
			t.Fatalf("Expected %d bytes but read only %d from remote command", want, n)
		}
	}

	if changes := hostDB.checkCount; changes < 4 {
		t.Errorf("got %d key changes, want 4", changes)
	}
}

func TestInvalidTerminalMode(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	if err = session.RequestPty("vt100", 80, 40, ssh.TerminalModes{255: 1984}); err == nil {
		t.Fatalf("req-pty failed: successful request with invalid mode")
	}
}

func TestValidTerminalMode(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	stdout, err := session.StdoutPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdout pipe: %s", err)
	}

	stdin, err := session.StdinPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdin pipe: %s", err)
	}

	tm := ssh.TerminalModes{ssh.ECHO: 0}
	if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
		t.Fatalf("req-pty failed: %s", err)
	}

	err = session.Shell()
	if err != nil {
		t.Fatalf("session failed: %s", err)
	}

	stdin.Write([]byte("stty -a && exit\n"))

	var buf bytes.Buffer
	if _, err := io.Copy(&buf, stdout); err != nil {
		t.Fatalf("reading failed: %s", err)
	}

	if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "-echo ") {
		t.Fatalf("terminal mode failure: expected -echo in stty output, got %s", sttyOutput)
	}
}

func TestWindowChange(t *testing.T) {
	server := newServer(t)
	defer server.Shutdown()
	conn := server.Dial(clientConfig())
	defer conn.Close()

	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("session failed: %v", err)
	}
	defer session.Close()

	stdout, err := session.StdoutPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdout pipe: %s", err)
	}

	stdin, err := session.StdinPipe()
	if err != nil {
		t.Fatalf("unable to acquire stdin pipe: %s", err)
	}

	tm := ssh.TerminalModes{ssh.ECHO: 0}
	if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
		t.Fatalf("req-pty failed: %s", err)
	}

	if err := session.WindowChange(100, 100); err != nil {
		t.Fatalf("window-change failed: %s", err)
	}

	err = session.Shell()
	if err != nil {
		t.Fatalf("session failed: %s", err)
	}

	stdin.Write([]byte("stty size && exit\n"))

	var buf bytes.Buffer
	if _, err := io.Copy(&buf, stdout); err != nil {
		t.Fatalf("reading failed: %s", err)
	}

	if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "100 100") {
		t.Fatalf("terminal WindowChange failure: expected \"100 100\" stty output, got %s", sttyOutput)
	}
}

func testOneCipher(t *testing.T, cipher string, cipherOrder []string) {
	server := newServer(t)
	defer server.Shutdown()
	conf := clientConfig()
	conf.Ciphers = []string{cipher}
	// Don't fail if sshd doesn't have the cipher.
	conf.Ciphers = append(conf.Ciphers, cipherOrder...)
	conn, err := server.TryDial(conf)
	if err != nil {
		t.Fatalf("TryDial: %v", err)
	}
	defer conn.Close()

	numBytes := 4096

	// Exercise sending data to the server
	if _, _, err := conn.Conn.SendRequest("drop-me", false, make([]byte, numBytes)); err != nil {
		t.Fatalf("SendRequest: %v", err)
	}

	// Exercise receiving data from the server
	session, err := conn.NewSession()
	if err != nil {
		t.Fatalf("NewSession: %v", err)
	}

	out, err := session.Output(fmt.Sprintf("dd if=/dev/zero of=/dev/stdout bs=%d count=1", numBytes))
	if err != nil {
		t.Fatalf("Output: %v", err)
	}

	if len(out) != numBytes {
		t.Fatalf("got %d bytes, want %d bytes", len(out), numBytes)
	}
}

var deprecatedCiphers = []string{
	"aes128-cbc", "3des-cbc",
	"arcfour128", "arcfour256",
}

func TestCiphers(t *testing.T) {
	var config ssh.Config
	config.SetDefaults()
	cipherOrder := append(config.Ciphers, deprecatedCiphers...)

	for _, ciph := range cipherOrder {
		t.Run(ciph, func(t *testing.T) {
			testOneCipher(t, ciph, cipherOrder)
		})
	}
}

func TestMACs(t *testing.T) {
	var config ssh.Config
	config.SetDefaults()
	macOrder := config.MACs

	for _, mac := range macOrder {
		t.Run(mac, func(t *testing.T) {
			server := newServer(t)
			defer server.Shutdown()
			conf := clientConfig()
			conf.MACs = []string{mac}
			// Don't fail if sshd doesn't have the MAC.
			conf.MACs = append(conf.MACs, macOrder...)
			if conn, err := server.TryDial(conf); err == nil {
				conn.Close()
			} else {
				t.Fatalf("failed for MAC %q", mac)
			}
		})
	}
}

func TestKeyExchanges(t *testing.T) {
	var config ssh.Config
	config.SetDefaults()
	kexOrder := config.KeyExchanges
	for _, kex := range kexOrder {
		t.Run(kex, func(t *testing.T) {
			server := newServer(t)
			defer server.Shutdown()
			conf := clientConfig()
			// Don't fail if sshd doesn't have the kex.
			conf.KeyExchanges = append([]string{kex}, kexOrder...)
			conn, err := server.TryDial(conf)
			if err == nil {
				conn.Close()
			} else {
				t.Errorf("failed for kex %q", kex)
			}
		})
	}
}

func TestClientAuthAlgorithms(t *testing.T) {
	for _, key := range []string{
		"rsa",
		"dsa",
		"ecdsa",
		"ed25519",
	} {
		t.Run(key, func(t *testing.T) {
			server := newServer(t)
			conf := clientConfig()
			conf.SetDefaults()
			conf.Auth = []ssh.AuthMethod{
				ssh.PublicKeys(testSigners[key]),
			}

			conn, err := server.TryDial(conf)
			if err == nil {
				conn.Close()
			} else {
				t.Errorf("failed for key %q", key)
			}

			server.Shutdown()
		})
	}
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

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

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

package api

import (
	"encoding/json"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/docker/distribution/manifest/schema1"
	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/models"
	svc_utils "github.com/vmware/harbor/service/utils"
	"github.com/vmware/harbor/utils/log"
	"github.com/vmware/harbor/utils/registry"
	"github.com/vmware/harbor/utils/registry/errors"
)

// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
// in the query string as the web framework can not parse the URL if it contains veriadic sectors.
// For repostiories, we won't check the session in this API due to search functionality, querying manifest will be contorlled by
// the security of registry
type RepositoryAPI struct {
	BaseAPI
}

// Get ...
func (ra *RepositoryAPI) Get() {
	projectID, err := ra.GetInt64("project_id")
	if err != nil {
		log.Errorf("Failed to get project id, error: %v", err)
		ra.RenderError(http.StatusBadRequest, "Invalid project id")
		return
	}
	p, err := dao.GetProjectByID(projectID)
	if err != nil {
		log.Errorf("Error occurred in GetProjectById, error: %v", err)
		ra.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	if p == nil {
		log.Warningf("Project with Id: %d does not exist", projectID)
		ra.RenderError(http.StatusNotFound, "")
		return
	}

	if p.Public == 0 {
		userID := ra.ValidateUser()

		if !checkProjectPermission(userID, projectID) {
			ra.RenderError(http.StatusForbidden, "")
			return
		}
	}

	repoList, err := svc_utils.GetRepoFromCache()
	if err != nil {
		log.Errorf("Failed to get repo from cache, error: %v", err)
		ra.RenderError(http.StatusInternalServerError, "internal sever error")
	}

	projectName := p.Name
	q := ra.GetString("q")
	var resp []string
	if len(q) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && strings.Contains(r[strings.LastIndex(r, "/")+1:], q) && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else if len(projectName) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else {
		ra.Data["json"] = repoList
	}
	ra.ServeJSON()
}

// Delete ...
func (ra *RepositoryAPI) Delete() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}
	tag := ra.GetString("tag")
	if len(tag) == 0 {
		tagList, err := rc.ListTag()
		if err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				log.Info(e)
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		tags = append(tags, tagList...)
	} else {
		tags = append(tags, tag)
	}

	for _, t := range tags {
		if err := rc.DeleteTag(t); err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		log.Infof("delete tag: %s %s", repoName, t)
	}

	go func() {
		log.Debug("refreshing catalog cache")
		if err := svc_utils.RefreshCatalogCache(); err != nil {
			log.Errorf("error occurred while refresh catalog cache: %v", err)
		}
	}()

}

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

// GetTags handles GET /api/repositories/tags
func (ra *RepositoryAPI) GetTags() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}

	ts, err := rc.ListTag()
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}

	tags = append(tags, ts...)

	sort.Strings(tags)

	ra.Data["json"] = tags
	ra.ServeJSON()
}

// GetManifests handles GET /api/repositories/manifests
func (ra *RepositoryAPI) GetManifests() {
	repoName := ra.GetString("repo_name")
	tag := ra.GetString("tag")

	if len(repoName) == 0 || len(tag) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name or tag is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	item := models.RepoItem{}

	mediaTypes := []string{schema1.MediaTypeManifest}
	_, _, payload, err := rc.PullManifest(tag, mediaTypes)
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}
	mani := models.Manifest{}
	err = json.Unmarshal(payload, &mani)
	if err != nil {
		log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	v1Compatibility := mani.History[0].V1Compatibility

	err = json.Unmarshal([]byte(v1Compatibility), &item)
	if err != nil {
		log.Errorf("Failed to decode V1 field for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days"

	ra.Data["json"] = item
	ra.ServeJSON()
}

func (ra *RepositoryAPI) initializeRepositoryClient(repoName string) (r *registry.Repository, err error) {
	var username string
	var sessionUsername, sessionUserID interface{}

	// get username from basic auth
	username, _, ok := ra.Ctx.Request.BasicAuth()
	if ok {
		goto enter
	}

	// get username from session
	sessionUsername = ra.GetSession("username")
	if sessionUsername != nil {
		username, ok = sessionUsername.(string)
		if ok {
			goto enter
		}
	}

	// if username does not exist in session, try to get userId from sessiion
	// and then get username from DB according to the userId
	sessionUserID = ra.GetSession("userId")
	if sessionUserID != nil {
		userID, ok := sessionUserID.(int)
		if ok {
			u := models.User{
				UserID: userID,
			}
			user, err := dao.GetUser(u)
			if err != nil {
				return nil, err
			}
			username = user.Username
			goto enter
		}
	}

enter:
	endpoint := os.Getenv("REGISTRY_URL")

	return registry.NewRepositoryWithUsername(repoName, endpoint, username)
}
<commit_msg>remove goto<commit_after>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

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

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

package api

import (
	"encoding/json"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/docker/distribution/manifest/schema1"
	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/models"
	svc_utils "github.com/vmware/harbor/service/utils"
	"github.com/vmware/harbor/utils/log"
	"github.com/vmware/harbor/utils/registry"
	"github.com/vmware/harbor/utils/registry/errors"
)

// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
// in the query string as the web framework can not parse the URL if it contains veriadic sectors.
// For repostiories, we won't check the session in this API due to search functionality, querying manifest will be contorlled by
// the security of registry
type RepositoryAPI struct {
	BaseAPI
}

// Get ...
func (ra *RepositoryAPI) Get() {
	projectID, err := ra.GetInt64("project_id")
	if err != nil {
		log.Errorf("Failed to get project id, error: %v", err)
		ra.RenderError(http.StatusBadRequest, "Invalid project id")
		return
	}
	p, err := dao.GetProjectByID(projectID)
	if err != nil {
		log.Errorf("Error occurred in GetProjectById, error: %v", err)
		ra.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	if p == nil {
		log.Warningf("Project with Id: %d does not exist", projectID)
		ra.RenderError(http.StatusNotFound, "")
		return
	}

	if p.Public == 0 {
		userID := ra.ValidateUser()

		if !checkProjectPermission(userID, projectID) {
			ra.RenderError(http.StatusForbidden, "")
			return
		}
	}

	repoList, err := svc_utils.GetRepoFromCache()
	if err != nil {
		log.Errorf("Failed to get repo from cache, error: %v", err)
		ra.RenderError(http.StatusInternalServerError, "internal sever error")
	}

	projectName := p.Name
	q := ra.GetString("q")
	var resp []string
	if len(q) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && strings.Contains(r[strings.LastIndex(r, "/")+1:], q) && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else if len(projectName) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else {
		ra.Data["json"] = repoList
	}
	ra.ServeJSON()
}

// Delete ...
func (ra *RepositoryAPI) Delete() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}
	tag := ra.GetString("tag")
	if len(tag) == 0 {
		tagList, err := rc.ListTag()
		if err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				log.Info(e)
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		tags = append(tags, tagList...)
	} else {
		tags = append(tags, tag)
	}

	for _, t := range tags {
		if err := rc.DeleteTag(t); err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		log.Infof("delete tag: %s %s", repoName, t)
	}

	go func() {
		log.Debug("refreshing catalog cache")
		if err := svc_utils.RefreshCatalogCache(); err != nil {
			log.Errorf("error occurred while refresh catalog cache: %v", err)
		}
	}()

}

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

// GetTags handles GET /api/repositories/tags
func (ra *RepositoryAPI) GetTags() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}

	ts, err := rc.ListTag()
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}

	tags = append(tags, ts...)

	sort.Strings(tags)

	ra.Data["json"] = tags
	ra.ServeJSON()
}

// GetManifests handles GET /api/repositories/manifests
func (ra *RepositoryAPI) GetManifests() {
	repoName := ra.GetString("repo_name")
	tag := ra.GetString("tag")

	if len(repoName) == 0 || len(tag) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name or tag is nil")
	}

	rc, err := ra.initRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	item := models.RepoItem{}

	mediaTypes := []string{schema1.MediaTypeManifest}
	_, _, payload, err := rc.PullManifest(tag, mediaTypes)
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}
	mani := models.Manifest{}
	err = json.Unmarshal(payload, &mani)
	if err != nil {
		log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	v1Compatibility := mani.History[0].V1Compatibility

	err = json.Unmarshal([]byte(v1Compatibility), &item)
	if err != nil {
		log.Errorf("Failed to decode V1 field for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days"

	ra.Data["json"] = item
	ra.ServeJSON()
}

func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) {
	username, err := ra.getUsername()
	if err != nil {
		return nil, err
	}

	endpoint := os.Getenv("REGISTRY_URL")

	return registry.NewRepositoryWithUsername(repoName, endpoint, username)
}

func (ra *RepositoryAPI) getUsername() (string, error) {
	// get username from basic auth
	username, _, ok := ra.Ctx.Request.BasicAuth()
	if ok {
		return username, nil
	}

	// get username from session
	sessionUsername := ra.GetSession("username")
	if sessionUsername != nil {
		username, ok = sessionUsername.(string)
		if ok {
			return username, nil
		}
	}

	// if username does not exist in session, try to get userId from sessiion
	// and then get username from DB according to the userId
	sessionUserID := ra.GetSession("userId")
	if sessionUserID != nil {
		userID, ok := sessionUserID.(int)
		if ok {
			u := models.User{
				UserID: userID,
			}
			user, err := dao.GetUser(u)
			if err != nil {
				return "", err
			}

			return user.Username, nil
		}
	}

	return "", nil
}
<|endoftext|>


<commit_before>
package main

import (
        "errors"
        "fmt"

        "encoding/json"
        "github.com/hyperledger/fabric/core/chaincode/shim"
)

// CrowdFundChaincode implementation
type CrowdFundChaincode struct {
}
type Info struct {

        Rollno []string   `json:"rollno"`
        Name []string `json:"name"`
        Sem  []string   `json:"sem"`
        Marks []string `json:"marks"`

}
//
// Init creates the state variable with name "account" and stores the value
// from the incoming request into this variable. We now have a key/value pair
// for account --> accountValue.
//
func (t *CrowdFundChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

        var err error

        if len(args) != 2 {
                return nil, errors.New("Incorrect number of arguments. Expecting 2.")
        }

     if err!=nil {
                        return nil, err
                }
         record := Info{}

        record.Rollno=append(record.Rollno,"MT2016001");
        record.Name=append(record.Name,"Aarushi");
        record.Sem=append(record.Sem,"Ist");
        record.Marks=append(record.Marks,"78");
        newrecordByte, err := json.Marshal(record);
        if err!=nil {

            return nil, err
        }
                err=stub.PutState("default",newrecordByte);
         if err!=nil {
                        return nil, err
                }

        return nil, nil
}

func (t *CrowdFundChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

var account string

fmt.Printf(" the function which has been recieved as input is : %s" , function)
fmt.Printf(" the function which has been recieved as input is : %s" , args[0])
fmt.Printf(" the function which has been recieved as input is : %s" , args[1])
fmt.Printf(" the function which has been recieved as input is : %s" , args[2])
fmt.Printf(" the function which has been recieved as input is : %s" , args[3])

        var err error

        if len(args) != 4 {
                return nil, errors.New("Incorrect number of arguments. Expecting 2.")
        }
          account = args[0]
          fmt.Printf(" key is : %s" , account)

         recordByte, err := stub.GetState(account);
        fmt.Println(recordByte);
        if err != nil {

            return nil, err
        }
        record := Info{}
        if recordByte != nil {
        errrecordmarshal := json.Unmarshal(recordByte,&record);
        fmt.Printf(" the unmarshall function output is : %s" , errrecordmarshal)

        if errrecordmarshal != nil {
            return nil, errrecordmarshal
        }

        }

        record.Rollno = append(record.Rollno,args[0]);
        record.Name = append(record.Name,args[1]);
        record.Sem=append(record.Sem,args[2]);
        record.Marks=append(record.Marks,args[3]);

        fmt.Printf(" record structure rollno is : %s" , record.Rollno)
        fmt.Printf(" record structure rollno is : %s" , record.Name)
        fmt.Printf(" record structure rollno is : %s" , record.Sem)
        fmt.Printf(" record structure rollno is : : %s" , record.Marks)

        newrecordByte, err := json.Marshal(record);

        stringNewRecordByte := string(newrecordByte)

        fmt.Printf(" the marshall function output is : %s" , stringNewRecordByte)

        if err!=nil {

            return nil, err
        }
        err =stub.PutState(account,newrecordByte);
        if err != nil {

            return nil, err;
        }
        return nil, nil
}

func (t *CrowdFundChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
  if function != "query" {
                return nil, errors.New("Invalid query function name. Expecting \"query\".")
        }

       var err error

         if len(args) != 1 {
                return nil, errors.New("Incorrect number of arguments. Expecting name of the state variable to query.")
        }

     var   account = args[0]

        accountValueBytes ,err := stub.GetState(account)
        if err != nil {

                 return nil, err
        }

        return accountValueBytes, nil
}

func main() {
        err := shim.Start(new(CrowdFundChaincode))

        if err != nil {
                fmt.Printf("Error starting CrowdFundChaincode: %s", err)
        }
}

<commit_msg>Update chaincode_start.go<commit_after>package main

import (
        "errors"
        "fmt"

        "encoding/json"
        "github.com/hyperledger/fabric/core/chaincode/shim"
)

// CrowdFundChaincode implementation
type CrowdFundChaincode struct {
}
type studentInfo struct {
        studentRollNo     string  `json:"studentrollno"`
        studentName        []string   `json:"studentname"`
        studentBadge       []string  `json:"studentbadge"`
        studentmarks       []string   `json:"studentmarks"`
        studentSem         []string   `json:"studentsem"`
        issuedBy        []string   `json:"issuedby"`

}
type BadgeInfo struct {

        badgeName       []string   `json:"rollno"`
        badgeUrl        []string `json:"name"`
        badgeIssuedBy   []string   `json:"sem"`
        badgeIssuedTo   []string `json:"marks"`
        //time
}

type Issuer struct {

        issuerInfo      []string   `json:"rollno"`
        issuerName        string `json:"name"`
       // time            string   `json:"sem"`

}
//
// Init creates the state variable with name "account" and stores the value
// from the incoming request into this variable. We now have a key/value pair
// for account --> accountValue.
//
func (t *CrowdFundChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

        var err error

        if len(args) != 2 {
                return nil, errors.New("Incorrect number of arguments. Expecting 2.")
        }

     if err!=nil {
                        return nil, err
                }
         record := studentInfo{}

        record.studentRollNo=append(record.studentRollNo,"MT2016001");
        record.studentName=append(record.studentName,"Aarushi");
        record.studentBadge=append(record.studentBadge,"Mtech");
        record.studentMarks=append(record.studentMarks,"78");
        record.studentSem=append(record.studentMarks,"1st");
        record.issuedBy=append(record.issuedBy,"RC Sir");

        newrecordByte, err := json.Marshal(record);
        if err!=nil {

            return nil, err
        }
                err=stub.PutState("default",newrecordByte);
         if err!=nil {
                        return nil, err
                }

        return nil, nil
}

func (t *CrowdFundChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

var account string

fmt.Printf(" the function which has been recieved as input is : %s" , function)
fmt.Printf(" the function which has been recieved as input is : %s" , args[0])
fmt.Printf(" the function which has been recieved as input is : %s" , args[1])
fmt.Printf(" the function which has been recieved as input is : %s" , args[2])
fmt.Printf(" the function which has been recieved as input is : %s" , args[3])

        var err error

        if len(args) != 6 {
                return nil, errors.New("Incorrect number of arguments. Expecting 6.")
        }
          account = args[0]
          fmt.Printf(" key is : %s" , account)

         recordByte, err := stub.GetState(account);
        fmt.Println(recordByte);
        if err != nil {

            return nil, err
        }
        record := studentInfo{}
        if recordByte != nil {
        errrecordmarshal := json.Unmarshal(recordByte,&record);
        fmt.Printf(" the unmarshall function output is : %s" , errrecordmarshal)

        if errrecordmarshal != nil {
            return nil, errrecordmarshal
        }

        }

        record.studentRollNo=append(record.studentRollNo,args[0]);
        record.studentName=append(record.studentName,args[1]);
        record.studentBadge=append(record.studentBadge,args[2]);
        record.studentMarks=append(record.studentMarks,args[3]);
        record.studentSem=append(record.studentMarks,args[4]);
        record.issuedBy=append(record.issuedBy,args[5]);

        /*record.Rollno = append(record.Rollno,args[0]);
        record.Name = append(record.Name,args[1]);
        record.Sem=append(record.Sem,args[2]);
        record.Marks=append(record.Marks,args[3]);
*/
        fmt.Printf(" record structure rollno is : %s" ,  record.studentRollno)
        fmt.Printf(" record structure name is   : %s" ,  record.studentName)
        fmt.Printf(" record structure badge is : %s" ,   record.studentBadge)
        fmt.Printf(" record structure marks is : : %s" , record.studentMarks)
        fmt.Printf(" record structure sem is : %s" ,     record.studentSem)
        fmt.Printf(" record structure issuedby is : %s" ,record.issuedBy)

        newrecordByte, err := json.Marshal(record);

        stringNewRecordByte := string(newrecordByte)

        fmt.Printf(" the marshall function output is : %s" , stringNewRecordByte)

        if err!=nil {

            return nil, err
        }
        err =stub.PutState(account,newrecordByte);
        if err != nil {

            return nil, err;
        }
        return nil, nil
}

func (t *CrowdFundChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
  if function != "query" {
                return nil, errors.New("Invalid query function name. Expecting \"query\".")
        }

       var err error

         if len(args) != 1 {
                return nil, errors.New("Incorrect number of arguments. Expecting name of the state variable to query.")
        }

     var   account = args[0]

        accountValueBytes ,err := stub.GetState(account)
        if err != nil {

                 return nil, err
        }

        return accountValueBytes, nil
}

func main() {
        err := shim.Start(new(CrowdFundChaincode))

        if err != nil {
                fmt.Printf("Error starting CrowdFundChaincode: %s", err)
        }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Matthias Neugebauer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package startstopper_test

import (
	"context"
	"errors"
	"testing"

	"github.com/mtneug/pkg/startstopper"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

type MockStartStopper struct {
	mock.Mock
}

func (m *MockStartStopper) Start(ctx context.Context) error {
	args := m.Called(ctx)
	return args.Error(0)
}

func (m *MockStartStopper) Stop(ctx context.Context) error {
	args := m.Called(ctx)
	return args.Error(0)
}

func (m *MockStartStopper) Done() <-chan struct{} {
	panic("Done should not be called")
}

func (m *MockStartStopper) Err(ctx context.Context) error {
	panic("Done should not be called")
}

type InMemoryMapTestSuite struct {
	suite.Suite
	ctx context.Context
	err error
	ss1 *MockStartStopper
	ss2 *MockStartStopper
	m   startstopper.Map
}

func (s *InMemoryMapTestSuite) SetupTest() {
	s.ctx = context.Background()
	s.err = errors.New("test error")
	s.ss1 = &MockStartStopper{}
	s.ss2 = &MockStartStopper{}
	s.m = startstopper.NewInMemoryMap()
}

func (s *InMemoryMapTestSuite) TestLen() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss2.On("Start", s.ctx).Return(nil).Once()

	require.Equal(s.T(), 0, s.m.Len())
	s.m.AddAndStart(s.ctx, "test1", s.ss1)
	require.Equal(s.T(), 1, s.m.Len())
	s.m.AddAndStart(s.ctx, "test2", s.ss2)
	require.Equal(s.T(), 2, s.m.Len())

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestGetNotInserted() {
	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)
}

func (s *InMemoryMapTestSuite) TestAddAndStart() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()

	changed, err := s.m.AddAndStart(s.ctx, "test", s.ss1)
	require.NoError(s.T(), err)
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss1, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestAddAndStartErrStart() {
	s.ss1.On("Start", s.ctx).Return(s.err).Once()

	changed, err := s.m.AddAndStart(s.ctx, "test", s.ss1)
	require.EqualError(s.T(), err, s.err.Error())
	require.False(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestDeleteAndStopNotInserted() {
	changed, err := s.m.DeleteAndStop(s.ctx, "test")
	require.NoError(s.T(), err)
	require.False(s.T(), changed)

	s.ss1.AssertExpectations(s.T())
	s.ss1.AssertNotCalled(s.T(), "Stop")
}

func (s *InMemoryMapTestSuite) TestDeleteAndStopInserted() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(nil).Once()
	s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.DeleteAndStop(s.ctx, "test")
	require.NoError(s.T(), err)
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestDeleteAndStopInsertedErrStart() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(s.err).Once()
	s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.DeleteAndStop(s.ctx, "test")
	require.EqualError(s.T(), err, s.err.Error())
	require.False(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss1, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartNotInserted() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss1)
	require.NoError(s.T(), err)
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss1, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartNotInsertedErrStart() {
	s.ss1.On("Start", s.ctx).Return(s.err).Once()

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss1)
	require.EqualError(s.T(), err, s.err.Error())
	require.False(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartInserted() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(nil).Once()
	s.ss2.On("Start", s.ctx).Return(nil).Once()
	s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss2)
	require.NoError(s.T(), err)
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss2, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartInsertedErrStop() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(s.err).Once()
	s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss2)
	require.EqualError(s.T(), s.err, err.Error())
	require.False(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss1, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartInsertedErrStart() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(nil).Once()
	s.ss2.On("Start", s.ctx).Return(s.err).Once()
	s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss2)
	require.EqualError(s.T(), s.err, err.Error())
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestForEach() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss2.On("Start", s.ctx).Return(nil).Once()
	s.m.AddAndStart(s.ctx, "test1", s.ss1)
	s.m.AddAndStart(s.ctx, "test2", s.ss2)

	called := 0
	notSeen := map[string]*MockStartStopper{
		"test1": s.ss1,
		"test2": s.ss2,
	}

	s.m.ForEach(func(key string, ss startstopper.StartStopper) {
		called++
		require.Equal(s.T(), notSeen[key], ss)
		delete(notSeen, key)
	})

	require.Equal(s.T(), 2, called)
	require.Empty(s.T(), notSeen)

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func TestInMemoryMap(t *testing.T) {
	t.Parallel()
	suite.Run(t, &InMemoryMapTestSuite{})
}
<commit_msg>Fix linting errors<commit_after>// Copyright (c) 2016 Matthias Neugebauer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package startstopper_test

import (
	"context"
	"errors"
	"testing"

	"github.com/mtneug/pkg/startstopper"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

type MockStartStopper struct {
	mock.Mock
}

func (m *MockStartStopper) Start(ctx context.Context) error {
	args := m.Called(ctx)
	return args.Error(0)
}

func (m *MockStartStopper) Stop(ctx context.Context) error {
	args := m.Called(ctx)
	return args.Error(0)
}

func (m *MockStartStopper) Done() <-chan struct{} {
	panic("Done should not be called")
}

func (m *MockStartStopper) Err(ctx context.Context) error {
	panic("Done should not be called")
}

type InMemoryMapTestSuite struct {
	suite.Suite
	ctx context.Context
	err error
	ss1 *MockStartStopper
	ss2 *MockStartStopper
	m   startstopper.Map
}

func (s *InMemoryMapTestSuite) SetupTest() {
	s.ctx = context.Background()
	s.err = errors.New("test error")
	s.ss1 = &MockStartStopper{}
	s.ss2 = &MockStartStopper{}
	s.m = startstopper.NewInMemoryMap()
}

func (s *InMemoryMapTestSuite) TestLen() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss2.On("Start", s.ctx).Return(nil).Once()

	require.Equal(s.T(), 0, s.m.Len())
	_, _ = s.m.AddAndStart(s.ctx, "test1", s.ss1)
	require.Equal(s.T(), 1, s.m.Len())
	_, _ = s.m.AddAndStart(s.ctx, "test2", s.ss2)
	require.Equal(s.T(), 2, s.m.Len())

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestGetNotInserted() {
	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)
}

func (s *InMemoryMapTestSuite) TestAddAndStart() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()

	changed, err := s.m.AddAndStart(s.ctx, "test", s.ss1)
	require.NoError(s.T(), err)
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss1, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestAddAndStartErrStart() {
	s.ss1.On("Start", s.ctx).Return(s.err).Once()

	changed, err := s.m.AddAndStart(s.ctx, "test", s.ss1)
	require.EqualError(s.T(), err, s.err.Error())
	require.False(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestDeleteAndStopNotInserted() {
	changed, err := s.m.DeleteAndStop(s.ctx, "test")
	require.NoError(s.T(), err)
	require.False(s.T(), changed)

	s.ss1.AssertExpectations(s.T())
	s.ss1.AssertNotCalled(s.T(), "Stop")
}

func (s *InMemoryMapTestSuite) TestDeleteAndStopInserted() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(nil).Once()
	_, _ = s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.DeleteAndStop(s.ctx, "test")
	require.NoError(s.T(), err)
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestDeleteAndStopInsertedErrStart() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(s.err).Once()
	_, _ = s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.DeleteAndStop(s.ctx, "test")
	require.EqualError(s.T(), err, s.err.Error())
	require.False(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss1, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartNotInserted() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss1)
	require.NoError(s.T(), err)
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss1, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartNotInsertedErrStart() {
	s.ss1.On("Start", s.ctx).Return(s.err).Once()

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss1)
	require.EqualError(s.T(), err, s.err.Error())
	require.False(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartInserted() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(nil).Once()
	s.ss2.On("Start", s.ctx).Return(nil).Once()
	_, _ = s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss2)
	require.NoError(s.T(), err)
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss2, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartInsertedErrStop() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(s.err).Once()
	_, _ = s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss2)
	require.EqualError(s.T(), s.err, err.Error())
	require.False(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Equal(s.T(), s.ss1, ss)
	require.True(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestUpdateAndRestartInsertedErrStart() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss1.On("Stop", s.ctx).Return(nil).Once()
	s.ss2.On("Start", s.ctx).Return(s.err).Once()
	_, _ = s.m.AddAndStart(s.ctx, "test", s.ss1)

	changed, err := s.m.UpdateAndRestart(s.ctx, "test", s.ss2)
	require.EqualError(s.T(), s.err, err.Error())
	require.True(s.T(), changed)

	ss, ok := s.m.Get("test")
	require.Nil(s.T(), ss)
	require.False(s.T(), ok)

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func (s *InMemoryMapTestSuite) TestForEach() {
	s.ss1.On("Start", s.ctx).Return(nil).Once()
	s.ss2.On("Start", s.ctx).Return(nil).Once()
	_, _ = s.m.AddAndStart(s.ctx, "test1", s.ss1)
	_, _ = s.m.AddAndStart(s.ctx, "test2", s.ss2)

	called := 0
	notSeen := map[string]*MockStartStopper{
		"test1": s.ss1,
		"test2": s.ss2,
	}

	s.m.ForEach(func(key string, ss startstopper.StartStopper) {
		called++
		require.Equal(s.T(), notSeen[key], ss)
		delete(notSeen, key)
	})

	require.Equal(s.T(), 2, called)
	require.Empty(s.T(), notSeen)

	s.ss1.AssertExpectations(s.T())
	s.ss2.AssertExpectations(s.T())
}

func TestInMemoryMap(t *testing.T) {
	t.Parallel()
	suite.Run(t, &InMemoryMapTestSuite{})
}
<|endoftext|>