Skip to content

Commit

Permalink
Refactor GetTargetImageWithConfigs
Browse files Browse the repository at this point in the history
  • Loading branch information
emcfarlane committed Dec 18, 2024
1 parent 662ae5f commit 528ba48
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 197 deletions.
253 changes: 93 additions & 160 deletions private/buf/bufctl/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,18 @@ type Controller interface {
workspace bufworkspace.Workspace,
options ...FunctionOption,
) (bufimage.Image, error)
GetTargetImageWithConfigs(
// GetTargetImageWithConfigsAndCheckClient gets the target ImageWithConfigs
// with a configured bufcheck Client.
//
// ImageWithConfig scopes the configuration per image for use with breaking
// and lint checks. The Client is bound to the input to ensure that the
// correct plugin dependencies are used.
GetTargetImageWithConfigsAndCheckClient(
ctx context.Context,
input string,
wasmRuntime wasm.Runtime,
options ...FunctionOption,
) ([]ImageWithConfig, error)
) ([]ImageWithConfig, bufcheck.Client, error)
// GetImportableImageFileInfos gets the importable .proto FileInfos for the given input.
//
// This includes all files that can be possible imported. For example, if a Module
Expand Down Expand Up @@ -130,23 +137,11 @@ type Controller interface {
defaultMessageEncoding buffetch.MessageEncoding,
options ...FunctionOption,
) error
// NewCheckClient returns a new bufcheck.Client for the given input.
//
// Clients are bound to a specific input to ensure that the correct
// plugin dependencies are used. Where the input is resolvable to a
// Workspace, this function wraps NewCheckClientForWorkspace. For
// message inputs, the local directory or config override is used.
NewCheckClient(
ctx context.Context,
input string,
wasmRuntime wasm.Runtime,
options ...FunctionOption,
) (bufcheck.Client, error)
// NewCheckClientForWorkspace returns a new bufcheck.Client for the given Workspace.
// GetCheckClientForWorkspace returns a new bufcheck Client for the given Workspace.
//
// Clients are bound to a specific Workspace to ensure that the correct
// plugin dependencies are used.
NewCheckClientForWorkspace(
GetCheckClientForWorkspace(
ctx context.Context,
workspace bufworkspace.Workspace,
wasmRuntime wasm.Runtime,
Expand Down Expand Up @@ -357,54 +352,54 @@ func (c *controller) GetImageForWorkspace(
return c.getImageForWorkspace(ctx, workspace, functionOptions)
}

func (c *controller) GetTargetImageWithConfigs(
func (c *controller) GetTargetImageWithConfigsAndCheckClient(
ctx context.Context,
input string,
wasmRuntime wasm.Runtime,
options ...FunctionOption,
) (_ []ImageWithConfig, retErr error) {
) (_ []ImageWithConfig, _ bufcheck.Client, retErr error) {
defer c.handleFileAnnotationSetRetError(&retErr)
functionOptions := newFunctionOptions(c)
for _, option := range options {
option(functionOptions)
}
ref, err := c.buffetchRefParser.GetRef(ctx, input)
if err != nil {
return nil, err
return nil, nil, err
}
var workspace bufworkspace.Workspace
switch t := ref.(type) {
case buffetch.ProtoFileRef:
workspace, err := c.getWorkspaceForProtoFileRef(ctx, t, functionOptions)
workspace, err = c.getWorkspaceForProtoFileRef(ctx, t, functionOptions)
if err != nil {
return nil, err
return nil, nil, err
}
return c.buildTargetImageWithConfigs(ctx, workspace, functionOptions)
case buffetch.SourceRef:
workspace, err := c.getWorkspaceForSourceRef(ctx, t, functionOptions)
workspace, err = c.getWorkspaceForSourceRef(ctx, t, functionOptions)
if err != nil {
return nil, err
return nil, nil, err
}
return c.buildTargetImageWithConfigs(ctx, workspace, functionOptions)
case buffetch.ModuleRef:
workspace, err := c.getWorkspaceForModuleRef(ctx, t, functionOptions)
workspace, err = c.getWorkspaceForModuleRef(ctx, t, functionOptions)
if err != nil {
return nil, err
return nil, nil, err
}
return c.buildTargetImageWithConfigs(ctx, workspace, functionOptions)
case buffetch.MessageRef:
image, err := c.getImageForMessageRef(ctx, t, functionOptions)
if err != nil {
return nil, err
return nil, nil, err
}
bucket, err := c.storageosProvider.NewReadWriteBucket(
".",
storageos.ReadWriteBucketWithSymlinksIfSupported(),
)
if err != nil {
return nil, err
return nil, nil, err
}
lintConfig := bufconfig.DefaultLintConfigV1
breakingConfig := bufconfig.DefaultBreakingConfigV1
var pluginConfigs []bufconfig.PluginConfig
pluginKeyProvider := bufplugin.NopPluginKeyProvider
bufYAMLFile, err := bufconfig.GetBufYAMLFileForPrefixOrOverride(
ctx,
bucket,
Expand All @@ -413,16 +408,15 @@ func (c *controller) GetTargetImageWithConfigs(
)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, err
return nil, nil, err
}
// We did not find a buf.yaml in our current directory, and there was no config override.
// Use the defaults.
} else {
pluginConfigs = bufYAMLFile.PluginConfigs()
if topLevelLintConfig := bufYAMLFile.TopLevelLintConfig(); topLevelLintConfig == nil {
// Ensure that this is a v2 config
if fileVersion := bufYAMLFile.FileVersion(); fileVersion != bufconfig.FileVersionV2 {
return nil, syserror.Newf("non-v2 version with no top-level lint config: %s", fileVersion)
return nil, nil, syserror.Newf("non-v2 version with no top-level lint config: %s", fileVersion)
}
// v2 config without a top-level lint config, use v2 default
lintConfig = bufconfig.DefaultLintConfigV2
Expand All @@ -431,26 +425,87 @@ func (c *controller) GetTargetImageWithConfigs(
}
if topLevelBreakingConfig := bufYAMLFile.TopLevelBreakingConfig(); topLevelBreakingConfig == nil {
if fileVersion := bufYAMLFile.FileVersion(); fileVersion != bufconfig.FileVersionV2 {
return nil, syserror.Newf("non-v2 version with no top-level breaking config: %s", fileVersion)
return nil, nil, syserror.Newf("non-v2 version with no top-level breaking config: %s", fileVersion)
}
// v2 config without a top-level breaking config, use v2 default
breakingConfig = bufconfig.DefaultBreakingConfigV2
} else {
breakingConfig = topLevelBreakingConfig
}
// The directory path is resolved to a buf.yaml file and a buf.lock file. If the
// buf.yaml file is found, the PluginConfigs from the buf.yaml file and the PluginKeys
// from the buf.lock file are resolved to create the PluginKeyProvider.
pluginConfigs = bufYAMLFile.PluginConfigs()
// If a config override is provided, the PluginConfig remote Refs use the BSR
// to resolve the PluginKeys. No buf.lock is required.
// If the buf.yaml file is not found, the bufplugin.NopPluginKeyProvider is returned.
// If the buf.lock file is not found, the bufplugin.NopPluginKeyProvider is returned.
if functionOptions.configOverride != "" {
// To support remote plugins in the override, we need to resolve the remote
// Refs to PluginKeys. A buf.lock file is not required for this operation.
// We use the BSR to resolve any remote plugin Refs.
pluginKeyProvider = c.pluginKeyProvider
} else if bufYAMLFile.FileVersion() == bufconfig.FileVersionV2 {
var pluginKeys []bufplugin.PluginKey
if bufLockFile, err := bufconfig.GetBufLockFileForPrefix(
ctx,
bucket,
// buf.lock files live next to the buf.yaml
".",
); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, nil, err
}
// We did not find a buf.lock in our current directory.
// Remote plugins are not available.
pluginKeys = nil
} else {
pluginKeys = bufLockFile.RemotePluginKeys()
}
pluginKeyProvider, err = newStaticPluginKeyProviderForPluginConfigs(
pluginConfigs,
pluginKeys,
)
if err != nil {
return nil, nil, err
}
}
}
return []ImageWithConfig{
imageWithConfigs := []ImageWithConfig{
newImageWithConfig(
image,
lintConfig,
breakingConfig,
pluginConfigs,
),
}, nil
}
pluginRunnerProvider := bufcheck.NewLocalRunnerProvider(
wasmRuntime,
pluginKeyProvider,
c.pluginDataProvider,
)
checkClient, err := bufcheck.NewClient(
c.logger,
pluginRunnerProvider,
bufcheck.ClientWithStderr(c.container.Stderr()),
)
if err != nil {
return nil, nil, err
}
return imageWithConfigs, checkClient, nil
default:
// This is a system error.
return nil, syserror.Newf("invalid Ref: %T", ref)
return nil, nil, syserror.Newf("invalid Ref: %T", ref)
}
targetImageWithConfigs, err := c.buildTargetImageWithConfigs(ctx, workspace, functionOptions)
if err != nil {
return nil, nil, err
}
checkClient, err := c.GetCheckClientForWorkspace(ctx, workspace, wasmRuntime)
if err != nil {
return nil, nil, err
}
return targetImageWithConfigs, checkClient, err
}

func (c *controller) GetImportableImageFileInfos(
Expand Down Expand Up @@ -730,65 +785,7 @@ func (c *controller) PutMessage(
return errors.Join(err, writeCloser.Close())
}

func (c *controller) NewCheckClient(
ctx context.Context,
input string,
wasmRuntime wasm.Runtime,
options ...FunctionOption,
) (_ bufcheck.Client, retErr error) {
defer c.handleFileAnnotationSetRetError(&retErr)
functionOptions := newFunctionOptions(c)
for _, option := range options {
option(functionOptions)
}
ref, err := c.buffetchRefParser.GetRef(ctx, input)
if err != nil {
return nil, err
}
switch t := ref.(type) {
case buffetch.ProtoFileRef:
workspace, err := c.getWorkspaceForProtoFileRef(ctx, t, functionOptions)
if err != nil {
return nil, err
}
return c.NewCheckClientForWorkspace(ctx, workspace, wasmRuntime)
case buffetch.SourceRef:
workspace, err := c.getWorkspaceForSourceRef(ctx, t, functionOptions)
if err != nil {
return nil, err
}
return c.NewCheckClientForWorkspace(ctx, workspace, wasmRuntime)
case buffetch.ModuleRef:
workspace, err := c.getWorkspaceForModuleRef(ctx, t, functionOptions)
if err != nil {
return nil, err
}
return c.NewCheckClientForWorkspace(ctx, workspace, wasmRuntime)
case buffetch.MessageRef:
// MessageRefs do not resolve to a Workspace, so we resolve the
// PluginKeyProvider by the buf.yaml provided in the current directory.
// This follows the logic in GetTargetImageWithConfigs.
pluginKeyProvider, err := c.getPluginKeyProviderForDirPath(ctx, ".", functionOptions)
if err != nil {
return nil, err
}
pluginRunnerProvider := bufcheck.NewLocalRunnerProvider(
wasmRuntime,
pluginKeyProvider,
c.pluginDataProvider,
)
return bufcheck.NewClient(
c.logger,
pluginRunnerProvider,
bufcheck.ClientWithStderr(c.container.Stderr()),
)
default:
// This is a system error.
return nil, syserror.Newf("invalid Ref: %T", ref)
}
}

func (c *controller) NewCheckClientForWorkspace(
func (c *controller) GetCheckClientForWorkspace(
ctx context.Context,
workspace bufworkspace.Workspace,
wasmRuntime wasm.Runtime,
Expand Down Expand Up @@ -1291,70 +1288,6 @@ func (c *controller) handleFileAnnotationSetRetError(retErrAddr *error) {
}
}

// getPluginKeyProviderForDirPath creates a new PluginKeyProvider for the directory path.
//
// The directory path is resolved to a buf.yaml file and a buf.lock file. If the
// buf.yaml file is found, the PluginConfigs from the buf.yaml file and the PluginKeys
// from the buf.lock file are resolved to create the PluginKeyProvider.
//
// If a config override is provided, the PluginConfig remote Refs use the BSR
// to resolve the PluginKeys. No buf.lock is required.
// If the buf.yaml file is not found, the bufplugin.NopPluginKeyProvider is returned.
// If the buf.lock file is not found, the bufplugin.NopPluginKeyProvider is returned.
func (c *controller) getPluginKeyProviderForDirPath(
ctx context.Context,
dirPath string,
functionOptions *functionOptions,
) (_ bufplugin.PluginKeyProvider, retErr error) {
// If there is a config override, we do not use the buf.lock file.
// Remote plugins are available, use the BSR to resolve any plugin Refs.
if functionOptions.configOverride != "" {
return c.pluginKeyProvider, nil
}
var (
pluginConfigs []bufconfig.PluginConfig
pluginKeys []bufplugin.PluginKey
)
bucket, err := c.storageosProvider.NewReadWriteBucket(
dirPath,
storageos.ReadWriteBucketWithSymlinksIfSupported(),
)
if err != nil {
return nil, err
}
bufYAMLFile, err := bufconfig.GetBufYAMLFileForPrefix(ctx, bucket, ".")
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, err
}
// We did not find a buf.yaml in our current directory,
// and there was no config override.
// Remote plugins are not available.
return bufplugin.NopPluginKeyProvider, nil
}
pluginConfigs = bufYAMLFile.PluginConfigs()
if len(pluginConfigs) == 0 || bufYAMLFile.FileVersion() != bufconfig.FileVersionV2 {
// No plugins were found or they are not supported by the current version.
return bufplugin.NopPluginKeyProvider, nil
}
bufLockFile, err := bufconfig.GetBufLockFileForPrefix(
ctx,
bucket,
// buf.lock files live next to the buf.yaml
".",
)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, err
}
// We did not find a buf.lock in our current directory.
// Remote plugins are not available.
return bufplugin.NopPluginKeyProvider, nil
}
pluginKeys = bufLockFile.RemotePluginKeys()
return newStaticPluginKeyProviderForPluginConfigs(pluginConfigs, pluginKeys)
}

func getImageFileInfosForModuleSet(ctx context.Context, moduleSet bufmodule.ModuleSet) ([]bufimage.ImageFileInfo, error) {
// Sorted.
fileInfos, err := bufmodule.GetFileInfos(
Expand Down
2 changes: 1 addition & 1 deletion private/buf/buflsp/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ func (f *file) FindModule(ctx context.Context) {
}

// Get the check client for this workspace.
checkClient, err := f.lsp.controller.NewCheckClientForWorkspace(ctx, workspace, f.lsp.wasmRuntime)
checkClient, err := f.lsp.controller.GetCheckClientForWorkspace(ctx, workspace, f.lsp.wasmRuntime)
if err != nil {
f.lsp.logger.Warn("could not get check client", slogext.ErrorAttr(err))
return
Expand Down
Loading

0 comments on commit 528ba48

Please sign in to comment.