Skip to content
Open
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
33 changes: 25 additions & 8 deletions go/core/cmd/controller/auth_mode_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"strings"
"testing"

authimpl "github.com/kagent-dev/kagent/go/core/internal/httpserver/auth"
Expand Down Expand Up @@ -32,7 +33,10 @@ func TestGetAuthenticator(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
authenticator := getAuthenticator(tt.authCfg)
authenticator, err := getAuthenticator(tt.authCfg)
if err != nil {
t.Fatalf("getAuthenticator() unexpected error: %v", err)
}
gotType := getTypeName(authenticator)
if gotType != tt.wantType {
t.Errorf("getAuthenticator() = %s, want %s", gotType, tt.wantType)
Expand All @@ -41,14 +45,27 @@ func TestGetAuthenticator(t *testing.T) {
}
}

func TestGetAuthenticatorPanicsOnUnknownMode(t *testing.T) {
defer func() {
r := recover()
if r == nil {
t.Fatal("expected panic for unknown auth mode, got none")
func TestGetAuthenticatorErrorsOnUnknownMode(t *testing.T) {
const invalidMode = "proxy"
authenticator, err := getAuthenticator(struct{ Mode, UserIDClaim string }{invalidMode, ""})
if err == nil {
t.Fatal("expected error for unknown auth mode, got nil")
}
if authenticator != nil {
t.Errorf("expected nil authenticator on error, got %T", authenticator)
}
// The error message must surface the invalid mode and the supported values
// so misconfigured deployments get an actionable message rather than just a
// generic failure.
msg := err.Error()
if !strings.Contains(msg, invalidMode) {
t.Errorf("error message %q does not include the invalid mode %q", msg, invalidMode)
}
for _, valid := range []string{"unsecure", "trusted-proxy"} {
if !strings.Contains(msg, valid) {
t.Errorf("error message %q does not list supported mode %q", msg, valid)
}
}()
getAuthenticator(struct{ Mode, UserIDClaim string }{"proxy", ""})
}
}

func getTypeName(v auth.AuthProvider) string {
Expand Down
15 changes: 10 additions & 5 deletions go/core/cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package main

import (
"fmt"

"github.com/kagent-dev/kagent/go/core/internal/httpserver/auth"
"github.com/kagent-dev/kagent/go/core/pkg/app"
pkgauth "github.com/kagent-dev/kagent/go/core/pkg/auth"
Expand All @@ -31,7 +33,10 @@ import (
func main() {
authorizer := &auth.NoopAuthorizer{}
app.Start(func(bootstrap app.BootstrapConfig) (*app.ExtensionConfig, error) {
authenticator := getAuthenticator(bootstrap.Config.Auth)
authenticator, err := getAuthenticator(bootstrap.Config.Auth)
if err != nil {
return nil, err
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

you're right, the defer-skip is real. app.Start calls os.Exit(1) at app.go:496 when the extension-config callback returns err, skipping the tracing-shutdown defer registered at app.go:320 that the previous panic would have unwound through.

practical flush impact at this specific call site is small (very few spans are buffered between tracing init and getAuthenticator), but the defer-skip applies to every config-failure path in startup, not just this one. the underlying issue is app.Start using os.Exit rather than returning errors.

the suggested panic(err) keeps getAuthenticator returning a typed error (so the test assertions still hold) and panics at the call site so defers run. happy to apply it. otherwise i can refactor app.Start to thread errors back to main as a separate PR. which would you prefer?

}
return &app.ExtensionConfig{
Authenticator: authenticator,
Authorizer: authorizer,
Expand All @@ -41,13 +46,13 @@ func main() {
}, nil)
}

func getAuthenticator(authCfg struct{ Mode, UserIDClaim string }) pkgauth.AuthProvider {
func getAuthenticator(authCfg struct{ Mode, UserIDClaim string }) (pkgauth.AuthProvider, error) {
switch authCfg.Mode {
case "trusted-proxy":
return auth.NewProxyAuthenticator(authCfg.UserIDClaim)
return auth.NewProxyAuthenticator(authCfg.UserIDClaim), nil
case "unsecure":
return &auth.UnsecureAuthenticator{}
return &auth.UnsecureAuthenticator{}, nil
default:
panic("unknown auth mode: " + authCfg.Mode + " (valid modes: unsecure, trusted-proxy)")
return nil, fmt.Errorf("unknown auth mode %q (valid modes: unsecure, trusted-proxy)", authCfg.Mode)
}
}