Skip to content

Commit 0070add

Browse files
authored
Allow creating analyzer plugins without config (pulumi#19392)
When running `pulumi policy publish` we are not running in the context of a project stack, but we have to be able to spawn an Analyzer to call `GetAnalyzerInfo`. Fixes pulumi#19383
1 parent 993e5dd commit 0070add

File tree

6 files changed

+124
-13
lines changed

6 files changed

+124
-13
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
changes:
2+
- type: fix
3+
scope: cli/plugin
4+
description: Allow creating analyzer plugins without config

sdk/go/common/resource/plugin/analyzer_plugin.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -150,24 +150,29 @@ func NewPolicyAnalyzer(
150150
dir := policyPackPath
151151
client := pulumirpc.NewAnalyzerClient(conn)
152152

153-
mconfig, err := MarshalProperties(resource.ToResourcePropertyMap(opts.Config),
154-
MarshalOptions{KeepSecrets: true})
155-
if err != nil {
156-
return nil, fmt.Errorf("marshalling config: %w", err)
157-
}
158-
159-
res, err := client.Handshake(ctx, &pulumirpc.AnalyzerHandshakeRequest{
153+
req := pulumirpc.AnalyzerHandshakeRequest{
160154
EngineAddress: host.ServerAddr(),
161155
RootDirectory: &dir,
162156
ProgramDirectory: &dir,
157+
}
163158

164-
Stack: opts.Stack,
165-
Project: opts.Project,
166-
Organization: opts.Organization,
159+
// We might not have options. For example example when running `pulumi
160+
// policy publish`, we are not running in the context of a project or
161+
// stack.
162+
if opts != nil {
163+
req.Stack = opts.Stack
164+
req.Project = opts.Project
165+
req.Organization = opts.Organization
166+
req.DryRun = opts.DryRun
167+
mconfig, err := MarshalProperties(resource.ToResourcePropertyMap(opts.Config),
168+
MarshalOptions{KeepSecrets: true})
169+
if err != nil {
170+
return nil, fmt.Errorf("marshalling config: %w", err)
171+
}
172+
req.Config = mconfig
173+
}
167174

168-
DryRun: opts.DryRun,
169-
Config: mconfig,
170-
})
175+
res, err := client.Handshake(ctx, &req)
171176
if err != nil {
172177
status, ok := status.FromError(err)
173178
if ok && status.Code() == codes.Unimplemented {

sdk/go/common/resource/plugin/analyzer_plugin_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,23 @@ func TestAnalyzerSpawn(t *testing.T) {
6969
err = analyzer.Close()
7070
require.NoError(t, err)
7171
}
72+
73+
func TestAnalyzerSpawnNoConfig(t *testing.T) {
74+
d := diagtest.LogSink(t)
75+
ctx, err := NewContext(d, d, nil, nil, "", nil, false, nil)
76+
require.NoError(t, err)
77+
78+
pluginPath, err := filepath.Abs("./testdata/analyzer-no-config")
79+
require.NoError(t, err)
80+
81+
path := os.Getenv("PATH")
82+
t.Setenv("PATH", pluginPath+string(os.PathListSeparator)+path)
83+
84+
// Pass `nil` for the config, this is used for example in `pulumi policy
85+
// publish`, which does not run in the context of a stack.
86+
analyzer, err := NewPolicyAnalyzer(ctx.Host, ctx, "policypack", "./testdata/policypack", nil)
87+
require.NoError(t, err)
88+
89+
err = analyzer.Close()
90+
require.NoError(t, err)
91+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"github.com/pulumi/pulumi/sdk/v3/go/common/util/rpcutil"
9+
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
10+
"google.golang.org/grpc"
11+
)
12+
13+
type analyzer struct {
14+
pulumirpc.UnimplementedAnalyzerServer
15+
}
16+
17+
func (a *analyzer) Handshake(ctx context.Context, req *pulumirpc.AnalyzerHandshakeRequest) (*pulumirpc.AnalyzerHandshakeResponse, error) {
18+
if req.Stack != "" {
19+
return nil, fmt.Errorf("expected stack not to be set, got %s", req.Stack)
20+
}
21+
if req.Project != "" {
22+
return nil, fmt.Errorf("expected project not to be set, got %s", req.Project)
23+
}
24+
if req.Organization != "" {
25+
return nil, fmt.Errorf("expected organization not to be set, got %s", req.Organization)
26+
}
27+
if req.DryRun {
28+
return nil, fmt.Errorf("expected dry run to be false, got true")
29+
}
30+
31+
if req.Config != nil {
32+
return nil, fmt.Errorf("expected config not to be set, got %s", req.Config)
33+
}
34+
35+
return &pulumirpc.AnalyzerHandshakeResponse{}, nil
36+
}
37+
38+
func main() {
39+
// Bootup a policy plugin but first assert that no config was passed
40+
41+
config := os.Getenv("PULUMI_CONFIG")
42+
if config != "" {
43+
fmt.Printf("fatal: expected no config, got %v\n", config)
44+
os.Exit(1)
45+
}
46+
47+
var cancelChannel chan bool
48+
handle, err := rpcutil.ServeWithOptions(rpcutil.ServeOptions{
49+
Cancel: cancelChannel,
50+
Init: func(srv *grpc.Server) error {
51+
pulumirpc.RegisterAnalyzerServer(srv, &analyzer{})
52+
return nil
53+
},
54+
Options: rpcutil.OpenTracingServerInterceptorOptions(nil),
55+
})
56+
if err != nil {
57+
fmt.Printf("fatal: %v\n", err)
58+
os.Exit(1)
59+
}
60+
61+
fmt.Printf("%d\n", handle.Port)
62+
63+
if err := <-handle.Done; err != nil {
64+
fmt.Printf("fatal: %v\n", err)
65+
os.Exit(1)
66+
}
67+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/sh
2+
3+
# Absolute path to this script, e.g. /home/user/bin/foo.sh
4+
SCRIPT=$(readlink -f "$0")
5+
# Absolute path this script is in, thus /home/user/bin
6+
SCRIPTPATH=$(dirname "$SCRIPT")
7+
8+
go run $SCRIPTPATH
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@echo off
2+
3+
REM Get the absolute path to this script
4+
for %%I in ("%~f0") do set SCRIPT=%%~dpI
5+
6+
REM Run the Go program in the script's directory
7+
go run "%SCRIPT%"

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy