Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
// Copyright 2016-2018, Pulumi Corporation.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-04-06 21:30:40 +02:00
|
|
|
"bytes"
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
2020-04-06 21:30:40 +02:00
|
|
|
"io"
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2018-09-20 00:22:33 +02:00
|
|
|
"path/filepath"
|
2020-03-06 18:54:59 +01:00
|
|
|
"runtime"
|
2020-04-06 21:30:40 +02:00
|
|
|
"strings"
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
"syscall"
|
|
|
|
|
2020-04-06 21:30:40 +02:00
|
|
|
"github.com/blang/semver"
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
pbempty "github.com/golang/protobuf/ptypes/empty"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
|
2020-04-06 21:30:40 +02:00
|
|
|
"github.com/pulumi/pulumi/sdk/go/common/util/buildutil"
|
2020-03-18 23:39:00 +01:00
|
|
|
"github.com/pulumi/pulumi/sdk/go/common/util/cmdutil"
|
2020-03-18 23:34:58 +01:00
|
|
|
"github.com/pulumi/pulumi/sdk/go/common/util/logging"
|
2020-03-18 23:37:13 +01:00
|
|
|
"github.com/pulumi/pulumi/sdk/go/common/util/rpcutil"
|
2020-03-18 23:25:25 +01:00
|
|
|
"github.com/pulumi/pulumi/sdk/go/common/version"
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
"github.com/pulumi/pulumi/sdk/go/pulumi"
|
|
|
|
pulumirpc "github.com/pulumi/pulumi/sdk/proto/go"
|
|
|
|
)
|
|
|
|
|
2020-03-18 20:41:45 +01:00
|
|
|
const unableToFindProgramTemplate = "unable to find program: %s"
|
|
|
|
|
|
|
|
// findExecutable attempts to find the needed executable in various locations on the
|
|
|
|
// filesystem, eventually resorting to searching in $PATH.
|
|
|
|
func findExecutable(program string) (string, error) {
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
program = fmt.Sprintf("%s.exe", program)
|
|
|
|
}
|
|
|
|
// look in the same directory
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "unable to get current working directory")
|
|
|
|
}
|
|
|
|
|
|
|
|
cwdProgram := filepath.Join(cwd, program)
|
|
|
|
if fileInfo, err := os.Stat(cwdProgram); !os.IsNotExist(err) && !fileInfo.Mode().IsDir() {
|
|
|
|
logging.V(5).Infof("program %s found in CWD", program)
|
|
|
|
return cwdProgram, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// look in $GOPATH/bin
|
|
|
|
if goPath := os.Getenv("GOPATH"); len(goPath) > 0 {
|
|
|
|
goPathProgram := filepath.Join(goPath, "bin", program)
|
|
|
|
if fileInfo, err := os.Stat(goPathProgram); !os.IsNotExist(err) && !fileInfo.Mode().IsDir() {
|
|
|
|
logging.V(5).Infof("program %s found in $GOPATH/bin", program)
|
|
|
|
return goPathProgram, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// look in the $PATH somewhere
|
|
|
|
if fullPath, err := exec.LookPath(program); err == nil {
|
|
|
|
logging.V(5).Infof("program %s found in $PATH", program)
|
|
|
|
return fullPath, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", errors.Errorf(unableToFindProgramTemplate, program)
|
|
|
|
}
|
|
|
|
|
|
|
|
func findProgram(project string) (*exec.Cmd, error) {
|
|
|
|
// 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 := findExecutable(project)
|
|
|
|
if err == nil {
|
|
|
|
return exec.Command(program), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const message = "problem executing program (could not run language executor)"
|
|
|
|
if err.Error() == fmt.Sprintf(unableToFindProgramTemplate, project) {
|
|
|
|
logging.V(5).Infof("Unable to find program %s in $PATH, attempting invocation via 'go run'", program)
|
|
|
|
program, err = findExecutable("go")
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, message)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall back to 'go run' style execution
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
return exec.Command(program, "run", cwd), nil
|
|
|
|
}
|
|
|
|
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
// Launches the language host, which in turn fires up an RPC server implementing the LanguageRuntimeServer endpoint.
|
|
|
|
func main() {
|
|
|
|
var tracing string
|
|
|
|
flag.StringVar(&tracing, "tracing", "", "Emit tracing to a Zipkin-compatible tracing endpoint")
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
args := flag.Args()
|
|
|
|
logging.InitLogging(false, 0, false)
|
|
|
|
cmdutil.InitTracing("pulumi-language-go", "pulumi-language-go", tracing)
|
|
|
|
|
|
|
|
// Pluck out the engine so we can do logging, etc.
|
|
|
|
if len(args) == 0 {
|
|
|
|
cmdutil.Exit(errors.New("missing required engine RPC address argument"))
|
|
|
|
}
|
|
|
|
engineAddress := args[0]
|
|
|
|
|
|
|
|
// Fire up a gRPC server, letting the kernel choose a free port.
|
|
|
|
port, done, err := rpcutil.Serve(0, nil, []func(*grpc.Server) error{
|
|
|
|
func(srv *grpc.Server) error {
|
|
|
|
host := newLanguageHost(engineAddress, tracing)
|
|
|
|
pulumirpc.RegisterLanguageRuntimeServer(srv, host)
|
|
|
|
return nil
|
|
|
|
},
|
2019-09-16 23:16:43 +02:00
|
|
|
}, nil)
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
if err != nil {
|
|
|
|
cmdutil.Exit(errors.Wrapf(err, "could not start language host RPC server"))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, print out the port so that the spawner knows how to reach us.
|
|
|
|
fmt.Printf("%d\n", port)
|
|
|
|
|
|
|
|
// And finally wait for the server to stop serving.
|
|
|
|
if err := <-done; err != nil {
|
|
|
|
cmdutil.Exit(errors.Wrapf(err, "language host RPC stopped serving"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// goLanguageHost implements the LanguageRuntimeServer interface for use as an API endpoint.
|
|
|
|
type goLanguageHost struct {
|
|
|
|
engineAddress string
|
|
|
|
tracing string
|
|
|
|
}
|
|
|
|
|
|
|
|
func newLanguageHost(engineAddress, tracing string) pulumirpc.LanguageRuntimeServer {
|
|
|
|
return &goLanguageHost{
|
|
|
|
engineAddress: engineAddress,
|
|
|
|
tracing: tracing,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-06 21:30:40 +02:00
|
|
|
// modInfo is the useful portion of the output from `go list -m -json all`
|
|
|
|
// with respect to plugin acquisition
|
|
|
|
type modInfo struct {
|
|
|
|
Path string
|
|
|
|
Version string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *modInfo) getPlugin() (*pulumirpc.PluginDependency, error) {
|
|
|
|
if !strings.HasPrefix(m.Path, "github.com/pulumi/pulumi-") {
|
|
|
|
return nil, errors.New("module is not a pulumi provider")
|
|
|
|
}
|
|
|
|
|
|
|
|
// github.com/pulumi/pulumi-aws/sdk/... => aws
|
|
|
|
pluginPart := strings.Split(m.Path, "/")[2]
|
|
|
|
name := strings.SplitN(pluginPart, "-", 2)[1]
|
|
|
|
|
|
|
|
v, err := semver.ParseTolerant(m.Version)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.New("module does not have semver compatible version")
|
|
|
|
}
|
|
|
|
version := m.Version
|
|
|
|
|
|
|
|
// psuedoversions are commits that don't have a corresponding tag at the specified git hash
|
|
|
|
// https://golang.org/cmd/go/#hdr-Pseudo_versions
|
|
|
|
// pulumi-aws v1.29.1-0.20200403140640-efb5e2a48a86 (first commit after 1.29.0 release)
|
|
|
|
if buildutil.IsPseudoVersion(version) {
|
|
|
|
// no prior tag means there was never a release build
|
|
|
|
if v.Major == 0 && v.Minor == 0 && v.Patch == 0 {
|
|
|
|
return nil, errors.New("invalid pseduoversion with no prior tag")
|
|
|
|
}
|
|
|
|
// patch is typically bumped from the previous tag when using pseudo version
|
|
|
|
// downgrade the patch by 1 to make sure we match a release that exists
|
|
|
|
patch := v.Patch
|
|
|
|
if patch > 0 {
|
|
|
|
patch--
|
|
|
|
}
|
|
|
|
version = fmt.Sprintf("v%v.%v.%v", v.Major, v.Minor, patch)
|
|
|
|
}
|
|
|
|
|
|
|
|
plugin := &pulumirpc.PluginDependency{
|
|
|
|
Name: name,
|
|
|
|
Version: version,
|
|
|
|
Kind: "resource",
|
|
|
|
}
|
|
|
|
|
|
|
|
return plugin, nil
|
|
|
|
}
|
|
|
|
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
// GetRequiredPlugins computes the complete set of anticipated plugins required by a program.
|
2020-04-06 21:30:40 +02:00
|
|
|
// We're lenient here as this relies on the `go list` command and the use of modules.
|
|
|
|
// If the consumer insists on using some other form of dependency management tool like
|
|
|
|
// dep or glide, the list command fails with "go list -m: not using modules"
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
func (host *goLanguageHost) GetRequiredPlugins(ctx context.Context,
|
|
|
|
req *pulumirpc.GetRequiredPluginsRequest) (*pulumirpc.GetRequiredPluginsResponse, error) {
|
2020-04-06 21:30:40 +02:00
|
|
|
|
|
|
|
logging.V(5).Infof("GetRequiredPlugins: Determining pulumi packages")
|
|
|
|
|
|
|
|
gobin, err := findExecutable("go")
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "couldn't find go binary")
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't wire up stderr so non-module users don't see error output from list
|
|
|
|
cmd := exec.Command(gobin, "list", "-m", "-json", "all")
|
|
|
|
cmd.Env = os.Environ()
|
|
|
|
|
|
|
|
stdout, err := cmd.Output()
|
|
|
|
if err != nil {
|
|
|
|
// will err if the project isn't using modules
|
|
|
|
logging.V(5).Infof("GetRequiredPlugins: Error discovering plugin requirements: %s", err.Error())
|
|
|
|
return &pulumirpc.GetRequiredPluginsResponse{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
plugins := []*pulumirpc.PluginDependency{}
|
|
|
|
|
|
|
|
dec := json.NewDecoder(bytes.NewReader(stdout))
|
|
|
|
for {
|
|
|
|
var m modInfo
|
|
|
|
if err := dec.Decode(&m); err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
logging.V(5).Infof("GetRequiredPlugins: Error parsing list output: %s", err.Error())
|
|
|
|
return &pulumirpc.GetRequiredPluginsResponse{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
plugin, err := m.getPlugin()
|
|
|
|
if err == nil {
|
|
|
|
logging.V(5).Infof("GetRequiredPlugins: Found plugin name: %s, version: %s", plugin.Name, plugin.Version)
|
|
|
|
plugins = append(plugins, plugin)
|
|
|
|
} else {
|
|
|
|
logging.V(5).Infof(
|
|
|
|
"GetRequiredPlugins: Ignoring dependency: %s, version: %s, error: %s",
|
|
|
|
m.Path,
|
|
|
|
m.Version,
|
|
|
|
err.Error(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &pulumirpc.GetRequiredPluginsResponse{
|
|
|
|
Plugins: plugins,
|
|
|
|
}, nil
|
2018-09-20 00:22:33 +02:00
|
|
|
}
|
|
|
|
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
// RPC endpoint for LanguageRuntimeServer::Run
|
|
|
|
func (host *goLanguageHost) Run(ctx context.Context, req *pulumirpc.RunRequest) (*pulumirpc.RunResponse, error) {
|
|
|
|
// Create the environment we'll use to run the process. This is how we pass the RunInfo to the actual
|
|
|
|
// Go program runtime, to avoid needing any sort of program interface other than just a main entrypoint.
|
|
|
|
env, err := host.constructEnv(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to prepare environment")
|
|
|
|
}
|
|
|
|
|
2020-03-18 20:41:45 +01:00
|
|
|
cmd, err := findProgram(req.GetProject())
|
2018-09-20 00:22:33 +02:00
|
|
|
if err != nil {
|
2020-03-18 20:41:45 +01:00
|
|
|
return nil, err
|
2018-09-20 00:22:33 +02:00
|
|
|
}
|
2020-03-18 20:41:45 +01:00
|
|
|
cmd.Env = env
|
|
|
|
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
2018-09-20 00:22:33 +02:00
|
|
|
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
var errResult string
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
|
|
|
// If the program ran, but exited with a non-zero error code. This will happen often, since user
|
|
|
|
// errors will trigger this. So, the error message should look as nice as possible.
|
|
|
|
if status, stok := exiterr.Sys().(syscall.WaitStatus); stok {
|
|
|
|
err = errors.Errorf("program exited with non-zero exit code: %d", status.ExitStatus())
|
|
|
|
} else {
|
|
|
|
err = errors.Wrapf(exiterr, "program exited unexpectedly")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Otherwise, we didn't even get to run the program. This ought to never happen unless there's
|
|
|
|
// a bug or system condition that prevented us from running the language exec. Issue a scarier error.
|
|
|
|
err = errors.Wrapf(err, "problem executing program (could not run language executor)")
|
|
|
|
}
|
|
|
|
|
|
|
|
errResult = err.Error()
|
|
|
|
}
|
|
|
|
|
|
|
|
return &pulumirpc.RunResponse{Error: errResult}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// constructEnv constructs an environment for a Go progam by enumerating all of the optional and non-optional
|
|
|
|
// arguments present in a RunRequest.
|
|
|
|
func (host *goLanguageHost) constructEnv(req *pulumirpc.RunRequest) ([]string, error) {
|
|
|
|
config, err := host.constructConfig(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-09-21 00:17:18 +02:00
|
|
|
env := os.Environ()
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
maybeAppendEnv := func(k, v string) {
|
|
|
|
if v != "" {
|
|
|
|
env = append(env, fmt.Sprintf("%s=%s", k, v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
maybeAppendEnv(pulumi.EnvProject, req.GetProject())
|
|
|
|
maybeAppendEnv(pulumi.EnvStack, req.GetStack())
|
|
|
|
maybeAppendEnv(pulumi.EnvConfig, config)
|
|
|
|
maybeAppendEnv(pulumi.EnvDryRun, fmt.Sprintf("%v", req.GetDryRun()))
|
|
|
|
maybeAppendEnv(pulumi.EnvParallel, fmt.Sprint(req.GetParallel()))
|
|
|
|
maybeAppendEnv(pulumi.EnvMonitor, req.GetMonitorAddress())
|
|
|
|
maybeAppendEnv(pulumi.EnvEngine, host.engineAddress)
|
|
|
|
|
|
|
|
return env, nil
|
|
|
|
}
|
|
|
|
|
2019-12-16 23:51:02 +01:00
|
|
|
// constructConfig JSON-serializes the configuration data given as part of a RunRequest.
|
Support Pulumi programs written in Go
This adds rudimentary support for Pulumi programs written in Go. It
is not complete yet but the basic resource registration works.
Note that, stylistically speaking, Go is a bit different from our other
languages. This made it a bit easier to build this initial prototype,
since what we want is actually a rather thin veneer atop our existing
RPC interfaces. The lack of generics, however, adds some friction and
is something I'm continuing to hammer on; this will most likely lead to
little specialized types (e.g. StringOutput) once the dust settles.
There are two primary components:
1) A new language host, `pulumi-language-go`, which is responsible for
communicating with the engine through the usual gRPC interfaces.
Because Go programs are pre-compiled, it very simply loads a binary
with the same name as the project.
2) A client SDK library that Pulumi programs bind against. This exports
the core resource types -- including assets -- properties -- including
output properties -- and configuration.
Most remaining TODOs are marked as such in the code, and this will not
be merged until they have been addressed, and some better tests written.
2018-06-03 19:37:26 +02:00
|
|
|
func (host *goLanguageHost) constructConfig(req *pulumirpc.RunRequest) (string, error) {
|
|
|
|
configMap := req.GetConfig()
|
|
|
|
if configMap == nil {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
configJSON, err := json.Marshal(configMap)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(configJSON), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (host *goLanguageHost) GetPluginInfo(ctx context.Context, req *pbempty.Empty) (*pulumirpc.PluginInfo, error) {
|
|
|
|
return &pulumirpc.PluginInfo{
|
|
|
|
Version: version.Version,
|
|
|
|
}, nil
|
|
|
|
}
|