Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 32 additions & 64 deletions cli/azd/cmd/middleware/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,99 +10,76 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/ext"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/lazy"
"github.com/azure/azure-dev/cli/azd/pkg/project"
)

type HooksMiddleware struct {
lazyEnvManager *lazy.Lazy[environment.Manager]
lazyEnv *lazy.Lazy[*environment.Environment]
lazyProjectConfig *lazy.Lazy[*project.ProjectConfig]
importManager *project.ImportManager
commandRunner exec.CommandRunner
console input.Console
options *Options
envManager environment.Manager
env *environment.Environment
projectConfig *project.ProjectConfig
importManager *project.ImportManager
commandRunner exec.CommandRunner
console input.Console
options *Options
}

// Creates a new instance of the Hooks middleware
func NewHooksMiddleware(
lazyEnvManager *lazy.Lazy[environment.Manager],
lazyEnv *lazy.Lazy[*environment.Environment],
lazyProjectConfig *lazy.Lazy[*project.ProjectConfig],
envManager environment.Manager,
env *environment.Environment,
projectConfig *project.ProjectConfig,
importManager *project.ImportManager,
commandRunner exec.CommandRunner,
console input.Console,
options *Options,
) Middleware {
return &HooksMiddleware{
lazyEnvManager: lazyEnvManager,
lazyEnv: lazyEnv,
lazyProjectConfig: lazyProjectConfig,
importManager: importManager,
commandRunner: commandRunner,
console: console,
options: options,
envManager: envManager,
env: env,
projectConfig: projectConfig,
importManager: importManager,
commandRunner: commandRunner,
console: console,
options: options,
}
}

// Runs the Hooks middleware
func (m *HooksMiddleware) Run(ctx context.Context, next NextFn) (*actions.ActionResult, error) {
env, err := m.lazyEnv.GetValue()
if err != nil {
log.Println("azd environment is not available, skipping all hook registrations.")
return next(ctx)
}

projectConfig, err := m.lazyProjectConfig.GetValue()
if err != nil || projectConfig == nil {
log.Println("azd project is not available, skipping all hook registrations.")
return next(ctx)
}

if err := m.registerServiceHooks(ctx, env, projectConfig); err != nil {
if err := m.registerServiceHooks(ctx); err != nil {
return nil, fmt.Errorf("failed registering service hooks, %w", err)
}

return m.registerCommandHooks(ctx, env, projectConfig, next)
return m.registerCommandHooks(ctx, next)
}

// Register command level hooks for the executing cobra command & action
// Invokes the middleware next function
func (m *HooksMiddleware) registerCommandHooks(
ctx context.Context,
env *environment.Environment,
projectConfig *project.ProjectConfig,
next NextFn,
) (*actions.ActionResult, error) {
if projectConfig.Hooks == nil || len(projectConfig.Hooks) == 0 {
func (m *HooksMiddleware) registerCommandHooks(ctx context.Context, next NextFn) (*actions.ActionResult, error) {
if m.projectConfig.Hooks == nil || len(m.projectConfig.Hooks) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we would get nil reference panic here if projectConfig is nil

log.Println(
"azd project is not available or does not contain any command hooks, skipping command hook registrations.",
"azd project does not contain any command hooks, skipping command hook registrations.",
)
return next(ctx)
}

envManager, err := m.lazyEnvManager.GetValue()
if err != nil {
return nil, fmt.Errorf("failed getting environment manager, %w", err)
}

hooksManager := ext.NewHooksManager(projectConfig.Path)
hooksManager := ext.NewHooksManager(m.projectConfig.Path)
hooksRunner := ext.NewHooksRunner(
hooksManager,
m.commandRunner,
envManager,
m.envManager,
m.console,
projectConfig.Path,
projectConfig.Hooks,
env,
m.projectConfig.Path,
m.projectConfig.Hooks,
m.env,
Comment on lines 67 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the environment isn't available and we have nil for environment I would expect a panic happens here.

)

var actionResult *actions.ActionResult

commandNames := []string{m.options.CommandPath}
commandNames = append(commandNames, m.options.Aliases...)

err = hooksRunner.Invoke(ctx, commandNames, func() error {
err := hooksRunner.Invoke(ctx, commandNames, func() error {
result, err := next(ctx)
if err != nil {
return err
Expand All @@ -121,17 +98,8 @@ func (m *HooksMiddleware) registerCommandHooks(

// Registers event handlers for all services within the project configuration
// Runs hooks for each matching event handler
func (m *HooksMiddleware) registerServiceHooks(
ctx context.Context,
env *environment.Environment,
projectConfig *project.ProjectConfig,
) error {
envManager, err := m.lazyEnvManager.GetValue()
if err != nil {
return fmt.Errorf("failed getting environment manager, %w", err)
}

stableServices, err := m.importManager.ServiceStable(ctx, projectConfig)
func (m *HooksMiddleware) registerServiceHooks(ctx context.Context) error {
stableServices, err := m.importManager.ServiceStable(ctx, m.projectConfig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when there isn't a project? Won't we get a panic for nil reference?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wbreza Do you see how that could happen? The DI framework we're using should guarantee project.ProjectConfig is non-nil. This same change is imported directly from #3940, so the same issue would occur.

if err != nil {
return fmt.Errorf("failed getting services: %w", err)
}
Expand All @@ -148,11 +116,11 @@ func (m *HooksMiddleware) registerServiceHooks(
serviceHooksRunner := ext.NewHooksRunner(
serviceHooksManager,
m.commandRunner,
envManager,
m.envManager,
m.console,
service.Path(),
service.Hooks,
env,
m.env,
)

for hookName := range service.Hooks {
Expand Down
19 changes: 3 additions & 16 deletions cli/azd/cmd/middleware/hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/environment/azdcontext"
"github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/ext"
"github.com/azure/azure-dev/cli/azd/pkg/lazy"
"github.com/azure/azure-dev/cli/azd/pkg/project"
"github.com/azure/azure-dev/cli/azd/test/mocks"
"github.com/azure/azure-dev/cli/azd/test/mocks/mockenv"
Expand Down Expand Up @@ -328,22 +327,10 @@ func runMiddleware(
envManager.On("Save", mock.Anything, mock.Anything).Return(nil)
envManager.On("Reload", mock.Anything, mock.Anything).Return(nil)

lazyEnvManager := lazy.NewLazy(func() (environment.Manager, error) {
return envManager, nil
})

lazyEnv := lazy.NewLazy(func() (*environment.Environment, error) {
return env, nil
})

lazyProjectConfig := lazy.NewLazy(func() (*project.ProjectConfig, error) {
return projectConfig, nil
})

middleware := NewHooksMiddleware(
lazyEnvManager,
lazyEnv,
lazyProjectConfig,
envManager,
env,
projectConfig,
project.NewImportManager(nil),
mockContext.CommandRunner,
mockContext.Console,
Expand Down