<commit_before>package api

import (
	"archive/tar"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gorilla/mux"
	"gopkg.in/yaml.v2"

	"github.com/ovh/cds/engine/api/event"
	"github.com/ovh/cds/engine/api/project"
	"github.com/ovh/cds/engine/api/workflow"
	"github.com/ovh/cds/engine/service"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/exportentities"
	"github.com/ovh/cds/sdk/log"
)

func (api *API) postWorkflowPreviewHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars[permProjectKey]

		//Load project
		proj, errp := project.Load(api.mustDB(), api.Cache, key, deprecatedGetUser(ctx),
			project.LoadOptions.WithGroups,
			project.LoadOptions.WithApplications,
			project.LoadOptions.WithEnvironments,
			project.LoadOptions.WithPipelines,
			project.LoadOptions.WithIntegrations,
			project.LoadOptions.WithApplicationWithDeploymentStrategies,
		)
		if errp != nil {
			return sdk.WrapError(errp, "postWorkflowPreviewHandler>> Unable load project")
		}

		body, errr := ioutil.ReadAll(r.Body)
		if errr != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errr)
		}
		defer r.Body.Close()

		contentType := r.Header.Get("Content-Type")
		if contentType == "" {
			contentType = http.DetectContentType(body)
		}

		var ew = new(exportentities.Workflow)
		var errw error
		switch contentType {
		case "application/json":
			errw = json.Unmarshal(body, ew)
		case "application/x-yaml", "text/x-yaml":
			errw = yaml.Unmarshal(body, ew)
		default:
			return sdk.NewError(sdk.ErrWrongRequest, fmt.Errorf("unsupported content-type: %s", contentType))
		}

		if errw != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errw)
		}

		wf, globalError := workflow.Parse(proj, ew, deprecatedGetUser(ctx))
		if globalError != nil {
			return sdk.WrapError(globalError, "postWorkflowPreviewHandler> Unable import workflow %s", ew.Name)
		}

		// Browse all node to find IDs
		if err := workflow.IsValid(ctx, api.Cache, api.mustDB(), wf, proj, deprecatedGetUser(ctx)); err != nil {
			return sdk.WrapError(err, "Workflow is not valid")
		}

		if err := workflow.RenameNode(api.mustDB(), wf); err != nil {
			return sdk.WrapError(err, "Unable to rename node")
		}

		wf.RetroMigrate()

		return service.WriteJSON(w, wf, http.StatusOK)
	}
}

func (api *API) postWorkflowImportHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars[permProjectKey]
		force := FormBool(r, "force")

		//Load project
		proj, errp := project.Load(api.mustDB(), api.Cache, key, deprecatedGetUser(ctx),
			project.LoadOptions.WithGroups,
			project.LoadOptions.WithApplications,
			project.LoadOptions.WithEnvironments,
			project.LoadOptions.WithPipelines,
			project.LoadOptions.WithApplicationWithDeploymentStrategies,
			project.LoadOptions.WithIntegrations,
		)
		if errp != nil {
			return sdk.WrapError(errp, "Unable load project")
		}

		body, errr := ioutil.ReadAll(r.Body)
		if errr != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errr)
		}
		defer r.Body.Close()

		contentType := r.Header.Get("Content-Type")
		if contentType == "" {
			contentType = http.DetectContentType(body)
		}

		var ew = new(exportentities.Workflow)
		var errw error
		switch contentType {
		case "application/json":
			errw = json.Unmarshal(body, ew)
		case "application/x-yaml", "text/x-yaml":
			errw = yaml.Unmarshal(body, ew)
		default:
			return sdk.WrapError(sdk.ErrWrongRequest, "Unsupported content-type: %s", contentType)
		}

		if errw != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errw)
		}

		tx, errtx := api.mustDB().Begin()
		if errtx != nil {
			return sdk.WrapError(errtx, "Unable to start transaction")
		}
		defer tx.Rollback()

		u := deprecatedGetUser(ctx)

		// load the workflow from database if exists
		workflowExists, err := workflow.Exists(tx, proj.Key, ew.Name)
		if err != nil {
			return sdk.WrapError(err, "Cannot check if workflow exists")
		}
		var wf *sdk.Workflow
		if workflowExists {
			wf, err = workflow.Load(ctx, tx, api.Cache, proj, ew.Name, u, workflow.LoadOptions{WithIcon: true})
			if err != nil {
				return sdk.WrapError(err, "Unable to load existing workflow")
			}
		}

		wrkflw, msgList, globalError := workflow.ParseAndImport(ctx, tx, api.Cache, proj, wf, ew, deprecatedGetUser(ctx), workflow.ImportOptions{Force: force})
		msgListString := translate(r, msgList)
		if globalError != nil {
			return sdk.WrapError(globalError, "Unable to import workflow %s", ew.Name)
		}

		if err := tx.Commit(); err != nil {
			return sdk.WrapError(err, "Cannot commit transaction")
		}

		if wrkflw != nil {
			w.Header().Add(sdk.ResponseWorkflowIDHeader, fmt.Sprintf("%d", wrkflw.ID))
			w.Header().Add(sdk.ResponseWorkflowNameHeader, wrkflw.Name)
		}

		return service.WriteJSON(w, msgListString, http.StatusOK)
	}
}

func (api *API) putWorkflowImportHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars["key"]
		wfName := vars["permWorkflowName"]

		//Load project
		proj, errp := project.Load(api.mustDB(), api.Cache, key, deprecatedGetUser(ctx),
			project.LoadOptions.WithGroups,
			project.LoadOptions.WithApplications,
			project.LoadOptions.WithEnvironments,
			project.LoadOptions.WithPipelines,
			project.LoadOptions.WithApplicationWithDeploymentStrategies,
			project.LoadOptions.WithIntegrations,
		)
		if errp != nil {
			return sdk.WrapError(errp, "Unable load project")
		}

		u := deprecatedGetUser(ctx)

		wf, err := workflow.Load(ctx, api.mustDB(), api.Cache, proj, wfName, u, workflow.LoadOptions{WithIcon: true})
		if err != nil {
			return sdk.WrapError(err, "Unable to load workflow")
		}

		body, errr := ioutil.ReadAll(r.Body)
		if errr != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errr)
		}
		defer r.Body.Close()

		contentType := r.Header.Get("Content-Type")
		if contentType == "" {
			contentType = http.DetectContentType(body)
		}

		var ew = new(exportentities.Workflow)
		var errw error
		switch contentType {
		case "application/json":
			errw = json.Unmarshal(body, ew)
		case "application/x-yaml", "text/x-yaml":
			errw = yaml.Unmarshal(body, ew)
		default:
			return sdk.WrapError(sdk.ErrWrongRequest, "Unsupported content-type: %s", contentType)
		}

		if errw != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errw)
		}

		tx, errtx := api.mustDB().Begin()
		if errtx != nil {
			return sdk.WrapError(errtx, "Unable to start transaction")
		}
		defer func() {
			_ = tx.Rollback()
		}()

		wrkflw, msgList, globalError := workflow.ParseAndImport(ctx, tx, api.Cache, proj, wf, ew, deprecatedGetUser(ctx), workflow.ImportOptions{Force: true, WorkflowName: wfName})
		msgListString := translate(r, msgList)
		if globalError != nil {

			return sdk.WrapError(globalError, "Unable to import workflow %s", ew.Name)
		}

		if err := tx.Commit(); err != nil {
			return sdk.WrapError(err, "Cannot commit transaction")
		}

		oldW, errL := workflow.Load(ctx, api.mustDB(), api.Cache, proj, wfName, deprecatedGetUser(ctx), workflow.LoadOptions{})
		if errL == nil {
			event.PublishWorkflowUpdate(key, *wrkflw, *oldW, deprecatedGetUser(ctx))
		}

		if wrkflw != nil {
			w.Header().Add(sdk.ResponseWorkflowIDHeader, fmt.Sprintf("%d", wrkflw.ID))
			w.Header().Add(sdk.ResponseWorkflowNameHeader, wrkflw.Name)
		}

		return service.WriteJSON(w, msgListString, http.StatusOK)
	}
}

func (api *API) postWorkflowPushHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		db := api.mustDB()
		vars := mux.Vars(r)
		key := vars[permProjectKey]

		if r.Body == nil {
			return sdk.ErrWrongRequest
		}

		btes, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Error("postWorkflowPushHandler> Unable to read body: %v", err)
			return sdk.ErrWrongRequest
		}
		defer r.Body.Close()

		log.Debug("Read %d bytes from body", len(btes))

		tr := tar.NewReader(bytes.NewReader(btes))

		var pushOptions *workflow.PushOption
		if r.Header.Get(sdk.WorkflowAsCodeHeader) != "" {
			pushOptions = &workflow.PushOption{
				FromRepository: r.Header.Get(sdk.WorkflowAsCodeHeader),
			}
		}

		//Load project
		proj, errp := project.Load(db, api.Cache, key, deprecatedGetUser(ctx),
			project.LoadOptions.WithGroups,
			project.LoadOptions.WithApplications,
			project.LoadOptions.WithEnvironments,
			project.LoadOptions.WithPipelines,
			project.LoadOptions.WithApplicationWithDeploymentStrategies,
			project.LoadOptions.WithIntegrations)
		if errp != nil {
			return sdk.WrapError(errp, "postWorkflowPushHandler> Cannot load project %s", key)
		}

		allMsg, wrkflw, err := workflow.Push(ctx, db, api.Cache, proj, tr, pushOptions, deprecatedGetUser(ctx), project.DecryptWithBuiltinKey)
		if err != nil {
			return sdk.WrapError(err, "Cannot push workflow")
		}
		msgListString := translate(r, allMsg)

		if wrkflw != nil {
			w.Header().Add(sdk.ResponseWorkflowIDHeader, fmt.Sprintf("%d", wrkflw.ID))
			w.Header().Add(sdk.ResponseWorkflowNameHeader, wrkflw.Name)
		}

		return service.WriteJSON(w, msgListString, http.StatusOK)
	}
}
<commit_msg>fix(api): workflow init (push) consider it's coming from the default branch (#4184)<commit_after>package api

import (
	"archive/tar"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gorilla/mux"
	"gopkg.in/yaml.v2"

	"github.com/ovh/cds/engine/api/event"
	"github.com/ovh/cds/engine/api/project"
	"github.com/ovh/cds/engine/api/workflow"
	"github.com/ovh/cds/engine/service"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/exportentities"
	"github.com/ovh/cds/sdk/log"
)

func (api *API) postWorkflowPreviewHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars[permProjectKey]

		//Load project
		proj, errp := project.Load(api.mustDB(), api.Cache, key, deprecatedGetUser(ctx),
			project.LoadOptions.WithGroups,
			project.LoadOptions.WithApplications,
			project.LoadOptions.WithEnvironments,
			project.LoadOptions.WithPipelines,
			project.LoadOptions.WithIntegrations,
			project.LoadOptions.WithApplicationWithDeploymentStrategies,
		)
		if errp != nil {
			return sdk.WrapError(errp, "postWorkflowPreviewHandler>> Unable load project")
		}

		body, errr := ioutil.ReadAll(r.Body)
		if errr != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errr)
		}
		defer r.Body.Close()

		contentType := r.Header.Get("Content-Type")
		if contentType == "" {
			contentType = http.DetectContentType(body)
		}

		var ew = new(exportentities.Workflow)
		var errw error
		switch contentType {
		case "application/json":
			errw = json.Unmarshal(body, ew)
		case "application/x-yaml", "text/x-yaml":
			errw = yaml.Unmarshal(body, ew)
		default:
			return sdk.NewError(sdk.ErrWrongRequest, fmt.Errorf("unsupported content-type: %s", contentType))
		}

		if errw != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errw)
		}

		wf, globalError := workflow.Parse(proj, ew, deprecatedGetUser(ctx))
		if globalError != nil {
			return sdk.WrapError(globalError, "postWorkflowPreviewHandler> Unable import workflow %s", ew.Name)
		}

		// Browse all node to find IDs
		if err := workflow.IsValid(ctx, api.Cache, api.mustDB(), wf, proj, deprecatedGetUser(ctx)); err != nil {
			return sdk.WrapError(err, "Workflow is not valid")
		}

		if err := workflow.RenameNode(api.mustDB(), wf); err != nil {
			return sdk.WrapError(err, "Unable to rename node")
		}

		wf.RetroMigrate()

		return service.WriteJSON(w, wf, http.StatusOK)
	}
}

func (api *API) postWorkflowImportHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars[permProjectKey]
		force := FormBool(r, "force")

		//Load project
		proj, errp := project.Load(api.mustDB(), api.Cache, key, deprecatedGetUser(ctx),
			project.LoadOptions.WithGroups,
			project.LoadOptions.WithApplications,
			project.LoadOptions.WithEnvironments,
			project.LoadOptions.WithPipelines,
			project.LoadOptions.WithApplicationWithDeploymentStrategies,
			project.LoadOptions.WithIntegrations,
		)
		if errp != nil {
			return sdk.WrapError(errp, "Unable load project")
		}

		body, errr := ioutil.ReadAll(r.Body)
		if errr != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errr)
		}
		defer r.Body.Close()

		contentType := r.Header.Get("Content-Type")
		if contentType == "" {
			contentType = http.DetectContentType(body)
		}

		var ew = new(exportentities.Workflow)
		var errw error
		switch contentType {
		case "application/json":
			errw = json.Unmarshal(body, ew)
		case "application/x-yaml", "text/x-yaml":
			errw = yaml.Unmarshal(body, ew)
		default:
			return sdk.WrapError(sdk.ErrWrongRequest, "Unsupported content-type: %s", contentType)
		}

		if errw != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errw)
		}

		tx, errtx := api.mustDB().Begin()
		if errtx != nil {
			return sdk.WrapError(errtx, "Unable to start transaction")
		}
		defer tx.Rollback()

		u := deprecatedGetUser(ctx)

		// load the workflow from database if exists
		workflowExists, err := workflow.Exists(tx, proj.Key, ew.Name)
		if err != nil {
			return sdk.WrapError(err, "Cannot check if workflow exists")
		}
		var wf *sdk.Workflow
		if workflowExists {
			wf, err = workflow.Load(ctx, tx, api.Cache, proj, ew.Name, u, workflow.LoadOptions{WithIcon: true})
			if err != nil {
				return sdk.WrapError(err, "Unable to load existing workflow")
			}
		}

		wrkflw, msgList, globalError := workflow.ParseAndImport(ctx, tx, api.Cache, proj, wf, ew, deprecatedGetUser(ctx), workflow.ImportOptions{Force: force})
		msgListString := translate(r, msgList)
		if globalError != nil {
			return sdk.WrapError(globalError, "Unable to import workflow %s", ew.Name)
		}

		if err := tx.Commit(); err != nil {
			return sdk.WrapError(err, "Cannot commit transaction")
		}

		if wrkflw != nil {
			w.Header().Add(sdk.ResponseWorkflowIDHeader, fmt.Sprintf("%d", wrkflw.ID))
			w.Header().Add(sdk.ResponseWorkflowNameHeader, wrkflw.Name)
		}

		return service.WriteJSON(w, msgListString, http.StatusOK)
	}
}

func (api *API) putWorkflowImportHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		vars := mux.Vars(r)
		key := vars["key"]
		wfName := vars["permWorkflowName"]

		//Load project
		proj, errp := project.Load(api.mustDB(), api.Cache, key, deprecatedGetUser(ctx),
			project.LoadOptions.WithGroups,
			project.LoadOptions.WithApplications,
			project.LoadOptions.WithEnvironments,
			project.LoadOptions.WithPipelines,
			project.LoadOptions.WithApplicationWithDeploymentStrategies,
			project.LoadOptions.WithIntegrations,
		)
		if errp != nil {
			return sdk.WrapError(errp, "Unable load project")
		}

		u := deprecatedGetUser(ctx)

		wf, err := workflow.Load(ctx, api.mustDB(), api.Cache, proj, wfName, u, workflow.LoadOptions{WithIcon: true})
		if err != nil {
			return sdk.WrapError(err, "Unable to load workflow")
		}

		body, errr := ioutil.ReadAll(r.Body)
		if errr != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errr)
		}
		defer r.Body.Close()

		contentType := r.Header.Get("Content-Type")
		if contentType == "" {
			contentType = http.DetectContentType(body)
		}

		var ew = new(exportentities.Workflow)
		var errw error
		switch contentType {
		case "application/json":
			errw = json.Unmarshal(body, ew)
		case "application/x-yaml", "text/x-yaml":
			errw = yaml.Unmarshal(body, ew)
		default:
			return sdk.WrapError(sdk.ErrWrongRequest, "Unsupported content-type: %s", contentType)
		}

		if errw != nil {
			return sdk.NewError(sdk.ErrWrongRequest, errw)
		}

		tx, errtx := api.mustDB().Begin()
		if errtx != nil {
			return sdk.WrapError(errtx, "Unable to start transaction")
		}
		defer func() {
			_ = tx.Rollback()
		}()

		wrkflw, msgList, globalError := workflow.ParseAndImport(ctx, tx, api.Cache, proj, wf, ew, deprecatedGetUser(ctx), workflow.ImportOptions{Force: true, WorkflowName: wfName})
		msgListString := translate(r, msgList)
		if globalError != nil {

			return sdk.WrapError(globalError, "Unable to import workflow %s", ew.Name)
		}

		if err := tx.Commit(); err != nil {
			return sdk.WrapError(err, "Cannot commit transaction")
		}

		oldW, errL := workflow.Load(ctx, api.mustDB(), api.Cache, proj, wfName, deprecatedGetUser(ctx), workflow.LoadOptions{})
		if errL == nil {
			event.PublishWorkflowUpdate(key, *wrkflw, *oldW, deprecatedGetUser(ctx))
		}

		if wrkflw != nil {
			w.Header().Add(sdk.ResponseWorkflowIDHeader, fmt.Sprintf("%d", wrkflw.ID))
			w.Header().Add(sdk.ResponseWorkflowNameHeader, wrkflw.Name)
		}

		return service.WriteJSON(w, msgListString, http.StatusOK)
	}
}

func (api *API) postWorkflowPushHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Get project name in URL
		db := api.mustDB()
		vars := mux.Vars(r)
		key := vars[permProjectKey]

		if r.Body == nil {
			return sdk.ErrWrongRequest
		}

		btes, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Error("postWorkflowPushHandler> Unable to read body: %v", err)
			return sdk.ErrWrongRequest
		}
		defer r.Body.Close()

		log.Debug("Read %d bytes from body", len(btes))

		tr := tar.NewReader(bytes.NewReader(btes))

		var pushOptions *workflow.PushOption
		if r.Header.Get(sdk.WorkflowAsCodeHeader) != "" {
			pushOptions = &workflow.PushOption{
				FromRepository:  r.Header.Get(sdk.WorkflowAsCodeHeader),
				IsDefaultBranch: true,
			}
		}

		//Load project
		proj, errp := project.Load(db, api.Cache, key, deprecatedGetUser(ctx),
			project.LoadOptions.WithGroups,
			project.LoadOptions.WithApplications,
			project.LoadOptions.WithEnvironments,
			project.LoadOptions.WithPipelines,
			project.LoadOptions.WithApplicationWithDeploymentStrategies,
			project.LoadOptions.WithIntegrations)
		if errp != nil {
			return sdk.WrapError(errp, "postWorkflowPushHandler> Cannot load project %s", key)
		}

		allMsg, wrkflw, err := workflow.Push(ctx, db, api.Cache, proj, tr, pushOptions, deprecatedGetUser(ctx), project.DecryptWithBuiltinKey)
		if err != nil {
			return sdk.WrapError(err, "Cannot push workflow")
		}
		msgListString := translate(r, allMsg)

		if wrkflw != nil {
			w.Header().Add(sdk.ResponseWorkflowIDHeader, fmt.Sprintf("%d", wrkflw.ID))
			w.Header().Add(sdk.ResponseWorkflowNameHeader, wrkflw.Name)
		}

		return service.WriteJSON(w, msgListString, http.StatusOK)
	}
}
<|endoftext|>


<commit_before>// Package klient provides an instance and abstraction to a remote klient kite.
// It is used to easily call methods of a klient kite
package klient

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"strings"
	"sync"
	"time"

	"koding/klient/fs"
	"koding/klient/machine/index"
	"koding/klient/os"
	"koding/klient/sshkeys"

	"github.com/koding/kite"
	"github.com/koding/kite/protocol"
	"github.com/koding/logging"
)

var (
	ErrDialingFailed = errors.New("Dialing klient failed.")
	DefaultTimeout   = 60 * time.Second
	DefaultInterval  = 10 * time.Second
)

// KlientPool represents a pool of connected klients
type KlientPool struct {
	kite    *kite.Kite
	klients map[string]*Klient
	log     logging.Logger
	sync.Mutex
}

type Usage struct {
	// InactiveDuration reports the minimum duration since the latest activity.
	InactiveDuration time.Duration `json:"inactive_duration"`
}

// Klient represents a remote klient instance
type Klient struct {
	Client   *kite.Client
	kite     *kite.Kite
	Username string
	Timeout  time.Duration

	mu  sync.Mutex
	ctx context.Context
}

func (k *Klient) timeout() time.Duration {
	if k.Timeout != 0 {
		return k.Timeout
	}

	return DefaultTimeout
}

// ShareRequest is used for klient's klient.share,klient.unshare methods.
type ShareRequest struct {
	Username string
}

func NewPool(k *kite.Kite) *KlientPool {
	return &KlientPool{
		kite:    k,
		klients: make(map[string]*Klient),
		log:     logging.NewLogger("klientpool"),
	}
}

// Get returns a ready to use and connected klient from the pool.
func (k *KlientPool) Get(queryString string) (*Klient, error) {
	var klient *Klient
	var ok bool
	var err error

	k.Lock()
	defer k.Unlock()

	klient, ok = k.klients[queryString]
	if !ok {
		klient, err = Connect(k.kite, queryString)
		if err != nil {
			return nil, err
		}

		k.log.Info("Creating new klient connection to %s", queryString)
		k.klients[queryString] = klient

		// remove from the pool if we loose the connection
		klient.Client.OnDisconnect(func() {
			k.log.Info("Klient %s disconnected. removing from the pool", queryString)
			k.Delete(queryString)
			klient.Close()
		})
	} else {
		k.log.Debug("Fetching already connected klient (%s) from pool", queryString)
	}

	return klient, nil
}

// Delete removes the klient with the given queryString from the pool
func (k *KlientPool) Delete(queryString string) {
	k.Lock()
	defer k.Unlock()

	delete(k.klients, queryString)
}

// Exists checks whether the given queryString exists in Kontrol or not
func Exists(k *kite.Kite, queryString string) error {
	query, err := protocol.KiteFromString(queryString)
	if err != nil {
		return err
	}

	k.Log.Debug("Checking whether %s exists in Kontrol", queryString)

	// an error indicates a non existing klient or another error.
	_, err = k.GetKites(query.Query())
	if err != nil {
		return err
	}

	return nil
}

// Connect returns a new connected klient instance to the given queryString. The
// klient is ready to use. It's connected and needs to be closed once the task
// is finished with it.
func Connect(k *kite.Kite, queryString string) (*Klient, error) {
	return ConnectTimeout(k, queryString, 0)
}

// ConnectTimeout returns a new connected klient instance to the given
// queryString. The klient is ready to use. It's tries to connect for the given
// timeout duration
func ConnectTimeout(k *kite.Kite, queryString string, t time.Duration) (*Klient, error) {
	query, err := protocol.KiteFromString(queryString)
	if err != nil {
		return nil, err
	}

	k.Log.Debug("Connecting with timeout=%s to Klient: %s", t, queryString)

	kites, err := k.GetKites(query.Query())
	if err != nil {
		return nil, err
	}

	remoteKite := kites[0]

	err = remoteKite.DialTimeout(t)
	if err != nil {
		// If kite exists but dialing failed, we still return the *Klient
		// value, although not connected, in order to allow the caller
		// inspect the URL and eventually recover.
		err = ErrDialingFailed
	}

	k.Log.Debug("Dialing %q (%s) kite failed: %s", queryString, remoteKite.URL, err)

	return &Klient{
		kite:     k,
		Client:   remoteKite,
		Username: remoteKite.Username,
	}, err
}

func (k *Klient) URL() string {
	return k.Client.URL
}

func (k *Klient) IpAddress() (string, error) {
	u, err := url.Parse(k.Client.URL)
	if err != nil {
		return "", err
	}

	return u.Host, nil
}

func NewWithTimeout(k *kite.Kite, queryString string, t time.Duration) (klient *Klient, err error) {
	timeout := time.After(t)

	for {
		select {
		case <-timeout:
			if err == nil {
				err = errors.New("timed out connecting to klient: " + queryString)
			}

			return klient, err
		default:
			k.Log.Debug("Trying to connect to klient: %s", queryString)

			if klient, err = ConnectTimeout(k, queryString, DefaultInterval); err == nil {
				return klient, nil
			}

			time.Sleep(DefaultInterval)
		}
	}
}

func (k *Klient) Close() {
	k.Client.Close()
}

// Exec calls the os.exec method of remote klient.
func (k *Klient) Exec(req *os.ExecRequest) (*os.ExecResponse, error) {
	var resp os.ExecResponse

	if err := k.call("os.exec", req, &resp); err != nil {
		return nil, err
	}

	return &resp, nil
}

// Kill calls the os.kill method of remote klient.
func (k *Klient) Kill(req *os.KillRequest) (*os.KillResponse, error) {
	var resp os.KillResponse

	if err := k.call("os.kill", req, &resp); err != nil {
		return nil, err
	}

	return &resp, nil
}

func (k *Klient) call(method string, req, resp interface{}) error {
	type validator interface {
		Valid() error
	}

	if v, ok := req.(validator); ok {
		if err := v.Valid(); err != nil {
			return err
		}
	}

	r, err := k.Client.TellWithTimeout(method, k.timeout(), req)
	if err != nil {
		return err
	}

	if resp != nil {
		if err := r.Unmarshal(resp); err != nil {
			return err
		}
	}

	return nil
}

// Usage calls the usage method of remote and get's the result back
func (k *Klient) Usage() (*Usage, error) {
	resp, err := k.Client.TellWithTimeout("klient.usage", k.timeout())
	if err != nil {
		return nil, err
	}

	var usg Usage
	if err := resp.Unmarshal(&usg); err != nil {
		return nil, err
	}

	return &usg, nil
}

// Ping checks if the given klient response with "pong" to the "ping" we send.
// A nil error means a successful pong result.
func (k *Klient) Ping() error {
	resp, err := k.Client.TellWithTimeout("kite.ping", 10*time.Second)
	if err != nil {
		return err
	}

	out, err := resp.String()
	if err != nil {
		return err
	}

	if out == "pong" {
		return nil
	}

	return fmt.Errorf("wrong response %s", out)
}

// PingTimeout issues a ping requests for the duration t. It returns
// with nil error as soon as a ping requests succeeds.
func (k *Klient) PingTimeout(t time.Duration) (err error) {
	timeout := time.After(t)
	ticker := time.NewTicker(DefaultInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if err = k.Ping(); err == nil {
				return nil
			}

		case <-timeout:
			return err
		}
	}
}

// AddUser adds the given username to the klient's permission list. Once added
// the user is able to make requests to Klient
func (k *Klient) AddUser(username string) error {
	resp, err := k.Client.TellWithTimeout("klient.share", k.timeout(), &ShareRequest{username})
	if err != nil {
		return err
	}

	out, err := resp.String()
	if err != nil {
		return err
	}

	if out == "shared" {
		return nil
	}

	return fmt.Errorf("wrong response %s", out)
}

// RemoveUser removes the given username from the klient's permission list.
// Once removed the user is not able to make requests to Klient anymore.
func (k *Klient) RemoveUser(username string) error {
	resp, err := k.Client.TellWithTimeout("klient.unshare", k.timeout(), &ShareRequest{username})
	if err != nil {
		return err
	}

	out, err := resp.String()
	if err != nil {
		return err
	}

	if out == "unshared" {
		return nil
	}

	return fmt.Errorf("wrong response %s", out)
}

// Username returns remote machine current username.
func (k *Klient) CurrentUser() (string, error) {
	resp, err := k.Client.TellWithTimeout("os.currentUsername", k.timeout())
	if err != nil {
		return "", err
	}

	var username string
	if err := resp.Unmarshal(&username); err != nil {
		return "", err
	}

	return username, nil
}

// Abs returns absolute representation of given path.
func (k *Klient) Abs(path string) (string, bool, bool, error) {
	req := fs.AbsRequest{
		Path: path,
	}

	raw, err := k.Client.TellWithTimeout("fs.abs", k.timeout(), req)
	if err != nil {
		return "", false, false, err
	}

	var res fs.AbsResponse
	if err := raw.Unmarshal(&res); err != nil {
		return "", false, false, err
	}

	return res.AbsPath, res.IsDir, res.Exist, nil
}

// SSHAddKeys adds SSH public keys to user's authorized_keys file.
func (k *Klient) SSHAddKeys(username string, keys ...string) error {
	addopts := sshkeys.AddOptions{
		Username: username,
		Keys:     keys,
	}

	_, err := k.Client.TellWithTimeout("sshkeys.add", k.timeout(), addopts)
	if err != nil {
		// Ignore errors about duplicate keys since we're adding on each run.
		if strings.Contains(err.Error(), "cannot add duplicate ssh key") {
			return nil
		}

		return err
	}

	// TODO(ppknap): currently sshkeys.add method can return either nil or true
	// as its response. Add proper support for this.
	return nil
}

// MountHeadIndex returns the number and the overall size of files in a given
// remote directory.
func (k *Klient) MountHeadIndex(path string) (absPath string, count int, diskSize int64, err error) {
	req := index.Request{
		Path: path,
	}

	raw, err := k.Client.TellWithTimeout("machine.index.head", k.timeout(), req)
	if err != nil {
		return "", 0, 0, err
	}

	resp := index.HeadResponse{}
	if err := raw.Unmarshal(&resp); err != nil {
		return "", 0, 0, err
	}

	return resp.AbsPath, resp.Count, resp.DiskSize, nil
}

// MountGetIndex returns an index that describes the current state of remote
// directory.
func (k *Klient) MountGetIndex(path string) (*index.Index, error) {
	req := index.Request{
		Path: path,
	}

	raw, err := k.Client.TellWithTimeout("machine.index.get", k.timeout(), req)
	if err != nil {
		return nil, err
	}

	resp := index.GetResponse{
		Index: index.NewIndex(),
	}
	if err := raw.Unmarshal(&resp); err != nil {
		return nil, err
	}

	if resp.Index == nil {
		return nil, errors.New("retrieved index is nil")
	}

	return resp.Index, nil
}

// SetContext sets provided context to Klient.
func (k *Klient) SetContext(ctx context.Context) {
	k.mu.Lock()
	defer k.mu.Unlock()

	k.ctx = ctx
}

// Context returns Klient's context.
func (k *Klient) Context() context.Context {
	k.mu.Lock()
	defer k.mu.Unlock()

	return k.ctx
}
<commit_msg>kloud/klient: use call method when calling Abs method<commit_after>// Package klient provides an instance and abstraction to a remote klient kite.
// It is used to easily call methods of a klient kite
package klient

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"strings"
	"sync"
	"time"

	"koding/klient/fs"
	"koding/klient/machine/index"
	"koding/klient/os"
	"koding/klient/sshkeys"

	"github.com/koding/kite"
	"github.com/koding/kite/protocol"
	"github.com/koding/logging"
)

var (
	ErrDialingFailed = errors.New("Dialing klient failed.")
	DefaultTimeout   = 60 * time.Second
	DefaultInterval  = 10 * time.Second
)

// KlientPool represents a pool of connected klients
type KlientPool struct {
	kite    *kite.Kite
	klients map[string]*Klient
	log     logging.Logger
	sync.Mutex
}

type Usage struct {
	// InactiveDuration reports the minimum duration since the latest activity.
	InactiveDuration time.Duration `json:"inactive_duration"`
}

// Klient represents a remote klient instance
type Klient struct {
	Client   *kite.Client
	kite     *kite.Kite
	Username string
	Timeout  time.Duration

	mu  sync.Mutex
	ctx context.Context
}

func (k *Klient) timeout() time.Duration {
	if k.Timeout != 0 {
		return k.Timeout
	}

	return DefaultTimeout
}

// ShareRequest is used for klient's klient.share,klient.unshare methods.
type ShareRequest struct {
	Username string
}

func NewPool(k *kite.Kite) *KlientPool {
	return &KlientPool{
		kite:    k,
		klients: make(map[string]*Klient),
		log:     logging.NewLogger("klientpool"),
	}
}

// Get returns a ready to use and connected klient from the pool.
func (k *KlientPool) Get(queryString string) (*Klient, error) {
	var klient *Klient
	var ok bool
	var err error

	k.Lock()
	defer k.Unlock()

	klient, ok = k.klients[queryString]
	if !ok {
		klient, err = Connect(k.kite, queryString)
		if err != nil {
			return nil, err
		}

		k.log.Info("Creating new klient connection to %s", queryString)
		k.klients[queryString] = klient

		// remove from the pool if we loose the connection
		klient.Client.OnDisconnect(func() {
			k.log.Info("Klient %s disconnected. removing from the pool", queryString)
			k.Delete(queryString)
			klient.Close()
		})
	} else {
		k.log.Debug("Fetching already connected klient (%s) from pool", queryString)
	}

	return klient, nil
}

// Delete removes the klient with the given queryString from the pool
func (k *KlientPool) Delete(queryString string) {
	k.Lock()
	defer k.Unlock()

	delete(k.klients, queryString)
}

// Exists checks whether the given queryString exists in Kontrol or not
func Exists(k *kite.Kite, queryString string) error {
	query, err := protocol.KiteFromString(queryString)
	if err != nil {
		return err
	}

	k.Log.Debug("Checking whether %s exists in Kontrol", queryString)

	// an error indicates a non existing klient or another error.
	_, err = k.GetKites(query.Query())
	if err != nil {
		return err
	}

	return nil
}

// Connect returns a new connected klient instance to the given queryString. The
// klient is ready to use. It's connected and needs to be closed once the task
// is finished with it.
func Connect(k *kite.Kite, queryString string) (*Klient, error) {
	return ConnectTimeout(k, queryString, 0)
}

// ConnectTimeout returns a new connected klient instance to the given
// queryString. The klient is ready to use. It's tries to connect for the given
// timeout duration
func ConnectTimeout(k *kite.Kite, queryString string, t time.Duration) (*Klient, error) {
	query, err := protocol.KiteFromString(queryString)
	if err != nil {
		return nil, err
	}

	k.Log.Debug("Connecting with timeout=%s to Klient: %s", t, queryString)

	kites, err := k.GetKites(query.Query())
	if err != nil {
		return nil, err
	}

	remoteKite := kites[0]

	err = remoteKite.DialTimeout(t)
	if err != nil {
		// If kite exists but dialing failed, we still return the *Klient
		// value, although not connected, in order to allow the caller
		// inspect the URL and eventually recover.
		err = ErrDialingFailed
	}

	k.Log.Debug("Dialing %q (%s) kite failed: %s", queryString, remoteKite.URL, err)

	return &Klient{
		kite:     k,
		Client:   remoteKite,
		Username: remoteKite.Username,
	}, err
}

func (k *Klient) URL() string {
	return k.Client.URL
}

func (k *Klient) IpAddress() (string, error) {
	u, err := url.Parse(k.Client.URL)
	if err != nil {
		return "", err
	}

	return u.Host, nil
}

func NewWithTimeout(k *kite.Kite, queryString string, t time.Duration) (klient *Klient, err error) {
	timeout := time.After(t)

	for {
		select {
		case <-timeout:
			if err == nil {
				err = errors.New("timed out connecting to klient: " + queryString)
			}

			return klient, err
		default:
			k.Log.Debug("Trying to connect to klient: %s", queryString)

			if klient, err = ConnectTimeout(k, queryString, DefaultInterval); err == nil {
				return klient, nil
			}

			time.Sleep(DefaultInterval)
		}
	}
}

func (k *Klient) Close() {
	k.Client.Close()
}

// Exec calls the os.exec method of remote klient.
func (k *Klient) Exec(req *os.ExecRequest) (*os.ExecResponse, error) {
	var resp os.ExecResponse

	if err := k.call("os.exec", req, &resp); err != nil {
		return nil, err
	}

	return &resp, nil
}

// Kill calls the os.kill method of remote klient.
func (k *Klient) Kill(req *os.KillRequest) (*os.KillResponse, error) {
	var resp os.KillResponse

	if err := k.call("os.kill", req, &resp); err != nil {
		return nil, err
	}

	return &resp, nil
}

func (k *Klient) call(method string, req, resp interface{}) error {
	type validator interface {
		Valid() error
	}

	if v, ok := req.(validator); ok {
		if err := v.Valid(); err != nil {
			return err
		}
	}

	r, err := k.Client.TellWithTimeout(method, k.timeout(), req)
	if err != nil {
		return err
	}

	if resp != nil {
		if err := r.Unmarshal(resp); err != nil {
			return err
		}
	}

	return nil
}

// Usage calls the usage method of remote and get's the result back
func (k *Klient) Usage() (*Usage, error) {
	resp, err := k.Client.TellWithTimeout("klient.usage", k.timeout())
	if err != nil {
		return nil, err
	}

	var usg Usage
	if err := resp.Unmarshal(&usg); err != nil {
		return nil, err
	}

	return &usg, nil
}

// Ping checks if the given klient response with "pong" to the "ping" we send.
// A nil error means a successful pong result.
func (k *Klient) Ping() error {
	resp, err := k.Client.TellWithTimeout("kite.ping", 10*time.Second)
	if err != nil {
		return err
	}

	out, err := resp.String()
	if err != nil {
		return err
	}

	if out == "pong" {
		return nil
	}

	return fmt.Errorf("wrong response %s", out)
}

// PingTimeout issues a ping requests for the duration t. It returns
// with nil error as soon as a ping requests succeeds.
func (k *Klient) PingTimeout(t time.Duration) (err error) {
	timeout := time.After(t)
	ticker := time.NewTicker(DefaultInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if err = k.Ping(); err == nil {
				return nil
			}

		case <-timeout:
			return err
		}
	}
}

// AddUser adds the given username to the klient's permission list. Once added
// the user is able to make requests to Klient
func (k *Klient) AddUser(username string) error {
	resp, err := k.Client.TellWithTimeout("klient.share", k.timeout(), &ShareRequest{username})
	if err != nil {
		return err
	}

	out, err := resp.String()
	if err != nil {
		return err
	}

	if out == "shared" {
		return nil
	}

	return fmt.Errorf("wrong response %s", out)
}

// RemoveUser removes the given username from the klient's permission list.
// Once removed the user is not able to make requests to Klient anymore.
func (k *Klient) RemoveUser(username string) error {
	resp, err := k.Client.TellWithTimeout("klient.unshare", k.timeout(), &ShareRequest{username})
	if err != nil {
		return err
	}

	out, err := resp.String()
	if err != nil {
		return err
	}

	if out == "unshared" {
		return nil
	}

	return fmt.Errorf("wrong response %s", out)
}

// Username returns remote machine current username.
func (k *Klient) CurrentUser() (string, error) {
	resp, err := k.Client.TellWithTimeout("os.currentUsername", k.timeout())
	if err != nil {
		return "", err
	}

	var username string
	if err := resp.Unmarshal(&username); err != nil {
		return "", err
	}

	return username, nil
}

// Abs returns absolute representation of given path.
func (k *Klient) Abs(path string) (string, bool, bool, error) {
	req := fs.AbsRequest{
		Path: path,
	}

	var resp fs.AbsResponse
	if err := k.call("fs.abs", req, &resp); err != nil {
		return "", false, false, err
	}

	return resp.AbsPath, resp.IsDir, resp.Exist, nil
}

// SSHAddKeys adds SSH public keys to user's authorized_keys file.
func (k *Klient) SSHAddKeys(username string, keys ...string) error {
	addopts := sshkeys.AddOptions{
		Username: username,
		Keys:     keys,
	}

	_, err := k.Client.TellWithTimeout("sshkeys.add", k.timeout(), addopts)
	if err != nil {
		// Ignore errors about duplicate keys since we're adding on each run.
		if strings.Contains(err.Error(), "cannot add duplicate ssh key") {
			return nil
		}

		return err
	}

	// TODO(ppknap): currently sshkeys.add method can return either nil or true
	// as its response. Add proper support for this.
	return nil
}

// MountHeadIndex returns the number and the overall size of files in a given
// remote directory.
func (k *Klient) MountHeadIndex(path string) (absPath string, count int, diskSize int64, err error) {
	req := index.Request{
		Path: path,
	}

	raw, err := k.Client.TellWithTimeout("machine.index.head", k.timeout(), req)
	if err != nil {
		return "", 0, 0, err
	}

	resp := index.HeadResponse{}
	if err := raw.Unmarshal(&resp); err != nil {
		return "", 0, 0, err
	}

	return resp.AbsPath, resp.Count, resp.DiskSize, nil
}

// MountGetIndex returns an index that describes the current state of remote
// directory.
func (k *Klient) MountGetIndex(path string) (*index.Index, error) {
	req := index.Request{
		Path: path,
	}

	raw, err := k.Client.TellWithTimeout("machine.index.get", k.timeout(), req)
	if err != nil {
		return nil, err
	}

	resp := index.GetResponse{
		Index: index.NewIndex(),
	}
	if err := raw.Unmarshal(&resp); err != nil {
		return nil, err
	}

	if resp.Index == nil {
		return nil, errors.New("retrieved index is nil")
	}

	return resp.Index, nil
}

// SetContext sets provided context to Klient.
func (k *Klient) SetContext(ctx context.Context) {
	k.mu.Lock()
	defer k.mu.Unlock()

	k.ctx = ctx
}

// Context returns Klient's context.
func (k *Klient) Context() context.Context {
	k.mu.Lock()
	defer k.mu.Unlock()

	return k.ctx
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/jinzhu/now"

	"gopkg.in/mgo.v2/bson"

	"koding/db/models"
	"koding/db/mongodb/modelhelper"
	"koding/db/mongodb/modelhelper/modeltesthelper"
	"time"
)

func init() {
	j.initializeRunner()
}

//----------------------------------------------------------
// helpers
//----------------------------------------------------------

var fakeEmails = []string{}

func resetFakeEmails() {
	fakeEmails = []string{}
}

func createInactiveUserWithWarning(daysInactive int, w string) (*models.User, error) {
	username := bson.NewObjectId().Hex()
	user := &models.User{
		ObjectId:      bson.NewObjectId(),
		Name:          username,
		Email:         username + "@koding.com",
		LastLoginDate: timeNow().Add(-time.Hour * 24 * time.Duration(daysInactive)),
		Inactive: &models.UserInactive{
			Warning:    w,
			ModifiedAt: timeNow(),
			Warnings: map[string]time.Time{
				w: timeNow(),
			},
		},
	}

	_, err := modeltesthelper.CreateUserWithQuery(user)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func createUserWithVM() (*models.User, error) {
	username := bson.NewObjectId().Hex()
	user, _, err := modeltesthelper.CreateUser(username)
	if err != nil {
		return nil, err
	}

	machine := &models.Machine{ObjectId: bson.NewObjectId(), Provider: "koding"}
	return user, modelhelper.CreateMachineForUser(machine, user)
}

func createUserWithManagedVM() (*models.User, error) {
	username := bson.NewObjectId().Hex()
	user, _, err := modeltesthelper.CreateUser(username)
	if err != nil {
		return nil, err
	}

	machine := &models.Machine{ObjectId: bson.NewObjectId(), Provider: "managed"}
	return user, modelhelper.CreateMachineForUser(machine, user)
}

func createInactiveUser(daysInactive int) (*models.User, error) {
	username := bson.NewObjectId().Hex()
	user := &models.User{
		Name:          username,
		ObjectId:      bson.NewObjectId(),
		Email:         username + "@koding.com",
		LastLoginDate: timeNow().Add(-time.Hour * 24 * time.Duration(daysInactive)),
	}

	_, err := modeltesthelper.CreateUserWithQuery(user)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func deleteUserWithUsername(user *models.User) {
	modelhelper.RemoveAllUsers(user.Name)
	modelhelper.RemoveAllAccountByUsername(user.Name)
	modelhelper.RemoveAllMachinesForUser(user.ObjectId)
}

func yesterday() time.Time {
	return now.BeginningOfDay().UTC()
}

func updateUserModifiedAt(user *models.User, m time.Time) error {
	selector := bson.M{"username": user.Name}
	update := bson.M{"inactive.modifiedAt": m}

	return modelhelper.UpdateUser(selector, update)
}

func fakeEmailActionFn() func(user *models.User, w string) error {
	return func(user *models.User, w string) error {
		fakeEmails = append(fakeEmails, w)
		return nil
	}
}
<commit_msg>go: make inactiveuser creation function configurable<commit_after>package main

import (
	"github.com/jinzhu/now"

	"gopkg.in/mgo.v2/bson"

	"koding/db/models"
	"koding/db/mongodb/modelhelper"
	"koding/db/mongodb/modelhelper/modeltesthelper"
	"time"
)

func init() {
	j.initializeRunner()
}

//----------------------------------------------------------
// helpers
//----------------------------------------------------------

var fakeEmails = []string{}

func resetFakeEmails() {
	fakeEmails = []string{}
}

func createInactiveUserWithWarning(daysInactive int, w string) (*models.User, error) {
	return createInactiveUserWithWarningAndModificationTime(daysInactive, w, timeNow())
}

func createInactiveUserWithWarningAndModificationTime(daysInactive int, w string, mtime time.Time) (*models.User, error) {
	username := bson.NewObjectId().Hex()
	user := &models.User{
		ObjectId:      bson.NewObjectId(),
		Name:          username,
		Email:         username + "@koding.com",
		LastLoginDate: timeNow().Add(-time.Hour * 24 * time.Duration(daysInactive)),
		Inactive: &models.UserInactive{
			Warning:    w,
			ModifiedAt: mtime,
			Warnings: map[string]time.Time{
				w: timeNow(),
			},
		},
	}

	_, err := modeltesthelper.CreateUserWithQuery(user)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func createUserWithVM() (*models.User, error) {
	username := bson.NewObjectId().Hex()
	user, _, err := modeltesthelper.CreateUser(username)
	if err != nil {
		return nil, err
	}

	machine := &models.Machine{ObjectId: bson.NewObjectId(), Provider: "koding"}
	return user, modelhelper.CreateMachineForUser(machine, user)
}

func createUserWithManagedVM() (*models.User, error) {
	username := bson.NewObjectId().Hex()
	user, _, err := modeltesthelper.CreateUser(username)
	if err != nil {
		return nil, err
	}

	machine := &models.Machine{ObjectId: bson.NewObjectId(), Provider: "managed"}
	return user, modelhelper.CreateMachineForUser(machine, user)
}

func createInactiveUser(daysInactive int) (*models.User, error) {
	username := bson.NewObjectId().Hex()
	user := &models.User{
		Name:          username,
		ObjectId:      bson.NewObjectId(),
		Email:         username + "@koding.com",
		LastLoginDate: timeNow().Add(-time.Hour * 24 * time.Duration(daysInactive)),
	}

	_, err := modeltesthelper.CreateUserWithQuery(user)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func deleteUserWithUsername(user *models.User) {
	modelhelper.RemoveAllUsers(user.Name)
	modelhelper.RemoveAllAccountByUsername(user.Name)
	modelhelper.RemoveAllMachinesForUser(user.ObjectId)
}

func yesterday() time.Time {
	return now.BeginningOfDay().UTC()
}

func updateUserModifiedAt(user *models.User, m time.Time) error {
	selector := bson.M{"username": user.Name}
	update := bson.M{"inactive.modifiedAt": m}

	return modelhelper.UpdateUser(selector, update)
}

func fakeEmailActionFn() func(user *models.User, w string) error {
	return func(user *models.User, w string) error {
		fakeEmails = append(fakeEmails, w)
		return nil
	}
}
<|endoftext|>


<commit_before>package context

import (
	"net/http"

	"code.google.com/p/go.net/context"
	gcontext "github.com/gorilla/context"
	"github.com/gorilla/mux"
)

const (
	RequestKey    = 0
	ResourceIdKey = "resource_id"
	FormatKey     = "format"
	VersionKey    = "version"
)

// RequestContext contains the context information for the current HTTP request. It's a wrapper
// around Google's Context (http://godoc.org/code.google.com/p/go.net/context), which provides
// facilities for sending request-scoped values, cancelation signals, and deadlines
// across API boundaries to all the goroutines involved in handling a request.
type RequestContext interface {
	context.Context
	ValueWithDefault(key, defaultVal interface{}) interface{}
	ResponseFormat() string
	ResourceId() string
	Version() string
}

// requestContext is an implementation of the RequestContext interface.
type requestContext struct {
	context.Context
	req *http.Request
}

// NewContext returns a RequestContext populated with parameters from the request path and
// query string.
func NewContext(parent context.Context, req *http.Request) RequestContext {
	if parent == nil {
		parent = context.Background()
	}

	for key, value := range req.URL.Query() {
		var val interface{}
		val = value

		// Query string values are slices (e.g. ?foo=bar,baz,qux yields
		// [bar, baz, qux] for foo), but we unbox single values (e.g. ?foo=bar
		// yields bar for foo).
		if len(value) == 1 {
			val = value[0]
		}

		gcontext.Set(req, key, val)
	}

	for key, value := range mux.Vars(req) {
		gcontext.Set(req, key, value)
	}

	// TODO: Keys can potentially be overwritten if the request path has
	// parameters with the same name as query string values. Figure out a
	// better way to handle this.

	return &requestContext{parent, req}
}

// Value returns Gorilla's context package's value for this Context's request
// and key. It delegates to the parent Context if there is no such value.
func (ctx *requestContext) Value(key interface{}) interface{} {
	if key == RequestKey {
		return ctx.req
	}
	if val, ok := gcontext.GetOk(ctx.req, key); ok {
		return val
	}
	return ctx.Context.Value(key)
}

// ValueWithDefault returns the context value for the given key. If there's no
// such value, the provided default is returned.
func (ctx *requestContext) ValueWithDefault(key, defaultVal interface{}) interface{} {
	value := ctx.Value(key)
	if value == nil {
		value = defaultVal
	}
	return value
}

// ResponseFormat returns the response format for the request, defaulting to "json"
// if one is not specified using the "format" query parameter.
func (ctx *requestContext) ResponseFormat() string {
	return ctx.ValueWithDefault(FormatKey, "json").(string)
}

// ResourceId returns the resource id for the request, defaulting to an empty string
// if there isn't one.
func (ctx *requestContext) ResourceId() string {
	return ctx.ValueWithDefault(ResourceIdKey, "").(string)
}

// Version returns the API version for the request, defaulting to an empty string
// if one is not specified in the request path.
func (ctx *requestContext) Version() string {
	return ctx.ValueWithDefault(VersionKey, "").(string)
}

// HTTPRequest returns the *http.Request associated with ctx using NewContext,
// if any.
func HTTPRequest(ctx context.Context) (*http.Request, bool) {
	// We cannot use ctx.(*requestContext).req to get the request because ctx may
	// be a Context derived from a *requestContext. Instead, we use Value to
	// access the request if it is anywhere up the Context tree.
	req, ok := ctx.Value(RequestKey).(*http.Request)
	return req, ok
}
<commit_msg>Add docstring to context package<commit_after>// Context is a package that contains the interfaces and implementations for working with
// request-scoped data.
package context

import (
	"net/http"

	"code.google.com/p/go.net/context"
	gcontext "github.com/gorilla/context"
	"github.com/gorilla/mux"
)

const (
	RequestKey    = 0
	ResourceIdKey = "resource_id"
	FormatKey     = "format"
	VersionKey    = "version"
)

// RequestContext contains the context information for the current HTTP request. It's a wrapper
// around Google's Context (http://godoc.org/code.google.com/p/go.net/context), which provides
// facilities for sending request-scoped values, cancelation signals, and deadlines
// across API boundaries to all the goroutines involved in handling a request.
type RequestContext interface {
	context.Context
	ValueWithDefault(key, defaultVal interface{}) interface{}
	ResponseFormat() string
	ResourceId() string
	Version() string
}

// requestContext is an implementation of the RequestContext interface.
type requestContext struct {
	context.Context
	req *http.Request
}

// NewContext returns a RequestContext populated with parameters from the request path and
// query string.
func NewContext(parent context.Context, req *http.Request) RequestContext {
	if parent == nil {
		parent = context.Background()
	}

	for key, value := range req.URL.Query() {
		var val interface{}
		val = value

		// Query string values are slices (e.g. ?foo=bar,baz,qux yields
		// [bar, baz, qux] for foo), but we unbox single values (e.g. ?foo=bar
		// yields bar for foo).
		if len(value) == 1 {
			val = value[0]
		}

		gcontext.Set(req, key, val)
	}

	for key, value := range mux.Vars(req) {
		gcontext.Set(req, key, value)
	}

	// TODO: Keys can potentially be overwritten if the request path has
	// parameters with the same name as query string values. Figure out a
	// better way to handle this.

	return &requestContext{parent, req}
}

// Value returns Gorilla's context package's value for this Context's request
// and key. It delegates to the parent Context if there is no such value.
func (ctx *requestContext) Value(key interface{}) interface{} {
	if key == RequestKey {
		return ctx.req
	}
	if val, ok := gcontext.GetOk(ctx.req, key); ok {
		return val
	}
	return ctx.Context.Value(key)
}

// ValueWithDefault returns the context value for the given key. If there's no
// such value, the provided default is returned.
func (ctx *requestContext) ValueWithDefault(key, defaultVal interface{}) interface{} {
	value := ctx.Value(key)
	if value == nil {
		value = defaultVal
	}
	return value
}

// ResponseFormat returns the response format for the request, defaulting to "json"
// if one is not specified using the "format" query parameter.
func (ctx *requestContext) ResponseFormat() string {
	return ctx.ValueWithDefault(FormatKey, "json").(string)
}

// ResourceId returns the resource id for the request, defaulting to an empty string
// if there isn't one.
func (ctx *requestContext) ResourceId() string {
	return ctx.ValueWithDefault(ResourceIdKey, "").(string)
}

// Version returns the API version for the request, defaulting to an empty string
// if one is not specified in the request path.
func (ctx *requestContext) Version() string {
	return ctx.ValueWithDefault(VersionKey, "").(string)
}

// HTTPRequest returns the *http.Request associated with ctx using NewContext,
// if any.
func HTTPRequest(ctx context.Context) (*http.Request, bool) {
	// We cannot use ctx.(*requestContext).req to get the request because ctx may
	// be a Context derived from a *requestContext. Instead, we use Value to
	// access the request if it is anywhere up the Context tree.
	req, ok := ctx.Value(RequestKey).(*http.Request)
	return req, ok
}
<|endoftext|>


<commit_before>package evedata

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/jmcvetta/napping"
)

var stations map[int64]int64

func goEMDRCrestBridge(c *AppContext) {

	type marketRegions struct {
		RegionID   int64  `db:"regionID"`
		RegionName string `db:"regionName"`
	}

	type marketTypes struct {
		TypeID   int64  `db:"typeID"`
		TypeName string `db:"typeName"`
	}

	regions := []marketRegion{}
	err := c.Db.Select(&regions, `
		SELECT 	regionID, regionName
		FROM 	mapRegions
		WHERE 	regionID < 11000000
			AND regionID NOT IN(10000017, 10000019, 10000004);
	`)
	if err != nil {
		log.Fatal("EMDRCrestBridge:", err)
	}
	log.Printf("EMDRCrestBridge: Loaded %d Regions", len(regions))

	types := []marketTypes{}
	err = c.Db.Select(&types, `
		SELECT 	typeID, typeName
		FROM 	invTypes
		WHERE 	marketGroupID IS NOT NULL
			AND typeID < 250000;
	`)
	if err != nil {
		log.Fatal("EMDRCrestBridge:", err)
	}
	log.Printf("EMDRCrestBridge: Loaded %d items", len(types))

	stations = make(map[int64]int64)
	rows, err := c.Db.Query(`
		SELECT stationID, solarSystemID
		FROM staStations;
	`)
	for rows.Next() {

		var stationID int64
		var systemID int64

		if err := rows.Scan(&stationID, &systemID); err != nil {
			log.Fatal("EMDRCrestBridge: ", err)
		}
		stations[stationID] = systemID
	}
	rows.Close()

	if err != nil {
		log.Fatal("EMDRCrestBridge: ", err)
	}
	log.Printf("EMDRCrestBridge: Loaded %d stations", len(stations))

	// Throttle Crest Requests
	rate := time.Second / 8
	throttle := time.Tick(rate)

	// semaphore to prevent runaways
	sem := make(chan bool, c.Conf.EMDRCrestBridge.MaxGoRoutines)

	// CREST Session
	crest := napping.Session{}

	for {
		// loop through all regions
		for _, r := range regions {
			// and each item per region
			for _, t := range types {
				<-throttle // impliment throttle

				sem <- true
				go func() {
					defer func() { <-sem }()
					// Process Market History
					h := marketHistory{}
					url := fmt.Sprintf("https://public-crest.eveonline.com/market/%d/types/%d/history/", r.RegionID, t.TypeID)
					response, err := crest.Get(url, nil, &h, nil)
					if err != nil {
						log.Printf("EMDRCrestBridge: %s", err)
						return
					}
					if response.Status() == 200 {
						go postHistory(sem, h, c, t.TypeID, r.RegionID)

					}
				}()

				sem <- true
				go func() {
					defer func() { <-sem }()
					// Process Market Buy Orders
					b := marketOrders{}
					url := fmt.Sprintf("https://public-crest.eveonline.com/market/%d/orders/buy/?type=https://public-crest.eveonline.com/types/%d/", r.RegionID, t.TypeID)
					response, err := crest.Get(url, nil, &b, nil)
					if err != nil {
						log.Printf("EMDRCrestBridge: %s", err)
						return
					}
					if response.Status() == 200 {

						go postOrders(sem, b, c, 1, t.TypeID, r.RegionID)

					}
				}()

				sem <- true
				go func() {
					defer func() { <-sem }()
					// Process Market Sell Orders
					s := marketOrders{}
					url := fmt.Sprintf("https://public-crest.eveonline.com/market/%d/orders/sell/?type=https://public-crest.eveonline.com/types/%d/", r.RegionID, t.TypeID)
					response, err := crest.Get(url, nil, &s, nil)
					if err != nil {
						log.Printf("EMDRCrestBridge: %s", err)
						return
					}
					if response.Status() == 200 {

						go postOrders(sem, s, c, 0, t.TypeID, r.RegionID)

					}
				}()
			}
		}
	}
}

func postHistory(sem chan bool, h marketHistory, c *AppContext, typeID int64, regionID int64) {
	sem <- true
	defer func() { <-sem }()

	if c.Conf.EMDRCrestBridge.Import {
		historyUpdate, err := c.Db.Prepare(`
			INSERT IGNORE INTO market_history
				(date, low, high, mean, quantity, orders, itemID, regionID)
				VALUES(?,?,?,?,?,?,?,?);
		`)
		defer historyUpdate.Close()
		if err != nil {
			log.Printf("EMDRCrestBridge: %s", err)
		} else {
			tx, _ := c.Db.Begin()
			for _, e := range h.Items {
				tx.Stmt(historyUpdate).Exec(e.Date, e.LowPrice, e.HighPrice, e.AvgPrice, typeID, regionID)
			}
			tx.Commit()
		}
	}

	if c.Conf.EMDRCrestBridge.Upload {
		u := newUUDIFHeader()
		u.ResultType = "history"
		u.Columns = []string{"date", "orders", "quantity", "low", "high", "average"}

		u.Rowsets = make([]rowsetsUUDIF, 1)

		u.Rowsets[0].RegionID = regionID
		u.Rowsets[0].TypeID = typeID
		u.Rowsets[0].GeneratedAt = time.Now()

		u.Rowsets[0].Rows = make([][]interface{}, len(h.Items))

		for i, e := range h.Items {
			u.Rowsets[0].Rows[i] = make([]interface{}, 6)
			u.Rowsets[0].Rows[i][0] = e.Date + "+00:00"
			u.Rowsets[0].Rows[i][1] = e.OrderCount
			u.Rowsets[0].Rows[i][2] = e.Volume
			u.Rowsets[0].Rows[i][3] = e.LowPrice
			u.Rowsets[0].Rows[i][4] = e.HighPrice
			u.Rowsets[0].Rows[i][5] = e.AvgPrice
		}

		enc, err := json.Marshal(u)
		if err != nil {
			log.Println("EMDRCrestBridge:", err)
		} else {
			postUUDIF(c.Conf.EMDRCrestBridge.URL, enc)
		}
	}
}

func postOrders(sem chan bool, o marketOrders, c *AppContext, buy int, typeID int64, regionID int64) {
	sem <- true
	defer func() { <-sem }()

	if c.Conf.EMDRCrestBridge.Import {
		orderUpdate, err := c.Db.Prepare(`
					INSERT INTO market
						(orderID, price, remainingVolume, typeID, enteredVolume, minVolume, bid, issued, duration, stationID, regionID, systemID, reported)
						VALUES(?,?,?,?,?,?,?,?,?,?,?,?,NOW())
						ON DUPLICATE KEY UPDATE price=VALUES(price),
												remainingVolume=VALUES(remainingVolume),
												issued=VALUES(issued),
												duration=VALUES(duration),
												reported=VALUES(reported),
												done=0;
				`)
		defer orderUpdate.Close()

		orderMark, err := c.Db.Prepare(`
					UPDATE market SET done = 1 WHERE regionID = ? AND typeID =?
				`)
		defer orderMark.Close()

		if err != nil {
			log.Fatalf("EMDRCrestBridge: %s", err)
		} else {

			// Mark orders complete
			tx, err := c.Db.Begin()
			tx.Stmt(orderMark).Exec(regionID, typeID)

			// Add or update orders
			for _, e := range o.Items {
				tx.Stmt(orderUpdate).Exec(e.ID, e.Price, e.Volume, typeID, e.VolumeEntered, e.MinVolume, buy, e.Issued, e.Duration, e.Location.ID, regionID, stations[e.Location.ID])
			}

			err = tx.Commit()

			if err != nil {
				log.Println("EMDRCrestBridge:", err)
			}
		}
	}

	if c.Conf.EMDRCrestBridge.Upload {
		u := newUUDIFHeader()
		u.ResultType = "orders"
		u.Columns = []string{"price", "volRemaining", "range", "orderID", "volEntered", "minVolume", "bid", "issueDate", "duration", "stationID", "solarSystemID"}

		u.Rowsets = make([]rowsetsUUDIF, 1)

		u.Rowsets[0].RegionID = regionID
		u.Rowsets[0].TypeID = typeID
		u.Rowsets[0].GeneratedAt = time.Now()

		u.Rowsets[0].Rows = make([][]interface{}, len(o.Items))

		for i, e := range o.Items {

			var r int
			switch {
			case e.Range == "station":
				r = -1
			case e.Range == "solarsystem":
				r = 0
			case e.Range == "region":
				r = 32767
			default:
				r, _ = strconv.Atoi(e.Range)
			}

			u.Rowsets[0].Rows[i] = make([]interface{}, 11)
			u.Rowsets[0].Rows[i][0] = e.Price
			u.Rowsets[0].Rows[i][1] = e.Volume
			u.Rowsets[0].Rows[i][2] = r
			u.Rowsets[0].Rows[i][3] = e.ID
			u.Rowsets[0].Rows[i][4] = e.VolumeEntered
			u.Rowsets[0].Rows[i][5] = e.MinVolume
			u.Rowsets[0].Rows[i][6] = e.Buy
			u.Rowsets[0].Rows[i][7] = e.Issued + "+00:00"
			u.Rowsets[0].Rows[i][8] = e.Duration
			u.Rowsets[0].Rows[i][9] = e.Location.ID
			u.Rowsets[0].Rows[i][10] = stations[e.Location.ID]
		}

		enc, err := json.Marshal(u)
		if err != nil {
			log.Println("EMDRCrestBridge:", err)
		} else {
			postUUDIF(c.Conf.EMDRCrestBridge.URL, enc)
		}
	}
}

func postUUDIF(url string, j []byte) {
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(j))
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println("EMDRCrestBridge:", err)
	} else {
		defer resp.Body.Close()

		if resp.Status != "200 OK" {
			body, _ := ioutil.ReadAll(resp.Body)
			log.Println("EMDRCrestBridge:", string(body))
			log.Println("EMDRCrestBridge:", string(resp.Status))
		}
	}
}

func newUUDIFHeader() marketUUDIF {
	n := marketUUDIF{}

	n.Version = "0.1"

	n.Generator.Name = "EveData.Org"
	n.Generator.Version = "0.025a"

	n.UploadKeys = make([]uploadKeysUUDIF, 1)
	n.UploadKeys[0] = uploadKeysUUDIF{"EveData.Org", "TheCheeseIsBree"}

	n.CurrentTime = time.Now()

	return n
}

type rowsetsUUDIF struct {
	GeneratedAt time.Time       `json:"generatedAt"`
	RegionID    int64           `json:"regionID"`
	TypeID      int64           `json:"typeID"`
	Rows        [][]interface{} `json:"rows"`
}

type uploadKeysUUDIF struct {
	Name string `json:"name"`
	Key  string `json:"key"`
}

type marketUUDIF struct {
	ResultType string            `json:"resultType"`
	Version    string            `json:"version"`
	UploadKeys []uploadKeysUUDIF `json:"uploadKeys"`
	Generator  struct {
		Name    string `json:"name"`
		Version string `json:"version"`
	} `json:"generator"`
	Columns     []string       `json:"columns"`
	CurrentTime time.Time      `json:"currentTime"`
	Rowsets     []rowsetsUUDIF `json:"rowsets"`
}

type marketHistory struct {
	TotalCount_Str string
	Items          []struct {
		OrderCount int64
		LowPrice   float64
		HighPrice  float64
		AvgPrice   float64
		Volume     int64
		Date       string
	}
	PageCount  int64
	TotalCount int64
}

type marketOrders struct {
	Items []struct {
		Buy           bool
		Issued        string
		Price         float64
		VolumeEntered int64
		MinVolume     int64
		Volume        int64
		Range         string
		Duration      int64
		ID            int64
		Location      struct {
			ID   int64
			Name string
		}
		Type struct {
			ID   int64
			Name string
		}
	}
	PageCount  int64
	TotalCount int64
}
<commit_msg>limited the firehose<commit_after>package evedata

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/jmcvetta/napping"
)

var stations map[int64]int64

func goEMDRCrestBridge(c *AppContext) {

	type marketRegions struct {
		RegionID   int64  `db:"regionID"`
		RegionName string `db:"regionName"`
	}

	type marketTypes struct {
		TypeID   int64  `db:"typeID"`
		TypeName string `db:"typeName"`
	}

	regions := []marketRegion{}
	err := c.Db.Select(&regions, `
		SELECT 	regionID, regionName
		FROM 	mapRegions
		WHERE 	regionID < 11000000
			AND regionID NOT IN(10000017, 10000019, 10000004);
	`)
	if err != nil {
		log.Fatal("EMDRCrestBridge:", err)
	}
	log.Printf("EMDRCrestBridge: Loaded %d Regions", len(regions))

	types := []marketTypes{}
	err = c.Db.Select(&types, `
		SELECT 	typeID, typeName
		FROM 	invTypes
		WHERE 	marketGroupID IS NOT NULL
			AND typeID < 250000;
	`)
	if err != nil {
		log.Fatal("EMDRCrestBridge:", err)
	}
	log.Printf("EMDRCrestBridge: Loaded %d items", len(types))

	stations = make(map[int64]int64)
	rows, err := c.Db.Query(`
		SELECT stationID, solarSystemID
		FROM staStations;
	`)
	for rows.Next() {

		var stationID int64
		var systemID int64

		if err := rows.Scan(&stationID, &systemID); err != nil {
			log.Fatal("EMDRCrestBridge: ", err)
		}
		stations[stationID] = systemID
	}
	rows.Close()

	if err != nil {
		log.Fatal("EMDRCrestBridge: ", err)
	}
	log.Printf("EMDRCrestBridge: Loaded %d stations", len(stations))

	/*var (
		gets  uint64
		posts uint64
	)

		debug := time.Tick(time.Second)
		go func() {
			for {
				<-debug
				fmt.Printf("Processed %d gets, %d posts\n", gets, posts)
				atomic.StoreUint64(&gets, 0)
				atomic.StoreUint64(&posts, 0)
			}
		}()*/

	// Throttle Crest Requests
	rate := time.Second / 41
	throttle := time.Tick(rate)

	// semaphore to prevent runaways
	sem := make(chan bool, c.Conf.EMDRCrestBridge.MaxGoRoutines)
	postSem := make(chan bool, 20)

	// CREST Session
	crest := napping.Session{}

	for {
		// loop through all regions
		for _, r := range regions {
			// and each item per region
			for _, t := range types {
				<-throttle // impliment throttle

				sem <- true
				go func() {
					defer func() { <-sem }()
					// Process Market History
					h := marketHistory{}
					url := fmt.Sprintf("https://public-crest.eveonline.com/market/%d/types/%d/history/", r.RegionID, t.TypeID)
					//		atomic.AddUint64(&gets, 1)
					response, err := crest.Get(url, nil, &h, nil)
					if err != nil {
						log.Printf("EMDRCrestBridge: %s", err)
						return
					}
					if response.Status() == 200 {
						//		atomic.AddUint64(&posts, 1)
						go postHistory(postSem, h, c, t.TypeID, r.RegionID)

					}
				}()

				sem <- true
				go func() {
					defer func() { <-sem }()
					// Process Market Buy Orders
					b := marketOrders{}
					url := fmt.Sprintf("https://public-crest.eveonline.com/market/%d/orders/buy/?type=https://public-crest.eveonline.com/types/%d/", r.RegionID, t.TypeID)
					//	atomic.AddUint64(&gets, 1)
					response, err := crest.Get(url, nil, &b, nil)
					if err != nil {
						log.Printf("EMDRCrestBridge: %s", err)
						return
					}
					if response.Status() == 200 {
						//		atomic.AddUint64(&posts, 1)
						go postOrders(postSem, b, c, 1, t.TypeID, r.RegionID)

					}
				}()

				sem <- true
				go func() {
					defer func() { <-sem }()
					// Process Market Sell Orders
					s := marketOrders{}
					url := fmt.Sprintf("https://public-crest.eveonline.com/market/%d/orders/sell/?type=https://public-crest.eveonline.com/types/%d/", r.RegionID, t.TypeID)
					//	atomic.AddUint64(&gets, 1)
					response, err := crest.Get(url, nil, &s, nil)
					if err != nil {
						log.Printf("EMDRCrestBridge: %s", err)
						return
					}
					if response.Status() == 200 {
						//	atomic.AddUint64(&posts, 1)
						go postOrders(postSem, s, c, 0, t.TypeID, r.RegionID)

					}
				}()
			}
		}
	}
}

func postHistory(sem chan bool, h marketHistory, c *AppContext, typeID int64, regionID int64) {
	sem <- true
	defer func() { <-sem }()

	if c.Conf.EMDRCrestBridge.Import {
		historyUpdate, err := c.Db.Prepare(`
			INSERT IGNORE INTO market_history
				(date, low, high, mean, quantity, orders, itemID, regionID)
				VALUES(?,?,?,?,?,?,?,?);
		`)
		defer historyUpdate.Close()
		if err != nil {
			log.Printf("EMDRCrestBridge: %s", err)
		} else {
			tx, _ := c.Db.Begin()
			for _, e := range h.Items {
				tx.Stmt(historyUpdate).Exec(e.Date, e.LowPrice, e.HighPrice, e.AvgPrice, typeID, regionID)
			}
			tx.Commit()
		}
	}

	if c.Conf.EMDRCrestBridge.Upload {
		u := newUUDIFHeader()
		u.ResultType = "history"
		u.Columns = []string{"date", "orders", "quantity", "low", "high", "average"}

		u.Rowsets = make([]rowsetsUUDIF, 1)

		u.Rowsets[0].RegionID = regionID
		u.Rowsets[0].TypeID = typeID
		u.Rowsets[0].GeneratedAt = time.Now()

		u.Rowsets[0].Rows = make([][]interface{}, len(h.Items))

		for i, e := range h.Items {
			u.Rowsets[0].Rows[i] = make([]interface{}, 6)
			u.Rowsets[0].Rows[i][0] = e.Date + "+00:00"
			u.Rowsets[0].Rows[i][1] = e.OrderCount
			u.Rowsets[0].Rows[i][2] = e.Volume
			u.Rowsets[0].Rows[i][3] = e.LowPrice
			u.Rowsets[0].Rows[i][4] = e.HighPrice
			u.Rowsets[0].Rows[i][5] = e.AvgPrice
		}

		enc, err := json.Marshal(u)
		if err != nil {
			log.Println("EMDRCrestBridge:", err)
		} else {
			postUUDIF(c.Conf.EMDRCrestBridge.URL, enc)
		}
	}
}

func postOrders(sem chan bool, o marketOrders, c *AppContext, buy int, typeID int64, regionID int64) {
	sem <- true
	defer func() { <-sem }()

	if c.Conf.EMDRCrestBridge.Import {
		orderUpdate, err := c.Db.Prepare(`
					INSERT INTO market
						(orderID, price, remainingVolume, typeID, enteredVolume, minVolume, bid, issued, duration, stationID, regionID, systemID, reported)
						VALUES(?,?,?,?,?,?,?,?,?,?,?,?,NOW())
						ON DUPLICATE KEY UPDATE price=VALUES(price),
												remainingVolume=VALUES(remainingVolume),
												issued=VALUES(issued),
												duration=VALUES(duration),
												reported=VALUES(reported),
												done=0;
				`)
		defer orderUpdate.Close()

		orderMark, err := c.Db.Prepare(`
					UPDATE market SET done = 1 WHERE regionID = ? AND typeID =?
				`)
		defer orderMark.Close()

		if err != nil {
			log.Fatalf("EMDRCrestBridge: %s", err)
		} else {

			// Mark orders complete
			tx, err := c.Db.Begin()
			tx.Stmt(orderMark).Exec(regionID, typeID)

			// Add or update orders
			for _, e := range o.Items {
				tx.Stmt(orderUpdate).Exec(e.ID, e.Price, e.Volume, typeID, e.VolumeEntered, e.MinVolume, buy, e.Issued, e.Duration, e.Location.ID, regionID, stations[e.Location.ID])
			}

			err = tx.Commit()

			if err != nil {
				log.Println("EMDRCrestBridge:", err)
			}
		}
	}

	if c.Conf.EMDRCrestBridge.Upload {
		u := newUUDIFHeader()
		u.ResultType = "orders"
		u.Columns = []string{"price", "volRemaining", "range", "orderID", "volEntered", "minVolume", "bid", "issueDate", "duration", "stationID", "solarSystemID"}

		u.Rowsets = make([]rowsetsUUDIF, 1)

		u.Rowsets[0].RegionID = regionID
		u.Rowsets[0].TypeID = typeID
		u.Rowsets[0].GeneratedAt = time.Now()

		u.Rowsets[0].Rows = make([][]interface{}, len(o.Items))

		for i, e := range o.Items {

			var r int
			switch {
			case e.Range == "station":
				r = -1
			case e.Range == "solarsystem":
				r = 0
			case e.Range == "region":
				r = 32767
			default:
				r, _ = strconv.Atoi(e.Range)
			}

			u.Rowsets[0].Rows[i] = make([]interface{}, 11)
			u.Rowsets[0].Rows[i][0] = e.Price
			u.Rowsets[0].Rows[i][1] = e.Volume
			u.Rowsets[0].Rows[i][2] = r
			u.Rowsets[0].Rows[i][3] = e.ID
			u.Rowsets[0].Rows[i][4] = e.VolumeEntered
			u.Rowsets[0].Rows[i][5] = e.MinVolume
			u.Rowsets[0].Rows[i][6] = e.Buy
			u.Rowsets[0].Rows[i][7] = e.Issued + "+00:00"
			u.Rowsets[0].Rows[i][8] = e.Duration
			u.Rowsets[0].Rows[i][9] = e.Location.ID
			u.Rowsets[0].Rows[i][10] = stations[e.Location.ID]
		}

		enc, err := json.Marshal(u)
		if err != nil {
			log.Println("EMDRCrestBridge:", err)
		} else {
			postUUDIF(c.Conf.EMDRCrestBridge.URL, enc)
		}
	}
}

func postUUDIF(url string, j []byte) {
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(j))
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println("EMDRCrestBridge:", err)
	} else {
		defer resp.Body.Close()

		if resp.Status != "200 OK" {
			body, _ := ioutil.ReadAll(resp.Body)
			log.Println("EMDRCrestBridge:", string(body))
			log.Println("EMDRCrestBridge:", string(resp.Status))
		}
	}
}

func newUUDIFHeader() marketUUDIF {
	n := marketUUDIF{}

	n.Version = "0.1"

	n.Generator.Name = "EveData.Org"
	n.Generator.Version = "0.025a"

	n.UploadKeys = make([]uploadKeysUUDIF, 1)
	n.UploadKeys[0] = uploadKeysUUDIF{"EveData.Org", "TheCheeseIsBree"}

	n.CurrentTime = time.Now()

	return n
}

type rowsetsUUDIF struct {
	GeneratedAt time.Time       `json:"generatedAt"`
	RegionID    int64           `json:"regionID"`
	TypeID      int64           `json:"typeID"`
	Rows        [][]interface{} `json:"rows"`
}

type uploadKeysUUDIF struct {
	Name string `json:"name"`
	Key  string `json:"key"`
}

type marketUUDIF struct {
	ResultType string            `json:"resultType"`
	Version    string            `json:"version"`
	UploadKeys []uploadKeysUUDIF `json:"uploadKeys"`
	Generator  struct {
		Name    string `json:"name"`
		Version string `json:"version"`
	} `json:"generator"`
	Columns     []string       `json:"columns"`
	CurrentTime time.Time      `json:"currentTime"`
	Rowsets     []rowsetsUUDIF `json:"rowsets"`
}

type marketHistory struct {
	TotalCount_Str string
	Items          []struct {
		OrderCount int64
		LowPrice   float64
		HighPrice  float64
		AvgPrice   float64
		Volume     int64
		Date       string
	}
	PageCount  int64
	TotalCount int64
}

type marketOrders struct {
	Items []struct {
		Buy           bool
		Issued        string
		Price         float64
		VolumeEntered int64
		MinVolume     int64
		Volume        int64
		Range         string
		Duration      int64
		ID            int64
		Location      struct {
			ID   int64
			Name string
		}
		Type struct {
			ID   int64
			Name string
		}
	}
	PageCount  int64
	TotalCount int64
}
<|endoftext|>


<commit_before>package gogs

// # Infos / notes:
//
// ## Webhook calls
//
// Official API docs: https://gogs.io/docs/features/webhook
//
// This module works very similarly to the Gitlab processor.
// Please look there for more discussion of its operation.

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

	"github.com/bitrise-io/bitrise-webhooks/bitriseapi"
	hookCommon "github.com/bitrise-io/bitrise-webhooks/service/hook/common"
)

// --------------------------
// --- Webhook Data Model ---

const (
	pushEventID = "push"
	tagEventID  = "create"
)

// CommitModel ...
type CommitModel struct {
	CommitHash    string `json:"id"`
	CommitMessage string `json:"message"`
}

// PushEventModel ...
type PushEventModel struct {
	Secret      string        `json:"secret"`
	Ref         string        `json:"ref"`
	CheckoutSHA string        `json:"after"`
	Commits     []CommitModel `json:"commits"`
}

type TagEventModel struct {
	Secret  string `json:"secret"`
	Ref     string `json:"ref"`
	RefType string `json:"ref_type"`
}

// ---------------------------------------
// --- Webhook Provider Implementation ---

// HookProvider ...
type HookProvider struct{}

func detectContentTypeAndEventID(header http.Header) (string, string, error) {
	contentType := header.Get("Content-Type")
	if contentType == "" {
		return "", "", errors.New("No Content-Type Header found")
	}

	eventID := header.Get("X-Gogs-Event")
	if eventID == "" {
		return "", "", errors.New("No X-Gogs-Event Header found")
	}

	return contentType, eventID, nil
}

func transformPushEvent(pushEvent PushEventModel) hookCommon.TransformResultModel {
	lastCommit := CommitModel{}
	isLastCommitFound := false
	for _, aCommit := range pushEvent.Commits {
		if aCommit.CommitHash == pushEvent.CheckoutSHA {
			isLastCommitFound = true
			lastCommit = aCommit
			break
		}
	}

	if !isLastCommitFound {
		return hookCommon.TransformResultModel{
			Error:      errors.New("The commit specified by 'after' was not included in the 'commits' array - no match found"),
			ShouldSkip: true,
		}
	}

	if len(lastCommit.CommitHash) == 0 {
		return hookCommon.TransformResultModel{
			Error:      fmt.Errorf("Missing commit hash"),
			ShouldSkip: true,
		}
	}

	branch := strings.TrimPrefix(pushEvent.Ref, "refs/heads/")

	return hookCommon.TransformResultModel{
		TriggerAPIParams: []bitriseapi.TriggerAPIParamsModel{
			{
				BuildParams: bitriseapi.BuildParamsModel{
					Branch:        branch,
					CommitHash:    lastCommit.CommitHash,
					CommitMessage: lastCommit.CommitMessage,
				},
			},
		},
	}
}

func transformTagEvent(tagEvent TagEventModel) hookCommon.TransformResultModel {
	return hookCommon.TransformResultModel{
		TriggerAPIParams: []bitriseapi.TriggerAPIParamsModel{
			{
				BuildParams: bitriseapi.BuildParamsModel{
					Tag: tagEvent.Ref,
				},
			},
		},
	}
}

// TransformRequest ...
func (hp HookProvider) TransformRequest(r *http.Request) hookCommon.TransformResultModel {
	contentType, eventID, err := detectContentTypeAndEventID(r.Header)
	if err != nil {
		return hookCommon.TransformResultModel{
			Error:      fmt.Errorf("Issue with Headers: %s", err),
			ShouldSkip: true,
		}
	}

	if contentType != hookCommon.ContentTypeApplicationJSON {
		return hookCommon.TransformResultModel{
			Error: fmt.Errorf("Content-Type is not supported: %s", contentType),
		}
	}

	if r.Body == nil {
		return hookCommon.TransformResultModel{
			Error: fmt.Errorf("Failed to read content of request body: no or empty request body"),
		}
	}

	if eventID == pushEventID {
		var pushEvent PushEventModel
		if err := json.NewDecoder(r.Body).Decode(&pushEvent); err != nil {
			return hookCommon.TransformResultModel{Error: fmt.Errorf("Failed to parse request body: %s", err)}
		}

		return transformPushEvent(pushEvent)

	} else if eventID == tagEventID {
		var tagEvent TagEventModel
		if err := json.NewDecoder(r.Body).Decode(&tagEvent); err != nil {
			return hookCommon.TransformResultModel{Error: fmt.Errorf("Failed to parse request body: %s", err)}
		}

		return transformTagEvent(tagEvent)

	}

	// Unsupported Event
	return hookCommon.TransformResultModel{
		Error:      fmt.Errorf("Unsupported Webhook event: %s", eventID),
		ShouldSkip: true,
	}
}
<commit_msg>Fix one Go Lint issue.<commit_after>package gogs

// # Infos / notes:
//
// ## Webhook calls
//
// Official API docs: https://gogs.io/docs/features/webhook
//
// This module works very similarly to the Gitlab processor.
// Please look there for more discussion of its operation.

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

	"github.com/bitrise-io/bitrise-webhooks/bitriseapi"
	hookCommon "github.com/bitrise-io/bitrise-webhooks/service/hook/common"
)

// --------------------------
// --- Webhook Data Model ---

const (
	pushEventID = "push"
	tagEventID  = "create"
)

// CommitModel ...
type CommitModel struct {
	CommitHash    string `json:"id"`
	CommitMessage string `json:"message"`
}

// PushEventModel ...
type PushEventModel struct {
	Secret      string        `json:"secret"`
	Ref         string        `json:"ref"`
	CheckoutSHA string        `json:"after"`
	Commits     []CommitModel `json:"commits"`
}

// TagEventModel ...
type TagEventModel struct {
	Secret  string `json:"secret"`
	Ref     string `json:"ref"`
	RefType string `json:"ref_type"`
}

// ---------------------------------------
// --- Webhook Provider Implementation ---

// HookProvider ...
type HookProvider struct{}

func detectContentTypeAndEventID(header http.Header) (string, string, error) {
	contentType := header.Get("Content-Type")
	if contentType == "" {
		return "", "", errors.New("No Content-Type Header found")
	}

	eventID := header.Get("X-Gogs-Event")
	if eventID == "" {
		return "", "", errors.New("No X-Gogs-Event Header found")
	}

	return contentType, eventID, nil
}

func transformPushEvent(pushEvent PushEventModel) hookCommon.TransformResultModel {
	lastCommit := CommitModel{}
	isLastCommitFound := false
	for _, aCommit := range pushEvent.Commits {
		if aCommit.CommitHash == pushEvent.CheckoutSHA {
			isLastCommitFound = true
			lastCommit = aCommit
			break
		}
	}

	if !isLastCommitFound {
		return hookCommon.TransformResultModel{
			Error:      errors.New("The commit specified by 'after' was not included in the 'commits' array - no match found"),
			ShouldSkip: true,
		}
	}

	if len(lastCommit.CommitHash) == 0 {
		return hookCommon.TransformResultModel{
			Error:      fmt.Errorf("Missing commit hash"),
			ShouldSkip: true,
		}
	}

	branch := strings.TrimPrefix(pushEvent.Ref, "refs/heads/")

	return hookCommon.TransformResultModel{
		TriggerAPIParams: []bitriseapi.TriggerAPIParamsModel{
			{
				BuildParams: bitriseapi.BuildParamsModel{
					Branch:        branch,
					CommitHash:    lastCommit.CommitHash,
					CommitMessage: lastCommit.CommitMessage,
				},
			},
		},
	}
}

func transformTagEvent(tagEvent TagEventModel) hookCommon.TransformResultModel {
	return hookCommon.TransformResultModel{
		TriggerAPIParams: []bitriseapi.TriggerAPIParamsModel{
			{
				BuildParams: bitriseapi.BuildParamsModel{
					Tag: tagEvent.Ref,
				},
			},
		},
	}
}

// TransformRequest ...
func (hp HookProvider) TransformRequest(r *http.Request) hookCommon.TransformResultModel {
	contentType, eventID, err := detectContentTypeAndEventID(r.Header)
	if err != nil {
		return hookCommon.TransformResultModel{
			Error:      fmt.Errorf("Issue with Headers: %s", err),
			ShouldSkip: true,
		}
	}

	if contentType != hookCommon.ContentTypeApplicationJSON {
		return hookCommon.TransformResultModel{
			Error: fmt.Errorf("Content-Type is not supported: %s", contentType),
		}
	}

	if r.Body == nil {
		return hookCommon.TransformResultModel{
			Error: fmt.Errorf("Failed to read content of request body: no or empty request body"),
		}
	}

	if eventID == pushEventID {
		var pushEvent PushEventModel
		if err := json.NewDecoder(r.Body).Decode(&pushEvent); err != nil {
			return hookCommon.TransformResultModel{Error: fmt.Errorf("Failed to parse request body: %s", err)}
		}

		return transformPushEvent(pushEvent)

	} else if eventID == tagEventID {
		var tagEvent TagEventModel
		if err := json.NewDecoder(r.Body).Decode(&tagEvent); err != nil {
			return hookCommon.TransformResultModel{Error: fmt.Errorf("Failed to parse request body: %s", err)}
		}

		return transformTagEvent(tagEvent)

	}

	// Unsupported Event
	return hookCommon.TransformResultModel{
		Error:      fmt.Errorf("Unsupported Webhook event: %s", eventID),
		ShouldSkip: true,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"hash/adler32"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"code.google.com/p/go-uuid/uuid"
	"github.com/pachyderm-io/pfs/lib/etcache"
)

var modulos uint64

func hashRequest(r *http.Request) uint64 {
	return uint64(adler32.Checksum([]byte(r.URL.Path))) % modulos
}

func Route(w http.ResponseWriter, r *http.Request, etcdKey string) {
	log.Printf("Request to `Route`: %s.\n", r.URL.Path)

	bucket := hashRequest(r)
	shard := fmt.Sprint(bucket, "-", os.Args[1])

	_master, err := etcache.Get(path.Join(etcdKey, shard), false, false)
	if err != nil {
		log.Fatal(err)
	}
	master := _master.Node.Value

	httpClient := &http.Client{}
	// `Do` will complain if r.RequestURI is set so we unset it
	r.RequestURI = ""
	r.URL.Scheme = "http"
	r.URL.Host = master
	log.Print("Proxying to: " + r.URL.String())
	resp, err := httpClient.Do(r)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		http.Error(w, fmt.Sprintf("Failed request (%s) to %s.", resp.Status, r.URL.String()), resp.StatusCode)
		log.Printf("Failed request (%s) to %s.\n", resp.Status, r.URL.String())
		return
	}
	if _, err := io.Copy(w, resp.Body); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
}

func Multicast(w http.ResponseWriter, r *http.Request, etcdKey string) {
	log.Printf("Request to `Multicast`: %s.\n", r.URL.String())
	_endpoints, err := etcache.Get(etcdKey, false, true)
	if err != nil {
		log.Fatal(err)
	}
	endpoints := _endpoints.Node.Nodes
	log.Print(endpoints)

	for i, node := range endpoints {
		log.Print("Multicasting to: ", node, node.Value)
		httpClient := &http.Client{}
		// `Do` will complain if r.RequestURI is set so we unset it
		r.RequestURI = ""
		r.URL.Scheme = "http"
		r.URL.Host = node.Value
		log.Print("Proxying to: " + r.URL.String())
		resp, err := httpClient.Do(r)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			http.Error(w, fmt.Sprintf("Failed request (%s) to %s.", resp.Status, r.URL.String()), resp.StatusCode)
			log.Printf("Failed request (%s) to %s.\n", resp.Status, r.URL.String())
			return
		}
		if i == 0 {
			if _, err := io.Copy(w, resp.Body); err != nil {
				http.Error(w, err.Error(), 500)
				log.Print(err)
				return
			}
		}
	}
}

func RouterMux() *http.ServeMux {
	mux := http.NewServeMux()

	fileHandler := func(w http.ResponseWriter, r *http.Request) {
		if strings.Contains(r.URL.Path, "*") {
			Multicast(w, r, "/pfs/master")
		} else {
			Route(w, r, "/pfs/master")
		}
	}
	commitHandler := func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			values := r.URL.Query()
			values.Add("commit", uuid.New())
			r.URL.RawQuery = values.Encode()
		}
		Multicast(w, r, "/pfs/master")
	}
	branchHandler := func(w http.ResponseWriter, r *http.Request) {
		Multicast(w, r, "/pfs/master")
	}

	mux.HandleFunc("/file/", fileHandler)
	mux.HandleFunc("/commit", commitHandler)
	mux.HandleFunc("/branch", branchHandler)
	mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "pong\n") })
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		r.URL.Path = path.Join("file", "README")
		fileHandler(w, r)
	})

	return mux
}

func main() {
	log.SetFlags(log.Lshortfile)
	log.Print("Starting up...")

	var err error
	modulos, err = strconv.ParseUint(os.Args[1], 10, 32)

	if err != nil {
		log.Fatalf("Failed to parse %s as Uint.")
	}
	log.Fatal(http.ListenAndServe(":80", RouterMux()))
}
<commit_msg>Remove this readme business.<commit_after>package main

import (
	"fmt"
	"hash/adler32"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"code.google.com/p/go-uuid/uuid"
	"github.com/pachyderm-io/pfs/lib/etcache"
)

var modulos uint64

func hashRequest(r *http.Request) uint64 {
	return uint64(adler32.Checksum([]byte(r.URL.Path))) % modulos
}

func Route(w http.ResponseWriter, r *http.Request, etcdKey string) {
	log.Printf("Request to `Route`: %s.\n", r.URL.Path)

	bucket := hashRequest(r)
	shard := fmt.Sprint(bucket, "-", os.Args[1])

	_master, err := etcache.Get(path.Join(etcdKey, shard), false, false)
	if err != nil {
		log.Fatal(err)
	}
	master := _master.Node.Value

	httpClient := &http.Client{}
	// `Do` will complain if r.RequestURI is set so we unset it
	r.RequestURI = ""
	r.URL.Scheme = "http"
	r.URL.Host = master
	log.Print("Proxying to: " + r.URL.String())
	resp, err := httpClient.Do(r)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		http.Error(w, fmt.Sprintf("Failed request (%s) to %s.", resp.Status, r.URL.String()), resp.StatusCode)
		log.Printf("Failed request (%s) to %s.\n", resp.Status, r.URL.String())
		return
	}
	if _, err := io.Copy(w, resp.Body); err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
}

func Multicast(w http.ResponseWriter, r *http.Request, etcdKey string) {
	log.Printf("Request to `Multicast`: %s.\n", r.URL.String())
	_endpoints, err := etcache.Get(etcdKey, false, true)
	if err != nil {
		log.Fatal(err)
	}
	endpoints := _endpoints.Node.Nodes
	log.Print(endpoints)

	for i, node := range endpoints {
		log.Print("Multicasting to: ", node, node.Value)
		httpClient := &http.Client{}
		// `Do` will complain if r.RequestURI is set so we unset it
		r.RequestURI = ""
		r.URL.Scheme = "http"
		r.URL.Host = node.Value
		log.Print("Proxying to: " + r.URL.String())
		resp, err := httpClient.Do(r)
		if err != nil {
			http.Error(w, err.Error(), 500)
			log.Print(err)
			return
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			http.Error(w, fmt.Sprintf("Failed request (%s) to %s.", resp.Status, r.URL.String()), resp.StatusCode)
			log.Printf("Failed request (%s) to %s.\n", resp.Status, r.URL.String())
			return
		}
		if i == 0 {
			if _, err := io.Copy(w, resp.Body); err != nil {
				http.Error(w, err.Error(), 500)
				log.Print(err)
				return
			}
		}
	}
}

func RouterMux() *http.ServeMux {
	mux := http.NewServeMux()

	fileHandler := func(w http.ResponseWriter, r *http.Request) {
		if strings.Contains(r.URL.Path, "*") {
			Multicast(w, r, "/pfs/master")
		} else {
			Route(w, r, "/pfs/master")
		}
	}
	commitHandler := func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			values := r.URL.Query()
			values.Add("commit", uuid.New())
			r.URL.RawQuery = values.Encode()
		}
		Multicast(w, r, "/pfs/master")
	}
	branchHandler := func(w http.ResponseWriter, r *http.Request) {
		Multicast(w, r, "/pfs/master")
	}

	mux.HandleFunc("/file/", fileHandler)
	mux.HandleFunc("/commit", commitHandler)
	mux.HandleFunc("/branch", branchHandler)
	mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "pong\n") })
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Welcome to pfs!\n")
	})

	return mux
}

func main() {
	log.SetFlags(log.Lshortfile)
	log.Print("Starting up...")

	var err error
	modulos, err = strconv.ParseUint(os.Args[1], 10, 32)

	if err != nil {
		log.Fatalf("Failed to parse %s as Uint.")
	}
	log.Fatal(http.ListenAndServe(":80", RouterMux()))
}
<|endoftext|>