upgrade to latest dependencies (#570)

bumping knative.dev/pkg d82be48...5708c4c:
  > 5708c4c codegen - support multiple annotation keys (# 2350)
  > 22c0eba Update community files (# 2352)
  > 54071ad Add retry checker for DNS failure from Ingress (# 2351)
bumping knative.dev/hack 128cf01...69a2295:
  > 69a2295 Update community files (# 117)

Signed-off-by: Knative Automation <automation@knative.team>
This commit is contained in:
knative-automation 2021-11-17 23:07:29 -08:00 committed by GitHub
parent da260af2ab
commit 77b107b17f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 149 additions and 51 deletions

4
go.mod
View File

@ -19,6 +19,6 @@ require (
k8s.io/client-go v0.21.4 k8s.io/client-go v0.21.4
k8s.io/code-generator v0.21.4 k8s.io/code-generator v0.21.4
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7
knative.dev/hack v0.0.0-20211112192837-128cf0150a69 knative.dev/hack v0.0.0-20211117134436-69a2295d54ce
knative.dev/pkg v0.0.0-20211116213053-d82be484e4c3 knative.dev/pkg v0.0.0-20211117215328-5708c4c44232
) )

7
go.sum
View File

@ -1130,10 +1130,11 @@ k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7Br
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
knative.dev/hack v0.0.0-20211112192837-128cf0150a69 h1:/3NW6B9VeqAwpW0ZAS+8xLfwgVMIqH+CPmesDATovhk=
knative.dev/hack v0.0.0-20211112192837-128cf0150a69/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI= knative.dev/hack v0.0.0-20211112192837-128cf0150a69/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
knative.dev/pkg v0.0.0-20211116213053-d82be484e4c3 h1:kJKOHViOLjLx1VpBVs5uJFSXTVEdyQylaHOJCU4AVVY= knative.dev/hack v0.0.0-20211117134436-69a2295d54ce h1:dPARWsX/9nmWg9g09MtGnsn7nZUwjA0UWpKSUozgS6g=
knative.dev/pkg v0.0.0-20211116213053-d82be484e4c3/go.mod h1:VqUp1KWJqpTDNoiSI/heaX3uMdubImslJE2tBkP+Bbw= knative.dev/hack v0.0.0-20211117134436-69a2295d54ce/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
knative.dev/pkg v0.0.0-20211117215328-5708c4c44232 h1:mye0h0gL0sI2YraL1X5FjyfQZoYVaaaRemTJq+uw+/8=
knative.dev/pkg v0.0.0-20211117215328-5708c4c44232/go.mod h1:VqUp1KWJqpTDNoiSI/heaX3uMdubImslJE2tBkP+Bbw=
pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=

View File

@ -19,25 +19,41 @@ import "strings"
// Adapted from the k8s.io comment parser https://github.com/kubernetes/gengo/blob/master/types/comments.go // Adapted from the k8s.io comment parser https://github.com/kubernetes/gengo/blob/master/types/comments.go
// CommentsTags maps marker prefixes to a set of tags containing keys and values
type CommentTags map[string]CommentTag
// CommentTags maps keys to a list of values
type CommentTag map[string][]string
// ExtractCommentTags parses comments for lines of the form: // ExtractCommentTags parses comments for lines of the form:
// //
// 'marker' + ':' "key=value,key2=value2". // "marker" + "prefix" + ':' + "key=value,key2=value2".
// //
// Values are optional; empty map is the default. A tag can be specified more than // In the following example the marker is '+' and the prefix is 'foo':
// +foo:key=value1,key2=value2,key=value3
//
// Values are optional; empty map is the default. A tag can be specified more than
// one time and all values are returned. If the resulting map has an entry for // one time and all values are returned. If the resulting map has an entry for
// a key, the value (a slice) is guaranteed to have at least 1 element. // a key, the value (a slice) is guaranteed to have at least 1 element.
// //
// Example: if you pass "+" for 'marker', and the following lines are in // Example: if you pass "+" for 'marker', and the following lines are in
// the comments: // the comments:
// +foo:key=value1,key2=value2 // +foo:key=value1,key2=value2,key=value3
// +bar // +bar
// //
// Then this function will return: // Then this function will return:
// map[string]map[string]string{"foo":{"key":value1","key2":"value2"}, "bar": nil} // map[string]map[string]string{
// "foo":{
// "key": []string{"value1", "value3"},
// "key2": []string{"value2"}
// },
// "bar": {},
// }
// //
// Users are not expected to repeat values. // Users are not expected to repeat values.
func ExtractCommentTags(marker string, lines []string) map[string]map[string]string { func ExtractCommentTags(marker string, lines []string) CommentTags {
out := map[string]map[string]string{} out := CommentTags{}
for _, line := range lines { for _, line := range lines {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if len(line) == 0 || !strings.HasPrefix(line, marker) { if len(line) == 0 || !strings.HasPrefix(line, marker) {
@ -45,29 +61,32 @@ func ExtractCommentTags(marker string, lines []string) map[string]map[string]str
} }
options := strings.SplitN(line[len(marker):], ":", 2) options := strings.SplitN(line[len(marker):], ":", 2)
prefix := options[0]
if len(options) == 2 { if len(options) == 2 {
vals := strings.Split(options[1], ",") vals := strings.Split(options[1], ",")
opts := out[options[0]] opts := out[prefix]
if opts == nil { if opts == nil {
opts = make(map[string]string, len(vals)) opts = make(CommentTag, len(vals))
out[prefix] = opts
} }
for _, pair := range vals { for _, pair := range vals {
if kv := strings.SplitN(pair, "=", 2); len(kv) == 2 { kv := strings.SplitN(pair, "=", 2)
opts[kv[0]] = kv[1] if len(kv) == 1 && kv[0] == "" {
} else if kv[0] != "" { continue
opts[kv[0]] = "" }
if _, ok := opts[kv[0]]; !ok {
opts[kv[0]] = []string{}
}
if len(kv) == 2 {
opts[kv[0]] = append(opts[kv[0]], kv[1])
} }
} }
if len(opts) == 0 {
out[options[0]] = nil
} else {
out[options[0]] = opts
}
} else if len(options) == 1 && options[0] != "" { } else if len(options) == 1 && options[0] != "" {
if _, has := out[options[0]]; !has { if _, has := out[prefix]; !has {
out[options[0]] = nil out[prefix] = CommentTag{}
} }
} }
} }

View File

@ -193,29 +193,36 @@ func MustParseClientGenTags(lines []string) Tags {
return ret return ret
} }
func extractCommentTags(t *types.Type) map[string]map[string]string { func extractCommentTags(t *types.Type) CommentTags {
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
return ExtractCommentTags("+", comments) return ExtractCommentTags("+", comments)
} }
func extractReconcilerClassTag(tags map[string]map[string]string) (string, bool) { func extractReconcilerClassesTag(tags CommentTags) ([]string, bool) {
vals, ok := tags["genreconciler"] vals, ok := tags["genreconciler"]
if !ok { if !ok {
return "", false return nil, false
} }
classname, has := vals["class"] classnames, has := vals["class"]
return classname, has if has && len(classnames) == 0 {
return nil, false
}
return classnames, has
} }
func isKRShaped(tags map[string]map[string]string) bool { func isKRShaped(tags CommentTags) bool {
vals, has := tags["genreconciler"] vals, has := tags["genreconciler"]
if !has { if !has {
return false return false
} }
return vals["krshapedlogic"] != "false" stringVals, has := vals["krshapedlogic"]
if !has || len(vals) == 0 {
return true // Default is true
}
return stringVals[0] != "false"
} }
func isNonNamespaced(tags map[string]map[string]string) bool { func isNonNamespaced(tags CommentTags) bool {
vals, has := tags["genclient"] vals, has := tags["genclient"]
if !has { if !has {
return false return false
@ -224,7 +231,7 @@ func isNonNamespaced(tags map[string]map[string]string) bool {
return has return has
} }
func stubs(tags map[string]map[string]string) bool { func stubs(tags CommentTags) bool {
vals, has := tags["genreconciler"] vals, has := tags["genreconciler"]
if !has { if !has {
return false return false
@ -545,7 +552,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
// Fix for golang iterator bug. // Fix for golang iterator bug.
t := t t := t
extracted := extractCommentTags(t) extracted := extractCommentTags(t)
reconcilerClass, hasReconcilerClass := extractReconcilerClassTag(extracted) reconcilerClasses, hasReconcilerClass := extractReconcilerClassesTag(extracted)
nonNamespaced := isNonNamespaced(extracted) nonNamespaced := isNonNamespaced(extracted)
isKRShaped := isKRShaped(extracted) isKRShaped := isKRShaped(extracted)
stubs := stubs(extracted) stubs := stubs(extracted)
@ -573,7 +580,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
clientPkg: clientPackagePath, clientPkg: clientPackagePath,
informerPackagePath: informerPackagePath, informerPackagePath: informerPackagePath,
schemePkg: filepath.Join(customArgs.VersionedClientSetPackage, "scheme"), schemePkg: filepath.Join(customArgs.VersionedClientSetPackage, "scheme"),
reconcilerClass: reconcilerClass, reconcilerClasses: reconcilerClasses,
hasReconcilerClass: hasReconcilerClass, hasReconcilerClass: hasReconcilerClass,
hasStatus: hasStatus(t), hasStatus: hasStatus(t),
}) })
@ -602,7 +609,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
outputPackage: filepath.Join(packagePath, "stub"), outputPackage: filepath.Join(packagePath, "stub"),
imports: generator.NewImportTracker(), imports: generator.NewImportTracker(),
informerPackagePath: informerPackagePath, informerPackagePath: informerPackagePath,
reconcilerClass: reconcilerClass, reconcilerClasses: reconcilerClasses,
hasReconcilerClass: hasReconcilerClass, hasReconcilerClass: hasReconcilerClass,
}) })
return generators return generators
@ -633,7 +640,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
listerPkg: listerPackagePath, listerPkg: listerPackagePath,
groupGoName: groupGoName, groupGoName: groupGoName,
groupVersion: gv, groupVersion: gv,
reconcilerClass: reconcilerClass, reconcilerClasses: reconcilerClasses,
hasReconcilerClass: hasReconcilerClass, hasReconcilerClass: hasReconcilerClass,
nonNamespaced: nonNamespaced, nonNamespaced: nonNamespaced,
isKRShaped: isKRShaped, isKRShaped: isKRShaped,

View File

@ -38,7 +38,7 @@ type reconcilerControllerGenerator struct {
schemePkg string schemePkg string
informerPackagePath string informerPackagePath string
reconcilerClass string reconcilerClasses []string
hasReconcilerClass bool hasReconcilerClass bool
hasStatus bool hasStatus bool
} }
@ -69,7 +69,7 @@ func (g *reconcilerControllerGenerator) GenerateType(c *generator.Context, t *ty
m := map[string]interface{}{ m := map[string]interface{}{
"type": t, "type": t,
"group": g.groupName, "group": g.groupName,
"class": g.reconcilerClass, "classes": g.reconcilerClasses,
"hasClass": g.hasReconcilerClass, "hasClass": g.hasReconcilerClass,
"hasStatus": g.hasStatus, "hasStatus": g.hasStatus,
"controllerImpl": c.Universe.Type(types.Name{ "controllerImpl": c.Universe.Type(types.Name{
@ -195,12 +195,24 @@ var reconcilerControllerNewImpl = `
const ( const (
defaultControllerAgentName = "{{.type|lowercaseSingular}}-controller" defaultControllerAgentName = "{{.type|lowercaseSingular}}-controller"
defaultFinalizerName = "{{.type|allLowercasePlural}}.{{.group}}" defaultFinalizerName = "{{.type|allLowercasePlural}}.{{.group}}"
{{if .hasClass}} {{if .hasClass }}
// ClassAnnotationKey points to the annotation for the class of this resource. // ClassAnnotationKey points to the annotation for the class of this resource.
ClassAnnotationKey = "{{ .class }}" {{if gt (.classes | len) 1 }}// Deprecated: Use ClassAnnotationKeys given multiple keys exist
{{end -}}
ClassAnnotationKey = "{{ index .classes 0 }}"
{{end}} {{end}}
) )
{{if gt (.classes | len) 1 }}
var (
// ClassAnnotationKeys points to the annotation for the class of this resource.
ClassAnnotationKeys = []string{
{{range $class := .classes}}"{{$class}}",
{{end}}
}
)
{{end}}
// NewImpl returns a {{.controllerImpl|raw}} that handles queuing and feeding work from // NewImpl returns a {{.controllerImpl|raw}} that handles queuing and feeding work from
// the queue through an implementation of {{.controllerReconciler|raw}}, delegating to // the queue through an implementation of {{.controllerReconciler|raw}}, delegating to
// the provided Interface and optional Finalizer methods. OptionsFn is used to return // the provided Interface and optional Finalizer methods. OptionsFn is used to return

View File

@ -35,7 +35,7 @@ type reconcilerControllerStubGenerator struct {
reconcilerPkg string reconcilerPkg string
informerPackagePath string informerPackagePath string
reconcilerClass string reconcilerClasses []string
hasReconcilerClass bool hasReconcilerClass bool
} }
@ -64,7 +64,7 @@ func (g *reconcilerControllerStubGenerator) GenerateType(c *generator.Context, t
m := map[string]interface{}{ m := map[string]interface{}{
"type": t, "type": t,
"class": g.reconcilerClass, "classes": g.reconcilerClasses,
"hasClass": g.hasReconcilerClass, "hasClass": g.hasReconcilerClass,
"informerGet": c.Universe.Function(types.Name{ "informerGet": c.Universe.Function(types.Name{
Package: g.informerPackagePath, Package: g.informerPackagePath,
@ -91,10 +91,18 @@ func (g *reconcilerControllerStubGenerator) GenerateType(c *generator.Context, t
Package: g.reconcilerPkg, Package: g.reconcilerPkg,
Name: "ClassAnnotationKey", Name: "ClassAnnotationKey",
}), }),
"classAnnotationKeys": c.Universe.Variable(types.Name{
Package: g.reconcilerPkg,
Name: "ClassAnnotationKeys",
}),
"annotationFilterFunc": c.Universe.Function(types.Name{ "annotationFilterFunc": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/reconciler", Package: "knative.dev/pkg/reconciler",
Name: "AnnotationFilterFunc", Name: "AnnotationFilterFunc",
}), }),
"orFilterFunc": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "Or",
}),
"filterHandler": c.Universe.Type(types.Name{ "filterHandler": c.Universe.Type(types.Name{
Package: "k8s.io/client-go/tools/cache", Package: "k8s.io/client-go/tools/cache",
Name: "FilteringResourceEventHandler", Name: "FilteringResourceEventHandler",
@ -120,7 +128,17 @@ func NewController(
{{if .hasClass}} {{if .hasClass}}
classValue := "default" // TODO: update this to the appropriate value. classValue := "default" // TODO: update this to the appropriate value.
{{if len .classes | eq 1 }}
classFilter := {{.annotationFilterFunc|raw}}({{.classAnnotationKey|raw}}, classValue, false /*allowUnset*/) classFilter := {{.annotationFilterFunc|raw}}({{.classAnnotationKey|raw}}, classValue, false /*allowUnset*/)
{{else if gt (len .classes) 1 }}
filterFuncs := make([]func(interface{}) bool, 0, len({{.classAnnotationKeys|raw}}))
for _, key := range {{.classAnnotationKeys|raw}} {
filterFuncs = append(filterFuncs, {{.annotationFilterFunc|raw}}(key, classValue, false /*allowUnset*/))
}
classFilter := {{.orFilterFunc|raw}}(filterFuncs...)
{{end}}
{{end}} {{end}}
// TODO: setup additional informers here. // TODO: setup additional informers here.

View File

@ -37,7 +37,7 @@ type reconcilerReconcilerGenerator struct {
listerName string listerName string
listerPkg string listerPkg string
reconcilerClass string reconcilerClasses []string
hasReconcilerClass bool hasReconcilerClass bool
nonNamespaced bool nonNamespaced bool
isKRShaped bool isKRShaped bool
@ -74,7 +74,7 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty
"type": t, "type": t,
"group": namer.IC(g.groupGoName), "group": namer.IC(g.groupGoName),
"version": namer.IC(g.groupVersion.Version.String()), "version": namer.IC(g.groupVersion.Version.String()),
"class": g.reconcilerClass, "classes": g.reconcilerClasses,
"hasClass": g.hasReconcilerClass, "hasClass": g.hasReconcilerClass,
"isKRShaped": g.isKRShaped, "isKRShaped": g.isKRShaped,
"hasStatus": g.hasStatus, "hasStatus": g.hasStatus,
@ -219,6 +219,9 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty
sw.Do(reconcilerInterfaceFactory, m) sw.Do(reconcilerInterfaceFactory, m)
sw.Do(reconcilerNewReconciler, m) sw.Do(reconcilerNewReconciler, m)
sw.Do(reconcilerImplFactory, m) sw.Do(reconcilerImplFactory, m)
if len(g.reconcilerClasses) > 1 {
sw.Do(reconcilerLookupClass, m)
}
if g.hasStatus { if g.hasStatus {
sw.Do(reconcilerStatusFactory, m) sw.Do(reconcilerStatusFactory, m)
} }
@ -227,6 +230,17 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty
return sw.Error() return sw.Error()
} }
var reconcilerLookupClass = `
func lookupClass(annotations map[string]string) (string, bool) {
for _, key := range ClassAnnotationKeys {
if val, ok := annotations[key]; ok {
return val, true
}
}
return "", false
}
`
var reconcilerInterfaceFactory = ` var reconcilerInterfaceFactory = `
// Interface defines the strongly typed interfaces to be implemented by a // Interface defines the strongly typed interfaces to be implemented by a
// controller reconciling {{.type|raw}}. // controller reconciling {{.type|raw}}.
@ -293,8 +307,15 @@ type reconcilerImpl struct {
skipStatusUpdates bool skipStatusUpdates bool
{{end}} {{end}}
{{if .hasClass}} {{if len .classes | eq 1 }}
// classValue is the resource annotation[{{ .class }}] instance value this reconciler instance filters on. // classValue is the resource annotation[{{ index .classes 0 }}] instance value this reconciler instance filters on.
classValue string
{{else if gt (len .classes) 1 }}
// classValue is the resource annotation instance value this reconciler instance filters on.
// The annotations key are:
{{- range $class := .classes}}
// {{$class}}
{{- end}}
classValue string classValue string
{{end}} {{end}}
} }
@ -416,14 +437,22 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
} else if err != nil { } else if err != nil {
return err return err
} }
{{if .hasClass}}
{{if len .classes | eq 1 }}
if classValue, found := original.GetAnnotations()[ClassAnnotationKey]; !found || classValue != r.classValue { if classValue, found := original.GetAnnotations()[ClassAnnotationKey]; !found || classValue != r.classValue {
logger.Debugw("Skip reconciling resource, class annotation value does not match reconciler instance value.", logger.Debugw("Skip reconciling resource, class annotation value does not match reconciler instance value.",
zap.String("classKey", ClassAnnotationKey), zap.String("classKey", ClassAnnotationKey),
zap.String("issue", classValue+"!="+r.classValue)) zap.String("issue", classValue+"!="+r.classValue))
return nil return nil
} }
{{end}} {{else if gt (len .classes) 1 }}
if classValue, found := lookupClass(original.GetAnnotations()); !found || classValue != r.classValue {
logger.Debugw("Skip reconciling resource, class annotation value does not match reconciler instance value.",
zap.Strings("classKeys", ClassAnnotationKeys),
zap.String("issue", classValue+"!="+r.classValue))
return nil
}
{{end}}
// Don't modify the informers copy. // Don't modify the informers copy.
resource := original.DeepCopy() resource := original.DeepCopy()

View File

@ -77,6 +77,18 @@ func Not(f func(interface{}) bool) func(interface{}) bool {
} }
} }
// Or creates a FilterFunc which performs an OR of the passed in FilterFuncs
func Or(funcs ...func(interface{}) bool) func(interface{}) bool {
return func(obj interface{}) bool {
for _, f := range funcs {
if f(obj) {
return true
}
}
return false
}
}
// ChainFilterFuncs creates a FilterFunc which performs an AND of the passed FilterFuncs. // ChainFilterFuncs creates a FilterFunc which performs an AND of the passed FilterFuncs.
func ChainFilterFuncs(funcs ...func(interface{}) bool) func(interface{}) bool { func ChainFilterFuncs(funcs ...func(interface{}) bool) func(interface{}) bool {
return func(obj interface{}) bool { return func(obj interface{}) bool {

4
vendor/modules.txt vendored
View File

@ -608,10 +608,10 @@ k8s.io/kube-openapi/pkg/util/sets
k8s.io/utils/buffer k8s.io/utils/buffer
k8s.io/utils/integer k8s.io/utils/integer
k8s.io/utils/trace k8s.io/utils/trace
# knative.dev/hack v0.0.0-20211112192837-128cf0150a69 # knative.dev/hack v0.0.0-20211117134436-69a2295d54ce
## explicit ## explicit
knative.dev/hack knative.dev/hack
# knative.dev/pkg v0.0.0-20211116213053-d82be484e4c3 # knative.dev/pkg v0.0.0-20211117215328-5708c4c44232
## explicit ## explicit
knative.dev/pkg/apis knative.dev/pkg/apis
knative.dev/pkg/apis/duck knative.dev/pkg/apis/duck