Plugins extend p5 with authentication, import suggestions, and resource opening capabilities.
import "github.com/rfhold/p5/pkg/plugin"Every plugin must implement authentication:
type AuthPlugin interface {
Authenticate(ctx context.Context, req *AuthenticateRequest) (*AuthenticateResponse, error)
}Request:
ProgramConfig- Configuration fromPulumi.yamlStackConfig- Configuration fromPulumi.{stack}.yamlStackName,ProgramName- Current identifiersSecretsProvider- Stack secrets provider
Response:
Success- Authentication resultEnv- Environment variables to setTtlSeconds- Credential lifetime (0=never expires, -1=always re-auth)Error- Error message if failed
Provides import ID suggestions:
type ImportHelperPlugin interface {
GetImportSuggestions(ctx context.Context, req *ImportSuggestionsRequest) (*ImportSuggestionsResponse, error)
}Opens resources in external tools:
type ResourceOpenerPlugin interface {
GetSupportedOpenTypes(ctx context.Context, req *SupportedOpenTypesRequest) (*SupportedOpenTypesResponse, error)
OpenResource(ctx context.Context, req *OpenResourceRequest) (*OpenResourceResponse, error)
}Open actions:
- Browser: Opens URL in default browser
- Exec: Launches alternate screen program (e.g., k9s)
- Global (
p5.toml): TOML at git root, defaults for all programs - Program (
Pulumi.yaml): YAML underp5:key - Stack (
Pulumi.{stack}.yaml): Per-stack underconfig: -> p5:plugins
type PluginConfig struct {
Cmd string // External plugin executable
Args []string // Command arguments
Config map[string]any // Plugin-specific config
Refresh *RefreshTrigger // When to refresh credentials
ImportHelper bool // Enable import helper
UseAuthEnv bool // Pass auth env to import/opener
ResourceOpener bool // Enable resource opener
}type RefreshTrigger struct {
OnWorkspaceChange *bool // Default: true
OnStackChange *bool // Default: true
OnConfigChange *bool // Default: false
}External plugins use gRPC via HashiCorp's go-plugin:
package main
import "github.com/rfhold/p5/pkg/plugin"
type MyPlugin struct{}
func (p *MyPlugin) Authenticate(ctx context.Context, req *plugin.AuthenticateRequest) (*plugin.AuthenticateResponse, error) {
env := map[string]string{"MY_TOKEN": "secret"}
return plugin.SuccessResponse(env, 3600), nil // 1 hour TTL
}
func main() {
plugin.Serve(&MyPlugin{})
}Configuration:
p5:
plugins:
my-plugin:
cmd: /path/to/my-plugin
args: ["--verbose"]- Plugins in
orderarray run sequentially (credentials cached for subsequent plugins) - Remaining plugins run in parallel
- Credentials cached with TTL tracking
- Re-authentication on workspace/stack change (configurable)
TTL > 0: Expires after specified secondsTTL = 0: Never expiresTTL = -1: Always re-authenticate