Add support for "go run" style execution (#3503)
This commit is contained in:
parent
95ce3e2567
commit
5ae4149af5
|
@ -3,6 +3,9 @@ CHANGELOG
|
|||
|
||||
## HEAD (Unreleased)
|
||||
|
||||
- Support for a `go run` style workflow. Building or installing a pulumi program written in go is
|
||||
now optional. [3503](https://github.com/pulumi/pulumi/pull/3503)
|
||||
|
||||
- Re-apply "propagate resource inputs to resource state during preview, including first-class unknown values." The new
|
||||
set of changes have additional fixes to ensure backwards compatibility with earlier code. This allows the preview to
|
||||
better estimate the state of a resource after an update, including property values that were populated using defaults
|
||||
|
|
|
@ -1608,6 +1608,12 @@ func (pt *programTester) prepareGoProject(projinfo *engine.Projinfo) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip building if the 'go run' invocation path is requested.
|
||||
if !pt.opts.RunBuild {
|
||||
return nil
|
||||
}
|
||||
|
||||
outBin := filepath.Join(gopath, "bin", string(projinfo.Proj.Name))
|
||||
return pt.runCommand("go-build", []string{goBin, "build", "-o", outBin, "."}, cwd)
|
||||
}
|
||||
|
|
|
@ -92,6 +92,8 @@ func (host *goLanguageHost) GetRequiredPlugins(ctx context.Context,
|
|||
return &pulumirpc.GetRequiredPluginsResponse{}, nil
|
||||
}
|
||||
|
||||
const unableToFindProgramTemplate = "unable to find program: %s"
|
||||
|
||||
// findProgram attempts to find the needed program in various locations on the
|
||||
// filesystem, eventually resorting to searching in $PATH.
|
||||
func findProgram(program string) (string, error) {
|
||||
|
@ -123,7 +125,7 @@ func findProgram(program string) (string, error) {
|
|||
return fullPath, nil
|
||||
}
|
||||
|
||||
return "", errors.Errorf("unable to find program: %s", program)
|
||||
return "", errors.Errorf(unableToFindProgramTemplate, program)
|
||||
}
|
||||
|
||||
// RPC endpoint for LanguageRuntimeServer::Run
|
||||
|
@ -135,18 +137,51 @@ func (host *goLanguageHost) Run(ctx context.Context, req *pulumirpc.RunRequest)
|
|||
return nil, errors.Wrap(err, "failed to prepare environment")
|
||||
}
|
||||
|
||||
// by default we try to run a named executable on the path, but we will fallback to 'go run' style execution
|
||||
goRunInvoke := false
|
||||
|
||||
// The program to execute is simply the name of the project. This ensures good Go toolability, whereby
|
||||
// you can simply run `go install .` to build a Pulumi program prior to running it, among other benefits.
|
||||
// For ease of use, if we don't find a pre-built program, we attempt to invoke via 'go run' on behalf of the user.
|
||||
program, err := findProgram(req.GetProject())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "problem executing program (could not run language executor)")
|
||||
const message = "problem executing program (could not run language executor)"
|
||||
if err.Error() == fmt.Sprintf(unableToFindProgramTemplate, req.GetProject()) {
|
||||
logging.V(5).Infof("Unable to find program %s in $PATH, attempting invocation via 'go run'", program)
|
||||
program, err = findProgram("go")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, message)
|
||||
}
|
||||
goRunInvoke = true
|
||||
} else {
|
||||
return nil, errors.Wrap(err, message)
|
||||
}
|
||||
}
|
||||
|
||||
logging.V(5).Infof("language host launching process: %s", program)
|
||||
|
||||
// Now simply spawn a process to execute the requested program, wiring up stdout/stderr directly.
|
||||
var errResult string
|
||||
cmd := exec.Command(program)
|
||||
var cmd *exec.Cmd
|
||||
|
||||
if goRunInvoke {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to get current working directory")
|
||||
}
|
||||
|
||||
goFileSearchPattern := filepath.Join(cwd, "*.go")
|
||||
if matches, err := filepath.Glob(goFileSearchPattern); err != nil || len(matches) == 0 {
|
||||
return nil, errors.Errorf("Failed to find go files for 'go run' matching %s", goFileSearchPattern)
|
||||
}
|
||||
|
||||
args := []string{"run", cwd}
|
||||
// go run $cwd
|
||||
cmd = exec.Command(program, args...)
|
||||
} else {
|
||||
cmd = exec.Command(program)
|
||||
}
|
||||
|
||||
cmd.Env = env
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
|
3
tests/integration/empty/gorun/Pulumi.yaml
Normal file
3
tests/integration/empty/gorun/Pulumi.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
name: emptygorun
|
||||
description: An empty Go Pulumi program.
|
||||
runtime: go
|
13
tests/integration/empty/gorun/main.go
Normal file
13
tests/integration/empty/gorun/main.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pulumi/pulumi/sdk/go/pulumi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pulumi.Run(func(ctx *pulumi.Context) error {
|
||||
return nil
|
||||
})
|
||||
}
|
4
tests/integration/empty/gorun_main/Pulumi.yaml
Normal file
4
tests/integration/empty/gorun_main/Pulumi.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
name: emptygorunmain
|
||||
description: An empty Go Pulumi program.
|
||||
runtime: go
|
||||
main: ../gorun_main_src/
|
13
tests/integration/empty/gorun_main_src/main.go
Normal file
13
tests/integration/empty/gorun_main_src/main.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pulumi/pulumi/sdk/go/pulumi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pulumi.Run(func(ctx *pulumi.Context) error {
|
||||
return nil
|
||||
})
|
||||
}
|
|
@ -76,11 +76,28 @@ func TestEmptyPython(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// TestEmptyGo simply tests that we can run an empty Go project.
|
||||
// TestEmptyGo simply tests that we can build and run an empty Go project.
|
||||
func TestEmptyGo(t *testing.T) {
|
||||
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
||||
Dir: filepath.Join("empty", "go"),
|
||||
Quick: true,
|
||||
RunBuild: true,
|
||||
})
|
||||
}
|
||||
|
||||
// TestEmptyGoRun exercises the 'go run' invocation path that doesn't require an explicit build step.
|
||||
func TestEmptyGoRun(t *testing.T) {
|
||||
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
||||
Dir: filepath.Join("empty", "gorun"),
|
||||
Quick: true,
|
||||
})
|
||||
}
|
||||
|
||||
// TestEmptyGoRunMain exercises the 'go run' invocation path with a 'main' entrypoint specified in Pulumi.yml
|
||||
func TestEmptyGoRunMain(t *testing.T) {
|
||||
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
||||
Dir: filepath.Join("empty", "gorun_main"),
|
||||
Quick: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -959,6 +976,7 @@ func TestConfigBasicGo(t *testing.T) {
|
|||
{Key: "tokens[0]", Value: "shh", Path: true, Secret: true},
|
||||
{Key: "foo.bar", Value: "don't tell", Path: true, Secret: true},
|
||||
},
|
||||
RunBuild: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue