Skip to content

Commit

Permalink
Support policy via RunPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Frassle committed Sep 25, 2022
1 parent 0f6505e commit 490feab
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 25 deletions.
91 changes: 67 additions & 24 deletions sdk/go/common/resource/plugin/analyzer_plugin.go
Expand Up @@ -87,23 +87,73 @@ func NewPolicyAnalyzer(
return nil, errors.Wrapf(err, "failed to load Pulumi policy project located at %q", policyPackPath)
}

// For historical reasons, the Node.js plugin name is just "policy".
// All other languages have the runtime appended, e.g. "policy-<runtime>".
policyAnalyzerName := "policy"
if !strings.EqualFold(proj.Runtime.Name(), "nodejs") {
policyAnalyzerName = fmt.Sprintf("policy-%s", proj.Runtime.Name())
}
// Historically we've only support nodejs and python, and have done this via shim plugins that behaved
// sort of like mini language plugins (pulumi-analyzer-policy-python for example handles the --virtualenv
// option). For new languages (and eventually for nodejs and python as well) we just use a new method on
// the language runtime itself to launch the policy plugin.

if strings.EqualFold(proj.Runtime.Name(), "nodejs") || strings.EqualFold(proj.Runtime.Name(), "python") {
// For historical reasons, the Node.js plugin name is just "policy".
// All other languages have the runtime appended, e.g. "policy-<runtime>".
policyAnalyzerName := "policy"
if !strings.EqualFold(proj.Runtime.Name(), "nodejs") {
policyAnalyzerName = fmt.Sprintf("policy-%s", proj.Runtime.Name())
}

// Load the policy-booting analyzer plugin (i.e., `pulumi-analyzer-${policyAnalyzerName}`).
pluginPath, err := workspace.GetPluginPath(
workspace.AnalyzerPlugin, policyAnalyzerName, nil, host.GetProjectPlugins())
if err != nil {
return nil, rpcerror.Convert(err)
} else if pluginPath == "" {
return nil, fmt.Errorf("could not start policy pack %q because the built-in analyzer "+
"plugin that runs policy plugins is missing. This might occur when the plugin "+
"directory is not on your $PATH, or when the installed version of the Pulumi SDK "+
"does not support resource policies", string(name))
// Load the policy-booting analyzer plugin (i.e., `pulumi-analyzer-${policyAnalyzerName}`).
pluginPath, err := workspace.GetPluginPath(
workspace.AnalyzerPlugin, policyAnalyzerName, nil, host.GetProjectPlugins())
if err != nil {
return nil, rpcerror.Convert(err)
} else if pluginPath == "" {
return nil, fmt.Errorf("could not start policy pack %q because the built-in analyzer "+
"plugin that runs policy plugins is missing. This might occur when the plugin "+
"directory is not on your $PATH, or when the installed version of the Pulumi SDK "+
"does not support resource policies", string(name))
}

// Create the environment variables from the options.
env, err := constructEnv(opts, proj.Runtime.Name())
if err != nil {
return nil, err
}

// The `pulumi-analyzer-policy` plugin is a script that looks for the '@pulumi/pulumi/cmd/run-policy-pack'
// node module and runs it with node. To allow non-node Pulumi programs (e.g. Python, .NET, Go, etc.) to
// run node policy packs, we must set the plugin's pwd to the policy pack directory instead of the Pulumi
// program directory, so that the '@pulumi/pulumi/cmd/run-policy-pack' module from the policy pack's
// node_modules is used.
pwd := policyPackPath

args := []string{host.ServerAddr(), "."}
for k, v := range proj.Runtime.Options() {
if vstr := fmt.Sprintf("%v", v); vstr != "" {
args = append(args, fmt.Sprintf("-%s=%s", k, vstr))
}
}

plug, err := newPlugin(ctx, pwd, pluginPath, fmt.Sprintf("%v (analyzer)", name), args, env)
if err != nil {
// The original error might have been wrapped before being returned from newPlugin. So we look for
// the root cause of the error. This won't work if we switch to Go 1.13's new approach to wrapping.
if errors.Cause(err) == errRunPolicyModuleNotFound {
return nil, fmt.Errorf("it looks like the policy pack's dependencies are not installed; "+
"try running npm install or yarn install in %q", policyPackPath)
}
if errors.Cause(err) == errPluginNotFound {
return nil, fmt.Errorf("policy pack not found at %q", name)
}
return nil, errors.Wrapf(err, "policy pack %q failed to start", string(name))
}
contract.Assertf(plug != nil, "unexpected nil analyzer plugin for %s", name)

return &analyzer{
ctx: ctx,
name: name,
plug: plug,
client: pulumirpc.NewAnalyzerClient(plug.Conn),
version: proj.Version,
}, nil
}

// Create the environment variables from the options.
Expand All @@ -112,21 +162,14 @@ func NewPolicyAnalyzer(
return nil, err
}

// The `pulumi-analyzer-policy` plugin is a script that looks for the '@pulumi/pulumi/cmd/run-policy-pack'
// node module and runs it with node. To allow non-node Pulumi programs (e.g. Python, .NET, Go, etc.) to
// run node policy packs, we must set the plugin's pwd to the policy pack directory instead of the Pulumi
// program directory, so that the '@pulumi/pulumi/cmd/run-policy-pack' module from the policy pack's
// node_modules is used.
pwd := policyPackPath

args := []string{host.ServerAddr(), "."}
for k, v := range proj.Runtime.Options() {
if vstr := fmt.Sprintf("%v", v); vstr != "" {
args = append(args, fmt.Sprintf("-%s=%s", k, vstr))
}
}

plug, err := newPlugin(ctx, pwd, pluginPath, fmt.Sprintf("%v (analyzer)", name), args, env)
plug, err := newPlugin(ctx, ctx.Pwd, policyPackPath, fmt.Sprintf("%v (analyzer)", name), args, env)
if err != nil {
// The original error might have been wrapped before being returned from newPlugin. So we look for
// the root cause of the error. This won't work if we switch to Go 1.13's new approach to wrapping.
Expand Down
2 changes: 1 addition & 1 deletion sdk/go/common/resource/plugin/host.go
Expand Up @@ -426,7 +426,7 @@ func (host *defaultHost) EnsurePlugins(plugins []workspace.PluginSpec, kinds Fla
case workspace.LanguagePlugin:
if kinds&LanguagePlugins != 0 {
// Pass nil options here, we just need to check the language plugin is loadable. We can't use
// host.runtimePlugins because there might be other langauge plugins reported here (e.g
// host.runtimePlugins because there might be other language plugins reported here (e.g
// shimless multi-language providers).
if _, err := host.LanguageRuntime(plugin.Name, nil); err != nil {
result = multierror.Append(result,
Expand Down

0 comments on commit 490feab

Please sign in to comment.