mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-07 19:23:40 +00:00
Merge pull request #118942 from justinsb/switch_kubectl_prune_annotation
kubectl prune v2: switch to contains-group-kinds annotation
This commit is contained in:
commit
963400f1a2
@ -2403,7 +2403,7 @@ kind: Secret
|
|||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
applyset.kubernetes.io/additional-namespaces: ""
|
applyset.kubernetes.io/additional-namespaces: ""
|
||||||
applyset.kubernetes.io/contains-group-resources: replicationcontrollers
|
applyset.kubernetes.io/contains-group-kinds: ReplicationController
|
||||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
@ -2437,7 +2437,7 @@ kind: Secret
|
|||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
applyset.kubernetes.io/additional-namespaces: ""
|
applyset.kubernetes.io/additional-namespaces: ""
|
||||||
applyset.kubernetes.io/contains-group-resources: replicationcontrollers,services
|
applyset.kubernetes.io/contains-group-kinds: ReplicationController,Service
|
||||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
@ -2472,7 +2472,7 @@ kind: Secret
|
|||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
applyset.kubernetes.io/additional-namespaces: ""
|
applyset.kubernetes.io/additional-namespaces: ""
|
||||||
applyset.kubernetes.io/contains-group-resources: replicationcontrollers,services
|
applyset.kubernetes.io/contains-group-kinds: ReplicationController,Service
|
||||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
@ -2507,7 +2507,7 @@ kind: Secret
|
|||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
applyset.kubernetes.io/additional-namespaces: ""
|
applyset.kubernetes.io/additional-namespaces: ""
|
||||||
applyset.kubernetes.io/contains-group-resources: services
|
applyset.kubernetes.io/contains-group-kinds: Service
|
||||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
@ -2524,60 +2524,60 @@ func TestApplySetInvalidLiveParent(t *testing.T) {
|
|||||||
defer tf.Cleanup()
|
defer tf.Cleanup()
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
grsAnnotation string
|
gksAnnotation string
|
||||||
toolingAnnotation string
|
toolingAnnotation string
|
||||||
idLabel string
|
idLabel string
|
||||||
expectErr string
|
expectErr string
|
||||||
}
|
}
|
||||||
validIDLabel := "applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1"
|
validIDLabel := "applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1"
|
||||||
validToolingAnnotation := "kubectl/v1.27.0"
|
validToolingAnnotation := "kubectl/v1.27.0"
|
||||||
validGrsAnnotation := "deployments.apps,namespaces,secrets"
|
validGksAnnotation := "Deployment.apps,Namespace,Secret"
|
||||||
|
|
||||||
for name, test := range map[string]testCase{
|
for name, test := range map[string]testCase{
|
||||||
"group-resources annotation is required": {
|
"group-resources annotation is required": {
|
||||||
grsAnnotation: "",
|
gksAnnotation: "",
|
||||||
toolingAnnotation: validToolingAnnotation,
|
toolingAnnotation: validToolingAnnotation,
|
||||||
idLabel: validIDLabel,
|
idLabel: validIDLabel,
|
||||||
expectErr: "error: parsing ApplySet annotation on \"secrets./my-set\": kubectl requires the \"applyset.kubernetes.io/contains-group-resources\" annotation to be set on all ApplySet parent objects",
|
expectErr: "error: parsing ApplySet annotation on \"secrets./my-set\": kubectl requires the \"applyset.kubernetes.io/contains-group-kinds\" annotation to be set on all ApplySet parent objects",
|
||||||
},
|
},
|
||||||
"group-resources annotation should not contain invalid resources": {
|
"group-resources annotation should not contain invalid resources": {
|
||||||
grsAnnotation: "does-not-exist",
|
gksAnnotation: "does-not-exist",
|
||||||
toolingAnnotation: validToolingAnnotation,
|
toolingAnnotation: validToolingAnnotation,
|
||||||
idLabel: validIDLabel,
|
idLabel: validIDLabel,
|
||||||
expectErr: "error: parsing ApplySet annotation on \"secrets./my-set\": invalid group resource in \"applyset.kubernetes.io/contains-group-resources\" annotation: no matches for /, Resource=does-not-exist",
|
expectErr: "error: parsing ApplySet annotation on \"secrets./my-set\": could not find mapping for kind in \"applyset.kubernetes.io/contains-group-kinds\" annotation: no matches for kind \"does-not-exist\" in group \"\"",
|
||||||
},
|
},
|
||||||
"tooling annotation is required": {
|
"tooling annotation is required": {
|
||||||
grsAnnotation: validGrsAnnotation,
|
gksAnnotation: validGksAnnotation,
|
||||||
toolingAnnotation: "",
|
toolingAnnotation: "",
|
||||||
idLabel: validIDLabel,
|
idLabel: validIDLabel,
|
||||||
expectErr: "error: ApplySet parent object \"secrets./my-set\" already exists and is missing required annotation \"applyset.kubernetes.io/tooling\"",
|
expectErr: "error: ApplySet parent object \"secrets./my-set\" already exists and is missing required annotation \"applyset.kubernetes.io/tooling\"",
|
||||||
},
|
},
|
||||||
"tooling annotation must have kubectl prefix": {
|
"tooling annotation must have kubectl prefix": {
|
||||||
grsAnnotation: validGrsAnnotation,
|
gksAnnotation: validGksAnnotation,
|
||||||
toolingAnnotation: "helm/v3",
|
toolingAnnotation: "helm/v3",
|
||||||
idLabel: validIDLabel,
|
idLabel: validIDLabel,
|
||||||
expectErr: "error: ApplySet parent object \"secrets./my-set\" already exists and is managed by tooling \"helm\" instead of \"kubectl\"",
|
expectErr: "error: ApplySet parent object \"secrets./my-set\" already exists and is managed by tooling \"helm\" instead of \"kubectl\"",
|
||||||
},
|
},
|
||||||
"tooling annotation with invalid prefix with one segment can be parsed": {
|
"tooling annotation with invalid prefix with one segment can be parsed": {
|
||||||
grsAnnotation: validGrsAnnotation,
|
gksAnnotation: validGksAnnotation,
|
||||||
toolingAnnotation: "helm",
|
toolingAnnotation: "helm",
|
||||||
idLabel: validIDLabel,
|
idLabel: validIDLabel,
|
||||||
expectErr: "error: ApplySet parent object \"secrets./my-set\" already exists and is managed by tooling \"helm\" instead of \"kubectl\"",
|
expectErr: "error: ApplySet parent object \"secrets./my-set\" already exists and is managed by tooling \"helm\" instead of \"kubectl\"",
|
||||||
},
|
},
|
||||||
"tooling annotation with invalid prefix with many segments can be parsed": {
|
"tooling annotation with invalid prefix with many segments can be parsed": {
|
||||||
grsAnnotation: validGrsAnnotation,
|
gksAnnotation: validGksAnnotation,
|
||||||
toolingAnnotation: "example.com/tool/why/v1",
|
toolingAnnotation: "example.com/tool/why/v1",
|
||||||
idLabel: validIDLabel,
|
idLabel: validIDLabel,
|
||||||
expectErr: "error: ApplySet parent object \"secrets./my-set\" already exists and is managed by tooling \"example.com/tool/why\" instead of \"kubectl\"",
|
expectErr: "error: ApplySet parent object \"secrets./my-set\" already exists and is managed by tooling \"example.com/tool/why\" instead of \"kubectl\"",
|
||||||
},
|
},
|
||||||
"ID label is required": {
|
"ID label is required": {
|
||||||
grsAnnotation: validGrsAnnotation,
|
gksAnnotation: validGksAnnotation,
|
||||||
toolingAnnotation: validToolingAnnotation,
|
toolingAnnotation: validToolingAnnotation,
|
||||||
idLabel: "",
|
idLabel: "",
|
||||||
expectErr: "error: ApplySet parent object \"secrets./my-set\" exists and does not have required label applyset.kubernetes.io/id",
|
expectErr: "error: ApplySet parent object \"secrets./my-set\" exists and does not have required label applyset.kubernetes.io/id",
|
||||||
},
|
},
|
||||||
"ID label must match the ApplySet's real ID": {
|
"ID label must match the ApplySet's real ID": {
|
||||||
grsAnnotation: validGrsAnnotation,
|
gksAnnotation: validGksAnnotation,
|
||||||
toolingAnnotation: validToolingAnnotation,
|
toolingAnnotation: validToolingAnnotation,
|
||||||
idLabel: "somethingelse",
|
idLabel: "somethingelse",
|
||||||
expectErr: fmt.Sprintf("error: ApplySet parent object \"secrets./my-set\" exists and has incorrect value for label \"applyset.kubernetes.io/id\" (got: somethingelse, want: %s)", validIDLabel),
|
expectErr: fmt.Sprintf("error: ApplySet parent object \"secrets./my-set\" exists and has incorrect value for label \"applyset.kubernetes.io/id\" (got: somethingelse, want: %s)", validIDLabel),
|
||||||
@ -2596,8 +2596,8 @@ func TestApplySetInvalidLiveParent(t *testing.T) {
|
|||||||
secret.SetNamespace("test")
|
secret.SetNamespace("test")
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
labels := make(map[string]string)
|
labels := make(map[string]string)
|
||||||
if test.grsAnnotation != "" {
|
if test.gksAnnotation != "" {
|
||||||
annotations[ApplySetGRsAnnotation] = test.grsAnnotation
|
annotations[ApplySetGKsAnnotation] = test.gksAnnotation
|
||||||
}
|
}
|
||||||
if test.toolingAnnotation != "" {
|
if test.toolingAnnotation != "" {
|
||||||
annotations[ApplySetToolingAnnotation] = test.toolingAnnotation
|
annotations[ApplySetToolingAnnotation] = test.toolingAnnotation
|
||||||
@ -2670,7 +2670,7 @@ kind: ApplySet
|
|||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
applyset.kubernetes.io/additional-namespaces: test
|
applyset.kubernetes.io/additional-namespaces: test
|
||||||
applyset.kubernetes.io/contains-group-resources: replicationcontrollers
|
applyset.kubernetes.io/contains-group-kinds: ReplicationController
|
||||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
|
@ -54,13 +54,22 @@ const (
|
|||||||
// Example value: "kube-system,ns1,ns2".
|
// Example value: "kube-system,ns1,ns2".
|
||||||
ApplySetAdditionalNamespacesAnnotation = "applyset.kubernetes.io/additional-namespaces"
|
ApplySetAdditionalNamespacesAnnotation = "applyset.kubernetes.io/additional-namespaces"
|
||||||
|
|
||||||
// ApplySetGRsAnnotation is a list of group-resources used to optimize listing of ApplySet member objects.
|
// Deprecated: ApplySetGRsAnnotation is a list of group-resources used to optimize listing of ApplySet member objects.
|
||||||
|
// It is optional in the ApplySet specification, as tools can perform discovery or use a different optimization.
|
||||||
|
// However, it is currently required in kubectl.
|
||||||
|
// When present, the value of this annotation must be a comma separated list of the group-resources,
|
||||||
|
// in the fully-qualified name format, i.e. <resourcename>.<group>.
|
||||||
|
// Example value: "certificates.cert-manager.io,configmaps,deployments.apps,secrets,services"
|
||||||
|
// Deprecated and replaced by ApplySetGKsAnnotation, support for this can be removed in applyset beta or GA.
|
||||||
|
DeprecatedApplySetGRsAnnotation = "applyset.kubernetes.io/contains-group-resources"
|
||||||
|
|
||||||
|
// ApplySetGKsAnnotation is a list of group-kinds used to optimize listing of ApplySet member objects.
|
||||||
// It is optional in the ApplySet specification, as tools can perform discovery or use a different optimization.
|
// It is optional in the ApplySet specification, as tools can perform discovery or use a different optimization.
|
||||||
// However, it is currently required in kubectl.
|
// However, it is currently required in kubectl.
|
||||||
// When present, the value of this annotation must be a comma separated list of the group-kinds,
|
// When present, the value of this annotation must be a comma separated list of the group-kinds,
|
||||||
// in the fully-qualified name format, i.e. <resourcename>.<group>.
|
// in the fully-qualified name format, i.e. <kind>.<group>.
|
||||||
// Example value: "certificates.cert-manager.io,configmaps,deployments.apps,secrets,services"
|
// Example value: "Certificate.cert-manager.io,ConfigMap,deployments.apps,Secret,Service"
|
||||||
ApplySetGRsAnnotation = "applyset.kubernetes.io/contains-group-resources"
|
ApplySetGKsAnnotation = "applyset.kubernetes.io/contains-group-kinds"
|
||||||
|
|
||||||
// ApplySetParentIDLabel is the key of the label that makes object an ApplySet parent object.
|
// ApplySetParentIDLabel is the key of the label that makes object an ApplySet parent object.
|
||||||
// Its value MUST use the format specified in V1ApplySetIdFormat below
|
// Its value MUST use the format specified in V1ApplySetIdFormat below
|
||||||
@ -92,13 +101,13 @@ type ApplySet struct {
|
|||||||
toolingID ApplySetTooling
|
toolingID ApplySetTooling
|
||||||
|
|
||||||
// currentResources is the set of resources that are part of the sever-side set as of when the current operation started.
|
// currentResources is the set of resources that are part of the sever-side set as of when the current operation started.
|
||||||
currentResources map[schema.GroupVersionResource]*meta.RESTMapping
|
currentResources map[schema.GroupKind]*kindInfo
|
||||||
|
|
||||||
// currentNamespaces is the set of namespaces that contain objects in this applyset as of when the current operation started.
|
// currentNamespaces is the set of namespaces that contain objects in this applyset as of when the current operation started.
|
||||||
currentNamespaces sets.Set[string]
|
currentNamespaces sets.Set[string]
|
||||||
|
|
||||||
// updatedResources is the set of resources that will be part of the set as of when the current operation completes.
|
// updatedResources is the set of resources that will be part of the set as of when the current operation completes.
|
||||||
updatedResources map[schema.GroupVersionResource]*meta.RESTMapping
|
updatedResources map[schema.GroupKind]*kindInfo
|
||||||
|
|
||||||
// updatedNamespaces is the set of namespaces that will contain objects in this applyset as of when the current operation completes.
|
// updatedNamespaces is the set of namespaces that will contain objects in this applyset as of when the current operation completes.
|
||||||
updatedNamespaces sets.Set[string]
|
updatedNamespaces sets.Set[string]
|
||||||
@ -143,9 +152,9 @@ func (t ApplySetTooling) String() string {
|
|||||||
// NewApplySet creates a new ApplySet object tracked by the given parent object.
|
// NewApplySet creates a new ApplySet object tracked by the given parent object.
|
||||||
func NewApplySet(parent *ApplySetParentRef, tooling ApplySetTooling, mapper meta.RESTMapper, client resource.RESTClient) *ApplySet {
|
func NewApplySet(parent *ApplySetParentRef, tooling ApplySetTooling, mapper meta.RESTMapper, client resource.RESTClient) *ApplySet {
|
||||||
return &ApplySet{
|
return &ApplySet{
|
||||||
currentResources: make(map[schema.GroupVersionResource]*meta.RESTMapping),
|
currentResources: make(map[schema.GroupKind]*kindInfo),
|
||||||
currentNamespaces: make(sets.Set[string]),
|
currentNamespaces: make(sets.Set[string]),
|
||||||
updatedResources: make(map[schema.GroupVersionResource]*meta.RESTMapping),
|
updatedResources: make(map[schema.GroupKind]*kindInfo),
|
||||||
updatedNamespaces: make(sets.Set[string]),
|
updatedNamespaces: make(sets.Set[string]),
|
||||||
parentRef: parent,
|
parentRef: parent,
|
||||||
toolingID: tooling,
|
toolingID: tooling,
|
||||||
@ -284,7 +293,7 @@ func (a *ApplySet) fetchParent() error {
|
|||||||
return fmt.Errorf("ApplySet parent object %q exists and has incorrect value for label %q (got: %s, want: %s)", a.parentRef, ApplySetParentIDLabel, idLabel, a.ID())
|
return fmt.Errorf("ApplySet parent object %q exists and has incorrect value for label %q (got: %s, want: %s)", a.parentRef, ApplySetParentIDLabel, idLabel, a.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.currentResources, err = parseResourcesAnnotation(annotations, a.restMapper); err != nil {
|
if a.currentResources, err = parseKindAnnotation(annotations, a.restMapper); err != nil {
|
||||||
// TODO: handle GVRs for now-deleted CRDs
|
// TODO: handle GVRs for now-deleted CRDs
|
||||||
return fmt.Errorf("parsing ApplySet annotation on %q: %w", a.parentRef, err)
|
return fmt.Errorf("parsing ApplySet annotation on %q: %w", a.parentRef, err)
|
||||||
}
|
}
|
||||||
@ -302,8 +311,8 @@ func (a *ApplySet) LabelSelectorForMembers() string {
|
|||||||
|
|
||||||
// AllPrunableResources returns the list of all resources that should be considered for pruning.
|
// AllPrunableResources returns the list of all resources that should be considered for pruning.
|
||||||
// This is potentially a superset of the resources types that actually contain resources.
|
// This is potentially a superset of the resources types that actually contain resources.
|
||||||
func (a *ApplySet) AllPrunableResources() []*meta.RESTMapping {
|
func (a *ApplySet) AllPrunableResources() []*kindInfo {
|
||||||
var ret []*meta.RESTMapping
|
var ret []*kindInfo
|
||||||
for _, m := range a.currentResources {
|
for _, m := range a.currentResources {
|
||||||
ret = append(ret, m)
|
ret = append(ret, m)
|
||||||
}
|
}
|
||||||
@ -336,14 +345,43 @@ func toolingBaseName(toolAnnotation string) string {
|
|||||||
return toolAnnotation
|
return toolAnnotation
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseResourcesAnnotation(annotations map[string]string, mapper meta.RESTMapper) (map[schema.GroupVersionResource]*meta.RESTMapping, error) {
|
// kindInfo holds type information about a particular resource type.
|
||||||
annotation, ok := annotations[ApplySetGRsAnnotation]
|
type kindInfo struct {
|
||||||
|
restMapping *meta.RESTMapping
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseKindAnnotation(annotations map[string]string, mapper meta.RESTMapper) (map[schema.GroupKind]*kindInfo, error) {
|
||||||
|
annotation, ok := annotations[ApplySetGKsAnnotation]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if annotations[DeprecatedApplySetGRsAnnotation] != "" {
|
||||||
|
return parseDeprecatedResourceAnnotation(annotations[DeprecatedApplySetGRsAnnotation], mapper)
|
||||||
|
}
|
||||||
|
|
||||||
// The spec does not require this annotation. However, 'missing' means 'perform discovery'.
|
// The spec does not require this annotation. However, 'missing' means 'perform discovery'.
|
||||||
// We return an error because we do not currently support dynamic discovery in kubectl apply.
|
// We return an error because we do not currently support dynamic discovery in kubectl apply.
|
||||||
return nil, fmt.Errorf("kubectl requires the %q annotation to be set on all ApplySet parent objects", ApplySetGRsAnnotation)
|
return nil, fmt.Errorf("kubectl requires the %q annotation to be set on all ApplySet parent objects", ApplySetGKsAnnotation)
|
||||||
}
|
}
|
||||||
mappings := make(map[schema.GroupVersionResource]*meta.RESTMapping)
|
mappings := make(map[schema.GroupKind]*kindInfo)
|
||||||
|
// Annotation present but empty means that this is currently an empty set.
|
||||||
|
if annotation == "" {
|
||||||
|
return mappings, nil
|
||||||
|
}
|
||||||
|
for _, gkString := range strings.Split(annotation, ",") {
|
||||||
|
gk := schema.ParseGroupKind(gkString)
|
||||||
|
restMapping, err := mapper.RESTMapping(gk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not find mapping for kind in %q annotation: %w", ApplySetGKsAnnotation, err)
|
||||||
|
}
|
||||||
|
mappings[gk] = &kindInfo{
|
||||||
|
restMapping: restMapping,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDeprecatedResourceAnnotation(annotation string, mapper meta.RESTMapper) (map[schema.GroupKind]*kindInfo, error) {
|
||||||
|
mappings := make(map[schema.GroupKind]*kindInfo)
|
||||||
// Annotation present but empty means that this is currently an empty set.
|
// Annotation present but empty means that this is currently an empty set.
|
||||||
if annotation == "" {
|
if annotation == "" {
|
||||||
return mappings, nil
|
return mappings, nil
|
||||||
@ -352,13 +390,15 @@ func parseResourcesAnnotation(annotations map[string]string, mapper meta.RESTMap
|
|||||||
gr := schema.ParseGroupResource(grString)
|
gr := schema.ParseGroupResource(grString)
|
||||||
gvk, err := mapper.KindFor(gr.WithVersion(""))
|
gvk, err := mapper.KindFor(gr.WithVersion(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid group resource in %q annotation: %w", ApplySetGRsAnnotation, err)
|
return nil, fmt.Errorf("invalid group resource in %q annotation: %w", DeprecatedApplySetGRsAnnotation, err)
|
||||||
}
|
}
|
||||||
mapping, err := mapper.RESTMapping(gvk.GroupKind())
|
restMapping, err := mapper.RESTMapping(gvk.GroupKind())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not find kind for resource in %q annotation: %w", ApplySetGRsAnnotation, err)
|
return nil, fmt.Errorf("could not find kind for resource in %q annotation: %w", DeprecatedApplySetGRsAnnotation, err)
|
||||||
|
}
|
||||||
|
mappings[gvk.GroupKind()] = &kindInfo{
|
||||||
|
restMapping: restMapping,
|
||||||
}
|
}
|
||||||
mappings[mapping.Resource] = mapping
|
|
||||||
}
|
}
|
||||||
return mappings, nil
|
return mappings, nil
|
||||||
}
|
}
|
||||||
@ -377,9 +417,14 @@ func parseNamespacesAnnotation(annotations map[string]string) sets.Set[string] {
|
|||||||
|
|
||||||
// addResource registers the given resource and namespace as being part of the updated set of
|
// addResource registers the given resource and namespace as being part of the updated set of
|
||||||
// resources being applied by the current operation.
|
// resources being applied by the current operation.
|
||||||
func (a *ApplySet) addResource(resource *meta.RESTMapping, namespace string) {
|
func (a *ApplySet) addResource(restMapping *meta.RESTMapping, namespace string) {
|
||||||
a.updatedResources[resource.Resource] = resource
|
gk := restMapping.GroupVersionKind.GroupKind()
|
||||||
if resource.Scope == meta.RESTScopeNamespace && namespace != "" {
|
if _, found := a.updatedResources[gk]; !found {
|
||||||
|
a.updatedResources[gk] = &kindInfo{
|
||||||
|
restMapping: restMapping,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if restMapping.Scope == meta.RESTScopeNamespace && namespace != "" {
|
||||||
a.updatedNamespaces.Insert(namespace)
|
a.updatedNamespaces.Insert(namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,6 +439,8 @@ func (a *ApplySet) updateParent(mode ApplySetUpdateMode, dryRun cmdutil.DryRunSt
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to encode patch for ApplySet parent: %w", err)
|
return fmt.Errorf("failed to encode patch for ApplySet parent: %w", err)
|
||||||
}
|
}
|
||||||
|
// Note that because we are using SSA, we will remove any annotations we don't specify,
|
||||||
|
// which is how we remove the deprecated contains-group-resources annotation.
|
||||||
err = serverSideApplyRequest(a, data, dryRun, validation, false)
|
err = serverSideApplyRequest(a, data, dryRun, validation, false)
|
||||||
if err != nil && errors.IsConflict(err) {
|
if err != nil && errors.IsConflict(err) {
|
||||||
// Try again with conflicts forced
|
// Try again with conflicts forced
|
||||||
@ -429,17 +476,17 @@ func serverSideApplyRequest(a *ApplySet, data []byte, dryRun cmdutil.DryRunStrat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *ApplySet) buildParentPatch(mode ApplySetUpdateMode) *metav1.PartialObjectMetadata {
|
func (a *ApplySet) buildParentPatch(mode ApplySetUpdateMode) *metav1.PartialObjectMetadata {
|
||||||
var newGRsAnnotation, newNsAnnotation string
|
var newGKsAnnotation, newNsAnnotation string
|
||||||
switch mode {
|
switch mode {
|
||||||
case updateToSuperset:
|
case updateToSuperset:
|
||||||
// If the apply succeeded but pruning failed, the set of group resources that
|
// If the apply succeeded but pruning failed, the set of group resources that
|
||||||
// the ApplySet should track is the superset of the previous and current resources.
|
// the ApplySet should track is the superset of the previous and current resources.
|
||||||
// This ensures that the resources that failed to be pruned are not orphaned from the set.
|
// This ensures that the resources that failed to be pruned are not orphaned from the set.
|
||||||
grSuperset := sets.KeySet(a.currentResources).Union(sets.KeySet(a.updatedResources))
|
grSuperset := sets.KeySet(a.currentResources).Union(sets.KeySet(a.updatedResources))
|
||||||
newGRsAnnotation = generateResourcesAnnotation(grSuperset)
|
newGKsAnnotation = generateKindsAnnotation(grSuperset)
|
||||||
newNsAnnotation = generateNamespacesAnnotation(a.currentNamespaces.Union(a.updatedNamespaces), a.parentRef.Namespace)
|
newNsAnnotation = generateNamespacesAnnotation(a.currentNamespaces.Union(a.updatedNamespaces), a.parentRef.Namespace)
|
||||||
case updateToLatestSet:
|
case updateToLatestSet:
|
||||||
newGRsAnnotation = generateResourcesAnnotation(sets.KeySet(a.updatedResources))
|
newGKsAnnotation = generateKindsAnnotation(sets.KeySet(a.updatedResources))
|
||||||
newNsAnnotation = generateNamespacesAnnotation(a.updatedNamespaces, a.parentRef.Namespace)
|
newNsAnnotation = generateNamespacesAnnotation(a.updatedNamespaces, a.parentRef.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,7 +500,7 @@ func (a *ApplySet) buildParentPatch(mode ApplySetUpdateMode) *metav1.PartialObje
|
|||||||
Namespace: a.parentRef.Namespace,
|
Namespace: a.parentRef.Namespace,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
ApplySetToolingAnnotation: a.toolingID.String(),
|
ApplySetToolingAnnotation: a.toolingID.String(),
|
||||||
ApplySetGRsAnnotation: newGRsAnnotation,
|
ApplySetGKsAnnotation: newGKsAnnotation,
|
||||||
ApplySetAdditionalNamespacesAnnotation: newNsAnnotation,
|
ApplySetAdditionalNamespacesAnnotation: newNsAnnotation,
|
||||||
},
|
},
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@ -469,13 +516,13 @@ func generateNamespacesAnnotation(namespaces sets.Set[string], skip string) stri
|
|||||||
return strings.Join(nsList, ",")
|
return strings.Join(nsList, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateResourcesAnnotation(resources sets.Set[schema.GroupVersionResource]) string {
|
func generateKindsAnnotation(resources sets.Set[schema.GroupKind]) string {
|
||||||
var grs []string
|
var gks []string
|
||||||
for gvr := range resources {
|
for gk := range resources {
|
||||||
grs = append(grs, gvr.GroupResource().String())
|
gks = append(gks, gk.String())
|
||||||
}
|
}
|
||||||
sort.Strings(grs)
|
sort.Strings(gks)
|
||||||
return strings.Join(grs, ",")
|
return strings.Join(gks, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a ApplySet) FieldManager() string {
|
func (a ApplySet) FieldManager() string {
|
||||||
|
@ -77,27 +77,29 @@ func (a *ApplySet) FindAllObjectsToPrune(ctx context.Context, dynamicClient dyna
|
|||||||
|
|
||||||
// We run discovery in parallel, in as many goroutines as priority and fairness will allow
|
// We run discovery in parallel, in as many goroutines as priority and fairness will allow
|
||||||
// (We don't expect many requests in real-world scenarios - maybe tens, unlikely to be hundreds)
|
// (We don't expect many requests in real-world scenarios - maybe tens, unlikely to be hundreds)
|
||||||
for _, restMapping := range a.AllPrunableResources() {
|
for gvk, resource := range a.AllPrunableResources() {
|
||||||
switch restMapping.Scope.Name() {
|
scope := resource.restMapping.Scope
|
||||||
|
|
||||||
|
switch scope.Name() {
|
||||||
case meta.RESTScopeNameNamespace:
|
case meta.RESTScopeNameNamespace:
|
||||||
for _, namespace := range a.AllPrunableNamespaces() {
|
for _, namespace := range a.AllPrunableNamespaces() {
|
||||||
if namespace == "" {
|
if namespace == "" {
|
||||||
// Just double-check because otherwise we get cryptic error messages
|
// Just double-check because otherwise we get cryptic error messages
|
||||||
return nil, fmt.Errorf("unexpectedly encountered empty namespace during prune of namespace-scoped resource %v", restMapping.GroupVersionKind)
|
return nil, fmt.Errorf("unexpectedly encountered empty namespace during prune of namespace-scoped resource %v", gvk)
|
||||||
}
|
}
|
||||||
tasks = append(tasks, &task{
|
tasks = append(tasks, &task{
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
restMapping: restMapping,
|
restMapping: resource.restMapping,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
case meta.RESTScopeNameRoot:
|
case meta.RESTScopeNameRoot:
|
||||||
tasks = append(tasks, &task{
|
tasks = append(tasks, &task{
|
||||||
restMapping: restMapping,
|
restMapping: resource.restMapping,
|
||||||
})
|
})
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unhandled scope %q", restMapping.Scope.Name())
|
return nil, fmt.Errorf("unhandled scope %q", scope.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,6 @@ metadata:
|
|||||||
name: my-set
|
name: my-set
|
||||||
annotations:
|
annotations:
|
||||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0
|
applyset.kubernetes.io/tooling: kubectl/v0.0.0
|
||||||
applyset.kubernetes.io/contains-group-resources: ""
|
applyset.kubernetes.io/contains-group-kinds: ""
|
||||||
labels:
|
labels:
|
||||||
applyset.kubernetes.io/id: applyset-rhp1a-HVAVT_dFgyEygyA1BEB82HPp2o10UiFTpqtAs-v1
|
applyset.kubernetes.io/id: applyset-rhp1a-HVAVT_dFgyEygyA1BEB82HPp2o10UiFTpqtAs-v1
|
||||||
|
Loading…
Reference in New Issue
Block a user