diff --git a/changelog/v0.35.4/add-delete-ns-snapshot.yaml b/changelog/v0.35.4/add-delete-ns-snapshot.yaml new file mode 100644 index 000000000..d8a9be689 --- /dev/null +++ b/changelog/v0.35.4/add-delete-ns-snapshot.yaml @@ -0,0 +1,8 @@ +changelog: +- type: FIX + issueLink: https://github.com/solo-io/gloo/issues/9274 + resolvesIssue: false + description: >- + Adds the `RemoveMatches` method in `snapshot_template.go` that removes all resources that match the given predicate. + Adds the ability for event loops to determine if the Syncer passed is a `SyncDecider` to determine if a sync is necessary. + diff --git a/changelog/v0.35.4/fix-duplicate-predicate.yaml b/changelog/v0.35.4/fix-duplicate-predicate.yaml new file mode 100644 index 000000000..9a0d63f6c --- /dev/null +++ b/changelog/v0.35.4/fix-duplicate-predicate.yaml @@ -0,0 +1,6 @@ +changelog: +- type: FIX + issueLink: https://github.com/solo-io/gloo/issues/9274 + resolvesIssue: false + description: Fixes the issue of multiple declarations of Predicate when multiple snapshots are generated + diff --git a/changelog/v0.36.2/no-external-links.yaml b/changelog/v0.36.2/no-external-links.yaml new file mode 100644 index 000000000..984f45e22 --- /dev/null +++ b/changelog/v0.36.2/no-external-links.yaml @@ -0,0 +1,6 @@ +changelog: +- type: FIX + issueLink: https://github.com/solo-io/solo-projects/issues/6768 + resolvesIssue: false + description: >- + Update protobuf processing to no longer render links to locally hosted external API docs, as they are being removed. diff --git a/pkg/api/v1/resources/core/metadata_extensions.go b/pkg/api/v1/resources/core/metadata_extensions.go index 0063256c8..a39c791da 100644 --- a/pkg/api/v1/resources/core/metadata_extensions.go +++ b/pkg/api/v1/resources/core/metadata_extensions.go @@ -17,3 +17,5 @@ func (m *Metadata) Ref() *ResourceRef { func (m *Metadata) Match(ref *ResourceRef) bool { return m.GetNamespace() == ref.GetNamespace() && m.GetName() == ref.GetName() } + +type Predicate func(metadata *Metadata) bool diff --git a/pkg/code-generator/codegen/templates/event_loop_template.go b/pkg/code-generator/codegen/templates/event_loop_template.go index 73f722d9f..4c34d18dc 100644 --- a/pkg/code-generator/codegen/templates/event_loop_template.go +++ b/pkg/code-generator/codegen/templates/event_loop_template.go @@ -92,16 +92,33 @@ func (el *{{ lower_camel .GoName }}EventLoop) Run(namespaces []string, opts clie go errutils.AggregateErrs(opts.Ctx, errs, emitterErrs, "{{ .Project.ProjectConfig.Version }}.emitter errors") go func() { var channelClosed bool + // create a new context for each loop, cancel it before each loop var cancel context.CancelFunc = func() {} + // use closure to allow cancel function to be updated as context changes defer func() { cancel() }() + + // cache the previous snapshot for comparison + var previousSnapshot *{{ .GoName }}Snapshot + for { select { case snapshot, ok := <-watch: if !ok { return } + + if syncDecider, isDecider := el.syncer.({{ .GoName }}SyncDecider); isDecider { + if shouldSync := syncDecider.ShouldSync(previousSnapshot, snapshot); !shouldSync { + continue // skip syncing this syncer + } + } else if syncDeciderWithContext, isDecider := el.syncer.({{ .GoName }}SyncDeciderWithContext); isDecider { + if shouldSync := syncDeciderWithContext.ShouldSync(opts.Ctx, previousSnapshot, snapshot); !shouldSync { + continue // skip syncing this syncer + } + } + // cancel any open watches from previous loop cancel() @@ -129,6 +146,9 @@ func (el *{{ lower_camel .GoName }}EventLoop) Run(namespaces []string, opts clie channelClosed = true close(el.ready) } + + previousSnapshot = snapshot + case <-opts.Ctx.Done(): return } diff --git a/pkg/code-generator/codegen/templates/snapshot_template.go b/pkg/code-generator/codegen/templates/snapshot_template.go index 897473eb8..1d3df9dcc 100644 --- a/pkg/code-generator/codegen/templates/snapshot_template.go +++ b/pkg/code-generator/codegen/templates/snapshot_template.go @@ -22,6 +22,7 @@ import ( "github.com/rotisserie/eris" "github.com/solo-io/go-utils/hashutils" "go.uber.org/zap" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" ) type {{ .GoName }}Snapshot struct { @@ -106,15 +107,28 @@ func (s *{{ .GoName }}Snapshot) RemoveFromResourceList(resource resources.Resour break } } - return nil + return nil {{- end }} default: return eris.Errorf("did not remove the resource because its type does not exist [%T]", resource) } } +func (s *{{ .GoName }}Snapshot) RemoveMatches(predicate core.Predicate) { +{{- range .Resources }} + var {{ upper_camel .PluralName }} {{ .ImportPrefix }}{{ .Name }}List + for _, res := range s.{{ upper_camel .PluralName }} { + if matches := predicate(res.GetMetadata()); !matches { + {{ upper_camel .PluralName }} = append({{ upper_camel .PluralName }}, res) + } + } + s.{{ upper_camel .PluralName }} = {{ upper_camel .PluralName }} +{{- end }} +} + + func (s *{{ .GoName }}Snapshot) UpsertToResourceList(resource resources.Resource) error { - refKey := resource.GetMetadata().Ref().Key() + refKey := resource.GetMetadata().Ref().Key() switch typed := resource.(type) { {{- range .Resources }} case *{{ .ImportPrefix }}{{ .Name }}: @@ -129,7 +143,7 @@ func (s *{{ .GoName }}Snapshot) UpsertToResourceList(resource resources.Resource s.{{ upper_camel .PluralName }} = append(s.{{ upper_camel .PluralName }}, typed) } s.{{ upper_camel .PluralName }}.Sort() - return nil + return nil {{- end }} default: return eris.Errorf("did not add/replace the resource type because it does not exist %T", resource) @@ -176,7 +190,7 @@ func (s {{ .GoName }}Snapshot) Stringer() {{ .GoName }}SnapshotStringer { var {{.GoName }}GvkToHashableResource = map[schema.GroupVersionKind]func() resources.HashableResource { {{- range .Resources}} {{ .ImportPrefix }}{{ .Name }}GVK: {{ .ImportPrefix }}New{{ .Name }}HashableResource, -{{- end }} +{{- end }} } `)) diff --git a/pkg/code-generator/docgen/funcs/template_funcs.go b/pkg/code-generator/docgen/funcs/template_funcs.go index 59d4063eb..847f2cc99 100644 --- a/pkg/code-generator/docgen/funcs/template_funcs.go +++ b/pkg/code-generator/docgen/funcs/template_funcs.go @@ -318,8 +318,15 @@ func linkForField(project *model.Project, docsOptions *options.DocsOptions) func linkedFile = filepath.Base(file.GetName()) //return "", errors.Errorf("failed to get generated file path for proto %v in list %v", file.GetName(), project.Request.FileToGenerate) } - linkedFile = relativeFilename(forFile.GetName(), linkedFile) + // Skip links for packages that are configured to be skipped + for _, pkg := range docsOptions.RenderOptions.GetSkipLinksForPathPrefixes() { + if strings.HasPrefix(linkedFile, pkg) { + return typeName, nil + } + } + + linkedFile = relativeFilename(forFile.GetName(), linkedFile) if docsOptions.Output == options.Restructured { linkText = ":ref:`message." + strings.TrimPrefix(field.GetTypeName(), ".") + "`" } else { diff --git a/pkg/code-generator/docgen/options/options.go b/pkg/code-generator/docgen/options/options.go index 27eda005c..29ef61941 100644 --- a/pkg/code-generator/docgen/options/options.go +++ b/pkg/code-generator/docgen/options/options.go @@ -21,8 +21,23 @@ type HugoOptions struct { } type DocsOptions struct { - Output DocsOutput - HugoOptions *HugoOptions + Output DocsOutput + HugoOptions *HugoOptions + RenderOptions *RenderOptions +} + +// RenderOptions provides options for rendering documentation +type RenderOptions struct { + // SkipLinksForPathPrefixes is a list of file path prefixes of APIs to which we should not be attempting to link + // For example: "github.com/solo-io/gloo/projects/gloo/api/external" + SkipLinksForPathPrefixes []string +} + +func (o *RenderOptions) GetSkipLinksForPathPrefixes() []string { + if o == nil { + return nil + } + return o.SkipLinksForPathPrefixes } const ( diff --git a/test/mocks/v1/testing_event_loop.sk.go b/test/mocks/v1/testing_event_loop.sk.go index 544dd1d4c..7190e2140 100644 --- a/test/mocks/v1/testing_event_loop.sk.go +++ b/test/mocks/v1/testing_event_loop.sk.go @@ -87,16 +87,33 @@ func (el *testingEventLoop) Run(namespaces []string, opts clients.WatchOpts) (<- go errutils.AggregateErrs(opts.Ctx, errs, emitterErrs, "v1.emitter errors") go func() { var channelClosed bool + // create a new context for each loop, cancel it before each loop var cancel context.CancelFunc = func() {} + // use closure to allow cancel function to be updated as context changes defer func() { cancel() }() + + // cache the previous snapshot for comparison + var previousSnapshot *TestingSnapshot + for { select { case snapshot, ok := <-watch: if !ok { return } + + if syncDecider, isDecider := el.syncer.(TestingSyncDecider); isDecider { + if shouldSync := syncDecider.ShouldSync(previousSnapshot, snapshot); !shouldSync { + continue // skip syncing this syncer + } + } else if syncDeciderWithContext, isDecider := el.syncer.(TestingSyncDeciderWithContext); isDecider { + if shouldSync := syncDeciderWithContext.ShouldSync(opts.Ctx, previousSnapshot, snapshot); !shouldSync { + continue // skip syncing this syncer + } + } + // cancel any open watches from previous loop cancel() @@ -124,6 +141,9 @@ func (el *testingEventLoop) Run(namespaces []string, opts clients.WatchOpts) (<- channelClosed = true close(el.ready) } + + previousSnapshot = snapshot + case <-opts.Ctx.Done(): return } diff --git a/test/mocks/v1/testing_snapshot.sk.go b/test/mocks/v1/testing_snapshot.sk.go index ce962ce2b..6412e1916 100644 --- a/test/mocks/v1/testing_snapshot.sk.go +++ b/test/mocks/v1/testing_snapshot.sk.go @@ -13,6 +13,7 @@ import ( "github.com/rotisserie/eris" "github.com/solo-io/go-utils/hashutils" "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -257,6 +258,65 @@ func (s *TestingSnapshot) RemoveFromResourceList(resource resources.Resource) er } } +func (s *TestingSnapshot) RemoveMatches(predicate core.Predicate) { + var Simplemocks SimpleMockResourceList + for _, res := range s.Simplemocks { + if matches := predicate(res.GetMetadata()); !matches { + Simplemocks = append(Simplemocks, res) + } + } + s.Simplemocks = Simplemocks + var Mocks MockResourceList + for _, res := range s.Mocks { + if matches := predicate(res.GetMetadata()); !matches { + Mocks = append(Mocks, res) + } + } + s.Mocks = Mocks + var Fakes FakeResourceList + for _, res := range s.Fakes { + if matches := predicate(res.GetMetadata()); !matches { + Fakes = append(Fakes, res) + } + } + s.Fakes = Fakes + var Anothermockresources AnotherMockResourceList + for _, res := range s.Anothermockresources { + if matches := predicate(res.GetMetadata()); !matches { + Anothermockresources = append(Anothermockresources, res) + } + } + s.Anothermockresources = Anothermockresources + var Clusterresources ClusterResourceList + for _, res := range s.Clusterresources { + if matches := predicate(res.GetMetadata()); !matches { + Clusterresources = append(Clusterresources, res) + } + } + s.Clusterresources = Clusterresources + var Mcts MockCustomTypeList + for _, res := range s.Mcts { + if matches := predicate(res.GetMetadata()); !matches { + Mcts = append(Mcts, res) + } + } + s.Mcts = Mcts + var Mcshts MockCustomSpecHashTypeList + for _, res := range s.Mcshts { + if matches := predicate(res.GetMetadata()); !matches { + Mcshts = append(Mcshts, res) + } + } + s.Mcshts = Mcshts + var Pods github_com_solo_io_solo_kit_pkg_api_v1_resources_common_kubernetes.PodList + for _, res := range s.Pods { + if matches := predicate(res.GetMetadata()); !matches { + Pods = append(Pods, res) + } + } + s.Pods = Pods +} + func (s *TestingSnapshot) UpsertToResourceList(resource resources.Resource) error { refKey := resource.GetMetadata().Ref().Key() switch typed := resource.(type) { diff --git a/test/mocks/v1alpha1/testing_event_loop.sk.go b/test/mocks/v1alpha1/testing_event_loop.sk.go index 8416c559c..2fa267af0 100644 --- a/test/mocks/v1alpha1/testing_event_loop.sk.go +++ b/test/mocks/v1alpha1/testing_event_loop.sk.go @@ -87,16 +87,33 @@ func (el *testingEventLoop) Run(namespaces []string, opts clients.WatchOpts) (<- go errutils.AggregateErrs(opts.Ctx, errs, emitterErrs, "v1alpha1.emitter errors") go func() { var channelClosed bool + // create a new context for each loop, cancel it before each loop var cancel context.CancelFunc = func() {} + // use closure to allow cancel function to be updated as context changes defer func() { cancel() }() + + // cache the previous snapshot for comparison + var previousSnapshot *TestingSnapshot + for { select { case snapshot, ok := <-watch: if !ok { return } + + if syncDecider, isDecider := el.syncer.(TestingSyncDecider); isDecider { + if shouldSync := syncDecider.ShouldSync(previousSnapshot, snapshot); !shouldSync { + continue // skip syncing this syncer + } + } else if syncDeciderWithContext, isDecider := el.syncer.(TestingSyncDeciderWithContext); isDecider { + if shouldSync := syncDeciderWithContext.ShouldSync(opts.Ctx, previousSnapshot, snapshot); !shouldSync { + continue // skip syncing this syncer + } + } + // cancel any open watches from previous loop cancel() @@ -124,6 +141,9 @@ func (el *testingEventLoop) Run(namespaces []string, opts clients.WatchOpts) (<- channelClosed = true close(el.ready) } + + previousSnapshot = snapshot + case <-opts.Ctx.Done(): return } diff --git a/test/mocks/v1alpha1/testing_snapshot.sk.go b/test/mocks/v1alpha1/testing_snapshot.sk.go index c86ad8967..b2470d816 100644 --- a/test/mocks/v1alpha1/testing_snapshot.sk.go +++ b/test/mocks/v1alpha1/testing_snapshot.sk.go @@ -11,6 +11,7 @@ import ( "github.com/rotisserie/eris" "github.com/solo-io/go-utils/hashutils" "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -80,6 +81,16 @@ func (s *TestingSnapshot) RemoveFromResourceList(resource resources.Resource) er } } +func (s *TestingSnapshot) RemoveMatches(predicate core.Predicate) { + var Mocks MockResourceList + for _, res := range s.Mocks { + if matches := predicate(res.GetMetadata()); !matches { + Mocks = append(Mocks, res) + } + } + s.Mocks = Mocks +} + func (s *TestingSnapshot) UpsertToResourceList(resource resources.Resource) error { refKey := resource.GetMetadata().Ref().Key() switch typed := resource.(type) { diff --git a/test/mocks/v2alpha1/testing_event_loop.sk.go b/test/mocks/v2alpha1/testing_event_loop.sk.go index 2fe948bcb..9382a2526 100644 --- a/test/mocks/v2alpha1/testing_event_loop.sk.go +++ b/test/mocks/v2alpha1/testing_event_loop.sk.go @@ -87,16 +87,33 @@ func (el *testingEventLoop) Run(namespaces []string, opts clients.WatchOpts) (<- go errutils.AggregateErrs(opts.Ctx, errs, emitterErrs, "v2alpha1.emitter errors") go func() { var channelClosed bool + // create a new context for each loop, cancel it before each loop var cancel context.CancelFunc = func() {} + // use closure to allow cancel function to be updated as context changes defer func() { cancel() }() + + // cache the previous snapshot for comparison + var previousSnapshot *TestingSnapshot + for { select { case snapshot, ok := <-watch: if !ok { return } + + if syncDecider, isDecider := el.syncer.(TestingSyncDecider); isDecider { + if shouldSync := syncDecider.ShouldSync(previousSnapshot, snapshot); !shouldSync { + continue // skip syncing this syncer + } + } else if syncDeciderWithContext, isDecider := el.syncer.(TestingSyncDeciderWithContext); isDecider { + if shouldSync := syncDeciderWithContext.ShouldSync(opts.Ctx, previousSnapshot, snapshot); !shouldSync { + continue // skip syncing this syncer + } + } + // cancel any open watches from previous loop cancel() @@ -124,6 +141,9 @@ func (el *testingEventLoop) Run(namespaces []string, opts clients.WatchOpts) (<- channelClosed = true close(el.ready) } + + previousSnapshot = snapshot + case <-opts.Ctx.Done(): return } diff --git a/test/mocks/v2alpha1/testing_snapshot.sk.go b/test/mocks/v2alpha1/testing_snapshot.sk.go index 39b213e70..591a058f4 100644 --- a/test/mocks/v2alpha1/testing_snapshot.sk.go +++ b/test/mocks/v2alpha1/testing_snapshot.sk.go @@ -13,6 +13,7 @@ import ( "github.com/rotisserie/eris" "github.com/solo-io/go-utils/hashutils" "github.com/solo-io/solo-kit/pkg/api/v1/resources" + "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -136,6 +137,30 @@ func (s *TestingSnapshot) RemoveFromResourceList(resource resources.Resource) er } } +func (s *TestingSnapshot) RemoveMatches(predicate core.Predicate) { + var Mocks MockResourceList + for _, res := range s.Mocks { + if matches := predicate(res.GetMetadata()); !matches { + Mocks = append(Mocks, res) + } + } + s.Mocks = Mocks + var Fcars FrequentlyChangingAnnotationsResourceList + for _, res := range s.Fcars { + if matches := predicate(res.GetMetadata()); !matches { + Fcars = append(Fcars, res) + } + } + s.Fcars = Fcars + var Fakes testing_solo_io.FakeResourceList + for _, res := range s.Fakes { + if matches := predicate(res.GetMetadata()); !matches { + Fakes = append(Fakes, res) + } + } + s.Fakes = Fakes +} + func (s *TestingSnapshot) UpsertToResourceList(resource resources.Resource) error { refKey := resource.GetMetadata().Ref().Key() switch typed := resource.(type) {