github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/fly/commands/execute.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"os"
     7  	"os/signal"
     8  	"strconv"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/pf-qiu/concourse/v6/atc"
    13  	"github.com/pf-qiu/concourse/v6/fly/commands/internal/displayhelpers"
    14  	"github.com/pf-qiu/concourse/v6/fly/commands/internal/executehelpers"
    15  	"github.com/pf-qiu/concourse/v6/fly/commands/internal/flaghelpers"
    16  	"github.com/pf-qiu/concourse/v6/fly/commands/internal/templatehelpers"
    17  	"github.com/pf-qiu/concourse/v6/fly/config"
    18  	"github.com/pf-qiu/concourse/v6/fly/eventstream"
    19  	"github.com/pf-qiu/concourse/v6/fly/rc"
    20  	"github.com/pf-qiu/concourse/v6/fly/ui"
    21  	"github.com/pf-qiu/concourse/v6/fly/ui/progress"
    22  	"github.com/pf-qiu/concourse/v6/go-concourse/concourse"
    23  	"github.com/vbauerster/mpb/v4"
    24  )
    25  
    26  type ExecuteCommand struct {
    27  	TaskConfig     atc.PathFlag                       `short:"c" long:"config" required:"true"                description:"The task config to execute"`
    28  	Privileged     bool                               `short:"p" long:"privileged"                            description:"Run the task with full privileges"`
    29  	IncludeIgnored bool                               `          long:"include-ignored"                       description:"Including .gitignored paths. Disregards .gitignore entries and uploads everything"`
    30  	Inputs         []flaghelpers.InputPairFlag        `short:"i" long:"input"       value-name:"NAME=PATH"    description:"An input to provide to the task (can be specified multiple times)"`
    31  	InputMappings  []flaghelpers.InputMappingPairFlag `short:"m" long:"input-mapping"       value-name:"[NAME=STRING]"    description:"Map a resource to a different name as task input"`
    32  	InputsFrom     flaghelpers.JobFlag                `short:"j" long:"inputs-from" value-name:"PIPELINE/JOB" description:"A job to base the inputs on"`
    33  	Outputs        []flaghelpers.OutputPairFlag       `short:"o" long:"output"      value-name:"NAME=PATH"    description:"An output to fetch from the task (can be specified multiple times)"`
    34  	Image          string                             `long:"image" description:"Image resource for the one-off build"`
    35  	Tags           []string                           `          long:"tag"         value-name:"TAG"          description:"A tag for a specific environment (can be specified multiple times)"`
    36  	Var            []flaghelpers.VariablePairFlag     `short:"v"  long:"var"       value-name:"[NAME=STRING]"  unquote:"false"  description:"Specify a string value to set for a variable in the pipeline"`
    37  	YAMLVar        []flaghelpers.YAMLVariablePairFlag `short:"y"  long:"yaml-var"  value-name:"[NAME=YAML]"    unquote:"false"  description:"Specify a YAML value to set for a variable in the pipeline"`
    38  	VarsFrom       []atc.PathFlag                     `short:"l"  long:"load-vars-from"  description:"Variable flag that can be used for filling in template values in configuration from a YAML file"`
    39  }
    40  
    41  func (command *ExecuteCommand) Execute(args []string) error {
    42  	target, err := rc.LoadTarget(Fly.Target, Fly.Verbose)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	err = target.Validate()
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	taskConfig, err := command.CreateTaskConfig(args)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	planFactory := atc.NewPlanFactory(time.Now().Unix())
    58  
    59  	inputs, inputMappings, imageResource, resourceTypes, err := executehelpers.DetermineInputs(
    60  		planFactory,
    61  		target.Team(),
    62  		taskConfig.Inputs,
    63  		command.Inputs,
    64  		command.InputMappings,
    65  		command.Image,
    66  		command.InputsFrom,
    67  		command.IncludeIgnored,
    68  		taskConfig.Platform,
    69  		command.Tags,
    70  	)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	if imageResource != nil {
    76  		taskConfig.ImageResource = imageResource
    77  	}
    78  
    79  	outputs, err := executehelpers.DetermineOutputs(
    80  		planFactory,
    81  		taskConfig.Outputs,
    82  		command.Outputs,
    83  	)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	plan, err := executehelpers.CreateBuildPlan(
    89  		planFactory,
    90  		target,
    91  		command.Privileged,
    92  		inputs,
    93  		inputMappings,
    94  		resourceTypes,
    95  		outputs,
    96  		taskConfig,
    97  		command.Tags,
    98  	)
    99  
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	client := target.Client()
   105  	clientURL, err := url.Parse(client.URL())
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	var build atc.Build
   111  	var buildURL *url.URL
   112  
   113  	if command.InputsFrom.PipelineRef.Name != "" {
   114  		build, err = target.Team().CreatePipelineBuild(command.InputsFrom.PipelineRef, plan)
   115  		if err != nil {
   116  			return err
   117  		}
   118  	} else {
   119  		build, err = target.Team().CreateBuild(plan)
   120  		if err != nil {
   121  			return err
   122  		}
   123  	}
   124  
   125  	buildURL, err = url.Parse(fmt.Sprintf("/builds/%d", build.ID))
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	fmt.Printf("executing build %d at %s\n", build.ID, clientURL.ResolveReference(buildURL))
   131  
   132  	terminate := make(chan os.Signal, 1)
   133  
   134  	go abortOnSignal(client, terminate, build)
   135  
   136  	signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)
   137  
   138  	eventSource, err := client.BuildEvents(strconv.Itoa(build.ID))
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	renderOptions := eventstream.RenderOptions{}
   144  
   145  	exitCode := eventstream.Render(os.Stdout, eventSource, renderOptions)
   146  	eventSource.Close()
   147  
   148  	artifactList, err := client.ListBuildArtifacts(strconv.Itoa(build.ID))
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	artifacts := map[string]atc.WorkerArtifact{}
   154  
   155  	for _, artifact := range artifactList {
   156  		artifacts[artifact.Name] = artifact
   157  	}
   158  
   159  	prog := progress.New()
   160  
   161  	for _, output := range outputs {
   162  		name := output.Name
   163  		path := output.Path
   164  
   165  		artifact, ok := artifacts[name]
   166  		if !ok {
   167  			continue
   168  		}
   169  
   170  		prog.Go("downloading "+output.Name, func(bar *mpb.Bar) error {
   171  			return executehelpers.Download(bar, target.Team(), artifact.ID, path)
   172  		})
   173  	}
   174  
   175  	err = prog.Wait()
   176  	if err != nil {
   177  		displayhelpers.FailWithErrorf("downloading failed: %s", err)
   178  		return err
   179  	}
   180  
   181  	os.Exit(exitCode)
   182  
   183  	return nil
   184  }
   185  
   186  func (command *ExecuteCommand) CreateTaskConfig(args []string) (atc.TaskConfig, error) {
   187  
   188  	taskTemplate := templatehelpers.NewYamlTemplateWithParams(
   189  		command.TaskConfig,
   190  		command.VarsFrom,
   191  		command.Var,
   192  		command.YAMLVar,
   193  		nil,
   194  	)
   195  
   196  	taskTemplateEvaluated, err := taskTemplate.Evaluate(false, false)
   197  	if err != nil {
   198  		return atc.TaskConfig{}, err
   199  	}
   200  
   201  	return config.OverrideTaskParams(taskTemplateEvaluated, args)
   202  }
   203  
   204  func abortOnSignal(
   205  	client concourse.Client,
   206  	terminate <-chan os.Signal,
   207  	build atc.Build,
   208  ) {
   209  	<-terminate
   210  
   211  	fmt.Fprintf(ui.Stderr, "\naborting...\n")
   212  
   213  	err := client.AbortBuild(strconv.Itoa(build.ID))
   214  	if err != nil {
   215  		fmt.Fprintln(ui.Stderr, "failed to abort:", err)
   216  		os.Exit(2)
   217  	}
   218  
   219  	// if told to terminate again, exit immediately
   220  	<-terminate
   221  	fmt.Fprintln(ui.Stderr, "exiting immediately")
   222  	os.Exit(2)
   223  }