Skip to content

✨ improve logging: catalog http server, op-con resolver #1564

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 21, 2025
Merged
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
37 changes: 36 additions & 1 deletion catalogd/internal/serverutil/serverutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package serverutil
import (
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"time"

"github.com/go-logr/logr"
"github.com/gorilla/handlers"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
"sigs.k8s.io/controller-runtime/pkg/manager"
Expand Down Expand Up @@ -40,12 +43,16 @@ func AddCatalogServerToManager(mgr ctrl.Manager, cfg CatalogServerConfig, tlsFil

shutdownTimeout := 30 * time.Second

l := mgr.GetLogger().WithName("catalogd-http-server")
handler := catalogdmetrics.AddMetricsToHandler(cfg.LocalStorage.StorageServerHandler())
handler = logrLoggingHandler(l, handler)

catalogServer := manager.Server{
Name: "catalogs",
OnlyServeWhenLeader: true,
Server: &http.Server{
Addr: cfg.CatalogAddr,
Handler: catalogdmetrics.AddMetricsToHandler(cfg.LocalStorage.StorageServerHandler()),
Handler: handler,
ReadTimeout: 5 * time.Second,
// TODO: Revert this to 10 seconds if/when the API
// evolves to have significantly smaller responses
Expand All @@ -62,3 +69,31 @@ func AddCatalogServerToManager(mgr ctrl.Manager, cfg CatalogServerConfig, tlsFil

return nil
}

func logrLoggingHandler(l logr.Logger, handler http.Handler) http.Handler {
return handlers.CustomLoggingHandler(nil, handler, func(_ io.Writer, params handlers.LogFormatterParams) {
// extract parameters used in apache common log format, but then log using `logr` to remain consistent
// with other loggers used in this codebase.
username := "-"
if params.URL.User != nil {
if name := params.URL.User.Username(); name != "" {
username = name
}
}

host, _, err := net.SplitHostPort(params.Request.RemoteAddr)
if err != nil {
host = params.Request.RemoteAddr
}

uri := params.Request.RequestURI
if params.Request.ProtoMajor == 2 && params.Request.Method == http.MethodConnect {
uri = params.Request.Host
}
if uri == "" {
uri = params.URL.RequestURI()
}

l.Info("handled request", "host", host, "username", username, "method", params.Request.Method, "uri", uri, "protocol", params.Request.Proto, "status", params.StatusCode, "size", params.Size)
})
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/go-logr/logr v1.4.2
github.com/google/go-cmp v0.6.0
github.com/google/go-containerregistry v0.20.3
github.com/gorilla/handlers v1.5.2
github.com/klauspost/compress v1.17.11
github.com/onsi/ginkgo/v2 v2.22.2
github.com/onsi/gomega v1.36.2
Expand Down
43 changes: 43 additions & 0 deletions internal/resolve/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type foundBundle struct {

// Resolve returns a Bundle from a catalog that needs to get installed on the cluster.
func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bsemver.Version, *declcfg.Deprecation, error) {
l := log.FromContext(ctx)
packageName := ext.Spec.Source.Catalog.PackageName
versionRange := ext.Spec.Source.Catalog.Version
channels := ext.Spec.Source.Catalog.Channels
Expand All @@ -65,6 +66,15 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtensio
}
}

type catStat struct {
CatalogName string `json:"catalogName"`
PackageFound bool `json:"packageFound"`
TotalBundles int `json:"totalBundles"`
MatchedBundles int `json:"matchedBundles"`
}

var catStats []*catStat

resolvedBundles := []foundBundle{}
var priorDeprecation *declcfg.Deprecation

Expand All @@ -76,6 +86,16 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtensio
return fmt.Errorf("error getting package %q from catalog %q: %w", packageName, cat.Name, err)
}

cs := catStat{CatalogName: cat.Name}
catStats = append(catStats, &cs)

if isFBCEmpty(packageFBC) {
return nil
}
Comment on lines +92 to +94
Copy link
Contributor

Choose a reason for hiding this comment

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

This early exit did not exist before. Are we worried about a change in behavior?

Copy link
Member Author

Choose a reason for hiding this comment

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

Before, if len(packageFBC.Bundles) was 0, we would evaluate/run the predicates against a 0-length slice, which would return a 0-length slice, and then we'd return nil in line 103 (old) / line 124 (new)

So I don't think there's a behavior change here other than skipping some unnecessary predicate setup.


cs.PackageFound = true
cs.TotalBundles = len(packageFBC.Bundles)

var predicates []filter.Predicate[declcfg.Bundle]
if len(channels) > 0 {
channelSet := sets.New(channels...)
Expand All @@ -99,6 +119,7 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtensio

// Apply the predicates to get the candidate bundles
packageFBC.Bundles = filter.Filter(packageFBC.Bundles, filter.And(predicates...))
cs.MatchedBundles = len(packageFBC.Bundles)
if len(packageFBC.Bundles) == 0 {
return nil
}
Expand Down Expand Up @@ -158,6 +179,7 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtensio

// Check for ambiguity
if len(resolvedBundles) != 1 {
l.Info("resolution failed", "stats", catStats)
Copy link
Contributor

@tmshort tmshort Jan 10, 2025

Choose a reason for hiding this comment

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

Do we want this before all error returns from this point forward?

Copy link
Member Author

@joelanford joelanford Jan 14, 2025

Choose a reason for hiding this comment

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

We could. I felt like those other returns include context in the error message that would make it possible to figure out what happened, so that's why I only included it here.

return nil, nil, nil, resolutionError{
PackageName: packageName,
Version: versionRange,
Expand All @@ -174,12 +196,15 @@ func (r *CatalogResolver) Resolve(ctx context.Context, ext *ocv1.ClusterExtensio

// Run validations against the resolved bundle to ensure only valid resolved bundles are being returned
// Open Question: Should we grab the first valid bundle earlier?
// Answer: No, that would be a hidden resolution input, which we should avoid at all costs; the query can be
// constrained in order to eliminate the invalid bundle from the resolution.
for _, validation := range r.Validations {
if err := validation(resolvedBundle); err != nil {
return nil, nil, nil, fmt.Errorf("validating bundle %q: %w", resolvedBundle.Name, err)
}
}

l.V(4).Info("resolution succeeded", "stats", catStats)
return resolvedBundle, resolvedBundleVersion, priorDeprecation, nil
}

Expand Down Expand Up @@ -257,6 +282,9 @@ func CatalogWalker(
return false
})

availableCatalogNames := mapSlice(catalogs, func(c catalogd.ClusterCatalog) string { return c.Name })
l.Info("using ClusterCatalogs for resolution", "catalogs", availableCatalogNames)

for i := range catalogs {
cat := &catalogs[i]

Expand All @@ -271,3 +299,18 @@ func CatalogWalker(
return nil
}
}

func isFBCEmpty(fbc *declcfg.DeclarativeConfig) bool {
if fbc == nil {
return true
}
return len(fbc.Packages) == 0 && len(fbc.Channels) == 0 && len(fbc.Bundles) == 0 && len(fbc.Deprecations) == 0 && len(fbc.Others) == 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @joelanford

IHMO IsEmpty should be implemented in DeclarativeConfig itself. However, that is not a blocker for us to move forward here.

}

func mapSlice[I any, O any](in []I, f func(I) O) []O {
out := make([]O, len(in))
for i := range in {
out[i] = f(in[i])
}
return out
}
Loading