<commit_before>package workflow

import (
	"fmt"
	"time"

	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/event"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

//execute is called by the scheduler. You should not call this by yourself
func execute(db gorp.SqlExecutor, store cache.Store, p *sdk.Project, n *sdk.WorkflowNodeRun) (errExecute error) {
	t0 := time.Now()
	log.Debug("workflow.execute> Begin [#%d.%d] runID=%d (%s)", n.Number, n.SubNumber, n.WorkflowRunID, n.Status)
	defer func() {
		log.Debug("workflow.execute> End [#%d.%d] runID=%d (%s) - %.3fs", n.Number, n.SubNumber, n.WorkflowRunID, n.Status, time.Since(t0).Seconds())
		if errExecute != nil {
			log.Error("workflow.execute> Unable to execute run %d: %v", n.WorkflowRunID, errExecute)
			run, errw := loadAndLockRunByID(db, n.WorkflowRunID)
			if errw != nil {
				log.Error("workflow.execute> Unable to add infos on run %d: %v", n.WorkflowRunID, errw)
				return
			}
			AddWorkflowRunInfo(run, true, sdk.SpawnMsg{
				ID:   sdk.MsgWorkflowError.ID,
				Args: []interface{}{errExecute},
			})
		}
	}()

	//If status is not waiting neither build: nothing to do
	if n.Status != sdk.StatusWaiting.String() && n.Status != sdk.StatusBuilding.String() {
		return nil
	}

	var newStatus = n.Status

	//If no stages ==> success
	if len(n.Stages) == 0 {
		newStatus = sdk.StatusSuccess.String()
	}

	//Browse stages
	for stageIndex := range n.Stages {
		stage := &n.Stages[stageIndex]
		log.Debug("workflow.execute> checking stage %s (status=%s)", stage.Name, stage.Status)
		//Initialize stage status at waiting
		if stage.Status.String() == "" {
			if stageIndex == 0 {
				newStatus = sdk.StatusWaiting.String()
			}

			stage.Status = sdk.StatusWaiting
			//Add job to Queue
			//Insert data in workflow_node_run_job
			log.Debug("workflow.execute> stage %s call addJobsToQueue", stage.Name)
			if err := addJobsToQueue(db, stage, n); err != nil {
				return err
			}
			if stage.Status == sdk.StatusSkipped || stage.Status == sdk.StatusDisabled {
				continue
			}
			break
		}

		//If stage is waiting, nothing to do
		if stage.Status == sdk.StatusWaiting {
			log.Debug("workflow.execute> stage %s status:%s - nothing to do", stage.Name, stage.Status)
			break
		}

		if stage.Status == sdk.StatusBuilding {
			newStatus = sdk.StatusBuilding.String()

			var end bool
			end, errSync := syncStage(db, store, stage)
			if errSync != nil {
				return errSync
			}
			if !end {
				break
			} else {
				//The stage is over
				if stage.Status == sdk.StatusFail {
					n.Done = time.Now()
					newStatus = sdk.StatusFail.String()
					break
				}
				if stageIndex == len(n.Stages)-1 {
					n.Done = time.Now()
					newStatus = sdk.StatusSuccess.String()
					break
				}
				if stageIndex != len(n.Stages)-1 {
					continue
				}
			}
		}
	}

	log.Debug("workflow.execute> status from %s to %s", n.Status, newStatus)
	n.Status = newStatus
	//If the node is over, push the status in the build parameter, so it would be availabe in children build parameters
	if n.Status == sdk.StatusSuccess.String() || n.Status == sdk.StatusFail.String() {
		sdk.AddParameter(&n.BuildParameters, "cds.status", sdk.StringParameter, n.Status)
	}

	// Save the node run in database
	if err := UpdateNodeRun(db, n); err != nil {
		return sdk.WrapError(fmt.Errorf("Unable to update node id=%d at status %s. err:%s", n.ID, n.Status, err), "workflow.execute> Unable to execute node")
	}

	//Reload the workflow
	updatedWorkflowRun, err := LoadRunByID(db, n.WorkflowRunID)
	if err != nil {
		return sdk.WrapError(err, "workflow.execute> Unable to reload workflow run id=%d", n.WorkflowRunID)
	}

	// If pipeline build succeed, reprocess the workflow (in the same transaction)
	//Delete jobs only when node is over
	if n.Status == sdk.StatusSuccess.String() || n.Status == sdk.StatusFail.String() {
		if err := processWorkflowRun(db, store, p, updatedWorkflowRun, nil, nil, nil); err != nil {
			return sdk.WrapError(err, "workflow.execute> Unable to reprocess workflow !")
		}

		//Delete the line in workflow_node_run_job
		if err := DeleteNodeJobRuns(db, n.ID); err != nil {
			return sdk.WrapError(err, "workflow.execute> Unable to delete node %d job runs ", n.ID)
		}
	}

	return nil
}

func addJobsToQueue(db gorp.SqlExecutor, stage *sdk.Stage, run *sdk.WorkflowNodeRun) error {
	log.Debug("addJobsToQueue> add %d in stage %s", run.ID, stage.Name)

	conditionsOK, err := sdk.WorkflowCheckConditions(stage.Conditions(), run.BuildParameters)
	if err != nil {
		return sdk.WrapError(err, "addJobsToQueue> Cannot compute prerequisites on stage %s(%d)", stage.Name, stage.ID)
	}

	if !conditionsOK {
		stage.Status = sdk.StatusSkipped
	}
	if !stage.Enabled {
		stage.Status = sdk.StatusDisabled
	}

	//Browse the jobs
	for j := range stage.Jobs {
		job := &stage.Jobs[j]
		errs := sdk.MultiError{}
		//Process variables for the jobs
		jobParams, errParam := getNodeJobRunParameters(db, *job, run, stage)
		if errParam != nil {
			errs.Join(*errParam)
		}
		jobRequirements, errReq := getNodeJobRunRequirements(db, *job, run)
		if errReq != nil {
			errs.Join(*errReq)
		}
		job.Action.Requirements = jobRequirements

		//Create the job run
		wjob := sdk.WorkflowNodeJobRun{
			WorkflowNodeRunID: run.ID,
			Start:             time.Time{},
			Queued:            time.Now(),
			Status:            sdk.StatusWaiting.String(),
			Parameters:        jobParams,
			Job: sdk.ExecutedJob{
				Job: *job,
			},
		}

		if !stage.Enabled || !wjob.Job.Enabled {
			wjob.Status = sdk.StatusDisabled.String()
		} else if !conditionsOK {
			wjob.Status = sdk.StatusSkipped.String()
		}

		if errParam != nil {
			wjob.Status = sdk.StatusFail.String()
			spawnInfos := sdk.SpawnMsg{
				ID: sdk.MsgSpawnInfoJobError.ID,
			}

			for _, e := range *errParam {
				spawnInfos.Args = append(spawnInfos.Args, e.Error())
			}

			wjob.SpawnInfos = []sdk.SpawnInfo{sdk.SpawnInfo{
				APITime:    time.Now(),
				Message:    spawnInfos,
				RemoteTime: time.Now(),
			}}
		}

		//Insert in database
		if err := insertWorkflowNodeJobRun(db, &wjob); err != nil {
			return sdk.WrapError(err, "addJobsToQueue> Unable to insert in table workflow_node_run_job")
		}

		//Put the job run in database
		event.PublishJobRun(run, &wjob)
		stage.RunJobs = append(stage.RunJobs, wjob)
	}

	return nil
}

func syncStage(db gorp.SqlExecutor, store cache.Store, stage *sdk.Stage) (bool, error) {
	stageEnd := true
	finalStatus := sdk.StatusBuilding

	log.Debug("syncStage> work on stage %s", stage.Name)
	// browse all running jobs
	for indexJob := range stage.RunJobs {
		pbJob := &stage.RunJobs[indexJob]
		// If job is runnning, sync it
		if pbJob.Status == sdk.StatusBuilding.String() || pbJob.Status == sdk.StatusWaiting.String() {
			pbJobDB, errJob := LoadNodeJobRun(db, store, pbJob.ID)
			if errJob != nil {
				return stageEnd, errJob
			}

			if pbJobDB.Status == sdk.StatusBuilding.String() || pbJobDB.Status == sdk.StatusWaiting.String() {
				stageEnd = false
			}

			pbJob.SpawnInfos = pbJobDB.SpawnInfos

			// If same status, sync step status
			if pbJobDB.Status == pbJob.Status {
				pbJob.Job.StepStatus = pbJobDB.Job.StepStatus
			} else {
				pbJob.Status = pbJobDB.Status
				pbJob.Start = pbJobDB.Start
				pbJob.Done = pbJobDB.Done
				pbJob.Model = pbJobDB.Model
				pbJob.Job = pbJobDB.Job
			}
		}
	}
	log.Debug("syncStage> stage %s stageEnd:%t len(stage.RunJobs):%d", stage.Name, stageEnd, len(stage.RunJobs))

	if stageEnd || len(stage.RunJobs) == 0 {
		finalStatus = sdk.StatusSuccess
		stageEnd = true
		// Determine final stage status
	finalStageLoop:
		for _, runJob := range stage.RunJobs {
			switch runJob.Status {
			case sdk.StatusDisabled.String():
				if finalStatus == sdk.StatusBuilding {
					finalStatus = sdk.StatusDisabled
				}
			case sdk.StatusSkipped.String():
				if finalStatus == sdk.StatusBuilding || finalStatus == sdk.StatusDisabled {
					finalStatus = sdk.StatusSkipped
				}
			case sdk.StatusFail.String():
				finalStatus = sdk.StatusFail
				break finalStageLoop
			case sdk.StatusSuccess.String():
				if finalStatus != sdk.StatusFail {
					finalStatus = sdk.StatusSuccess
				}
			}
		}
	}
	log.Debug("syncStage> set stage %s from %s to %s", stage.Name, stage.Status, finalStatus)
	stage.Status = finalStatus
	return stageEnd, nil
}

//NodeBuildParameters returns build_parameters for a node given its id
func NodeBuildParameters(proj *sdk.Project, wf *sdk.Workflow, wr *sdk.WorkflowRun, id int64, u *sdk.User) ([]sdk.Parameter, error) {
	refNode := wf.GetNode(id)
	if refNode == nil {
		return nil, sdk.WrapError(sdk.ErrWorkflowNodeNotFound, "getWorkflowTriggerConditionHandler> Unable to load workflow node")
	}

	res := []sdk.Parameter{}

	if wr != nil {
		for nodeID, nodeRuns := range wr.WorkflowNodeRuns {
			oldNode := wr.Workflow.GetNode(nodeID)
			if oldNode == nil {
				log.Warning("getWorkflowTriggerConditionHandler> Unable to find last run")
				break
			}
			if oldNode.EqualsTo(refNode) {
				for _, p := range nodeRuns[0].BuildParameters {
					sdk.AddParameter(&res, p.Name, p.Type, p.Value)
				}
				break
			}
		}
	}

	if len(res) == 0 {
		var err error
		res, err = GetNodeBuildParameters(proj, wf, refNode, refNode.Context.DefaultPipelineParameters, refNode.Context.DefaultPayload)
		if err != nil {
			return nil, sdk.WrapError(sdk.ErrWorkflowNodeNotFound, "getWorkflowTriggerConditionHandler> Unable to get workflow node parameters: %v", err)
		}
	}

	return res, nil
}

// StopWorkflowNodeRun to stop a workflow node run with a specific spawn info
func StopWorkflowNodeRun(db *gorp.DbMap, store cache.Store, proj *sdk.Project, nodeRun sdk.WorkflowNodeRun, stopInfos sdk.SpawnInfo) error {
	// Load node job run ID
	ids, errIDS := LoadNodeJobRunIDByNodeRunID(db, nodeRun.ID)
	if errIDS != nil {
		return sdk.WrapError(errIDS, "stopWorkflowNodeRunHandler> Cannot load node job run id")
	}

	tx, errT := db.Begin()
	if errT != nil {
		return sdk.WrapError(errT, "StopWorkflowNodeRun> Cannot start transaction")
	}
	defer tx.Rollback()

	for _, nrjID := range ids {
		njr, errNRJ := LoadAndLockNodeJobRunWait(tx, store, nrjID)
		if errNRJ != nil {
			return sdk.WrapError(errNRJ, "StopWorkflowNodeRun> Cannot load node job run id")
		}
		njr.SpawnInfos = append(njr.SpawnInfos, stopInfos)
		if err := UpdateNodeJobRunStatus(tx, store, proj, njr, sdk.StatusStopped); err != nil {
			return sdk.WrapError(err, "StopWorkflowNodeRun> Cannot update node job run")
		}
	}

	if err := updateNodeRunStatus(tx, nodeRun.ID, sdk.StatusStopped.String()); err != nil {
		return sdk.WrapError(err, "StopWorkflowNodeRun> Cannot update node run status")
	}

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

	return nil
}
<commit_msg>fix (api): remove add infos from defer execute workflow (#1446)<commit_after>package workflow

import (
	"fmt"
	"time"

	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/event"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

//execute is called by the scheduler. You should not call this by yourself
func execute(db gorp.SqlExecutor, store cache.Store, p *sdk.Project, n *sdk.WorkflowNodeRun) (errExecute error) {
	t0 := time.Now()
	log.Debug("workflow.execute> Begin [#%d.%d] runID=%d (%s)", n.Number, n.SubNumber, n.WorkflowRunID, n.Status)
	defer func() {
		log.Debug("workflow.execute> End [#%d.%d] runID=%d (%s) - %.3fs", n.Number, n.SubNumber, n.WorkflowRunID, n.Status, time.Since(t0).Seconds())
		if errExecute != nil {
			log.Error("workflow.execute.defer> Unable to execute run %d: %v", n.WorkflowRunID, errExecute)
		}
	}()

	//If status is not waiting neither build: nothing to do
	if n.Status != sdk.StatusWaiting.String() && n.Status != sdk.StatusBuilding.String() {
		return nil
	}

	var newStatus = n.Status

	//If no stages ==> success
	if len(n.Stages) == 0 {
		newStatus = sdk.StatusSuccess.String()
	}

	//Browse stages
	for stageIndex := range n.Stages {
		stage := &n.Stages[stageIndex]
		log.Debug("workflow.execute> checking stage %s (status=%s)", stage.Name, stage.Status)
		//Initialize stage status at waiting
		if stage.Status.String() == "" {
			if stageIndex == 0 {
				newStatus = sdk.StatusWaiting.String()
			}

			stage.Status = sdk.StatusWaiting
			//Add job to Queue
			//Insert data in workflow_node_run_job
			log.Debug("workflow.execute> stage %s call addJobsToQueue", stage.Name)
			if err := addJobsToQueue(db, stage, n); err != nil {
				return err
			}
			if stage.Status == sdk.StatusSkipped || stage.Status == sdk.StatusDisabled {
				continue
			}
			break
		}

		//If stage is waiting, nothing to do
		if stage.Status == sdk.StatusWaiting {
			log.Debug("workflow.execute> stage %s status:%s - nothing to do", stage.Name, stage.Status)
			break
		}

		if stage.Status == sdk.StatusBuilding {
			newStatus = sdk.StatusBuilding.String()

			var end bool
			end, errSync := syncStage(db, store, stage)
			if errSync != nil {
				return errSync
			}
			if !end {
				break
			} else {
				//The stage is over
				if stage.Status == sdk.StatusFail {
					n.Done = time.Now()
					newStatus = sdk.StatusFail.String()
					break
				}
				if stageIndex == len(n.Stages)-1 {
					n.Done = time.Now()
					newStatus = sdk.StatusSuccess.String()
					break
				}
				if stageIndex != len(n.Stages)-1 {
					continue
				}
			}
		}
	}

	log.Debug("workflow.execute> status from %s to %s", n.Status, newStatus)
	n.Status = newStatus
	//If the node is over, push the status in the build parameter, so it would be availabe in children build parameters
	if n.Status == sdk.StatusSuccess.String() || n.Status == sdk.StatusFail.String() {
		sdk.AddParameter(&n.BuildParameters, "cds.status", sdk.StringParameter, n.Status)
	}

	// Save the node run in database
	if err := UpdateNodeRun(db, n); err != nil {
		return sdk.WrapError(fmt.Errorf("Unable to update node id=%d at status %s. err:%s", n.ID, n.Status, err), "workflow.execute> Unable to execute node")
	}

	//Reload the workflow
	updatedWorkflowRun, err := LoadRunByID(db, n.WorkflowRunID)
	if err != nil {
		return sdk.WrapError(err, "workflow.execute> Unable to reload workflow run id=%d", n.WorkflowRunID)
	}

	// If pipeline build succeed, reprocess the workflow (in the same transaction)
	//Delete jobs only when node is over
	if n.Status == sdk.StatusSuccess.String() || n.Status == sdk.StatusFail.String() {
		if err := processWorkflowRun(db, store, p, updatedWorkflowRun, nil, nil, nil); err != nil {
			return sdk.WrapError(err, "workflow.execute> Unable to reprocess workflow !")
		}

		//Delete the line in workflow_node_run_job
		if err := DeleteNodeJobRuns(db, n.ID); err != nil {
			return sdk.WrapError(err, "workflow.execute> Unable to delete node %d job runs ", n.ID)
		}
	}

	return nil
}

func addJobsToQueue(db gorp.SqlExecutor, stage *sdk.Stage, run *sdk.WorkflowNodeRun) error {
	log.Debug("addJobsToQueue> add %d in stage %s", run.ID, stage.Name)

	conditionsOK, err := sdk.WorkflowCheckConditions(stage.Conditions(), run.BuildParameters)
	if err != nil {
		return sdk.WrapError(err, "addJobsToQueue> Cannot compute prerequisites on stage %s(%d)", stage.Name, stage.ID)
	}

	if !conditionsOK {
		stage.Status = sdk.StatusSkipped
	}
	if !stage.Enabled {
		stage.Status = sdk.StatusDisabled
	}

	//Browse the jobs
	for j := range stage.Jobs {
		job := &stage.Jobs[j]
		errs := sdk.MultiError{}
		//Process variables for the jobs
		jobParams, errParam := getNodeJobRunParameters(db, *job, run, stage)
		if errParam != nil {
			errs.Join(*errParam)
		}
		jobRequirements, errReq := getNodeJobRunRequirements(db, *job, run)
		if errReq != nil {
			errs.Join(*errReq)
		}
		job.Action.Requirements = jobRequirements

		//Create the job run
		wjob := sdk.WorkflowNodeJobRun{
			WorkflowNodeRunID: run.ID,
			Start:             time.Time{},
			Queued:            time.Now(),
			Status:            sdk.StatusWaiting.String(),
			Parameters:        jobParams,
			Job: sdk.ExecutedJob{
				Job: *job,
			},
		}

		if !stage.Enabled || !wjob.Job.Enabled {
			wjob.Status = sdk.StatusDisabled.String()
		} else if !conditionsOK {
			wjob.Status = sdk.StatusSkipped.String()
		}

		if errParam != nil {
			wjob.Status = sdk.StatusFail.String()
			spawnInfos := sdk.SpawnMsg{
				ID: sdk.MsgSpawnInfoJobError.ID,
			}

			for _, e := range *errParam {
				spawnInfos.Args = append(spawnInfos.Args, e.Error())
			}

			wjob.SpawnInfos = []sdk.SpawnInfo{sdk.SpawnInfo{
				APITime:    time.Now(),
				Message:    spawnInfos,
				RemoteTime: time.Now(),
			}}
		}

		//Insert in database
		if err := insertWorkflowNodeJobRun(db, &wjob); err != nil {
			return sdk.WrapError(err, "addJobsToQueue> Unable to insert in table workflow_node_run_job")
		}

		//Put the job run in database
		event.PublishJobRun(run, &wjob)
		stage.RunJobs = append(stage.RunJobs, wjob)
	}

	return nil
}

func syncStage(db gorp.SqlExecutor, store cache.Store, stage *sdk.Stage) (bool, error) {
	stageEnd := true
	finalStatus := sdk.StatusBuilding

	log.Debug("syncStage> work on stage %s", stage.Name)
	// browse all running jobs
	for indexJob := range stage.RunJobs {
		pbJob := &stage.RunJobs[indexJob]
		// If job is runnning, sync it
		if pbJob.Status == sdk.StatusBuilding.String() || pbJob.Status == sdk.StatusWaiting.String() {
			pbJobDB, errJob := LoadNodeJobRun(db, store, pbJob.ID)
			if errJob != nil {
				return stageEnd, errJob
			}

			if pbJobDB.Status == sdk.StatusBuilding.String() || pbJobDB.Status == sdk.StatusWaiting.String() {
				stageEnd = false
			}

			pbJob.SpawnInfos = pbJobDB.SpawnInfos

			// If same status, sync step status
			if pbJobDB.Status == pbJob.Status {
				pbJob.Job.StepStatus = pbJobDB.Job.StepStatus
			} else {
				pbJob.Status = pbJobDB.Status
				pbJob.Start = pbJobDB.Start
				pbJob.Done = pbJobDB.Done
				pbJob.Model = pbJobDB.Model
				pbJob.Job = pbJobDB.Job
			}
		}
	}
	log.Debug("syncStage> stage %s stageEnd:%t len(stage.RunJobs):%d", stage.Name, stageEnd, len(stage.RunJobs))

	if stageEnd || len(stage.RunJobs) == 0 {
		finalStatus = sdk.StatusSuccess
		stageEnd = true
		// Determine final stage status
	finalStageLoop:
		for _, runJob := range stage.RunJobs {
			switch runJob.Status {
			case sdk.StatusDisabled.String():
				if finalStatus == sdk.StatusBuilding {
					finalStatus = sdk.StatusDisabled
				}
			case sdk.StatusSkipped.String():
				if finalStatus == sdk.StatusBuilding || finalStatus == sdk.StatusDisabled {
					finalStatus = sdk.StatusSkipped
				}
			case sdk.StatusFail.String():
				finalStatus = sdk.StatusFail
				break finalStageLoop
			case sdk.StatusSuccess.String():
				if finalStatus != sdk.StatusFail {
					finalStatus = sdk.StatusSuccess
				}
			}
		}
	}
	log.Debug("syncStage> set stage %s from %s to %s", stage.Name, stage.Status, finalStatus)
	stage.Status = finalStatus
	return stageEnd, nil
}

//NodeBuildParameters returns build_parameters for a node given its id
func NodeBuildParameters(proj *sdk.Project, wf *sdk.Workflow, wr *sdk.WorkflowRun, id int64, u *sdk.User) ([]sdk.Parameter, error) {
	refNode := wf.GetNode(id)
	if refNode == nil {
		return nil, sdk.WrapError(sdk.ErrWorkflowNodeNotFound, "getWorkflowTriggerConditionHandler> Unable to load workflow node")
	}

	res := []sdk.Parameter{}

	if wr != nil {
		for nodeID, nodeRuns := range wr.WorkflowNodeRuns {
			oldNode := wr.Workflow.GetNode(nodeID)
			if oldNode == nil {
				log.Warning("getWorkflowTriggerConditionHandler> Unable to find last run")
				break
			}
			if oldNode.EqualsTo(refNode) {
				for _, p := range nodeRuns[0].BuildParameters {
					sdk.AddParameter(&res, p.Name, p.Type, p.Value)
				}
				break
			}
		}
	}

	if len(res) == 0 {
		var err error
		res, err = GetNodeBuildParameters(proj, wf, refNode, refNode.Context.DefaultPipelineParameters, refNode.Context.DefaultPayload)
		if err != nil {
			return nil, sdk.WrapError(sdk.ErrWorkflowNodeNotFound, "getWorkflowTriggerConditionHandler> Unable to get workflow node parameters: %v", err)
		}
	}

	return res, nil
}

// StopWorkflowNodeRun to stop a workflow node run with a specific spawn info
func StopWorkflowNodeRun(db *gorp.DbMap, store cache.Store, proj *sdk.Project, nodeRun sdk.WorkflowNodeRun, stopInfos sdk.SpawnInfo) error {
	// Load node job run ID
	ids, errIDS := LoadNodeJobRunIDByNodeRunID(db, nodeRun.ID)
	if errIDS != nil {
		return sdk.WrapError(errIDS, "stopWorkflowNodeRunHandler> Cannot load node job run id")
	}

	tx, errT := db.Begin()
	if errT != nil {
		return sdk.WrapError(errT, "StopWorkflowNodeRun> Cannot start transaction")
	}
	defer tx.Rollback()

	for _, nrjID := range ids {
		njr, errNRJ := LoadAndLockNodeJobRunWait(tx, store, nrjID)
		if errNRJ != nil {
			return sdk.WrapError(errNRJ, "StopWorkflowNodeRun> Cannot load node job run id")
		}
		njr.SpawnInfos = append(njr.SpawnInfos, stopInfos)
		if err := UpdateNodeJobRunStatus(tx, store, proj, njr, sdk.StatusStopped); err != nil {
			return sdk.WrapError(err, "StopWorkflowNodeRun> Cannot update node job run")
		}
	}

	if err := updateNodeRunStatus(tx, nodeRun.ID, sdk.StatusStopped.String()); err != nil {
		return sdk.WrapError(err, "StopWorkflowNodeRun> Cannot update node run status")
	}

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

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/foize/go.sgr"
	"github.com/howeyc/fsnotify"
	"go/build"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (
	wd string // working directory

	pkg *build.Package

	monAngo *exec.Cmd // command builds ango on file change (uses gomon)

	// last ango generation
	timeLastGenCodeOverwrite time.Time

	// genLock is write-locked when generated source was detected modified unexpectedly
	genLock sync.RWMutex

	// closed on stop
	stopCh = make(chan bool)
	stopWg sync.WaitGroup
)

const exampleAngoFile = "example/chatService.ango"

type CheckWriter struct {
	wr     io.Writer
	filter string
	action func()
}

func (c *CheckWriter) Write(b []byte) (n int, err error) {
	n, err = c.wr.Write(b)
	if strings.Contains(string(b), c.filter) {
		c.action()
	}
	return n, err
}
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	var err error
	fmt.Println("Starting ango dev tool.")

	wd, err = os.Getwd()
	if err != nil {
		fmt.Printf("Error getting wd: %s\n", err)
		stop(1)
		select {}
	}

	pkg, err = build.ImportDir(wd, 0)
	if err != nil {
		fmt.Printf("Error loading package: %s\n", err)
		stop(1)
		select {}
	}

	if pkg.Name != "main" || !pkg.IsCommand() || filepath.Base(wd) != "ango" {
		fmt.Println("Is tool executed from the right directory? (github.com/GeertJohan/ango or a fork)?")
		fmt.Printf("Current package (%s) is invalid.\n", pkg.Name)
		stop(1)
		select {}
	}

	// watch generated source and lock on modification
	go watchGeneratedSource()

	// watch and rebuild/restart example cmd
	go rerunExample()

	// watch and rebuild ango tool
	go rebuildAngo()

	// watch ango tool and templates and re-generate on change
	go watchExampleAngo()

	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, os.Kill, os.Interrupt)
	sig := <-sigChan
	signal.Stop(sigChan)

	fmt.Printf("Received %s, closing...\n", sig)
	if sig == os.Interrupt {
		stop(0)
	} else {
		stop(1)
	}
	select {}
}

func stop(exitCode int) {
	go func() {
		// synced stop
		close(stopCh)
		stopWg.Wait()

		// os.Exit(..)
		os.Exit(exitCode)
	}()
}

func watchGeneratedSource() {
	stopWg.Add(1)
	defer stopWg.Done()

	haveLine := make(chan bool)
	go func() {
		// clear stdin and read line when required
		for {
			fmt.Scanln()
			select {
			case haveLine <- true:
			default:
			}
		}
	}()

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		fmt.Printf("Error starting watcher: %s\n", err)
		stop(1)
		return
	}
	defer watcher.Close()
	err = watcher.WatchFlags(`example/ango-chatService.gen.go`, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on go generated file: %s\n", err)
		stop(1)
		return
	}
	err = watcher.WatchFlags(`example/http-files/ango-chatService.gen.js`, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on js generated file: %s\n", err)
		stop(1)
		return
	}
	for {
		select {
		case <-stopCh:
			return
		case <-watcher.Event:
			// short timeout consuming another event becuase sublime sometimes saves (modifies) the file twice
			select {
			case <-time.After(100 * time.Millisecond):
			case <-watcher.Event:
			}
			if time.Now().Sub(timeLastGenCodeOverwrite) > time.Duration(3*time.Second) {
				genLock.Lock()
				sgr.Println("[fg-red]Detected modification on generated source. Hit enter to continue devtool (will overwrite changes!).")
				<-haveLine
				timeLastGenCodeOverwrite = time.Now()
				genLock.Unlock()
			}
			select {
			case <-time.After(100 * time.Millisecond):
			case <-watcher.Event:
			}
		case err := <-watcher.Error:
			if err != nil {
				fmt.Printf("Error watching example ango file: %s\n", err)
				stop(1)
				return
			}
		}
	}
}

func rerunExample() {
	stopWg.Add(1)
	defer stopWg.Done()

	cmdRerun := exec.Command("rerun", filepath.Join(pkg.ImportPath, "example"))
	cmdRerun.Stdin = os.Stdin
	cmdRerun.Stdout = sgr.NewColorWriter(os.Stdout, sgr.FgYellow, false)
	cmdRerun.Stderr = sgr.NewColorWriter(os.Stderr, sgr.FgYellow, false)
	err := cmdRerun.Start()
	if err != nil {
		fmt.Printf("Error running rerun example: %s\n", err)
		stop(1)
		return
	}
	<-stopCh
	if cmdRerun.Process != nil {
		cmdRerun.Process.Signal(os.Interrupt)
	}
	err = cmdRerun.Wait()
	if err != nil && err.Error() != "exit status 2" {
		fmt.Printf("Error stopping rerun example: %s\n", err)
		return
	}
}

func rebuildAngo() {
	stopWg.Add(1)
	defer stopWg.Done()

	cmdRerun := exec.Command("rerun", "--no-run", pkg.ImportPath)
	cmdRerun.Stdin = os.Stdin
	cw := &CheckWriter{
		wr:     os.Stderr,
		filter: "build passed",
		action: func() {
			angoExample()
		},
	}
	cmdRerun.Stdout = sgr.NewColorWriter(os.Stdout, sgr.FgCyan, false)
	cmdRerun.Stderr = sgr.NewColorWriter(cw, sgr.FgCyan, false)
	err := cmdRerun.Start()
	if err != nil {
		fmt.Printf("Error running rerun ango build: %s\n", err)
		stop(1)
		return
	}
	<-stopCh
	if cmdRerun.Process != nil {
		cmdRerun.Process.Signal(os.Interrupt)
	}
	err = cmdRerun.Wait()
	if err != nil && err.Error() != "exit status 2" {
		fmt.Printf("Error stopping rerun ango build: %s\n", err)
		return
	}
}

func watchExampleAngo() {
	stopWg.Add(1)
	defer stopWg.Done()

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		fmt.Printf("Error starting watcher: %s\n", err)
		stop(1)
		return
	}
	defer watcher.Close()
	err = watcher.WatchFlags(exampleAngoFile, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on example ango file: %s\n", err)
		stop(1)
		return
	}
	err = watcher.WatchFlags(`templates/ango-service.tmpl.go`, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on go template file: %s\n", err)
		stop(1)
		return
	}
	err = watcher.WatchFlags(`templates/ango-service.tmpl.js`, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on js template file: %s\n", err)
		stop(1)
		return
	}
	for {
		select {
		case <-stopCh:
			return
		case <-watcher.Event:
			// short timeout consuming other events becuase sublime sometimes saves (modifies) the file twice
			for {
				select {
				case <-time.After(100 * time.Millisecond):
					goto ango
				case <-watcher.Event:
				}
			}
		ango:
			angoExample()
			// short timeout consuming another event because somehow ango modifies the template files!?!?!?!?
			select {
			case <-time.After(100 * time.Millisecond):
			case <-watcher.Event:
			}
		case err := <-watcher.Error:
			if err != nil {
				fmt.Printf("Error watching example ango file: %s\n", err)
				stop(1)
				return
			}
		}
	}
}

func angoExample() {
	stopWg.Add(1)
	defer stopWg.Done()
	genLock.RLock()
	defer genLock.RUnlock()
	timeLastGenCodeOverwrite = time.Now()
	fmt.Println("Running ango tool for example/chatService.ango")
	cmdAngoExample := exec.Command(filepath.Join(wd, "ango"), "--verbose", "-i", exampleAngoFile, "--js", "example/http-files", "--go", "example", "--force-overwrite")
	cmdAngoExample.Stdin = os.Stdin
	cmdAngoExample.Stdout = sgr.NewColorWriter(os.Stdout, sgr.FgBlue, false)
	cmdAngoExample.Stderr = sgr.NewColorWriter(os.Stderr, sgr.FgBlue, false)
	err := cmdAngoExample.Run()
	if err != nil {
		fmt.Printf("Error running ango tool: %s\n", err)
	}
}
<commit_msg>Use new rerun usage flags<commit_after>package main

import (
	"fmt"
	"github.com/foize/go.sgr"
	"github.com/howeyc/fsnotify"
	"go/build"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (
	wd string // working directory

	pkg *build.Package

	monAngo *exec.Cmd // command builds ango on file change (uses gomon)

	// last ango generation
	timeLastGenCodeOverwrite time.Time

	// genLock is write-locked when generated source was detected modified unexpectedly
	genLock sync.RWMutex

	// closed on stop
	stopCh = make(chan bool)
	stopWg sync.WaitGroup
)

const exampleAngoFile = "example/chatService.ango"

type CheckWriter struct {
	wr     io.Writer
	filter string
	action func()
}

func (c *CheckWriter) Write(b []byte) (n int, err error) {
	n, err = c.wr.Write(b)
	if strings.Contains(string(b), c.filter) {
		c.action()
	}
	return n, err
}
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	var err error
	fmt.Println("Starting ango dev tool.")

	wd, err = os.Getwd()
	if err != nil {
		fmt.Printf("Error getting wd: %s\n", err)
		stop(1)
		select {}
	}

	pkg, err = build.ImportDir(wd, 0)
	if err != nil {
		fmt.Printf("Error loading package: %s\n", err)
		stop(1)
		select {}
	}

	if pkg.Name != "main" || !pkg.IsCommand() || filepath.Base(wd) != "ango" {
		fmt.Println("Is tool executed from the right directory? (github.com/GeertJohan/ango or a fork)?")
		fmt.Printf("Current package (%s) is invalid.\n", pkg.Name)
		stop(1)
		select {}
	}

	// watch generated source and lock on modification
	go watchGeneratedSource()

	// watch and rebuild/restart example cmd
	go rerunExample()

	// watch and rebuild ango tool
	go rebuildAngo()

	// watch ango tool and templates and re-generate on change
	go watchExampleAngo()

	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, os.Kill, os.Interrupt)
	sig := <-sigChan
	signal.Stop(sigChan)

	fmt.Printf("Received %s, closing...\n", sig)
	if sig == os.Interrupt {
		stop(0)
	} else {
		stop(1)
	}
	select {}
}

func stop(exitCode int) {
	go func() {
		// synced stop
		close(stopCh)
		stopWg.Wait()

		// os.Exit(..)
		os.Exit(exitCode)
	}()
}

func watchGeneratedSource() {
	stopWg.Add(1)
	defer stopWg.Done()

	haveLine := make(chan bool)
	go func() {
		// clear stdin and read line when required
		for {
			fmt.Scanln()
			select {
			case haveLine <- true:
			default:
			}
		}
	}()

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		fmt.Printf("Error starting watcher: %s\n", err)
		stop(1)
		return
	}
	defer watcher.Close()
	err = watcher.WatchFlags(`example/ango-chatService.gen.go`, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on go generated file: %s\n", err)
		stop(1)
		return
	}
	err = watcher.WatchFlags(`example/http-files/ango-chatService.gen.js`, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on js generated file: %s\n", err)
		stop(1)
		return
	}
	for {
		select {
		case <-stopCh:
			return
		case <-watcher.Event:
			// short timeout consuming another event becuase sublime sometimes saves (modifies) the file twice
			select {
			case <-time.After(100 * time.Millisecond):
			case <-watcher.Event:
			}
			if time.Now().Sub(timeLastGenCodeOverwrite) > time.Duration(3*time.Second) {
				genLock.Lock()
				sgr.Println("[fg-red]Detected modification on generated source. Hit enter to continue devtool (will overwrite changes!).")
				<-haveLine
				timeLastGenCodeOverwrite = time.Now()
				genLock.Unlock()
			}
			select {
			case <-time.After(100 * time.Millisecond):
			case <-watcher.Event:
			}
		case err := <-watcher.Error:
			if err != nil {
				fmt.Printf("Error watching example ango file: %s\n", err)
				stop(1)
				return
			}
		}
	}
}

func rerunExample() {
	stopWg.Add(1)
	defer stopWg.Done()

	cmdRerun := exec.Command("rerun", filepath.Join(pkg.ImportPath, "example"))
	cmdRerun.Stdin = os.Stdin
	cmdRerun.Stdout = sgr.NewColorWriter(os.Stdout, sgr.FgYellow, false)
	cmdRerun.Stderr = sgr.NewColorWriter(os.Stderr, sgr.FgYellow, false)
	err := cmdRerun.Start()
	if err != nil {
		fmt.Printf("Error running rerun example: %s\n", err)
		stop(1)
		return
	}
	<-stopCh
	if cmdRerun.Process != nil {
		cmdRerun.Process.Signal(os.Interrupt)
	}
	err = cmdRerun.Wait()
	if err != nil && err.Error() != "exit status 2" {
		fmt.Printf("Error stopping rerun example: %s\n", err)
		return
	}
}

func rebuildAngo() {
	stopWg.Add(1)
	defer stopWg.Done()

	cmdRerun := exec.Command("rerun", "--no-run", "--build", pkg.ImportPath)
	cmdRerun.Stdin = os.Stdin
	cw := &CheckWriter{
		wr:     os.Stderr,
		filter: "build passed",
		action: func() {
			angoExample()
		},
	}
	cmdRerun.Stdout = sgr.NewColorWriter(os.Stdout, sgr.FgCyan, false)
	cmdRerun.Stderr = sgr.NewColorWriter(cw, sgr.FgCyan, false)
	err := cmdRerun.Start()
	if err != nil {
		fmt.Printf("Error running rerun ango build: %s\n", err)
		stop(1)
		return
	}
	<-stopCh
	if cmdRerun.Process != nil {
		cmdRerun.Process.Signal(os.Interrupt)
	}
	err = cmdRerun.Wait()
	if err != nil && err.Error() != "exit status 2" {
		fmt.Printf("Error stopping rerun ango build: %s\n", err)
		return
	}
}

func watchExampleAngo() {
	stopWg.Add(1)
	defer stopWg.Done()

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		fmt.Printf("Error starting watcher: %s\n", err)
		stop(1)
		return
	}
	defer watcher.Close()
	err = watcher.WatchFlags(exampleAngoFile, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on example ango file: %s\n", err)
		stop(1)
		return
	}
	err = watcher.WatchFlags(`templates/ango-service.tmpl.go`, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on go template file: %s\n", err)
		stop(1)
		return
	}
	err = watcher.WatchFlags(`templates/ango-service.tmpl.js`, fsnotify.FSN_MODIFY)
	if err != nil {
		fmt.Printf("Error starting watch on js template file: %s\n", err)
		stop(1)
		return
	}
	for {
		select {
		case <-stopCh:
			return
		case <-watcher.Event:
			// short timeout consuming other events becuase sublime sometimes saves (modifies) the file twice
			for {
				select {
				case <-time.After(100 * time.Millisecond):
					goto ango
				case <-watcher.Event:
				}
			}
		ango:
			angoExample()
			// short timeout consuming another event because somehow ango modifies the template files!?!?!?!?
			select {
			case <-time.After(100 * time.Millisecond):
			case <-watcher.Event:
			}
		case err := <-watcher.Error:
			if err != nil {
				fmt.Printf("Error watching example ango file: %s\n", err)
				stop(1)
				return
			}
		}
	}
}

func angoExample() {
	stopWg.Add(1)
	defer stopWg.Done()
	genLock.RLock()
	defer genLock.RUnlock()
	timeLastGenCodeOverwrite = time.Now()
	fmt.Println("Running ango tool for example/chatService.ango")
	cmdAngoExample := exec.Command(filepath.Join(wd, "ango"), "--verbose", "-i", exampleAngoFile, "--js", "example/http-files", "--go", "example", "--force-overwrite")
	cmdAngoExample.Stdin = os.Stdin
	cmdAngoExample.Stdout = sgr.NewColorWriter(os.Stdout, sgr.FgBlue, false)
	cmdAngoExample.Stderr = sgr.NewColorWriter(os.Stderr, sgr.FgBlue, false)
	err := cmdAngoExample.Run()
	if err != nil {
		fmt.Printf("Error running ango tool: %s\n", err)
	}
}
<|endoftext|>


<commit_before>/*
In this file we handle 'git archive' downloads
*/

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"time"
)

func handleGetArchive(w http.ResponseWriter, r *gitRequest) {
	var format string
	urlPath := r.URL.Path
	switch filepath.Base(urlPath) {
	case "archive.zip":
		format = "zip"
	case "archive.tar":
		format = "tar"
	case "archive", "archive.tar.gz":
		format = "tar.gz"
	case "archive.tar.bz2":
		format = "tar.bz2"
	default:
		fail500(w, fmt.Errorf("handleGetArchive: invalid format: %s", urlPath))
	}

	archiveFilename := path.Base(r.ArchivePath)

	if cachedArchive, err := os.Open(r.ArchivePath); err == nil {
		defer cachedArchive.Close()
		log.Printf("Serving cached file %q", r.ArchivePath)
		setArchiveHeaders(w, format, archiveFilename)
		// Even if somebody deleted the cachedArchive from disk since we opened
		// the file, Unix file semantics guarantee we can still read from the
		// open file in this process.
		http.ServeContent(w, r.Request, "", time.Unix(0, 0), cachedArchive)
		return
	}

	// We assume the tempFile has a unique name so that concurrent requests are
	// safe. We create the tempfile in the same directory as the final cached
	// archive we want to create so that we can use an atomic link(2) operation
	// to finalize the cached archive.
	tempFile, err := prepareArchiveTempfile(path.Dir(r.ArchivePath), archiveFilename)
	if err != nil {
		fail500(w, fmt.Errorf("handleGetArchive: create tempfile: %v", err))
	}
	defer tempFile.Close()
	defer os.Remove(tempFile.Name())

	compressCmd, archiveFormat := parseArchiveFormat(format)

	archiveCmd := gitCommand("", "git", "--git-dir="+r.RepoPath, "archive", "--format="+archiveFormat, "--prefix="+r.ArchivePrefix+"/", r.CommitId)
	archiveStdout, err := archiveCmd.StdoutPipe()
	if err != nil {
		fail500(w, fmt.Errorf("handleGetArchive: archive stdout: %v", err))
		return
	}
	defer archiveStdout.Close()
	if err := archiveCmd.Start(); err != nil {
		fail500(w, fmt.Errorf("handleGetArchive: start %v: %v", archiveCmd.Args, err))
		return
	}
	defer cleanUpProcessGroup(archiveCmd) // Ensure brute force subprocess clean-up

	var stdout io.ReadCloser
	if compressCmd == nil {
		stdout = archiveStdout
	} else {
		compressCmd.Stdin = archiveStdout

		stdout, err = compressCmd.StdoutPipe()
		if err != nil {
			fail500(w, fmt.Errorf("handleGetArchive: compress stdout: %v", err))
			return
		}
		defer stdout.Close()

		if err := compressCmd.Start(); err != nil {
			fail500(w, fmt.Errorf("handleGetArchive: start %v: %v", compressCmd.Args, err))
			return
		}
		defer compressCmd.Wait()

		archiveStdout.Close()
	}
	// Every Read() from stdout will be synchronously written to tempFile
	// before it comes out the TeeReader.
	archiveReader := io.TeeReader(stdout, tempFile)

	// Start writing the response
	setArchiveHeaders(w, format, archiveFilename)
	w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just return
	if _, err := io.Copy(w, archiveReader); err != nil {
		log.Printf("handleGetArchive: read: %v", err)
		return
	}
	if err := archiveCmd.Wait(); err != nil {
		log.Printf("handleGetArchive: archiveCmd: %v", err)
		return
	}
	if compressCmd != nil {
		if err := compressCmd.Wait(); err != nil {
			log.Printf("handleGetArchive: compressCmd: %v", err)
			return
		}
	}

	if err := finalizeCachedArchive(tempFile, r.ArchivePath); err != nil {
		log.Printf("handleGetArchive: finalize cached archive: %v", err)
		return
	}
}

func setArchiveHeaders(w http.ResponseWriter, format string, archiveFilename string) {
	w.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, archiveFilename))
	if format == "zip" {
		w.Header().Add("Content-Type", "application/zip")
	} else {
		w.Header().Add("Content-Type", "application/octet-stream")
	}
	w.Header().Add("Content-Transfer-Encoding", "binary")
	w.Header().Add("Cache-Control", "private")
}

func parseArchiveFormat(format string) (*exec.Cmd, string) {
	switch format {
	case "tar":
		return nil, "tar"
	case "tar.gz":
		return exec.Command("gzip", "-c", "-n"), "tar"
	case "tar.bz2":
		return exec.Command("bzip2", "-c"), "tar"
	case "zip":
		return nil, "zip"
	}
	return nil, "unknown"
}

func prepareArchiveTempfile(dir string, prefix string) (*os.File, error) {
	if err := os.MkdirAll(dir, 0700); err != nil {
		return nil, err
	}
	return ioutil.TempFile(dir, prefix)
}

func finalizeCachedArchive(tempFile *os.File, archivePath string) error {
	if err := tempFile.Close(); err != nil {
		return err
	}
	return os.Link(tempFile.Name(), archivePath)
}
<commit_msg>Add missing returns<commit_after>/*
In this file we handle 'git archive' downloads
*/

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"time"
)

func handleGetArchive(w http.ResponseWriter, r *gitRequest) {
	var format string
	urlPath := r.URL.Path
	switch filepath.Base(urlPath) {
	case "archive.zip":
		format = "zip"
	case "archive.tar":
		format = "tar"
	case "archive", "archive.tar.gz":
		format = "tar.gz"
	case "archive.tar.bz2":
		format = "tar.bz2"
	default:
		fail500(w, fmt.Errorf("handleGetArchive: invalid format: %s", urlPath))
		return
	}

	archiveFilename := path.Base(r.ArchivePath)

	if cachedArchive, err := os.Open(r.ArchivePath); err == nil {
		defer cachedArchive.Close()
		log.Printf("Serving cached file %q", r.ArchivePath)
		setArchiveHeaders(w, format, archiveFilename)
		// Even if somebody deleted the cachedArchive from disk since we opened
		// the file, Unix file semantics guarantee we can still read from the
		// open file in this process.
		http.ServeContent(w, r.Request, "", time.Unix(0, 0), cachedArchive)
		return
	}

	// We assume the tempFile has a unique name so that concurrent requests are
	// safe. We create the tempfile in the same directory as the final cached
	// archive we want to create so that we can use an atomic link(2) operation
	// to finalize the cached archive.
	tempFile, err := prepareArchiveTempfile(path.Dir(r.ArchivePath), archiveFilename)
	if err != nil {
		fail500(w, fmt.Errorf("handleGetArchive: create tempfile: %v", err))
		return
	}
	defer tempFile.Close()
	defer os.Remove(tempFile.Name())

	compressCmd, archiveFormat := parseArchiveFormat(format)

	archiveCmd := gitCommand("", "git", "--git-dir="+r.RepoPath, "archive", "--format="+archiveFormat, "--prefix="+r.ArchivePrefix+"/", r.CommitId)
	archiveStdout, err := archiveCmd.StdoutPipe()
	if err != nil {
		fail500(w, fmt.Errorf("handleGetArchive: archive stdout: %v", err))
		return
	}
	defer archiveStdout.Close()
	if err := archiveCmd.Start(); err != nil {
		fail500(w, fmt.Errorf("handleGetArchive: start %v: %v", archiveCmd.Args, err))
		return
	}
	defer cleanUpProcessGroup(archiveCmd) // Ensure brute force subprocess clean-up

	var stdout io.ReadCloser
	if compressCmd == nil {
		stdout = archiveStdout
	} else {
		compressCmd.Stdin = archiveStdout

		stdout, err = compressCmd.StdoutPipe()
		if err != nil {
			fail500(w, fmt.Errorf("handleGetArchive: compress stdout: %v", err))
			return
		}
		defer stdout.Close()

		if err := compressCmd.Start(); err != nil {
			fail500(w, fmt.Errorf("handleGetArchive: start %v: %v", compressCmd.Args, err))
			return
		}
		defer compressCmd.Wait()

		archiveStdout.Close()
	}
	// Every Read() from stdout will be synchronously written to tempFile
	// before it comes out the TeeReader.
	archiveReader := io.TeeReader(stdout, tempFile)

	// Start writing the response
	setArchiveHeaders(w, format, archiveFilename)
	w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just return
	if _, err := io.Copy(w, archiveReader); err != nil {
		log.Printf("handleGetArchive: read: %v", err)
		return
	}
	if err := archiveCmd.Wait(); err != nil {
		log.Printf("handleGetArchive: archiveCmd: %v", err)
		return
	}
	if compressCmd != nil {
		if err := compressCmd.Wait(); err != nil {
			log.Printf("handleGetArchive: compressCmd: %v", err)
			return
		}
	}

	if err := finalizeCachedArchive(tempFile, r.ArchivePath); err != nil {
		log.Printf("handleGetArchive: finalize cached archive: %v", err)
		return
	}
}

func setArchiveHeaders(w http.ResponseWriter, format string, archiveFilename string) {
	w.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, archiveFilename))
	if format == "zip" {
		w.Header().Add("Content-Type", "application/zip")
	} else {
		w.Header().Add("Content-Type", "application/octet-stream")
	}
	w.Header().Add("Content-Transfer-Encoding", "binary")
	w.Header().Add("Cache-Control", "private")
}

func parseArchiveFormat(format string) (*exec.Cmd, string) {
	switch format {
	case "tar":
		return nil, "tar"
	case "tar.gz":
		return exec.Command("gzip", "-c", "-n"), "tar"
	case "tar.bz2":
		return exec.Command("bzip2", "-c"), "tar"
	case "zip":
		return nil, "zip"
	}
	return nil, "unknown"
}

func prepareArchiveTempfile(dir string, prefix string) (*os.File, error) {
	if err := os.MkdirAll(dir, 0700); err != nil {
		return nil, err
	}
	return ioutil.TempFile(dir, prefix)
}

func finalizeCachedArchive(tempFile *os.File, archivePath string) error {
	if err := tempFile.Close(); err != nil {
		return err
	}
	return os.Link(tempFile.Name(), archivePath)
}
<|endoftext|>


<commit_before>package virt

import (
	"bytes"
	"context"
	"encoding/json"
	"io"
	"strings"

	virttypes "github.com/projecteru2/libyavirt/types"

	enginetypes "github.com/projecteru2/core/engine/types"
	"github.com/projecteru2/core/log"
	"github.com/projecteru2/core/types"
)

// ImageList lists images.
func (v *Virt) ImageList(ctx context.Context, imageName string) (imgs []*enginetypes.Image, err error) {
	images, err := v.client.ListImage(ctx, imageName)
	if err != nil {
		return nil, err
	}

	imgs = []*enginetypes.Image{}

	for _, image := range images {
		imgs = append(imgs, &enginetypes.Image{
			ID:   image.Id,
			Tags: []string{image.Name},
		})
	}

	return
}

// ImageRemove removes a specific image.
func (v *Virt) ImageRemove(ctx context.Context, tag string, force, prune bool) (names []string, err error) {
	user, imgName, err := splitUserImage(tag)
	if err != nil {
		return nil, err
	}

	return v.client.RemoveImage(ctx, imgName, user, force, prune)
}

// ImagesPrune prunes one.
func (v *Virt) ImagesPrune(ctx context.Context) (err error) {
	log.Warnf(ctx, "ImagesPrune does not implement")
	return
}

// ImagePull pulls an image to local virt-node.
func (v *Virt) ImagePull(ctx context.Context, ref string, all bool) (rc io.ReadCloser, err error) {
	// ref is a simple image name without username for now
	_, imgName, err := splitUserImage(ref)
	if err != nil {
		return nil, err
	}

	msg, err := v.client.PullImage(ctx, imgName, all)
	if err != nil {
		return nil, err
	}

	rc = io.NopCloser(strings.NewReader(msg))
	defer rc.Close()

	return rc, err
}

// ImagePush pushes to central image registry.
func (v *Virt) ImagePush(ctx context.Context, ref string) (rc io.ReadCloser, err error) {
	user, imgName, err := splitUserImage(ref)
	if err != nil {
		return nil, err
	}

	msg, err := v.client.PushImage(ctx, imgName, user)
	if err != nil {
		return nil, err
	}

	reply, err := json.Marshal(&types.BuildImageMessage{Error: msg})
	if err != nil {
		return nil, err
	}

	rc = io.NopCloser(bytes.NewReader(reply))
	defer rc.Close()

	return rc, nil
}

// ImageBuild captures from a guest.
func (v *Virt) ImageBuild(ctx context.Context, input io.Reader, refs []string) (rc io.ReadCloser, err error) {
	log.Warnf(ctx, "imageBuild does not implement")
	return
}

// ImageBuildFromExist builds vm image from running vm
func (v *Virt) ImageBuildFromExist(ctx context.Context, ID string, refs []string, user string) (string, error) {
	if len(user) < 1 {
		return "", types.ErrNoImageUser
	}
	if len(refs) != 1 {
		return "", types.ErrBadRefs
	}

	_, imgName, err := splitUserImage(refs[0])
	if err != nil {
		return "", err
	}

	req := virttypes.CaptureGuestReq{Name: imgName, User: user}
	req.ID = ID

	uimg, err := v.client.CaptureGuest(ctx, req)
	if err != nil {
		return "", err
	}

	return uimg.ID, nil
}

// ImageBuildCachePrune prunes cached one.
func (v *Virt) ImageBuildCachePrune(ctx context.Context, all bool) (reclaimed uint64, err error) {
	log.Warnf(ctx, "ImageBuildCachePrune does not implement and not required by vm")
	return
}

// ImageLocalDigests shows local images' digests.
// If local image file not exists return error
// If exists return digests
// Same for remote digest
func (v *Virt) ImageLocalDigests(ctx context.Context, image string) ([]string, error) {
	// If not exists return error
	// If exists return digests
	_, imgName, err := splitUserImage(image)
	if err != nil {
		return nil, err
	}

	return v.client.DigestImage(ctx, imgName, true)
}

// ImageRemoteDigest shows remote one's digest.
func (v *Virt) ImageRemoteDigest(ctx context.Context, image string) (string, error) {
	_, imgName, err := splitUserImage(image)
	if err != nil {
		return "", err
	}

	digests, err := v.client.DigestImage(ctx, imgName, true)
	switch {
	case err != nil:
		return "", err
	case len(digests) < 1:
		return "", types.ErrNoRemoteDigest
	default:
		return digests[0], nil
	}
}
<commit_msg>enable remote digest (#564)<commit_after>package virt

import (
	"bytes"
	"context"
	"encoding/json"
	"io"
	"strings"

	virttypes "github.com/projecteru2/libyavirt/types"

	enginetypes "github.com/projecteru2/core/engine/types"
	"github.com/projecteru2/core/log"
	"github.com/projecteru2/core/types"
)

// ImageList lists images.
func (v *Virt) ImageList(ctx context.Context, imageName string) (imgs []*enginetypes.Image, err error) {
	images, err := v.client.ListImage(ctx, imageName)
	if err != nil {
		return nil, err
	}

	imgs = []*enginetypes.Image{}

	for _, image := range images {
		imgs = append(imgs, &enginetypes.Image{
			ID:   image.Id,
			Tags: []string{image.Name},
		})
	}

	return
}

// ImageRemove removes a specific image.
func (v *Virt) ImageRemove(ctx context.Context, tag string, force, prune bool) (names []string, err error) {
	user, imgName, err := splitUserImage(tag)
	if err != nil {
		return nil, err
	}

	return v.client.RemoveImage(ctx, imgName, user, force, prune)
}

// ImagesPrune prunes one.
func (v *Virt) ImagesPrune(ctx context.Context) (err error) {
	log.Warnf(ctx, "ImagesPrune does not implement")
	return
}

// ImagePull pulls an image to local virt-node.
func (v *Virt) ImagePull(ctx context.Context, ref string, all bool) (rc io.ReadCloser, err error) {
	// ref is a simple image name without username for now
	_, imgName, err := splitUserImage(ref)
	if err != nil {
		return nil, err
	}

	msg, err := v.client.PullImage(ctx, imgName, all)
	if err != nil {
		return nil, err
	}

	rc = io.NopCloser(strings.NewReader(msg))
	defer rc.Close()

	return rc, err
}

// ImagePush pushes to central image registry.
func (v *Virt) ImagePush(ctx context.Context, ref string) (rc io.ReadCloser, err error) {
	user, imgName, err := splitUserImage(ref)
	if err != nil {
		return nil, err
	}

	msg, err := v.client.PushImage(ctx, imgName, user)
	if err != nil {
		return nil, err
	}

	reply, err := json.Marshal(&types.BuildImageMessage{Error: msg})
	if err != nil {
		return nil, err
	}

	rc = io.NopCloser(bytes.NewReader(reply))
	defer rc.Close()

	return rc, nil
}

// ImageBuild captures from a guest.
func (v *Virt) ImageBuild(ctx context.Context, input io.Reader, refs []string) (rc io.ReadCloser, err error) {
	log.Warnf(ctx, "imageBuild does not implement")
	return
}

// ImageBuildFromExist builds vm image from running vm
func (v *Virt) ImageBuildFromExist(ctx context.Context, ID string, refs []string, user string) (string, error) {
	if len(user) < 1 {
		return "", types.ErrNoImageUser
	}
	if len(refs) != 1 {
		return "", types.ErrBadRefs
	}

	_, imgName, err := splitUserImage(refs[0])
	if err != nil {
		return "", err
	}

	req := virttypes.CaptureGuestReq{Name: imgName, User: user}
	req.ID = ID

	uimg, err := v.client.CaptureGuest(ctx, req)
	if err != nil {
		return "", err
	}

	return uimg.ID, nil
}

// ImageBuildCachePrune prunes cached one.
func (v *Virt) ImageBuildCachePrune(ctx context.Context, all bool) (reclaimed uint64, err error) {
	log.Warnf(ctx, "ImageBuildCachePrune does not implement and not required by vm")
	return
}

// ImageLocalDigests shows local images' digests.
// If local image file not exists return error
// If exists return digests
// Same for remote digest
func (v *Virt) ImageLocalDigests(ctx context.Context, image string) ([]string, error) {
	// If not exists return error
	// If exists return digests
	_, imgName, err := splitUserImage(image)
	if err != nil {
		return nil, err
	}

	return v.client.DigestImage(ctx, imgName, true)
}

// ImageRemoteDigest shows remote one's digest.
func (v *Virt) ImageRemoteDigest(ctx context.Context, image string) (string, error) {
	_, imgName, err := splitUserImage(image)
	if err != nil {
		return "", err
	}

	digests, err := v.client.DigestImage(ctx, imgName, false)
	switch {
	case err != nil:
		return "", err
	case len(digests) < 1:
		return "", types.ErrNoRemoteDigest
	default:
		return digests[0], nil
	}
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"encoding/json"
	"fmt"
	"mig.ninja/mig"
	"time"
)

// Given an agent queue location queueLoc, send kill actions for duplicate
// agents. Where multiple agents exist on the same host, we will attempt
// to kill the older agents.
func killDupAgents(queueLoc string, ctx Context) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("killDupAgents() -> %v", e)
		}
		ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: "leaving killDupAgents()"}.Debug()
	}()
	hbfreq, err := time.ParseDuration(ctx.Agent.HeartbeatFreq)
	if err != nil {
		return err
	}
	pointInTime := time.Now().Add(-hbfreq)
	agents, err := ctx.DB.ActiveAgentsByQueue(queueLoc, pointInTime)
	agentsCount := len(agents)
	if agentsCount < 2 {
		return
	}
	destroyedAgents := 0
	leftAloneAgents := 0
	for _, agent := range agents {
		if agent.Status == "destroyed" {
			// if the agent has already been marked as destroyed, check if
			// that was done longer than 3 heartbeats ago. If it did, the
			// destruction failed, and we need to reissue a destruction order
			hbFreq, err := time.ParseDuration(ctx.Agent.HeartbeatFreq)
			if err != nil {
				panic(err)
			}
			pointInTime := time.Now().Add(-hbFreq * 3)
			if agent.DestructionTime.Before(pointInTime) {
				err = issueKillAction(agent, ctx)
				if err != nil {
					panic(err)
				}
				destroyedAgents++
				desc := fmt.Sprintf("Re-issuing destruction action for "+
					"agent '%s' with PID '%d'.", agent.Name, agent.PID)
				ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: desc}.Debug()
			} else {
				leftAloneAgents++
			}
		}
	}

	remainingAgents := agentsCount - destroyedAgents - leftAloneAgents
	if remainingAgents > 1 {
		// there's still some agents left. if killdupagents is set, issue kill orders
		if ctx.Agent.KillDupAgents {
			oldest := agents[0]
			for _, agent := range agents {
				if agent.Status != "online" {
					continue
				}
				if agent.StartTime.Before(oldest.StartTime) {
					oldest = agent
				}
			}
			desc := fmt.Sprintf("Issuing destruction action for agent '%s' "+
				"with PID '%d'.", oldest.Name, oldest.PID)
			ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: desc}
			err = issueKillAction(oldest, ctx)
			if err != nil {
				panic(err)
			}
			// throttling to prevent issuing too many kill orders at the same time
			time.Sleep(5 * time.Second)
		} else {
			desc := fmt.Sprintf("found '%d' agents running on '%s'. Require "+
				"manual inspection.", remainingAgents, queueLoc)
			ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: desc}.Warning()
		}
	}
	return
}

// issueKillAction issues an `agentdestroy` action targeted to a specific agent
// and updates the status of the agent in the database
func issueKillAction(agent mig.Agent, ctx Context) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("issueKillAction() -> %v", e)
		}
		ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: "leaving issueKillAction()"}.Debug()
	}()
	// generate an `agentdestroy` action for this agent
	killAction := mig.Action{
		ID:            mig.GenID(),
		Name:          fmt.Sprintf("Kill agent %s", agent.Name),
		Target:        fmt.Sprintf("queueloc='%s'", agent.QueueLoc),
		ValidFrom:     time.Now().Add(-60 * time.Second).UTC(),
		ExpireAfter:   time.Now().Add(30 * time.Minute).UTC(),
		SyntaxVersion: 2,
	}
	var opparams struct {
		PID     int    `json:"pid"`
		Version string `json:"version"`
	}
	opparams.PID = agent.PID
	opparams.Version = agent.Version
	killOperation := mig.Operation{
		Module:     "agentdestroy",
		Parameters: opparams,
	}
	killAction.Operations = append(killAction.Operations, killOperation)

	// sign the action with the scheduler PGP key
	secring, err := getSecring(ctx)
	if err != nil {
		panic(err)
	}
	pgpsig, err := killAction.Sign(ctx.PGP.PrivKeyID, secring)
	if err != nil {
		panic(err)
	}
	killAction.PGPSignatures = append(killAction.PGPSignatures, pgpsig)
	var jsonAction []byte
	jsonAction, err = json.Marshal(killAction)
	if err != nil {
		panic(err)
	}

	// write the action to the spool for scheduling
	dest := fmt.Sprintf("%s/%.0f.json", ctx.Directories.Action.New, killAction.ID)
	err = safeWrite(ctx, dest, jsonAction)
	if err != nil {
		panic(err)
	}

	// mark the agent as `destroyed` in the database
	err = ctx.DB.MarkAgentDestroyed(agent)
	if err != nil {
		panic(err)
	}
	ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("issued kill action for agent '%s' "+
		"with PID '%d'", agent.Name, agent.PID)}.Warning()
	return
}
<commit_msg>[minor] dup agent manual inspect notice, include agent names<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
package main

import (
	"encoding/json"
	"fmt"
	"mig.ninja/mig"
	"time"
)

// Given an agent queue location queueLoc, send kill actions for duplicate
// agents. Where multiple agents exist on the same host, we will attempt
// to kill the older agents.
func killDupAgents(queueLoc string, ctx Context) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("killDupAgents() -> %v", e)
		}
		ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: "leaving killDupAgents()"}.Debug()
	}()
	hbfreq, err := time.ParseDuration(ctx.Agent.HeartbeatFreq)
	if err != nil {
		return err
	}
	pointInTime := time.Now().Add(-hbfreq)
	agents, err := ctx.DB.ActiveAgentsByQueue(queueLoc, pointInTime)
	agentsCount := len(agents)
	if agentsCount < 2 {
		return
	}
	destroyedAgents := 0
	leftAloneAgents := 0
	for _, agent := range agents {
		if agent.Status == "destroyed" {
			// if the agent has already been marked as destroyed, check if
			// that was done longer than 3 heartbeats ago. If it did, the
			// destruction failed, and we need to reissue a destruction order
			hbFreq, err := time.ParseDuration(ctx.Agent.HeartbeatFreq)
			if err != nil {
				panic(err)
			}
			pointInTime := time.Now().Add(-hbFreq * 3)
			if agent.DestructionTime.Before(pointInTime) {
				err = issueKillAction(agent, ctx)
				if err != nil {
					panic(err)
				}
				destroyedAgents++
				desc := fmt.Sprintf("Re-issuing destruction action for "+
					"agent '%s' with PID '%d'.", agent.Name, agent.PID)
				ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: desc}.Debug()
			} else {
				leftAloneAgents++
			}
		}
	}

	remainingAgents := agentsCount - destroyedAgents - leftAloneAgents
	if remainingAgents > 1 {
		// there's still some agents left. if killdupagents is set, issue kill orders
		if ctx.Agent.KillDupAgents {
			oldest := agents[0]
			for _, agent := range agents {
				if agent.Status != "online" {
					continue
				}
				if agent.StartTime.Before(oldest.StartTime) {
					oldest = agent
				}
			}
			desc := fmt.Sprintf("Issuing destruction action for agent '%s' "+
				"with PID '%d'.", oldest.Name, oldest.PID)
			ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: desc}
			err = issueKillAction(oldest, ctx)
			if err != nil {
				panic(err)
			}
			// throttling to prevent issuing too many kill orders at the same time
			time.Sleep(5 * time.Second)
		} else {
			// Build a list of relevant agent names to include in the manual inspection
			// notification
			var namelist string
			for _, agent := range agents {
				if namelist == "" {
					namelist = agent.Name
				} else {
					namelist += ", " + agent.Name
				}
			}
			desc := fmt.Sprintf("found %v agents running on %v. Require "+
				"manual inspection (%v).", remainingAgents, queueLoc, namelist)
			ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: desc}.Warning()
		}
	}
	return
}

// issueKillAction issues an `agentdestroy` action targeted to a specific agent
// and updates the status of the agent in the database
func issueKillAction(agent mig.Agent, ctx Context) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("issueKillAction() -> %v", e)
		}
		ctx.Channels.Log <- mig.Log{OpID: ctx.OpID, Desc: "leaving issueKillAction()"}.Debug()
	}()
	// generate an `agentdestroy` action for this agent
	killAction := mig.Action{
		ID:            mig.GenID(),
		Name:          fmt.Sprintf("Kill agent %s", agent.Name),
		Target:        fmt.Sprintf("queueloc='%s'", agent.QueueLoc),
		ValidFrom:     time.Now().Add(-60 * time.Second).UTC(),
		ExpireAfter:   time.Now().Add(30 * time.Minute).UTC(),
		SyntaxVersion: 2,
	}
	var opparams struct {
		PID     int    `json:"pid"`
		Version string `json:"version"`
	}
	opparams.PID = agent.PID
	opparams.Version = agent.Version
	killOperation := mig.Operation{
		Module:     "agentdestroy",
		Parameters: opparams,
	}
	killAction.Operations = append(killAction.Operations, killOperation)

	// sign the action with the scheduler PGP key
	secring, err := getSecring(ctx)
	if err != nil {
		panic(err)
	}
	pgpsig, err := killAction.Sign(ctx.PGP.PrivKeyID, secring)
	if err != nil {
		panic(err)
	}
	killAction.PGPSignatures = append(killAction.PGPSignatures, pgpsig)
	var jsonAction []byte
	jsonAction, err = json.Marshal(killAction)
	if err != nil {
		panic(err)
	}

	// write the action to the spool for scheduling
	dest := fmt.Sprintf("%s/%.0f.json", ctx.Directories.Action.New, killAction.ID)
	err = safeWrite(ctx, dest, jsonAction)
	if err != nil {
		panic(err)
	}

	// mark the agent as `destroyed` in the database
	err = ctx.DB.MarkAgentDestroyed(agent)
	if err != nil {
		panic(err)
	}
	ctx.Channels.Log <- mig.Log{Desc: fmt.Sprintf("issued kill action for agent '%s' "+
		"with PID '%d'", agent.Name, agent.PID)}.Warning()
	return
}
<|endoftext|>


<commit_before>package main

import "io"

var envTemplateUnix string = `
########################################
#
# This file is generated by goautoenv
#
# Please do not change this file.
#

ENV_DIR={{.Env.Root}}

GOPATH_OLD=${GOPATH}
GOPATH={{.Env.GOPATH}}
export GOPATH

_GOAUTOENV_OLD_PATH=${PATH}
PATH=${GOPATH}/bin:${PATH}
export PATH

GOPACKAGE_OLD=${GOPACKAGE}
GOPACKAGE={{.Env.Package}}
export GOPACKAGE

_GOAUTOENV_WORKSPACE=${GOPATH}/src/${GOPACKAGE}

_OLD_GOAUTOENV_PS1="$PS1"
PS1="($GOPACKAGE)\n$PS1"

{{range $e := .Aliases}}
_GOAUTOENV_{{$e}}=$(which {{$e}})

{{$e}} () {
  pushd $_GOAUTOENV_WORKSPACE > /dev/null 2>&1
  $_GOAUTOENV_{{$e}} $@
  popd > /dev/null 2>&1
}
{{end}}

deactivate () {
  GOPATH=$GOPATH_OLD
  export GOPATH
  GOPACKAGE=$GOPACKAGE_OLD
  export GOPACKAGE
  PATH=${_GOAUTOENV_OLD_PATH}
  export PATH
  PS1=$_OLD_GOAUTOENV_PS1
  export PS1

  unset -f deactivate
  {{range $e := .Aliases}}
  unset -f {{$e}}
  unset _GOAUTOENV_{{$e}}
  {{end}}
}
`

func WriteEnvUnixFile(env *Environment, writer io.Writer) error {
	return writeEnvFile(envWrap{env, aliases}, writer, envTemplateUnix)
}
<commit_msg>fix newline in zsh<commit_after>package main

import "io"

var envTemplateUnix string = `
########################################
#
# This file is generated by goautoenv
#
# Please do not change this file.
#

ENV_DIR={{.Env.Root}}

GOPATH_OLD=${GOPATH}
GOPATH={{.Env.GOPATH}}
export GOPATH

_GOAUTOENV_OLD_PATH=${PATH}
PATH=${GOPATH}/bin:${PATH}
export PATH

GOPACKAGE_OLD=${GOPACKAGE}
GOPACKAGE={{.Env.Package}}
export GOPACKAGE

_GOAUTOENV_WORKSPACE=${GOPATH}/src/${GOPACKAGE}

_OLD_GOAUTOENV_PS1="$PS1"
_GOAUTOENV_NEWLINE=$'\n'
PS1="($GOPACKAGE)$_GOAUTOENV_NEWLINE$PS1"

{{range $e := .Aliases}}
_GOAUTOENV_{{$e}}=$(which {{$e}})

{{$e}} () {
  pushd $_GOAUTOENV_WORKSPACE > /dev/null 2>&1
  $_GOAUTOENV_{{$e}} $@
  popd > /dev/null 2>&1
}
{{end}}

deactivate () {
  GOPATH=$GOPATH_OLD
  export GOPATH
  GOPACKAGE=$GOPACKAGE_OLD
  export GOPACKAGE
  PATH=${_GOAUTOENV_OLD_PATH}
  export PATH
  PS1=$_OLD_GOAUTOENV_PS1
  export PS1

  unset -f deactivate
  {{range $e := .Aliases}}
  unset -f {{$e}}
  unset _GOAUTOENV_{{$e}}
  {{end}}
}
`

func WriteEnvUnixFile(env *Environment, writer io.Writer) error {
	return writeEnvFile(envWrap{env, aliases}, writer, envTemplateUnix)
}
<|endoftext|>


<commit_before>package http_test

import (
	"fmt"

	"github.com/Cepave/open-falcon-backend/common/http"
	"github.com/Cepave/open-falcon-backend/common/http/client"
)

// Constructs a client object by set-up configuration.
func ExampleApiService_newClient() {
	httpClientConfig := client.NewDefaultConfig()
	httpClientConfig.Url = "http://some-1.mock.server/"

	restfulConfig := &http.RestfulClientConfig{
		HttpClientConfig: httpClientConfig,
		FromModule:       "query",
	}

	apiService := http.NewApiService(restfulConfig)
	client := apiService.NewClient()

	request := client.Get()

	fmt.Printf("%s", request.Context.Request.URL.Host)

	// Output:
	// some-1.mock.server
}
<commit_msg>[OWL-1397] Fix a failing example.<commit_after>package http_test

import (
	"github.com/Cepave/open-falcon-backend/common/http"
	"github.com/Cepave/open-falcon-backend/common/http/client"
)

// Constructs a client object by set-up configuration.
func ExampleApiService_newClient() {
	httpClientConfig := client.NewDefaultConfig()
	httpClientConfig.Url = "http://some-1.mock.server/"

	restfulConfig := &http.RestfulClientConfig{
		HttpClientConfig: httpClientConfig,
		FromModule:       "query",
	}

	apiService := http.NewApiService(restfulConfig)
	client := apiService.NewClient()

	// The returned request is omitted in the example.
	_ = client.Get()
	// ...
}
<|endoftext|>


<commit_before>package aws

import(
    "fmt"
//    "math/rand"
//    "strings"
    "testing"
//    "time"
//
    "github.com/awslabs/aws-sdk-go/aws"
    "github.com/awslabs/aws-sdk-go/service/cloudwatch"
    "github.com/hashicorp/terraform/helper/resource"
    "github.com/hashicorp/terraform/terraform"
)

func TestAccAWSCloudWatchMetricAlarm_basic(t* testing.T) {
    var alarm cloudwatch.MetricAlarm

    resource.Test(t, resource.TestCase{
        PreCheck:       func () { testAccPreCheck(t) },
        Providers:      testAccProviders,
        CheckDestroy:   testAccCheckAWSCloudWatchMetricAlarmDestroy,
        Steps:          []resource.TestStep{
            resource.TestStep{
                Config: testAccAWSCloudWatchMetricAlarmConfig,
                Check:  resource.ComposeTestCheckFunc(
                    testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
                    resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "metric_name", "CPUUtilization"),
                    resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "statistic", "Average"),
                ),
            },
        },
    })
}

func testAccCheckCloudWatchMetricAlarmExists(n string, alarm *cloudwatch.MetricAlarm) resource.TestCheckFunc {
    return func(s *terraform.State) error {
        rs, ok := s.RootModule().Resources[n]
        if !ok {
            return fmt.Errorf("Not found: %s", n)
        }

        conn := testAccProvider.Meta().(*AWSClient).cloudwatchconn
        params := cloudwatch.DescribeAlarmsInput{
            AlarmNames: []*string{aws.String(rs.Primary.ID)},
        }
        resp, err := conn.DescribeAlarms(&params)
        if err != nil {
            return err
        }
        if len(resp.MetricAlarms) == 0 {
            return fmt.Errorf("Alarm not found")
        }
        *alarm = *resp.MetricAlarms[0]

        return nil
    }
}

func testAccCheckAWSCloudWatchMetricAlarmDestroy(s *terraform.State) error {
    conn := testAccProvider.Meta().(*AWSClient).cloudwatchconn

    for _, rs := range s.RootModule().Resources {
        if rs.Type != "aws_cloudwatch_metric_alarm" {
            continue
        }

        params := cloudwatch.DescribeAlarmsInput{
            AlarmNames: []*string{aws.String(rs.Primary.ID)},
        }

        resp, err := conn.DescribeAlarms(&params)

        if err == nil {
            if len(resp.ScalingPolicies) != 0 &&
                *resp.ScalingPolicies[0].AlarmName == rs.Primary.ID {
                return fmt.Errorf("Alarm Still Exists: %s". rs.Primary.ID)
            }
        }
    }

    return nil
}

var testAccAWSCloudWatchMetricAlarmConfig = fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" {
    alarm_name = "terraform-test-foobar5"
    comparison_operator = "GreaterThanOrEqualToThreshold"
    evaluation_periods = "2"
    metric_name = "CPUUtilization"
    namespace = "AWS/EC2"
    period = "120"
    statistic = "Average"
    threshold = "80"
    alarm_description = "This metric monitor ec2 cpu utilization"
    insufficient_data_actions = []
}
`)
<commit_msg>Another fix to cloudwatch test<commit_after>package aws

import(
    "fmt"
//    "math/rand"
//    "strings"
    "testing"
//    "time"
//
    "github.com/awslabs/aws-sdk-go/aws"
    "github.com/awslabs/aws-sdk-go/service/cloudwatch"
    "github.com/hashicorp/terraform/helper/resource"
    "github.com/hashicorp/terraform/terraform"
)

func TestAccAWSCloudWatchMetricAlarm_basic(t* testing.T) {
    var alarm cloudwatch.MetricAlarm

    resource.Test(t, resource.TestCase{
        PreCheck:       func () { testAccPreCheck(t) },
        Providers:      testAccProviders,
        CheckDestroy:   testAccCheckAWSCloudWatchMetricAlarmDestroy,
        Steps:          []resource.TestStep{
            resource.TestStep{
                Config: testAccAWSCloudWatchMetricAlarmConfig,
                Check:  resource.ComposeTestCheckFunc(
                    testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
                    resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "metric_name", "CPUUtilization"),
                    resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "statistic", "Average"),
                ),
            },
        },
    })
}

func testAccCheckCloudWatchMetricAlarmExists(n string, alarm *cloudwatch.MetricAlarm) resource.TestCheckFunc {
    return func(s *terraform.State) error {
        rs, ok := s.RootModule().Resources[n]
        if !ok {
            return fmt.Errorf("Not found: %s", n)
        }

        conn := testAccProvider.Meta().(*AWSClient).cloudwatchconn
        params := cloudwatch.DescribeAlarmsInput{
            AlarmNames: []*string{aws.String(rs.Primary.ID)},
        }
        resp, err := conn.DescribeAlarms(&params)
        if err != nil {
            return err
        }
        if len(resp.MetricAlarms) == 0 {
            return fmt.Errorf("Alarm not found")
        }
        *alarm = *resp.MetricAlarms[0]

        return nil
    }
}

func testAccCheckAWSCloudWatchMetricAlarmDestroy(s *terraform.State) error {
    conn := testAccProvider.Meta().(*AWSClient).cloudwatchconn

    for _, rs := range s.RootModule().Resources {
        if rs.Type != "aws_cloudwatch_metric_alarm" {
            continue
        }

        params := cloudwatch.DescribeAlarmsInput{
            AlarmNames: []*string{aws.String(rs.Primary.ID)},
        }

        resp, err := conn.DescribeAlarms(&params)

        if err == nil {
            if len(resp.MetricAlarms) != 0 &&
                *resp.MetricAlarms[0].AlarmName == rs.Primary.ID {
                return fmt.Errorf("Alarm Still Exists: %s", rs.Primary.ID)
            }
        }
    }

    return nil
}

var testAccAWSCloudWatchMetricAlarmConfig = fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" {
    alarm_name = "terraform-test-foobar5"
    comparison_operator = "GreaterThanOrEqualToThreshold"
    evaluation_periods = "2"
    metric_name = "CPUUtilization"
    namespace = "AWS/EC2"
    period = "120"
    statistic = "Average"
    threshold = "80"
    alarm_description = "This metric monitor ec2 cpu utilization"
    insufficient_data_actions = []
}
`)
<|endoftext|>


<commit_before>package consensus

import (
	"errors"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// diffs.go contains all of the functions related to diffs in the consensus
// set. Each block changes the consensus set in a deterministic way, these
// changes are recorded as diffs for easy rewinding and reapplying. The diffs
// are created, applied, reverted, and queried in this file.

var (
	errBadCommitSiacoinOutputDiff        = errors.New("rogue siacoin output diff in commitSiacoinOutputDiff")
	errBadCommitFileContractDiff         = errors.New("rogue file contract diff in commitFileContractDiff")
	errBadCommitSiafundOutputDiff        = errors.New("rogue siafund output diff in commitSiafundOutputDiff")
	errBadCommitDelayedSiacoinOutputDiff = errors.New("rogue delayed siacoin output diff in commitSiacoinOutputDiff")
	errBadMaturityHeight                 = errors.New("delayed siacoin output diff was submitted with illegal maturity height")
	errNegativePoolAdjustment            = errors.New("committing a siafund pool diff with a negative adjustment")
	errApplySiafundPoolDiffMismatch      = errors.New("committing a siafund pool diff with an invalid 'previous' field.")
	errRevertSiafundPoolDiffMismatch     = errors.New("committing a siafund pool diff with an invalid 'adjusted' field.")
)

// commitSiacoinOutputDiff applies or reverts a SiacoinOutputDiff.
func (cs *State) commitSiacoinOutputDiff(scod modules.SiacoinOutputDiff, dir modules.DiffDirection) {
	// Sanity check - should not be adding an output twice, or deleting an
	// output that does not exist.
	if build.DEBUG {
		_, exists := cs.siacoinOutputs[scod.ID]
		if exists == (scod.Direction == dir) {
			panic(errBadCommitSiacoinOutputDiff)
		}
	}

	if scod.Direction == dir {
		cs.siacoinOutputs[scod.ID] = scod.SiacoinOutput
	} else {
		delete(cs.siacoinOutputs, scod.ID)
	}
}

// commitFileContractDiff applies or reverts a FileContractDiff.
func (cs *State) commitFileContractDiff(fcd modules.FileContractDiff, dir modules.DiffDirection) {
	// Sanity check - should not be adding a contract twice, or deleting a
	// contract that does not exist.
	if build.DEBUG {
		_, exists := cs.fileContracts[fcd.ID]
		if exists == (fcd.Direction == dir) {
			panic(errBadCommitFileContractDiff)
		}
	}

	if fcd.Direction == dir {
		cs.fileContracts[fcd.ID] = fcd.FileContract
	} else {
		delete(cs.fileContracts, fcd.ID)
	}
}

// commitSiafundOutputDiff applies or reverts a SiafundOutputDiff.
func (cs *State) commitSiafundOutputDiff(sfod modules.SiafundOutputDiff, dir modules.DiffDirection) {
	// Sanity check - should not be adding an output twice, or deleting an
	// output that does not exist.
	if build.DEBUG {
		_, exists := cs.siafundOutputs[sfod.ID]
		if exists == (sfod.Direction == dir) {
			panic(errBadCommitSiafundOutputDiff)
		}
	}

	if sfod.Direction == dir {
		cs.siafundOutputs[sfod.ID] = sfod.SiafundOutput
	} else {
		delete(cs.siafundOutputs, sfod.ID)
	}
}

// commitDelayedSiacoinOutputDiff applies or reverts a delayedSiacoinOutputDiff.
func (cs *State) commitDelayedSiacoinOutputDiff(dscod modules.DelayedSiacoinOutputDiff, dir modules.DiffDirection) {
	// Sanity check - should not be adding an output twoice, or deleting an
	// output that does not exist.
	if build.DEBUG {
		_, exists := cs.delayedSiacoinOutputs[dscod.MaturityHeight]
		if !exists {
			panic(errBadMaturityHeight)
		}
		_, exists = cs.delayedSiacoinOutputs[dscod.MaturityHeight][dscod.ID]
		if exists == (dscod.Direction == dir) {
			panic(errBadCommitDelayedSiacoinOutputDiff)
		}
	}

	if dscod.Direction == dir {
		cs.delayedSiacoinOutputs[dscod.MaturityHeight][dscod.ID] = dscod.SiacoinOutput
	} else {
		delete(cs.delayedSiacoinOutputs[dscod.MaturityHeight], dscod.ID)
	}
}

// commitSiafundPoolDiff applies or reverts a SiafundPoolDiff.
func (cs *State) commitSiafundPoolDiff(sfpd modules.SiafundPoolDiff, dir modules.DiffDirection) {
	// Sanity check - siafund pool should only ever increase.
	if build.DEBUG {
		if sfpd.Adjusted.Cmp(sfpd.Previous) < 0 {
			panic(errNegativePoolAdjustment)
		}
	}

	if dir == modules.DiffApply {
		// Sanity check - sfpd.Previous should equal the current siafund pool.
		if build.DEBUG {
			if cs.siafundPool.Cmp(sfpd.Previous) != 0 {
				panic(errApplySiafundPoolDiffMismatch)
			}
		}
		cs.siafundPool = sfpd.Adjusted
	} else {
		// Sanity check - sfpd.Adjusted should equal the current siafund pool.
		if build.DEBUG {
			if cs.siafundPool.Cmp(sfpd.Adjusted) != 0 {
				panic(errRevertSiafundPoolDiffMismatch)
			}
		}
		cs.siafundPool = sfpd.Previous
	}
}

// commitDiffSet applies or reverts the diffs in a blockNode.
func (s *State) commitDiffSet(bn *blockNode, dir modules.DiffDirection) {
	// Sanity checks.
	if build.DEBUG {
		// Diffs should have already been generated for this node.
		if !bn.diffsGenerated {
			panic("misuse of applyDiffSet - diffs have not been generated!")
		}

		// Current node must be the input node's parent if applying, and
		// current node must be the input node if reverting.
		if dir == modules.DiffApply {
			if bn.parent.block.ID() != s.currentBlockID() {
				panic("applying a block node when it's not a valid successor")
			}
		} else {
			if bn.block.ID() != s.currentBlockID() {
				panic("applying a block node when it's not a valid successor")
			}
		}

		// All siafundPoolDiffs should be increasing the size of the siafund
		// pool.
		for _, sfpd := range bn.siafundPoolDiffs {
			if sfpd.Previous.Cmp(sfpd.Adjusted) < 0 {
				panic("a siafund pool diff exists that shrinks the siafund pool")
			}
		}
	}

	// Create the filling delayed siacoin output map.
	if dir == modules.DiffApply {
		if build.DEBUG {
			// Sanity check - the output map being created should not already
			// exist.
			_, exists := s.delayedSiacoinOutputs[bn.height+types.MaturityDelay]
			if exists {
				panic("trying to create a map that already exists")
			}
		}
		s.delayedSiacoinOutputs[bn.height+types.MaturityDelay] = make(map[types.SiacoinOutputID]types.SiacoinOutput)
	} else {
		// Skip creating maps for heights that can't have delayed outputs.
		if bn.height > types.MaturityDelay {
			// Sanity check - the output map being created should not already
			// exist.
			if build.DEBUG {
				_, exists := s.delayedSiacoinOutputs[bn.height]
				if exists {
					panic("trying to create a map that already exists")
				}
			}
			s.delayedSiacoinOutputs[bn.height] = make(map[types.SiacoinOutputID]types.SiacoinOutput)
		}
	}

	// Apply each of the diffs.
	if dir == modules.DiffApply {
		for _, scod := range bn.siacoinOutputDiffs {
			s.commitSiacoinOutputDiff(scod, dir)
		}
		for _, fcd := range bn.fileContractDiffs {
			s.commitFileContractDiff(fcd, dir)
		}
		for _, sfod := range bn.siafundOutputDiffs {
			s.commitSiafundOutputDiff(sfod, dir)
		}
		for _, dscod := range bn.delayedSiacoinOutputDiffs {
			s.commitDelayedSiacoinOutputDiff(dscod, dir)
		}
		for _, sfpd := range bn.siafundPoolDiffs {
			s.commitSiafundPoolDiff(sfpd, dir)
		}
	} else {
		for i := len(bn.siacoinOutputDiffs) - 1; i >= 0; i-- {
			s.commitSiacoinOutputDiff(bn.siacoinOutputDiffs[i], dir)
		}
		for i := len(bn.fileContractDiffs) - 1; i >= 0; i-- {
			s.commitFileContractDiff(bn.fileContractDiffs[i], dir)
		}
		for i := len(bn.siafundOutputDiffs) - 1; i >= 0; i-- {
			s.commitSiafundOutputDiff(bn.siafundOutputDiffs[i], dir)
		}
		for i := len(bn.delayedSiacoinOutputDiffs) - 1; i >= 0; i-- {
			s.commitDelayedSiacoinOutputDiff(bn.delayedSiacoinOutputDiffs[i], dir)
		}
		for i := len(bn.siafundPoolDiffs) - 1; i >= 0; i-- {
			s.commitSiafundPoolDiff(bn.siafundPoolDiffs[i], dir)
		}
	}

	// Delete the emptied siacoin output map.
	if dir == modules.DiffApply {
		// There are no outputs that mature in the first MaturityDelay blocks.
		if bn.height > types.MaturityDelay {
			// Sanity check - the map being deleted should be empty.
			if build.DEBUG {
				if len(s.delayedSiacoinOutputs[bn.height]) != 0 {
					panic("trying to delete a set of delayed outputs that is not empty.")
				}
			}
			delete(s.delayedSiacoinOutputs, bn.height)
		}
	} else {
		// Sanity check - the map being deleted should be empty
		if build.DEBUG {
			if len(s.delayedSiacoinOutputs[bn.height+types.MaturityDelay]) != 0 {
				panic("trying to delete a set of delayed outputs that is not empty.")
			}
		}
		delete(s.delayedSiacoinOutputs, bn.height+types.MaturityDelay)

	}

	// Update the current path.
	if dir == modules.DiffApply {
		s.currentPath = append(s.currentPath, bn.block.ID())
		s.db.AddBlock(bn.block)
	} else {
		s.currentPath = s.currentPath[:len(s.currentPath)-1]
		s.db.RemoveBlock()
	}
}

// generateAndApplyDiff will verify the block and then integrate it into the
// consensus state. These two actions must happen at the same time because
// transactions are allowed to depend on each other. We can't be sure that a
// transaction is valid unless we have applied all of the previous transactions
// in the block, which means we need to apply while we verify.
func (s *State) generateAndApplyDiff(bn *blockNode) error {
	// Sanity check
	if build.DEBUG {
		// Generate should only be called if the diffs have not yet been
		// generated.
		if bn.diffsGenerated {
			panic("misuse of generateAndApplyDiff")
		}

		// Current node must be the input node's parent.
		if bn.parent.block.ID() != s.currentBlockID() {
			panic("applying a block node when it's not a valid successor")
		}
	}

	// Update the state to point to the new block.
	s.currentPath = append(s.currentPath, bn.block.ID())
	s.db.AddBlock(bn.block)
	s.delayedSiacoinOutputs[bn.height+types.MaturityDelay] = make(map[types.SiacoinOutputID]types.SiacoinOutput)

	// diffsGenerated is set to true as soon as we start changing the set of
	// diffs in the block node. If at any point the block is found to be
	// invalid, the diffs can be safely reversed from whatever point.
	bn.diffsGenerated = true

	// Validate and apply each transaction in the block. They cannot be
	// validated all at once because some transactions may not be valid until
	// previous transactions have been applied.
	for _, txn := range bn.block.Transactions {
		err := s.validTransaction(txn)
		if err != nil {
			// Awkward: need to apply the matured outputs otherwise the diff
			// structure malforms due to the way the delayedOutput maps are
			// created and destroyed.
			s.applyMaturedSiacoinOutputs(bn)
			s.commitDiffSet(bn, modules.DiffRevert)
			s.dosBlocks[bn.block.ID()] = struct{}{}
			s.deleteNode(bn)
			return err
		}

		s.applyTransaction(bn, txn)
	}

	// After all of the transactions have been applied, 'maintenance' is
	// applied on the block. This includes adding any outputs that have reached
	// maturity, applying any contracts with missed storage proofs, and adding
	// the miner payouts to the list of delayed outputs.
	s.applyMaintenance(bn)

	if build.DEBUG {
		bn.consensusSetHash = s.consensusSetHash()
	}
	return nil
}
<commit_msg>break apart commitDiffSet<commit_after>package consensus

import (
	"errors"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// diffs.go contains all of the functions related to diffs in the consensus
// set. Each block changes the consensus set in a deterministic way, these
// changes are recorded as diffs for easy rewinding and reapplying. The diffs
// are created, applied, reverted, and queried in this file.

var (
	errBadCommitSiacoinOutputDiff        = errors.New("rogue siacoin output diff in commitSiacoinOutputDiff")
	errBadCommitFileContractDiff         = errors.New("rogue file contract diff in commitFileContractDiff")
	errBadCommitSiafundOutputDiff        = errors.New("rogue siafund output diff in commitSiafundOutputDiff")
	errBadCommitDelayedSiacoinOutputDiff = errors.New("rogue delayed siacoin output diff in commitSiacoinOutputDiff")
	errBadMaturityHeight                 = errors.New("delayed siacoin output diff was submitted with illegal maturity height")
	errNegativePoolAdjustment            = errors.New("committing a siafund pool diff with a negative adjustment")
	errApplySiafundPoolDiffMismatch      = errors.New("committing a siafund pool diff with an invalid 'previous' field.")
	errRevertSiafundPoolDiffMismatch     = errors.New("committing a siafund pool diff with an invalid 'adjusted' field.")
)

// commitSiacoinOutputDiff applies or reverts a SiacoinOutputDiff.
func (cs *State) commitSiacoinOutputDiff(scod modules.SiacoinOutputDiff, dir modules.DiffDirection) {
	// Sanity check - should not be adding an output twice, or deleting an
	// output that does not exist.
	if build.DEBUG {
		_, exists := cs.siacoinOutputs[scod.ID]
		if exists == (scod.Direction == dir) {
			panic(errBadCommitSiacoinOutputDiff)
		}
	}

	if scod.Direction == dir {
		cs.siacoinOutputs[scod.ID] = scod.SiacoinOutput
	} else {
		delete(cs.siacoinOutputs, scod.ID)
	}
}

// commitFileContractDiff applies or reverts a FileContractDiff.
func (cs *State) commitFileContractDiff(fcd modules.FileContractDiff, dir modules.DiffDirection) {
	// Sanity check - should not be adding a contract twice, or deleting a
	// contract that does not exist.
	if build.DEBUG {
		_, exists := cs.fileContracts[fcd.ID]
		if exists == (fcd.Direction == dir) {
			panic(errBadCommitFileContractDiff)
		}
	}

	if fcd.Direction == dir {
		cs.fileContracts[fcd.ID] = fcd.FileContract
	} else {
		delete(cs.fileContracts, fcd.ID)
	}
}

// commitSiafundOutputDiff applies or reverts a SiafundOutputDiff.
func (cs *State) commitSiafundOutputDiff(sfod modules.SiafundOutputDiff, dir modules.DiffDirection) {
	// Sanity check - should not be adding an output twice, or deleting an
	// output that does not exist.
	if build.DEBUG {
		_, exists := cs.siafundOutputs[sfod.ID]
		if exists == (sfod.Direction == dir) {
			panic(errBadCommitSiafundOutputDiff)
		}
	}

	if sfod.Direction == dir {
		cs.siafundOutputs[sfod.ID] = sfod.SiafundOutput
	} else {
		delete(cs.siafundOutputs, sfod.ID)
	}
}

// commitDelayedSiacoinOutputDiff applies or reverts a delayedSiacoinOutputDiff.
func (cs *State) commitDelayedSiacoinOutputDiff(dscod modules.DelayedSiacoinOutputDiff, dir modules.DiffDirection) {
	// Sanity check - should not be adding an output twoice, or deleting an
	// output that does not exist.
	if build.DEBUG {
		_, exists := cs.delayedSiacoinOutputs[dscod.MaturityHeight]
		if !exists {
			panic(errBadMaturityHeight)
		}
		_, exists = cs.delayedSiacoinOutputs[dscod.MaturityHeight][dscod.ID]
		if exists == (dscod.Direction == dir) {
			panic(errBadCommitDelayedSiacoinOutputDiff)
		}
	}

	if dscod.Direction == dir {
		cs.delayedSiacoinOutputs[dscod.MaturityHeight][dscod.ID] = dscod.SiacoinOutput
	} else {
		delete(cs.delayedSiacoinOutputs[dscod.MaturityHeight], dscod.ID)
	}
}

// commitSiafundPoolDiff applies or reverts a SiafundPoolDiff.
func (cs *State) commitSiafundPoolDiff(sfpd modules.SiafundPoolDiff, dir modules.DiffDirection) {
	// Sanity check - siafund pool should only ever increase.
	if build.DEBUG {
		if sfpd.Adjusted.Cmp(sfpd.Previous) < 0 {
			panic(errNegativePoolAdjustment)
		}
	}

	if dir == modules.DiffApply {
		// Sanity check - sfpd.Previous should equal the current siafund pool.
		if build.DEBUG {
			if cs.siafundPool.Cmp(sfpd.Previous) != 0 {
				panic(errApplySiafundPoolDiffMismatch)
			}
		}
		cs.siafundPool = sfpd.Adjusted
	} else {
		// Sanity check - sfpd.Adjusted should equal the current siafund pool.
		if build.DEBUG {
			if cs.siafundPool.Cmp(sfpd.Adjusted) != 0 {
				panic(errRevertSiafundPoolDiffMismatch)
			}
		}
		cs.siafundPool = sfpd.Previous
	}
}

// commitDiffSetSanity performs a series of sanity checks before commiting a
// diff set.
func (cs *State) commitDiffSetSanity(bn *blockNode, dir modules.DiffDirection) {
	// Sanity checks.
	if build.DEBUG {
		// Diffs should have already been generated for this node.
		if !bn.diffsGenerated {
			panic("misuse of applyDiffSet - diffs have not been generated!")
		}

		// Current node must be the input node's parent if applying, and
		// current node must be the input node if reverting.
		if dir == modules.DiffApply {
			if bn.parent.block.ID() != cs.currentBlockID() {
				panic("applying a block node when it's not a valid successor")
			}
		} else {
			if bn.block.ID() != cs.currentBlockID() {
				panic("reverting a block node when it's not a valid successor")
			}
		}

		// All siafundPoolDiffs should be increasing the size of the siafund
		// pool.
		for _, sfpd := range bn.siafundPoolDiffs {
			if sfpd.Previous.Cmp(sfpd.Adjusted) < 0 {
				panic("a siafund pool diff exists that shrinks the siafund pool")
			}
		}
	}
}

// createUpcomingDelayeOutputdMaps creates the delayed siacoin output maps that
// will be used when applying delayed siacoin outputs in the diff set.
func (cs *State) createUpcomingDelayedOutputMaps(bn *blockNode, dir modules.DiffDirection) {
	if dir == modules.DiffApply {
		if build.DEBUG {
			// Sanity check - the output map being created should not already
			// exist.
			_, exists := cs.delayedSiacoinOutputs[bn.height+types.MaturityDelay]
			if exists {
				panic("trying to create a map that already exists")
			}
		}
		cs.delayedSiacoinOutputs[bn.height+types.MaturityDelay] = make(map[types.SiacoinOutputID]types.SiacoinOutput)
	} else {
		// Skip creating maps for heights that can't have delayed outputs.
		if bn.height > types.MaturityDelay {
			// Sanity check - the output map being created should not already
			// exist.
			if build.DEBUG {
				_, exists := cs.delayedSiacoinOutputs[bn.height]
				if exists {
					panic("trying to create a map that already exists")
				}
			}
			cs.delayedSiacoinOutputs[bn.height] = make(map[types.SiacoinOutputID]types.SiacoinOutput)
		}
	}
}

// commitNodeDiffs commits all of the diffs in a block node.
func (cs *State) commitNodeDiffs(bn *blockNode, dir modules.DiffDirection) {
	if dir == modules.DiffApply {
		for _, scod := range bn.siacoinOutputDiffs {
			cs.commitSiacoinOutputDiff(scod, dir)
		}
		for _, fcd := range bn.fileContractDiffs {
			cs.commitFileContractDiff(fcd, dir)
		}
		for _, sfod := range bn.siafundOutputDiffs {
			cs.commitSiafundOutputDiff(sfod, dir)
		}
		for _, dscod := range bn.delayedSiacoinOutputDiffs {
			cs.commitDelayedSiacoinOutputDiff(dscod, dir)
		}
		for _, sfpd := range bn.siafundPoolDiffs {
			cs.commitSiafundPoolDiff(sfpd, dir)
		}
	} else {
		for i := len(bn.siacoinOutputDiffs) - 1; i >= 0; i-- {
			cs.commitSiacoinOutputDiff(bn.siacoinOutputDiffs[i], dir)
		}
		for i := len(bn.fileContractDiffs) - 1; i >= 0; i-- {
			cs.commitFileContractDiff(bn.fileContractDiffs[i], dir)
		}
		for i := len(bn.siafundOutputDiffs) - 1; i >= 0; i-- {
			cs.commitSiafundOutputDiff(bn.siafundOutputDiffs[i], dir)
		}
		for i := len(bn.delayedSiacoinOutputDiffs) - 1; i >= 0; i-- {
			cs.commitDelayedSiacoinOutputDiff(bn.delayedSiacoinOutputDiffs[i], dir)
		}
		for i := len(bn.siafundPoolDiffs) - 1; i >= 0; i-- {
			cs.commitSiafundPoolDiff(bn.siafundPoolDiffs[i], dir)
		}
	}
}

// deleteObsoleteDelayedOutputMaps deletes the delayed siacoin output maps that
// are no longer in use.
func (cs *State) deleteObsoleteDelayedOutputMaps(bn *blockNode, dir modules.DiffDirection) {
	if dir == modules.DiffApply {
		// There are no outputs that mature in the first MaturityDelay blocks.
		if bn.height > types.MaturityDelay {
			// Sanity check - the map being deleted should be empty.
			if build.DEBUG {
				if len(cs.delayedSiacoinOutputs[bn.height]) != 0 {
					panic("trying to delete a set of delayed outputs that is not empty.")
				}
			}
			delete(cs.delayedSiacoinOutputs, bn.height)
		}
	} else {
		// Sanity check - the map being deleted should be empty
		if build.DEBUG {
			if len(cs.delayedSiacoinOutputs[bn.height+types.MaturityDelay]) != 0 {
				panic("trying to delete a set of delayed outputs that is not empty.")
			}
		}
		delete(cs.delayedSiacoinOutputs, bn.height+types.MaturityDelay)
	}
}

// updateCurrentPath updates the current path after applying a diff set.
func (cs *State) updateCurrentPath(bn *blockNode, dir modules.DiffDirection) {
	// Update the current path.
	if dir == modules.DiffApply {
		cs.currentPath = append(cs.currentPath, bn.block.ID())
		cs.db.AddBlock(bn.block)
	} else {
		cs.currentPath = cs.currentPath[:len(cs.currentPath)-1]
		cs.db.RemoveBlock()
	}
}

// commitDiffSet applies or reverts the diffs in a blockNode.
func (cs *State) commitDiffSet(bn *blockNode, dir modules.DiffDirection) {
	cs.commitDiffSetSanity(bn, dir)
	cs.createUpcomingDelayedOutputMaps(bn, dir)
	cs.commitNodeDiffs(bn, dir)
	cs.deleteObsoleteDelayedOutputMaps(bn, dir)
	cs.updateCurrentPath(bn, dir)
}

// generateAndApplyDiff will verify the block and then integrate it into the
// consensus state. These two actions must happen at the same time because
// transactions are allowed to depend on each other. We can't be sure that a
// transaction is valid unless we have applied all of the previous transactions
// in the block, which means we need to apply while we verify.
func (s *State) generateAndApplyDiff(bn *blockNode) error {
	// Sanity check
	if build.DEBUG {
		// Generate should only be called if the diffs have not yet been
		// generated.
		if bn.diffsGenerated {
			panic("misuse of generateAndApplyDiff")
		}

		// Current node must be the input node's parent.
		if bn.parent.block.ID() != s.currentBlockID() {
			panic("applying a block node when it's not a valid successor")
		}
	}

	// Update the state to point to the new block.
	s.currentPath = append(s.currentPath, bn.block.ID())
	s.db.AddBlock(bn.block)
	s.delayedSiacoinOutputs[bn.height+types.MaturityDelay] = make(map[types.SiacoinOutputID]types.SiacoinOutput)

	// diffsGenerated is set to true as soon as we start changing the set of
	// diffs in the block node. If at any point the block is found to be
	// invalid, the diffs can be safely reversed from whatever point.
	bn.diffsGenerated = true

	// Validate and apply each transaction in the block. They cannot be
	// validated all at once because some transactions may not be valid until
	// previous transactions have been applied.
	for _, txn := range bn.block.Transactions {
		err := s.validTransaction(txn)
		if err != nil {
			// Awkward: need to apply the matured outputs otherwise the diff
			// structure malforms due to the way the delayedOutput maps are
			// created and destroyed.
			s.applyMaturedSiacoinOutputs(bn)
			s.commitDiffSet(bn, modules.DiffRevert)
			s.dosBlocks[bn.block.ID()] = struct{}{}
			s.deleteNode(bn)
			return err
		}

		s.applyTransaction(bn, txn)
	}

	// After all of the transactions have been applied, 'maintenance' is
	// applied on the block. This includes adding any outputs that have reached
	// maturity, applying any contracts with missed storage proofs, and adding
	// the miner payouts to the list of delayed outputs.
	s.applyMaintenance(bn)

	if build.DEBUG {
		bn.consensusSetHash = s.consensusSetHash()
	}
	return nil
}
<|endoftext|>