diff --git a/cmd/manager/main.go b/cmd/manager/main.go index b03472dfc..c353a4cb0 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -314,8 +314,9 @@ func main() { } if err = (&controllers.ClusterCatalogReconciler{ - Client: cl, - Cache: catalogClientBackend, + Client: cl, + CatalogCache: catalogClientBackend, + CatalogCachePopulator: catalogClient, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "ClusterCatalog") os.Exit(1) diff --git a/commitchecker.yaml b/commitchecker.yaml index b7200ace0..8d2d47df5 100644 --- a/commitchecker.yaml +++ b/commitchecker.yaml @@ -1,4 +1,4 @@ -expectedMergeBase: cd0b096468d5d3ca6268dbef22d9b1eea789f771 +expectedMergeBase: cfd4bec28827a94e717f824a111ddcac3a144709 upstreamBranch: main upstreamOrg: operator-framework upstreamRepo: operator-controller diff --git a/go.mod b/go.mod index 4bc5d40fa..7b4d2e819 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,8 @@ require ( github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.2 - github.com/onsi/ginkgo/v2 v2.20.2 - github.com/onsi/gomega v1.34.2 + github.com/onsi/ginkgo/v2 v2.21.0 + github.com/onsi/gomega v1.35.1 github.com/opencontainers/go-digest v1.0.0 github.com/operator-framework/api v0.27.0 github.com/operator-framework/catalogd v0.35.0 @@ -116,7 +116,7 @@ require ( github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect @@ -230,7 +230,7 @@ require ( golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.25.0 // indirect + golang.org/x/tools v0.26.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect diff --git a/go.sum b/go.sum index 095035bda..81cf3df9e 100644 --- a/go.sum +++ b/go.sum @@ -310,8 +310,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -518,13 +518,13 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -892,8 +892,8 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/catalogmetadata/client/client.go b/internal/catalogmetadata/client/client.go index fee3b1cc2..a68c6c989 100644 --- a/internal/catalogmetadata/client/client.go +++ b/internal/catalogmetadata/client/client.go @@ -65,17 +65,10 @@ func (c *Client) GetPackage(ctx context.Context, catalog *catalogd.ClusterCatalo catalogFsys, err := c.cache.Get(catalog.Name, catalog.Status.ResolvedSource.Image.Ref) if err != nil { - return nil, fmt.Errorf("error retrieving catalog cache: %v", err) + return nil, fmt.Errorf("error retrieving cache for catalog %q: %v", catalog.Name, err) } if catalogFsys == nil { - // TODO: https://github.com/operator-framework/operator-controller/pull/1284 - // For now we are still populating cache (if absent) on-demand, - // but we might end up just returning a "cache not found" error here - // once we implement cache population in the controller. - catalogFsys, err = c.PopulateCache(ctx, catalog) - if err != nil { - return nil, fmt.Errorf("error fetching catalog contents: %v", err) - } + return nil, fmt.Errorf("cache for catalog %q not found", catalog.Name) } pkgFsys, err := fs.Sub(catalogFsys, pkgName) diff --git a/internal/catalogmetadata/client/client_test.go b/internal/catalogmetadata/client/client_test.go index 33f43c1cf..15430d7c1 100644 --- a/internal/catalogmetadata/client/client_test.go +++ b/internal/catalogmetadata/client/client_test.go @@ -62,7 +62,7 @@ func TestClientGetPackage(t *testing.T) { catalog: defaultCatalog, cache: &fakeCache{getErr: errors.New("fetch error")}, assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { - assert.ErrorContains(t, err, `error retrieving catalog cache`) + assert.ErrorContains(t, err, `error retrieving cache for catalog "catalog-1"`) }, }, { @@ -114,18 +114,7 @@ func TestClientGetPackage(t *testing.T) { return testFS, nil }}, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { - require.NoError(t, err) - assert.Equal(t, &declcfg.DeclarativeConfig{Packages: []declcfg.Package{{Schema: declcfg.SchemaPackage, Name: "pkg-present"}}}, fbc) - }, - }, - { - name: "cache unpopulated and fails to populate", - catalog: defaultCatalog, - pkgName: "pkg-present", - cache: &fakeCache{putErr: errors.New("fake cache put error")}, - assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { - assert.Nil(t, fbc) - assert.ErrorContains(t, err, "error fetching catalog contents") + assert.ErrorContains(t, err, `cache for catalog "catalog-1" not found`) }, }, } { @@ -278,7 +267,6 @@ type fakeCache struct { getErr error putFunc func(source string, errToCache error) (fs.FS, error) - putErr error } func (c *fakeCache) Get(catalogName, resolvedRef string) (fs.FS, error) { @@ -293,9 +281,6 @@ func (c *fakeCache) Put(catalogName, resolvedRef string, source io.Reader, errTo } return c.putFunc(buf.String(), errToCache) } - if c.putErr != nil { - return nil, c.putErr - } return nil, errors.New("unexpected error") } diff --git a/internal/controllers/clustercatalog_controller.go b/internal/controllers/clustercatalog_controller.go index 0f7a26a6c..dc86ed59f 100644 --- a/internal/controllers/clustercatalog_controller.go +++ b/internal/controllers/clustercatalog_controller.go @@ -18,25 +18,30 @@ package controllers import ( "context" + "fmt" + "io/fs" apierrors "k8s.io/apimachinery/pkg/api/errors" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" ) -type CatalogCacheRemover interface { +type CatalogCache interface { + Get(catalogName, resolvedRef string) (fs.FS, error) Remove(catalogName string) error } +type CatalogCachePopulator interface { + PopulateCache(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) +} + // ClusterCatalogReconciler reconciles a ClusterCatalog object type ClusterCatalogReconciler struct { client.Client - Cache CatalogCacheRemover + CatalogCache CatalogCache + CatalogCachePopulator CatalogCachePopulator } //+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clustercatalogs,verbs=get;list;watch @@ -45,31 +50,44 @@ func (r *ClusterCatalogReconciler) Reconcile(ctx context.Context, req ctrl.Reque existingCatalog := &catalogd.ClusterCatalog{} err := r.Client.Get(ctx, req.NamespacedName, existingCatalog) if apierrors.IsNotFound(err) { - return ctrl.Result{}, r.Cache.Remove(req.Name) + if err := r.CatalogCache.Remove(req.Name); err != nil { + return ctrl.Result{}, fmt.Errorf("error removing cache for catalog %q: %v", req.Name, err) + } + return ctrl.Result{}, nil } if err != nil { return ctrl.Result{}, err } + + if existingCatalog.Status.ResolvedSource == nil || + existingCatalog.Status.ResolvedSource.Image == nil || + existingCatalog.Status.ResolvedSource.Image.Ref == "" { + // Reference is not known yet - skip cache population with no error. + // Once the reference is resolved another reconcile cycle + // will be triggered and we will progress further. + return ctrl.Result{}, nil + } + + catalogFsys, err := r.CatalogCache.Get(existingCatalog.Name, existingCatalog.Status.ResolvedSource.Image.Ref) + if err != nil { + return ctrl.Result{}, fmt.Errorf("error retrieving cache for catalog %q: %v", existingCatalog.Name, err) + } + if catalogFsys != nil { + // Cache already exists so we do not need to populate it + return ctrl.Result{}, nil + } + + if _, err = r.CatalogCachePopulator.PopulateCache(ctx, existingCatalog); err != nil { + return ctrl.Result{}, fmt.Errorf("error populating cache for catalog %q: %v", existingCatalog.Name, err) + } + return ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. func (r *ClusterCatalogReconciler) SetupWithManager(mgr ctrl.Manager) error { _, err := ctrl.NewControllerManagedBy(mgr). - For(&catalogd.ClusterCatalog{}, builder.WithPredicates(predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { - return false - }, - UpdateFunc: func(e event.UpdateEvent) bool { - return false - }, - DeleteFunc: func(e event.DeleteEvent) bool { - return true - }, - GenericFunc: func(e event.GenericEvent) bool { - return false - }, - })). + For(&catalogd.ClusterCatalog{}). Build(r) return err diff --git a/internal/controllers/clustercatalog_controller_test.go b/internal/controllers/clustercatalog_controller_test.go index 762fa15ec..639f20c96 100644 --- a/internal/controllers/clustercatalog_controller_test.go +++ b/internal/controllers/clustercatalog_controller_test.go @@ -3,7 +3,9 @@ package controllers_test import ( "context" "errors" + "io/fs" "testing" + "testing/fstest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,38 +21,142 @@ import ( ) func TestClusterCatalogReconcilerFinalizers(t *testing.T) { + const fakeResolvedRef = "fake/catalog@sha256:fakesha1" catalogKey := types.NamespacedName{Name: "test-catalog"} for _, tt := range []struct { - name string - catalog *catalogd.ClusterCatalog - cacheRemoveFunc func(catalogName string) error - wantCacheRemoveCalled bool - wantErr string + name string + catalog *catalogd.ClusterCatalog + catalogCache mockCatalogCache + catalogCachePopulator mockCatalogCachePopulator + wantGetCacheCalled bool + wantRemoveCacheCalled bool + wantPopulateCacheCalled bool + wantErr string }{ { - name: "catalog exists", + name: "catalog exists - cache unpopulated", catalog: &catalogd.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: catalogKey.Name, }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Image: &catalogd.ResolvedImageSource{ + Ref: fakeResolvedRef, + }, + }, + }, + }, + catalogCachePopulator: mockCatalogCachePopulator{ + populateCacheFunc: func(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalog.Name) + return nil, nil + }, + }, + wantGetCacheCalled: true, + wantPopulateCacheCalled: true, + }, + { + name: "catalog exists - cache already populated", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Image: &catalogd.ResolvedImageSource{ + Ref: fakeResolvedRef, + }, + }, + }, + }, + catalogCache: mockCatalogCache{ + getFunc: func(catalogName, resolvedRef string) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalogName) + assert.Equal(t, fakeResolvedRef, resolvedRef) + // Just any non-nil fs.FS to simulate existence of cache + return fstest.MapFS{}, nil + }, + }, + wantGetCacheCalled: true, + }, + { + name: "catalog exists - catalog not yet resolved", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + }, + }, + { + name: "catalog exists - error on cache population", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Image: &catalogd.ResolvedImageSource{ + Ref: fakeResolvedRef, + }, + }, + }, }, + catalogCachePopulator: mockCatalogCachePopulator{ + populateCacheFunc: func(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalog.Name) + return nil, errors.New("fake error from populate cache function") + }, + }, + wantGetCacheCalled: true, + wantPopulateCacheCalled: true, + wantErr: "error populating cache for catalog", + }, + { + name: "catalog exists - error on cache get", + catalog: &catalogd.ClusterCatalog{ + ObjectMeta: metav1.ObjectMeta{ + Name: catalogKey.Name, + }, + Status: catalogd.ClusterCatalogStatus{ + ResolvedSource: &catalogd.ResolvedCatalogSource{ + Image: &catalogd.ResolvedImageSource{ + Ref: fakeResolvedRef, + }, + }, + }, + }, + catalogCache: mockCatalogCache{ + getFunc: func(catalogName, resolvedRef string) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalogName) + assert.Equal(t, fakeResolvedRef, resolvedRef) + return nil, errors.New("fake error from cache get function") + }, + }, + wantGetCacheCalled: true, + wantErr: "error retrieving cache for catalog", }, { name: "catalog does not exist", - cacheRemoveFunc: func(catalogName string) error { - assert.Equal(t, catalogKey.Name, catalogName) - return nil + catalogCache: mockCatalogCache{ + removeFunc: func(catalogName string) error { + assert.Equal(t, catalogKey.Name, catalogName) + return nil + }, }, - wantCacheRemoveCalled: true, + wantRemoveCacheCalled: true, }, { name: "catalog does not exist - error on removal", - cacheRemoveFunc: func(catalogName string) error { - return errors.New("fake error from remove") + catalogCache: mockCatalogCache{ + removeFunc: func(catalogName string) error { + assert.Equal(t, catalogKey.Name, catalogName) + return errors.New("fake error from remove") + }, }, - wantCacheRemoveCalled: true, - wantErr: "fake error from remove", + wantRemoveCacheCalled: true, + wantErr: "error removing cache for catalog", }, } { t.Run(tt.name, func(t *testing.T) { @@ -62,13 +168,10 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { } cl := clientBuilder.Build() - cacheRemover := &mockCatalogCacheRemover{ - removeFunc: tt.cacheRemoveFunc, - } - reconciler := &controllers.ClusterCatalogReconciler{ - Client: cl, - Cache: cacheRemover, + Client: cl, + CatalogCache: controllers.CatalogCache(&tt.catalogCache), + CatalogCachePopulator: controllers.CatalogCachePopulator(&tt.catalogCachePopulator), } result, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catalogKey}) @@ -79,17 +182,48 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { } require.Equal(t, ctrl.Result{}, result) - assert.Equal(t, tt.wantCacheRemoveCalled, cacheRemover.called) + assert.Equal(t, tt.wantRemoveCacheCalled, tt.catalogCache.removeFuncCalled) + assert.Equal(t, tt.wantGetCacheCalled, tt.catalogCache.getFuncCalled) + assert.Equal(t, tt.wantPopulateCacheCalled, tt.catalogCachePopulator.populateCacheCalled) }) } } -type mockCatalogCacheRemover struct { - called bool - removeFunc func(catalogName string) error +type mockCatalogCache struct { + removeFuncCalled bool + removeFunc func(catalogName string) error + getFuncCalled bool + getFunc func(catalogName, resolvedRef string) (fs.FS, error) +} + +func (m *mockCatalogCache) Remove(catalogName string) error { + m.removeFuncCalled = true + if m.removeFunc != nil { + return m.removeFunc(catalogName) + } + + return nil +} + +func (m *mockCatalogCache) Get(catalogName, resolvedRef string) (fs.FS, error) { + m.getFuncCalled = true + if m.getFunc != nil { + return m.getFunc(catalogName, resolvedRef) + } + + return nil, nil } -func (m *mockCatalogCacheRemover) Remove(catalogName string) error { - m.called = true - return m.removeFunc(catalogName) +type mockCatalogCachePopulator struct { + populateCacheCalled bool + populateCacheFunc func(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) +} + +func (m *mockCatalogCachePopulator) PopulateCache(ctx context.Context, catalog *catalogd.ClusterCatalog) (fs.FS, error) { + m.populateCacheCalled = true + if m.populateCacheFunc != nil { + return m.populateCacheFunc(ctx, catalog) + } + + return nil, nil } diff --git a/openshift/HOWTO-origin-tests.md b/openshift/HOWTO-origin-tests.md new file mode 100644 index 000000000..50fbeea1d --- /dev/null +++ b/openshift/HOWTO-origin-tests.md @@ -0,0 +1,51 @@ +# How to Run the Origin Tests Against a Cluster + +## Create a Cluster + +The origin tests will run against any OpenShift cluster, you just need to set the +`KUBECONFIG` variable for the `oc` (or `kubectl`) command. + +As of this writing, the tests only work with a `techpreview` deployment. The tests will +be skipped if `techpreview` is not specified. + +### Example Clusterbot Command +``` +launch 4.18 aws,techpreview +``` + +## Get the openshift/origin Repo + +``` +git clone https://github.com/openshift/origin.git +``` + +## Build the Tests + +From the root of the repository: +``` +make WHAT=cmd/openshift-tests +``` + +## List the OLMv1 Tests + +All the OLMv1 tests use a `sig-olmv1` prefix, and this can be used to minimize the set of tests. From within the root of the `openshift/origin` repo: + +``` +./openshift-tests run all --dry-run | grep sig-olmv1 +``` + +## Run ALL the Tests + +To run all the tests, use the `run all` options. This requires the `KUBECONFIG` variable to reference the OpenShift cluster. + +``` +./openshift-tests run all +``` + +## Run a Subset (e.g. OLMv1) of the Tests + +To run a subset of the tests, use the `-f` option with the desrired tests. Passing `-` to the `-f` argument will let you specify the tests via stdin. + +``` +./openshift-tests run all --dry-run | grep sig-olmv1 | ./openshift-tests run -f - +``` diff --git a/requirements.txt b/requirements.txt index 91d817961..906260fd9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,4 +32,4 @@ requests==2.32.3 six==1.16.0 soupsieve==2.6 urllib3==2.2.3 -watchdog==4.0.2 +watchdog==6.0.0 diff --git a/vendor/github.com/google/pprof/profile/encode.go b/vendor/github.com/google/pprof/profile/encode.go index 860bb304c..8ce9d3cf3 100644 --- a/vendor/github.com/google/pprof/profile/encode.go +++ b/vendor/github.com/google/pprof/profile/encode.go @@ -122,6 +122,7 @@ func (p *Profile) preEncode() { } p.defaultSampleTypeX = addString(strings, p.DefaultSampleType) + p.docURLX = addString(strings, p.DocURL) p.stringTable = make([]string, len(strings)) for s, i := range strings { @@ -156,6 +157,7 @@ func (p *Profile) encode(b *buffer) { encodeInt64Opt(b, 12, p.Period) encodeInt64s(b, 13, p.commentX) encodeInt64(b, 14, p.defaultSampleTypeX) + encodeInt64Opt(b, 15, p.docURLX) } var profileDecoder = []decoder{ @@ -237,6 +239,8 @@ var profileDecoder = []decoder{ func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) }, // int64 defaultSampleType = 14 func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) }, + // string doc_link = 15; + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).docURLX) }, } // postDecode takes the unexported fields populated by decode (with @@ -384,6 +388,7 @@ func (p *Profile) postDecode() error { p.commentX = nil p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err) + p.DocURL, err = getString(p.stringTable, &p.docURLX, err) p.stringTable = nil return err } diff --git a/vendor/github.com/google/pprof/profile/merge.go b/vendor/github.com/google/pprof/profile/merge.go index eee0132e7..ba4d74640 100644 --- a/vendor/github.com/google/pprof/profile/merge.go +++ b/vendor/github.com/google/pprof/profile/merge.go @@ -476,6 +476,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) { var timeNanos, durationNanos, period int64 var comments []string seenComments := map[string]bool{} + var docURL string var defaultSampleType string for _, s := range srcs { if timeNanos == 0 || s.TimeNanos < timeNanos { @@ -494,6 +495,9 @@ func combineHeaders(srcs []*Profile) (*Profile, error) { if defaultSampleType == "" { defaultSampleType = s.DefaultSampleType } + if docURL == "" { + docURL = s.DocURL + } } p := &Profile{ @@ -509,6 +513,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) { Comments: comments, DefaultSampleType: defaultSampleType, + DocURL: docURL, } copy(p.SampleType, srcs[0].SampleType) return p, nil diff --git a/vendor/github.com/google/pprof/profile/profile.go b/vendor/github.com/google/pprof/profile/profile.go index 5551eb0bf..f47a24390 100644 --- a/vendor/github.com/google/pprof/profile/profile.go +++ b/vendor/github.com/google/pprof/profile/profile.go @@ -39,6 +39,7 @@ type Profile struct { Location []*Location Function []*Function Comments []string + DocURL string DropFrames string KeepFrames string @@ -53,6 +54,7 @@ type Profile struct { encodeMu sync.Mutex commentX []int64 + docURLX int64 dropFramesX int64 keepFramesX int64 stringTable []string @@ -555,6 +557,9 @@ func (p *Profile) String() string { for _, c := range p.Comments { ss = append(ss, "Comment: "+c) } + if url := p.DocURL; url != "" { + ss = append(ss, fmt.Sprintf("Doc: %s", url)) + } if pt := p.PeriodType; pt != nil { ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit)) } @@ -844,7 +849,7 @@ func (p *Profile) HasFileLines() bool { // Unsymbolizable returns true if a mapping points to a binary for which // locations can't be symbolized in principle, at least now. Examples are -// "[vdso]", [vsyscall]" and some others, see the code. +// "[vdso]", "[vsyscall]" and some others, see the code. func (m *Mapping) Unsymbolizable() bool { name := filepath.Base(m.File) return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/") || m.File == "//anon" diff --git a/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md b/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md index afc55af94..3011efb57 100644 --- a/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md +++ b/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md @@ -1,3 +1,18 @@ +## 2.21.0 + + + ### Features + - add support for GINKGO_TIME_FORMAT [a69eb39] + - add GINKGO_NO_COLOR to disable colors via environment variables [bcab9c8] + + ### Fixes + - increase threshold in timeline matcher [e548367] + - Fix the document by replacing `SpecsThatWillBeRun` with `SpecsThatWillRun` + [c2c4d3c] + + ### Maintenance + - bump various dependencies [7e65a00] + ## 2.20.2 Require Go 1.22+ diff --git a/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go b/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go index 743555dde..4d5749114 100644 --- a/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go +++ b/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go @@ -82,6 +82,10 @@ func New(colorMode ColorMode) Formatter { return fmt.Sprintf("\x1b[38;5;%dm", colorCode) } + if _, noColor := os.LookupEnv("GINKGO_NO_COLOR"); noColor { + colorMode = ColorModeNone + } + f := Formatter{ ColorMode: colorMode, colors: map[string]string{ diff --git a/vendor/github.com/onsi/ginkgo/v2/types/config.go b/vendor/github.com/onsi/ginkgo/v2/types/config.go index 97a049e0c..8c0dfab8c 100644 --- a/vendor/github.com/onsi/ginkgo/v2/types/config.go +++ b/vendor/github.com/onsi/ginkgo/v2/types/config.go @@ -328,7 +328,7 @@ var ParallelConfigFlags = GinkgoFlags{ // ReporterConfigFlags provides flags for the Ginkgo test process, and CLI var ReporterConfigFlags = GinkgoFlags{ {KeyPath: "R.NoColor", Name: "no-color", SectionKey: "output", DeprecatedName: "noColor", DeprecatedDocLink: "changed-command-line-flags", - Usage: "If set, suppress color output in default reporter."}, + Usage: "If set, suppress color output in default reporter. You can also set the environment variable GINKGO_NO_COLOR=TRUE"}, {KeyPath: "R.Verbose", Name: "v", SectionKey: "output", Usage: "If set, emits more output including GinkgoWriter contents."}, {KeyPath: "R.VeryVerbose", Name: "vv", SectionKey: "output", diff --git a/vendor/github.com/onsi/ginkgo/v2/types/types.go b/vendor/github.com/onsi/ginkgo/v2/types/types.go index aae69b04c..ddcbec1ba 100644 --- a/vendor/github.com/onsi/ginkgo/v2/types/types.go +++ b/vendor/github.com/onsi/ginkgo/v2/types/types.go @@ -3,13 +3,21 @@ package types import ( "encoding/json" "fmt" + "os" "sort" "strings" "time" ) const GINKGO_FOCUS_EXIT_CODE = 197 -const GINKGO_TIME_FORMAT = "01/02/06 15:04:05.999" + +var GINKGO_TIME_FORMAT = "01/02/06 15:04:05.999" + +func init() { + if os.Getenv("GINKGO_TIME_FORMAT") != "" { + GINKGO_TIME_FORMAT = os.Getenv("GINKGO_TIME_FORMAT") + } +} // Report captures information about a Ginkgo test run type Report struct { diff --git a/vendor/github.com/onsi/ginkgo/v2/types/version.go b/vendor/github.com/onsi/ginkgo/v2/types/version.go index 6dfb25f24..caf3c9f5e 100644 --- a/vendor/github.com/onsi/ginkgo/v2/types/version.go +++ b/vendor/github.com/onsi/ginkgo/v2/types/version.go @@ -1,3 +1,3 @@ package types -const VERSION = "2.20.2" +const VERSION = "2.21.0" diff --git a/vendor/github.com/onsi/gomega/CHANGELOG.md b/vendor/github.com/onsi/gomega/CHANGELOG.md index 7972bbc3a..9f6090b8d 100644 --- a/vendor/github.com/onsi/gomega/CHANGELOG.md +++ b/vendor/github.com/onsi/gomega/CHANGELOG.md @@ -1,3 +1,23 @@ +## 1.35.1 + +### Fixes +- Export EnforceDefaultTimeoutsWhenUsingContexts and DisableDefaultTimeoutsWhenUsingContext [ca36da1] + +## 1.35.0 + +### Features + +- You can now call `EnforceDefaultTimeoutsWhenUsingContexts()` to have `Eventually` honor the default timeout when passed a context. (prior to this you had to expclility add a timeout) [e4c4265] +- You can call `StopTrying(message).Successfully()` to abort a `Consistently` early without failure [eeca931] + +### Fixes + +- Stop memoizing the result of `HaveField` to avoid unexpected errors when used with async assertions. [3bdbc4e] + +### Maintenance + +- Bump all dependencies [a05a416] + ## 1.34.2 Require Go 1.22+ diff --git a/vendor/github.com/onsi/gomega/gomega_dsl.go b/vendor/github.com/onsi/gomega/gomega_dsl.go index edacf8c13..1038d7dd4 100644 --- a/vendor/github.com/onsi/gomega/gomega_dsl.go +++ b/vendor/github.com/onsi/gomega/gomega_dsl.go @@ -22,7 +22,7 @@ import ( "github.com/onsi/gomega/types" ) -const GOMEGA_VERSION = "1.34.2" +const GOMEGA_VERSION = "1.35.1" const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler. If you're using Ginkgo then you probably forgot to put your assertion in an It(). @@ -319,7 +319,19 @@ you an also use Eventually().WithContext(ctx) to pass in the context. Passed-in Eventually(client.FetchCount).WithContext(ctx).WithArguments("/users").Should(BeNumerically(">=", 17)) }, SpecTimeout(time.Second)) -Either way the context passd to Eventually is also passed to the underlying function. Now, when Ginkgo cancels the context both the FetchCount client and Gomega will be informed and can exit. +Either way the context pasesd to Eventually is also passed to the underlying function. Now, when Ginkgo cancels the context both the FetchCount client and Gomega will be informed and can exit. + +By default, when a context is passed to Eventually *without* an explicit timeout, Gomega will rely solely on the context's cancellation to determine when to stop polling. If you want to specify a timeout in addition to the context you can do so using the .WithTimeout() method. For example: + + Eventually(client.FetchCount).WithContext(ctx).WithTimeout(10*time.Second).Should(BeNumerically(">=", 17)) + +now either the context cacnellation or the timeout will cause Eventually to stop polling. + +If, instead, you would like to opt out of this behavior and have Gomega's default timeouts govern Eventuallys that take a context you can call: + + EnforceDefaultTimeoutsWhenUsingContexts() + +in the DSL (or on a Gomega instance). Now all calls to Eventually that take a context will fail if eitehr the context is cancelled or the default timeout elapses. **Category 3: Making assertions _in_ the function passed into Eventually** @@ -491,6 +503,16 @@ func SetDefaultConsistentlyPollingInterval(t time.Duration) { Default.SetDefaultConsistentlyPollingInterval(t) } +// EnforceDefaultTimeoutsWhenUsingContexts forces `Eventually` to apply a default timeout even when a context is provided. +func EnforceDefaultTimeoutsWhenUsingContexts() { + Default.EnforceDefaultTimeoutsWhenUsingContexts() +} + +// DisableDefaultTimeoutsWhenUsingContext disables the default timeout when a context is provided to `Eventually`. +func DisableDefaultTimeoutsWhenUsingContext() { + Default.DisableDefaultTimeoutsWhenUsingContext() +} + // AsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against // the matcher passed to the Should and ShouldNot methods. // diff --git a/vendor/github.com/onsi/gomega/internal/async_assertion.go b/vendor/github.com/onsi/gomega/internal/async_assertion.go index cde9e2ec8..8b4cd1f5b 100644 --- a/vendor/github.com/onsi/gomega/internal/async_assertion.go +++ b/vendor/github.com/onsi/gomega/internal/async_assertion.go @@ -335,7 +335,7 @@ func (assertion *AsyncAssertion) afterTimeout() <-chan time.Time { if assertion.asyncType == AsyncAssertionTypeConsistently { return time.After(assertion.g.DurationBundle.ConsistentlyDuration) } else { - if assertion.ctx == nil { + if assertion.ctx == nil || assertion.g.DurationBundle.EnforceDefaultTimeoutsWhenUsingContexts { return time.After(assertion.g.DurationBundle.EventuallyTimeout) } else { return nil @@ -496,7 +496,15 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch for _, err := range []error{actualErr, matcherErr} { if pollingSignalErr, ok := AsPollingSignalError(err); ok { if pollingSignalErr.IsStopTrying() { - fail("Told to stop trying") + if pollingSignalErr.IsSuccessful() { + if assertion.asyncType == AsyncAssertionTypeEventually { + fail("Told to stop trying (and ignoring call to Successfully(), as it is only relevant with Consistently)") + } else { + return true // early escape hatch for Consistently + } + } else { + fail("Told to stop trying") + } return false } if pollingSignalErr.IsTryAgainAfter() { diff --git a/vendor/github.com/onsi/gomega/internal/duration_bundle.go b/vendor/github.com/onsi/gomega/internal/duration_bundle.go index 6e0d90d3a..2e026c336 100644 --- a/vendor/github.com/onsi/gomega/internal/duration_bundle.go +++ b/vendor/github.com/onsi/gomega/internal/duration_bundle.go @@ -8,10 +8,11 @@ import ( ) type DurationBundle struct { - EventuallyTimeout time.Duration - EventuallyPollingInterval time.Duration - ConsistentlyDuration time.Duration - ConsistentlyPollingInterval time.Duration + EventuallyTimeout time.Duration + EventuallyPollingInterval time.Duration + ConsistentlyDuration time.Duration + ConsistentlyPollingInterval time.Duration + EnforceDefaultTimeoutsWhenUsingContexts bool } const ( @@ -20,15 +21,19 @@ const ( ConsistentlyDurationEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_DURATION" ConsistentlyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL" + + EnforceDefaultTimeoutsWhenUsingContextsEnvVarName = "GOMEGA_ENFORCE_DEFAULT_TIMEOUTS_WHEN_USING_CONTEXTS" ) func FetchDefaultDurationBundle() DurationBundle { + _, EnforceDefaultTimeoutsWhenUsingContexts := os.LookupEnv(EnforceDefaultTimeoutsWhenUsingContextsEnvVarName) return DurationBundle{ EventuallyTimeout: durationFromEnv(EventuallyTimeoutEnvVarName, time.Second), EventuallyPollingInterval: durationFromEnv(EventuallyPollingIntervalEnvVarName, 10*time.Millisecond), - ConsistentlyDuration: durationFromEnv(ConsistentlyDurationEnvVarName, 100*time.Millisecond), - ConsistentlyPollingInterval: durationFromEnv(ConsistentlyPollingIntervalEnvVarName, 10*time.Millisecond), + ConsistentlyDuration: durationFromEnv(ConsistentlyDurationEnvVarName, 100*time.Millisecond), + ConsistentlyPollingInterval: durationFromEnv(ConsistentlyPollingIntervalEnvVarName, 10*time.Millisecond), + EnforceDefaultTimeoutsWhenUsingContexts: EnforceDefaultTimeoutsWhenUsingContexts, } } diff --git a/vendor/github.com/onsi/gomega/internal/gomega.go b/vendor/github.com/onsi/gomega/internal/gomega.go index de1f4f336..c6e2fcc0e 100644 --- a/vendor/github.com/onsi/gomega/internal/gomega.go +++ b/vendor/github.com/onsi/gomega/internal/gomega.go @@ -127,3 +127,11 @@ func (g *Gomega) SetDefaultConsistentlyDuration(t time.Duration) { func (g *Gomega) SetDefaultConsistentlyPollingInterval(t time.Duration) { g.DurationBundle.ConsistentlyPollingInterval = t } + +func (g *Gomega) EnforceDefaultTimeoutsWhenUsingContexts() { + g.DurationBundle.EnforceDefaultTimeoutsWhenUsingContexts = true +} + +func (g *Gomega) DisableDefaultTimeoutsWhenUsingContext() { + g.DurationBundle.EnforceDefaultTimeoutsWhenUsingContexts = false +} diff --git a/vendor/github.com/onsi/gomega/internal/polling_signal_error.go b/vendor/github.com/onsi/gomega/internal/polling_signal_error.go index 83b04b1a4..3a4f7ddd9 100644 --- a/vendor/github.com/onsi/gomega/internal/polling_signal_error.go +++ b/vendor/github.com/onsi/gomega/internal/polling_signal_error.go @@ -17,6 +17,7 @@ type PollingSignalError interface { error Wrap(err error) PollingSignalError Attach(description string, obj any) PollingSignalError + Successfully() PollingSignalError Now() } @@ -45,6 +46,7 @@ type PollingSignalErrorImpl struct { wrappedErr error pollingSignalErrorType PollingSignalErrorType duration time.Duration + successful bool Attachments []PollingSignalErrorAttachment } @@ -73,6 +75,11 @@ func (s *PollingSignalErrorImpl) Unwrap() error { return s.wrappedErr } +func (s *PollingSignalErrorImpl) Successfully() PollingSignalError { + s.successful = true + return s +} + func (s *PollingSignalErrorImpl) Now() { panic(s) } @@ -81,6 +88,10 @@ func (s *PollingSignalErrorImpl) IsStopTrying() bool { return s.pollingSignalErrorType == PollingSignalErrorTypeStopTrying } +func (s *PollingSignalErrorImpl) IsSuccessful() bool { + return s.successful +} + func (s *PollingSignalErrorImpl) IsTryAgainAfter() bool { return s.pollingSignalErrorType == PollingSignalErrorTypeTryAgainAfter } diff --git a/vendor/github.com/onsi/gomega/matchers/have_field.go b/vendor/github.com/onsi/gomega/matchers/have_field.go index 6989f78c4..8dd3f871a 100644 --- a/vendor/github.com/onsi/gomega/matchers/have_field.go +++ b/vendor/github.com/onsi/gomega/matchers/have_field.go @@ -17,7 +17,7 @@ func (e missingFieldError) Error() string { return string(e) } -func extractField(actual interface{}, field string, matchername string) (interface{}, error) { +func extractField(actual interface{}, field string, matchername string) (any, error) { fields := strings.SplitN(field, ".", 2) actualValue := reflect.ValueOf(actual) @@ -64,36 +64,46 @@ func extractField(actual interface{}, field string, matchername string) (interfa type HaveFieldMatcher struct { Field string Expected interface{} +} - extractedField interface{} - expectedMatcher omegaMatcher +func (matcher *HaveFieldMatcher) expectedMatcher() omegaMatcher { + var isMatcher bool + expectedMatcher, isMatcher := matcher.Expected.(omegaMatcher) + if !isMatcher { + expectedMatcher = &EqualMatcher{Expected: matcher.Expected} + } + return expectedMatcher } func (matcher *HaveFieldMatcher) Match(actual interface{}) (success bool, err error) { - matcher.extractedField, err = extractField(actual, matcher.Field, "HaveField") + extractedField, err := extractField(actual, matcher.Field, "HaveField") if err != nil { return false, err } - var isMatcher bool - matcher.expectedMatcher, isMatcher = matcher.Expected.(omegaMatcher) - if !isMatcher { - matcher.expectedMatcher = &EqualMatcher{Expected: matcher.Expected} - } - - return matcher.expectedMatcher.Match(matcher.extractedField) + return matcher.expectedMatcher().Match(extractedField) } func (matcher *HaveFieldMatcher) FailureMessage(actual interface{}) (message string) { + extractedField, err := extractField(actual, matcher.Field, "HaveField") + if err != nil { + // this really shouldn't happen + return fmt.Sprintf("Failed to extract field '%s': %s", matcher.Field, err) + } message = fmt.Sprintf("Value for field '%s' failed to satisfy matcher.\n", matcher.Field) - message += matcher.expectedMatcher.FailureMessage(matcher.extractedField) + message += matcher.expectedMatcher().FailureMessage(extractedField) return message } func (matcher *HaveFieldMatcher) NegatedFailureMessage(actual interface{}) (message string) { + extractedField, err := extractField(actual, matcher.Field, "HaveField") + if err != nil { + // this really shouldn't happen + return fmt.Sprintf("Failed to extract field '%s': %s", matcher.Field, err) + } message = fmt.Sprintf("Value for field '%s' satisfied matcher, but should not have.\n", matcher.Field) - message += matcher.expectedMatcher.NegatedFailureMessage(matcher.extractedField) + message += matcher.expectedMatcher().NegatedFailureMessage(extractedField) return message } diff --git a/vendor/github.com/onsi/gomega/types/types.go b/vendor/github.com/onsi/gomega/types/types.go index 7c7adb941..30f2beed3 100644 --- a/vendor/github.com/onsi/gomega/types/types.go +++ b/vendor/github.com/onsi/gomega/types/types.go @@ -29,6 +29,8 @@ type Gomega interface { SetDefaultEventuallyPollingInterval(time.Duration) SetDefaultConsistentlyDuration(time.Duration) SetDefaultConsistentlyPollingInterval(time.Duration) + EnforceDefaultTimeoutsWhenUsingContexts() + DisableDefaultTimeoutsWhenUsingContext() } // All Gomega matchers must implement the GomegaMatcher interface diff --git a/vendor/golang.org/x/tools/go/ast/inspector/inspector.go b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go index 1fc1de0bd..0e0ba4c03 100644 --- a/vendor/golang.org/x/tools/go/ast/inspector/inspector.go +++ b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go @@ -73,6 +73,15 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { // check, Preorder is almost twice as fast as Nodes. The two // features seem to contribute similar slowdowns (~1.4x each). + // This function is equivalent to the PreorderSeq call below, + // but to avoid the additional dynamic call (which adds 13-35% + // to the benchmarks), we expand it out. + // + // in.PreorderSeq(types...)(func(n ast.Node) bool { + // f(n) + // return true + // }) + mask := maskOf(types) for i := 0; i < len(in.events); { ev := in.events[i] diff --git a/vendor/golang.org/x/tools/go/ast/inspector/iter.go b/vendor/golang.org/x/tools/go/ast/inspector/iter.go new file mode 100644 index 000000000..b7e959114 --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/inspector/iter.go @@ -0,0 +1,85 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.23 + +package inspector + +import ( + "go/ast" + "iter" +) + +// PreorderSeq returns an iterator that visits all the +// nodes of the files supplied to New in depth-first order. +// It visits each node n before n's children. +// The complete traversal sequence is determined by ast.Inspect. +// +// The types argument, if non-empty, enables type-based +// filtering of events: only nodes whose type matches an +// element of the types slice are included in the sequence. +func (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node] { + + // This implementation is identical to Preorder, + // except that it supports breaking out of the loop. + + return func(yield func(ast.Node) bool) { + mask := maskOf(types) + for i := 0; i < len(in.events); { + ev := in.events[i] + if ev.index > i { + // push + if ev.typ&mask != 0 { + if !yield(ev.node) { + break + } + } + pop := ev.index + if in.events[pop].typ&mask == 0 { + // Subtrees do not contain types: skip them and pop. + i = pop + 1 + continue + } + } + i++ + } + } +} + +// All[N] returns an iterator over all the nodes of type N. +// N must be a pointer-to-struct type that implements ast.Node. +// +// Example: +// +// for call := range All[*ast.CallExpr](in) { ... } +func All[N interface { + *S + ast.Node +}, S any](in *Inspector) iter.Seq[N] { + + // To avoid additional dynamic call overheads, + // we duplicate rather than call the logic of PreorderSeq. + + mask := typeOf((N)(nil)) + return func(yield func(N) bool) { + for i := 0; i < len(in.events); { + ev := in.events[i] + if ev.index > i { + // push + if ev.typ&mask != 0 { + if !yield(ev.node.(N)) { + break + } + } + pop := ev.index + if in.events[pop].typ&mask == 0 { + // Subtrees do not contain types: skip them and pop. + i = pop + 1 + continue + } + } + i++ + } + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3c182c308..e6afd1f5e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -543,7 +543,7 @@ github.com/google/go-containerregistry/pkg/v1/types ## explicit; go 1.12 github.com/google/gofuzz github.com/google/gofuzz/bytesource -# github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 +# github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db ## explicit; go 1.22 github.com/google/pprof/profile # github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 @@ -752,8 +752,8 @@ github.com/mxk/go-flowrate/flowrate # github.com/oklog/ulid v1.3.1 ## explicit github.com/oklog/ulid -# github.com/onsi/ginkgo/v2 v2.20.2 -## explicit; go 1.22 +# github.com/onsi/ginkgo/v2 v2.21.0 +## explicit; go 1.22.0 github.com/onsi/ginkgo/v2 github.com/onsi/ginkgo/v2/config github.com/onsi/ginkgo/v2/formatter @@ -774,7 +774,7 @@ github.com/onsi/ginkgo/v2/internal/parallel_support github.com/onsi/ginkgo/v2/internal/testingtproxy github.com/onsi/ginkgo/v2/reporters github.com/onsi/ginkgo/v2/types -# github.com/onsi/gomega v1.34.2 +# github.com/onsi/gomega v1.35.1 ## explicit; go 1.22 github.com/onsi/gomega github.com/onsi/gomega/format @@ -1158,7 +1158,7 @@ golang.org/x/text/width # golang.org/x/time v0.5.0 ## explicit; go 1.18 golang.org/x/time/rate -# golang.org/x/tools v0.25.0 +# golang.org/x/tools v0.26.0 ## explicit; go 1.22.0 golang.org/x/tools/cover golang.org/x/tools/go/ast/inspector